From ed9a84262a83ab0260325c2f5eae39e441003d55 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 29 Nov 2014 08:30:51 +0100 Subject: drm: tda998x: Protect the page register As the HDMI registers of the TDA998x chips are accessed by pages, the page register must be protected. Signed-off-by: Jean-Francois Moine Signed-off-by: Russell King diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index d476279..6795f09 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -32,6 +32,7 @@ struct tda998x_priv { struct i2c_client *cec; struct i2c_client *hdmi; + struct mutex mutex; uint16_t rev; uint8_t current_page; int dpms; @@ -402,9 +403,10 @@ reg_read_range(struct tda998x_priv *priv, uint16_t reg, char *buf, int cnt) uint8_t addr = REG2ADDR(reg); int ret; + mutex_lock(&priv->mutex); ret = set_page(priv, reg); if (ret < 0) - return ret; + goto out; ret = i2c_master_send(client, &addr, sizeof(addr)); if (ret < 0) @@ -414,10 +416,12 @@ reg_read_range(struct tda998x_priv *priv, uint16_t reg, char *buf, int cnt) if (ret < 0) goto fail; - return ret; + goto out; fail: dev_err(&client->dev, "Error %d reading from 0x%x\n", ret, reg); +out: + mutex_unlock(&priv->mutex); return ret; } @@ -431,13 +435,16 @@ reg_write_range(struct tda998x_priv *priv, uint16_t reg, uint8_t *p, int cnt) buf[0] = REG2ADDR(reg); memcpy(&buf[1], p, cnt); + mutex_lock(&priv->mutex); ret = set_page(priv, reg); if (ret < 0) - return; + goto out; ret = i2c_master_send(client, buf, cnt + 1); if (ret < 0) dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg); +out: + mutex_unlock(&priv->mutex); } static int @@ -459,13 +466,16 @@ reg_write(struct tda998x_priv *priv, uint16_t reg, uint8_t val) uint8_t buf[] = {REG2ADDR(reg), val}; int ret; + mutex_lock(&priv->mutex); ret = set_page(priv, reg); if (ret < 0) - return; + goto out; ret = i2c_master_send(client, buf, sizeof(buf)); if (ret < 0) dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg); +out: + mutex_unlock(&priv->mutex); } static void @@ -475,13 +485,16 @@ reg_write16(struct tda998x_priv *priv, uint16_t reg, uint16_t val) uint8_t buf[] = {REG2ADDR(reg), val >> 8, val}; int ret; + mutex_lock(&priv->mutex); ret = set_page(priv, reg); if (ret < 0) - return; + goto out; ret = i2c_master_send(client, buf, sizeof(buf)); if (ret < 0) dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg); +out: + mutex_unlock(&priv->mutex); } static void @@ -1268,6 +1281,8 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) priv->dpms = DRM_MODE_DPMS_OFF; + mutex_init(&priv->mutex); /* protect the page access */ + /* wake up the device: */ cec_write(priv, REG_CEC_ENAMODS, CEC_ENAMODS_EN_RXSENS | CEC_ENAMODS_EN_HDMI); -- cgit v0.10.2 From 6833d26ef823b20acbf9d4afcfd7077e623b302c Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 29 Nov 2014 08:57:15 +0100 Subject: drm: tda998x: Fix EDID read timeout on HDMI connect When the HDMI cable is disconnected and reconnected, EDID reading is called too early raising a EDID read timeout. This patch uses the system work queue to delay the notification of the HDMI connect/disconnect event. Signed-off-by: Jean-Francois Moine Signed-off-by: Russell King diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index 6795f09..98f3c4d 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -33,6 +33,7 @@ struct tda998x_priv { struct i2c_client *cec; struct i2c_client *hdmi; struct mutex mutex; + struct delayed_work dwork; uint16_t rev; uint8_t current_page; int dpms; @@ -549,6 +550,17 @@ tda998x_reset(struct tda998x_priv *priv) reg_write(priv, REG_MUX_VP_VIP_OUT, 0x24); } +/* handle HDMI connect/disconnect */ +static void tda998x_hpd(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct tda998x_priv *priv = + container_of(dwork, struct tda998x_priv, dwork); + + if (priv->encoder && priv->encoder->dev) + drm_kms_helper_hotplug_event(priv->encoder->dev); +} + /* * only 2 interrupts may occur: screen plug/unplug and EDID read */ @@ -572,8 +584,7 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data) priv->wq_edid_wait = 0; wake_up(&priv->wq_edid); } else if (cec != 0) { /* HPD change */ - if (priv->encoder && priv->encoder->dev) - drm_helper_hpd_irq_event(priv->encoder->dev); + schedule_delayed_work(&priv->dwork, HZ/10); } return IRQ_HANDLED; } @@ -1183,8 +1194,10 @@ static void tda998x_destroy(struct tda998x_priv *priv) /* disable all IRQs and free the IRQ handler */ cec_write(priv, REG_CEC_RXSHPDINTENA, 0); reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); - if (priv->hdmi->irq) + if (priv->hdmi->irq) { free_irq(priv->hdmi->irq, priv); + cancel_delayed_work_sync(&priv->dwork); + } i2c_unregister_device(priv->cec); } @@ -1338,8 +1351,9 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) if (client->irq) { int irqf_trigger; - /* init read EDID waitqueue */ + /* init read EDID waitqueue and HDP work */ init_waitqueue_head(&priv->wq_edid); + INIT_DELAYED_WORK(&priv->dwork, tda998x_hpd); /* clear pending interrupts */ reg_read(priv, REG_INT_FLAGS_0); -- cgit v0.10.2 From d6d55f0b9d900673548515614b56ab55aa2c51f8 Mon Sep 17 00:00:00 2001 From: Jacob Shin Date: Thu, 29 May 2014 17:26:50 +0200 Subject: perf/x86/amd: AMD support for bp_len > HW_BREAKPOINT_LEN_8 Implement hardware breakpoint address mask for AMD Family 16h and above processors. CPUID feature bit indicates hardware support for DRn_ADDR_MASK MSRs. These masks further qualify DRn/DR7 hardware breakpoint addresses to allow matching of larger addresses ranges. Valuable advice and pseudo code from Oleg Nesterov Signed-off-by: Jacob Shin Signed-off-by: Suravee Suthikulpanit Acked-by: Jiri Olsa Reviewed-by: Oleg Nesterov Cc: Arnaldo Carvalho de Melo Cc: Ingo Molnar Cc: Namhyung Kim Cc: Peter Zijlstra Cc: xiakaixu Signed-off-by: Frederic Weisbecker diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h index 0bb1335..53966d6 100644 --- a/arch/x86/include/asm/cpufeature.h +++ b/arch/x86/include/asm/cpufeature.h @@ -174,6 +174,7 @@ #define X86_FEATURE_TOPOEXT ( 6*32+22) /* topology extensions CPUID leafs */ #define X86_FEATURE_PERFCTR_CORE ( 6*32+23) /* core performance counter extensions */ #define X86_FEATURE_PERFCTR_NB ( 6*32+24) /* NB performance counter extensions */ +#define X86_FEATURE_BPEXT (6*32+26) /* data breakpoint extension */ #define X86_FEATURE_PERFCTR_L2 ( 6*32+28) /* L2 performance counter extensions */ /* @@ -383,6 +384,7 @@ extern const char * const x86_bug_flags[NBUGINTS*32]; #define cpu_has_cx16 boot_cpu_has(X86_FEATURE_CX16) #define cpu_has_eager_fpu boot_cpu_has(X86_FEATURE_EAGER_FPU) #define cpu_has_topoext boot_cpu_has(X86_FEATURE_TOPOEXT) +#define cpu_has_bpext boot_cpu_has(X86_FEATURE_BPEXT) #if __GNUC__ >= 4 extern void warn_pre_alternatives(void); diff --git a/arch/x86/include/asm/debugreg.h b/arch/x86/include/asm/debugreg.h index 61fd18b..12cb66f 100644 --- a/arch/x86/include/asm/debugreg.h +++ b/arch/x86/include/asm/debugreg.h @@ -114,5 +114,10 @@ static inline void debug_stack_usage_inc(void) { } static inline void debug_stack_usage_dec(void) { } #endif /* X86_64 */ +#ifdef CONFIG_CPU_SUP_AMD +extern void set_dr_addr_mask(unsigned long mask, int dr); +#else +static inline void set_dr_addr_mask(unsigned long mask, int dr) { } +#endif #endif /* _ASM_X86_DEBUGREG_H */ diff --git a/arch/x86/include/asm/hw_breakpoint.h b/arch/x86/include/asm/hw_breakpoint.h index ef1c4d2..6c98be8 100644 --- a/arch/x86/include/asm/hw_breakpoint.h +++ b/arch/x86/include/asm/hw_breakpoint.h @@ -12,6 +12,7 @@ */ struct arch_hw_breakpoint { unsigned long address; + unsigned long mask; u8 len; u8 type; }; diff --git a/arch/x86/include/uapi/asm/msr-index.h b/arch/x86/include/uapi/asm/msr-index.h index 8f02f69..b1fb4fa 100644 --- a/arch/x86/include/uapi/asm/msr-index.h +++ b/arch/x86/include/uapi/asm/msr-index.h @@ -212,6 +212,10 @@ /* Fam 16h MSRs */ #define MSR_F16H_L2I_PERF_CTL 0xc0010230 #define MSR_F16H_L2I_PERF_CTR 0xc0010231 +#define MSR_F16H_DR1_ADDR_MASK 0xc0011019 +#define MSR_F16H_DR2_ADDR_MASK 0xc001101a +#define MSR_F16H_DR3_ADDR_MASK 0xc001101b +#define MSR_F16H_DR0_ADDR_MASK 0xc0011027 /* Fam 15h MSRs */ #define MSR_F15H_PERF_CTL 0xc0010200 diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 813d29d..abe4ec7 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -870,3 +870,22 @@ static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum) return false; } + +void set_dr_addr_mask(unsigned long mask, int dr) +{ + if (!cpu_has_bpext) + return; + + switch (dr) { + case 0: + wrmsr(MSR_F16H_DR0_ADDR_MASK, mask, 0); + break; + case 1: + case 2: + case 3: + wrmsr(MSR_F16H_DR1_ADDR_MASK - 1 + dr, mask, 0); + break; + default: + break; + } +} diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c index 3d5fb50..b5cb0c5 100644 --- a/arch/x86/kernel/hw_breakpoint.c +++ b/arch/x86/kernel/hw_breakpoint.c @@ -126,6 +126,8 @@ int arch_install_hw_breakpoint(struct perf_event *bp) *dr7 |= encode_dr7(i, info->len, info->type); set_debugreg(*dr7, 7); + if (info->mask) + set_dr_addr_mask(info->mask, i); return 0; } @@ -161,6 +163,8 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp) *dr7 &= ~__encode_dr7(i, info->len, info->type); set_debugreg(*dr7, 7); + if (info->mask) + set_dr_addr_mask(0, i); } static int get_hbp_len(u8 hbp_len) @@ -277,6 +281,8 @@ static int arch_build_bp_info(struct perf_event *bp) } /* Len */ + info->mask = 0; + switch (bp->attr.bp_len) { case HW_BREAKPOINT_LEN_1: info->len = X86_BREAKPOINT_LEN_1; @@ -293,11 +299,17 @@ static int arch_build_bp_info(struct perf_event *bp) break; #endif default: - return -EINVAL; + if (!is_power_of_2(bp->attr.bp_len)) + return -EINVAL; + if (!cpu_has_bpext) + return -EOPNOTSUPP; + info->mask = bp->attr.bp_len - 1; + info->len = X86_BREAKPOINT_LEN_1; } return 0; } + /* * Validate the arch-specific HW Breakpoint register settings */ @@ -312,11 +324,11 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) if (ret) return ret; - ret = -EINVAL; - switch (info->len) { case X86_BREAKPOINT_LEN_1: align = 0; + if (info->mask) + align = info->mask; break; case X86_BREAKPOINT_LEN_2: align = 1; @@ -330,7 +342,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) break; #endif default: - return ret; + WARN_ON_ONCE(1); } /* -- cgit v0.10.2 From 3741eb9f8c3be3ec59583881c1f49980dad844e0 Mon Sep 17 00:00:00 2001 From: Jacob Shin Date: Thu, 29 May 2014 17:26:51 +0200 Subject: perf tools: allow user to specify hardware breakpoint bp_len Currently bp_len is given a default value of 4. Allow user to override it: $ perf stat -e mem:0x1000/8 ^ bp_len If no value is given, it will default to 4 as it did before. Signed-off-by: Jacob Shin Signed-off-by: Suravee Suthikulpanit Acked-by: Jiri Olsa Reviewed-by: Oleg Nesterov Cc: Arnaldo Carvalho de Melo Cc: Ingo Molnar Cc: Namhyung Kim Cc: Peter Zijlstra Cc: xiakaixu Signed-off-by: Frederic Weisbecker diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index af9a54e..81a20f2 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -33,12 +33,15 @@ OPTIONS - a raw PMU event (eventsel+umask) in the form of rNNN where NNN is a hexadecimal event descriptor. - - a hardware breakpoint event in the form of '\mem:addr[:access]' + - a hardware breakpoint event in the form of '\mem:addr[/len][:access]' where addr is the address in memory you want to break in. Access is the memory access type (read, write, execute) it can - be passed as follows: '\mem:addr[:[r][w][x]]'. + be passed as follows: '\mem:addr[:[r][w][x]]'. len is the range, + number of bytes from specified addr, which the breakpoint will cover. If you want to profile read-write accesses in 0x1000, just set 'mem:0x1000:rw'. + If you want to profile write accesses in [0x1000~1008), just set + 'mem:0x1000/8:w'. --filter=:: Event filter. diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index c659a3c..efa1ff4 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -526,7 +526,7 @@ do { \ } int parse_events_add_breakpoint(struct list_head *list, int *idx, - void *ptr, char *type) + void *ptr, char *type, u64 len) { struct perf_event_attr attr; @@ -536,14 +536,15 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx, if (parse_breakpoint_type(type, &attr)) return -EINVAL; - /* - * We should find a nice way to override the access length - * Provide some defaults for now - */ - if (attr.bp_type == HW_BREAKPOINT_X) - attr.bp_len = sizeof(long); - else - attr.bp_len = HW_BREAKPOINT_LEN_4; + /* Provide some defaults if len is not specified */ + if (!len) { + if (attr.bp_type == HW_BREAKPOINT_X) + len = sizeof(long); + else + len = HW_BREAKPOINT_LEN_4; + } + + attr.bp_len = len; attr.type = PERF_TYPE_BREAKPOINT; attr.sample_period = 1; @@ -1364,7 +1365,7 @@ void print_events(const char *event_glob, bool name_only) printf("\n"); printf(" %-50s [%s]\n", - "mem:[:access]", + "mem:[/len][:access]", event_type_descriptors[PERF_TYPE_BREAKPOINT]); printf("\n"); } diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index db2cf78..a19fbeb 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -104,7 +104,7 @@ int parse_events_add_numeric(struct list_head *list, int *idx, int parse_events_add_cache(struct list_head *list, int *idx, char *type, char *op_result1, char *op_result2); int parse_events_add_breakpoint(struct list_head *list, int *idx, - void *ptr, char *type); + void *ptr, char *type, u64 len); int parse_events_add_pmu(struct list_head *list, int *idx, char *pmu , struct list_head *head_config); enum perf_pmu_event_symbol_type diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 906630b..94eacb6 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -159,6 +159,7 @@ branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE { {modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); } : { return ':'; } +"/" { return '/'; } {num_dec} { return value(yyscanner, 10); } {num_hex} { return value(yyscanner, 16); } /* diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 93c4c9f..72def07 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -326,6 +326,28 @@ PE_NAME_CACHE_TYPE } event_legacy_mem: +PE_PREFIX_MEM PE_VALUE '/' PE_VALUE ':' PE_MODIFIER_BP sep_dc +{ + struct parse_events_evlist *data = _data; + struct list_head *list; + + ALLOC_LIST(list); + ABORT_ON(parse_events_add_breakpoint(list, &data->idx, + (void *) $2, $6, $4)); + $$ = list; +} +| +PE_PREFIX_MEM PE_VALUE '/' PE_VALUE sep_dc +{ + struct parse_events_evlist *data = _data; + struct list_head *list; + + ALLOC_LIST(list); + ABORT_ON(parse_events_add_breakpoint(list, &data->idx, + (void *) $2, NULL, $4)); + $$ = list; +} +| PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc { struct parse_events_evlist *data = _data; @@ -333,7 +355,7 @@ PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc ALLOC_LIST(list); ABORT_ON(parse_events_add_breakpoint(list, &data->idx, - (void *) $2, $4)); + (void *) $2, $4, 0)); $$ = list; } | @@ -344,7 +366,7 @@ PE_PREFIX_MEM PE_VALUE sep_dc ALLOC_LIST(list); ABORT_ON(parse_events_add_breakpoint(list, &data->idx, - (void *) $2, NULL)); + (void *) $2, NULL, 0)); $$ = list; } -- cgit v0.10.2 From ec32398c231a019d39017afee490728ab3b60b92 Mon Sep 17 00:00:00 2001 From: Jacob Shin Date: Thu, 29 May 2014 17:26:52 +0200 Subject: perf tools: add hardware breakpoint bp_len test cases Signed-off-by: Jacob Shin Signed-off-by: Suravee Suthikulpanit Acked-by: Jiri Olsa Reviewed-by: Oleg Nesterov Cc: Arnaldo Carvalho de Melo Cc: Ingo Molnar Cc: Namhyung Kim Cc: Peter Zijlstra Cc: xiakaixu Signed-off-by: Frederic Weisbecker diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 7f2f51f..4169f46 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -1145,6 +1145,49 @@ static int test__pinned_group(struct perf_evlist *evlist) return 0; } +static int test__checkevent_breakpoint_len(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = perf_evlist__first(evlist); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); + TEST_ASSERT_VAL("wrong bp_type", (HW_BREAKPOINT_R | HW_BREAKPOINT_W) == + evsel->attr.bp_type); + TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_1 == + evsel->attr.bp_len); + + return 0; +} + +static int test__checkevent_breakpoint_len_w(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = perf_evlist__first(evlist); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); + TEST_ASSERT_VAL("wrong bp_type", HW_BREAKPOINT_W == + evsel->attr.bp_type); + TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_2 == + evsel->attr.bp_len); + + return 0; +} + +static int +test__checkevent_breakpoint_len_rw_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = perf_evlist__first(evlist); + + TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); + + return test__checkevent_breakpoint_rw(evlist); +} + static int count_tracepoints(void) { char events_path[PATH_MAX]; @@ -1420,6 +1463,21 @@ static struct evlist_test test__events[] = { .check = test__pinned_group, .id = 41, }, + { + .name = "mem:0/1", + .check = test__checkevent_breakpoint_len, + .id = 42, + }, + { + .name = "mem:0/2:w", + .check = test__checkevent_breakpoint_len_w, + .id = 43, + }, + { + .name = "mem:0/4:rw:u", + .check = test__checkevent_breakpoint_len_rw_modifier, + .id = 44 + }, #if defined(__s390x__) { .name = "kvm-s390:kvm_s390_create_vm", -- cgit v0.10.2 From 36748b9518a2437beffe861b47dff6d12b736b3f Mon Sep 17 00:00:00 2001 From: Jacob Shin Date: Thu, 29 May 2014 17:26:53 +0200 Subject: perf/x86: Remove get_hbp_len and replace with bp_len Clean up the logic for determining the breakpoint length Signed-off-by: Jacob Shin Signed-off-by: Suravee Suthikulpanit Acked-by: Jiri Olsa Reviewed-by: Oleg Nesterov Cc: Arnaldo Carvalho de Melo Cc: Ingo Molnar Cc: Namhyung Kim Cc: Peter Zijlstra Cc: xiakaixu Signed-off-by: Frederic Weisbecker diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c index b5cb0c5..7114ba2 100644 --- a/arch/x86/kernel/hw_breakpoint.c +++ b/arch/x86/kernel/hw_breakpoint.c @@ -167,29 +167,6 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp) set_dr_addr_mask(0, i); } -static int get_hbp_len(u8 hbp_len) -{ - unsigned int len_in_bytes = 0; - - switch (hbp_len) { - case X86_BREAKPOINT_LEN_1: - len_in_bytes = 1; - break; - case X86_BREAKPOINT_LEN_2: - len_in_bytes = 2; - break; - case X86_BREAKPOINT_LEN_4: - len_in_bytes = 4; - break; -#ifdef CONFIG_X86_64 - case X86_BREAKPOINT_LEN_8: - len_in_bytes = 8; - break; -#endif - } - return len_in_bytes; -} - /* * Check for virtual address in kernel space. */ @@ -200,7 +177,7 @@ int arch_check_bp_in_kernelspace(struct perf_event *bp) struct arch_hw_breakpoint *info = counter_arch_bp(bp); va = info->address; - len = get_hbp_len(info->len); + len = bp->attr.bp_len; return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); } -- cgit v0.10.2 From 108cc2e7d212c7d52694fb400423da807e1e5fe4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 30 Apr 2013 12:42:50 +0300 Subject: SFI: fix compiler warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/sfi/sfi_core.c:164:26: warning: no previous prototype for ‘sfi_map_table’ [-Wmissing-prototypes] drivers/sfi/sfi_core.c:192:6: warning: no previous prototype for ‘sfi_unmap_table’ [-Wmissing-prototypes] Signed-off-by: Andy Shevchenko Signed-off-by: Len Brown diff --git a/drivers/sfi/sfi_core.c b/drivers/sfi/sfi_core.c index 1e824fb..296db7a 100644 --- a/drivers/sfi/sfi_core.c +++ b/drivers/sfi/sfi_core.c @@ -161,7 +161,7 @@ static int sfi_verify_table(struct sfi_table_header *table) * Check for common case that we can re-use mapping to SYST, * which requires syst_pa, syst_va to be initialized. */ -struct sfi_table_header *sfi_map_table(u64 pa) +static struct sfi_table_header *sfi_map_table(u64 pa) { struct sfi_table_header *th; u32 length; @@ -189,7 +189,7 @@ struct sfi_table_header *sfi_map_table(u64 pa) * Undoes effect of sfi_map_table() by unmapping table * if it did not completely fit on same page as SYST. */ -void sfi_unmap_table(struct sfi_table_header *th) +static void sfi_unmap_table(struct sfi_table_header *th) { if (!TABLE_ON_PAGE(syst_va, th, th->len)) sfi_unmap_memory(th, TABLE_ON_PAGE(th, th, th->len) ? -- cgit v0.10.2 From de3b7a06dfe15bda3e66a52285d422b954bb4832 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Thu, 4 Dec 2014 09:46:20 +0100 Subject: xfrm6: Fix transport header offset in _decode_session6. skb->transport_header might not be valid when we do a reverse decode because the ipv6 tunnel error handlers don't update it to the inner transport header. This leads to a wrong offset calculation and to wrong layer 4 informations. We fix this by using the size of the ipv6 header as the first offset. Signed-off-by: Steffen Klassert diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 5f98364..aa48302 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -130,8 +130,8 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse) { struct flowi6 *fl6 = &fl->u.ip6; int onlyproto = 0; - u16 offset = skb_network_header_len(skb); const struct ipv6hdr *hdr = ipv6_hdr(skb); + u16 offset = sizeof(*hdr); struct ipv6_opt_hdr *exthdr; const unsigned char *nh = skb_network_header(skb); u8 nexthdr = nh[IP6CB(skb)->nhoff]; -- cgit v0.10.2 From f855691975bb06373a98711e4cfe2c224244b536 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Mon, 8 Dec 2014 07:56:18 +0100 Subject: xfrm6: Fix the nexthdr offset in _decode_session6. xfrm_decode_session() was originally designed for the usage in the receive path where the correct nexthdr offset is stored in IP6CB(skb)->nhoff. Over time this function spread to code that is used in the output path (netfilter, vti) where IP6CB(skb)->nhoff is not set. As a result, we get a wrong nexthdr and the upper layer flow informations are wrong. This can leed to incorrect policy lookups. Signed-off-by: Steffen Klassert diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index aa48302..48bf5a0 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -134,8 +134,14 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse) u16 offset = sizeof(*hdr); struct ipv6_opt_hdr *exthdr; const unsigned char *nh = skb_network_header(skb); - u8 nexthdr = nh[IP6CB(skb)->nhoff]; + u16 nhoff = IP6CB(skb)->nhoff; int oif = 0; + u8 nexthdr; + + if (!nhoff) + nhoff = offsetof(struct ipv6hdr, nexthdr); + + nexthdr = nh[nhoff]; if (skb_dst(skb)) oif = skb_dst(skb)->dev->ifindex; -- cgit v0.10.2 From 488dad953092427e1ed4340927dda0dc2ae7dd8a Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 2 Dec 2014 10:55:53 +0200 Subject: dt: bindings: add ath10k wireless device Document how calibration data can be provided to ath10k via Device Tree. Signed-off-by: Kalle Valo diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.txt b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.txt new file mode 100644 index 0000000..edefc26 --- /dev/null +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.txt @@ -0,0 +1,30 @@ +* Qualcomm Atheros ath10k wireless devices + +For ath10k devices the calibration data can be provided through Device +Tree. The node is a child node of the PCI controller. + +Required properties: +-compatible : Should be "qcom,ath10k" + +Optional properties: +- qcom,ath10k-calibration-data : calibration data as an array, the + length can vary between hw versions + + +Example: + +pci { + pcie@0 { + reg = <0 0 0 0 0>; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + device_type = "pci"; + + ath10k@0,0 { + reg = <0 0 0 0 0>; + device_type = "pci"; + qcom,ath10k-calibration-data = [ 01 02 03 ... ]; + }; + }; +}; -- cgit v0.10.2 From 5aabff05df8e137c6690437f79b05408294fea57 Mon Sep 17 00:00:00 2001 From: Toshi Kikuchi Date: Tue, 2 Dec 2014 10:55:54 +0200 Subject: ath10k: read calibration data from Device Tree This patch adds support for reading calibration data from Device Tree. It looks for the calibration data in Device Tree if it can't find it in a file. If there's no node in Device Tree, ath10k will try to find the calibration data from OTP. The node for the calibration data should be defined like this: pci { pcie@0 { reg = <0 0 0 0 0>; #interrupt-cells = <1>; #size-cells = <2>; #address-cells = <3>; device_type = "pci"; ath10k@0,0 { reg = <0 0 0 0 0>; device_type = "pci"; qcom,ath10k-calibration-data = [ 01 02 03 ... ]; }; }; }; Signed-off-by: Toshi Kikuchi Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 7762061..6165f27 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -17,6 +17,7 @@ #include #include +#include #include "core.h" #include "mac.h" @@ -249,6 +250,63 @@ static int ath10k_download_cal_file(struct ath10k *ar) return 0; } +static int ath10k_download_cal_dt(struct ath10k *ar) +{ + struct device_node *node; + int data_len; + void *data; + int ret; + + node = ar->dev->of_node; + if (!node) + /* Device Tree is optional, don't print any warnings if + * there's no node for ath10k. + */ + return -ENOENT; + + if (!of_get_property(node, "qcom,ath10k-calibration-data", + &data_len)) { + /* The calibration data node is optional */ + return -ENOENT; + } + + if (data_len != QCA988X_CAL_DATA_LEN) { + ath10k_warn(ar, "invalid calibration data length in DT: %d\n", + data_len); + ret = -EMSGSIZE; + goto out; + } + + data = kmalloc(data_len, GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto out; + } + + ret = of_property_read_u8_array(node, "qcom,ath10k-calibration-data", + data, data_len); + if (ret) { + ath10k_warn(ar, "failed to read calibration data from DT: %d\n", + ret); + goto out_free; + } + + ret = ath10k_download_board_data(ar, data, data_len); + if (ret) { + ath10k_warn(ar, "failed to download calibration data from Device Tree: %d\n", + ret); + goto out_free; + } + + ret = 0; + +out_free: + kfree(data); + +out: + return ret; +} + static int ath10k_download_and_run_otp(struct ath10k *ar) { u32 result, address = ar->hw_params.patch_load_addr; @@ -662,7 +720,17 @@ static int ath10k_download_cal_data(struct ath10k *ar) } ath10k_dbg(ar, ATH10K_DBG_BOOT, - "boot did not find a calibration file, try OTP next: %d\n", + "boot did not find a calibration file, try DT next: %d\n", + ret); + + ret = ath10k_download_cal_dt(ar); + if (ret == 0) { + ar->cal_mode = ATH10K_CAL_MODE_DT; + goto done; + } + + ath10k_dbg(ar, ATH10K_DBG_BOOT, + "boot did not find DT entry, try OTP next: %d\n", ret); ret = ath10k_download_and_run_otp(ar); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 514c219..69f78bf 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -401,6 +401,7 @@ enum ath10k_dev_flags { enum ath10k_cal_mode { ATH10K_CAL_MODE_FILE, ATH10K_CAL_MODE_OTP, + ATH10K_CAL_MODE_DT, }; static inline const char *ath10k_cal_mode_str(enum ath10k_cal_mode mode) @@ -410,6 +411,8 @@ static inline const char *ath10k_cal_mode_str(enum ath10k_cal_mode mode) return "file"; case ATH10K_CAL_MODE_OTP: return "otp"; + case ATH10K_CAL_MODE_DT: + return "dt"; } return "unknown"; -- cgit v0.10.2 From 7505f7c3ec1d2f13ed75fef6b2681eb18e7d9147 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 2 Dec 2014 10:55:54 +0200 Subject: ath10k: create a chip revision whitelist This will make it easier to extend and maintain list of supported hardware. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 6165f27..3a299fc 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1136,34 +1136,6 @@ static int ath10k_core_probe_fw(struct ath10k *ar) return 0; } -static int ath10k_core_check_chip_id(struct ath10k *ar) -{ - u32 hw_revision = MS(ar->chip_id, SOC_CHIP_ID_REV); - - ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot chip_id 0x%08x hw_revision 0x%x\n", - ar->chip_id, hw_revision); - - /* Check that we are not using hw1.0 (some of them have same pci id - * as hw2.0) before doing anything else as ath10k crashes horribly - * due to missing hw1.0 workarounds. */ - switch (hw_revision) { - case QCA988X_HW_1_0_CHIP_ID_REV: - ath10k_err(ar, "ERROR: qca988x hw1.0 is not supported\n"); - return -EOPNOTSUPP; - - case QCA988X_HW_2_0_CHIP_ID_REV: - /* known hardware revision, continue normally */ - return 0; - - default: - ath10k_warn(ar, "Warning: hardware revision unknown (0x%x), expect problems\n", - ar->chip_id); - return 0; - } - - return 0; -} - static void ath10k_core_register_work(struct work_struct *work) { struct ath10k *ar = container_of(work, struct ath10k, register_work); @@ -1211,16 +1183,7 @@ err: int ath10k_core_register(struct ath10k *ar, u32 chip_id) { - int status; - ar->chip_id = chip_id; - - status = ath10k_core_check_chip_id(ar); - if (status) { - ath10k_err(ar, "Unsupported chip id 0x%08x\n", ar->chip_id); - return status; - } - queue_work(ar->workqueue, &ar->register_work); return 0; diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 7abb836..5e50214 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -64,6 +64,14 @@ static const struct pci_device_id ath10k_pci_id_table[] = { {0} }; +static const struct ath10k_pci_supp_chip ath10k_pci_supp_chips[] = { + /* QCA988X pre 2.0 chips are not supported because they need some nasty + * hacks. ath10k doesn't have them and these devices crash horribly + * because of that. + */ + { QCA988X_2_0_DEVICE_ID, QCA988X_HW_2_0_CHIP_ID_REV }, +}; + static void ath10k_pci_buffer_cleanup(struct ath10k *ar); static int ath10k_pci_cold_reset(struct ath10k *ar); static int ath10k_pci_warm_reset(struct ath10k *ar); @@ -2476,6 +2484,23 @@ static void ath10k_pci_release(struct ath10k *ar) pci_disable_device(pdev); } +static bool ath10k_pci_chip_is_supported(u32 dev_id, u32 chip_id) +{ + const struct ath10k_pci_supp_chip *supp_chip; + int i; + u32 rev_id = MS(chip_id, SOC_CHIP_ID_REV); + + for (i = 0; i < ARRAY_SIZE(ath10k_pci_supp_chips); i++) { + supp_chip = &ath10k_pci_supp_chips[i]; + + if (supp_chip->dev_id == dev_id && + supp_chip->rev_id == rev_id) + return true; + } + + return false; +} + static int ath10k_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_dev) { @@ -2521,6 +2546,12 @@ static int ath10k_pci_probe(struct pci_dev *pdev, goto err_sleep; } + if (!ath10k_pci_chip_is_supported(pdev->device, chip_id)) { + ath10k_err(ar, "device %04x with chip_id %08x isn't supported\n", + pdev->device, chip_id); + goto err_sleep; + } + ret = ath10k_pci_alloc_pipes(ar); if (ret) { ath10k_err(ar, "failed to allocate copy engine pipes: %d\n", diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index cf36511..ce4a1ef 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -152,6 +152,11 @@ struct ath10k_pci_pipe { struct tasklet_struct intr; }; +struct ath10k_pci_supp_chip { + u32 dev_id; + u32 rev_id; +}; + struct ath10k_pci { struct pci_dev *pdev; struct device *dev; -- cgit v0.10.2 From 9764a2af0d592c6a9b95c913b1d65a2d4a2dc78e Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 2 Dec 2014 10:55:54 +0200 Subject: ath10k: put board size into hw_params This makes it easier to extend the list of supported hardware. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 3a299fc..e038e3e 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -54,6 +54,8 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .fw = QCA988X_HW_2_0_FW_FILE, .otp = QCA988X_HW_2_0_OTP_FILE, .board = QCA988X_HW_2_0_BOARD_DATA_FILE, + .board_size = QCA988X_BOARD_DATA_SZ, + .board_ext_size = QCA988X_BOARD_EXT_DATA_SZ, }, }, }; @@ -147,8 +149,8 @@ static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar, static int ath10k_push_board_ext_data(struct ath10k *ar, const void *data, size_t data_len) { - u32 board_data_size = QCA988X_BOARD_DATA_SZ; - u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ; + u32 board_data_size = ar->hw_params.fw.board_size; + u32 board_ext_data_size = ar->hw_params.fw.board_ext_size; u32 board_ext_data_addr; int ret; @@ -194,7 +196,7 @@ static int ath10k_push_board_ext_data(struct ath10k *ar, const void *data, static int ath10k_download_board_data(struct ath10k *ar, const void *data, size_t data_len) { - u32 board_data_size = QCA988X_BOARD_DATA_SZ; + u32 board_data_size = ar->hw_params.fw.board_size; u32 address; int ret; diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 69f78bf..2de5a0c 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -489,6 +489,8 @@ struct ath10k { const char *fw; const char *otp; const char *board; + size_t board_size; + size_t board_ext_size; } fw; } hw_params; -- cgit v0.10.2 From 3a8200b226e683097945ae9620b0aef19df86a40 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 2 Dec 2014 10:55:55 +0200 Subject: ath10k: move uart pin config into hw_params This will make it possible to easily support different hardware with different uart pin configuration. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index e038e3e..54fdc71 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -49,6 +49,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .id = QCA988X_HW_2_0_VERSION, .name = "qca988x hw2.0", .patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR, + .uart_pin = 7, .fw = { .dir = QCA988X_HW_2_0_FW_DIR, .fw = QCA988X_HW_2_0_FW_FILE, @@ -766,7 +767,7 @@ static int ath10k_init_uart(struct ath10k *ar) if (!uart_print) return 0; - ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, 7); + ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, ar->hw_params.uart_pin); if (ret) { ath10k_warn(ar, "could not enable UART prints (%d)\n", ret); return ret; diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 2de5a0c..44bee88 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -483,6 +483,7 @@ struct ath10k { u32 id; const char *name; u32 patch_load_addr; + int uart_pin; struct ath10k_hw_params_fw { const char *dir; -- cgit v0.10.2 From c6ce492d03e89e1f1a30cbdab777e9367baeae34 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 3 Dec 2014 10:09:31 +0200 Subject: ath10k: clean up error handling in ath10k_core_probe_fw() Use the error handling style preferred in ath10k. Makes it easier to add ath10k_init_firmware_features() function in the next patch. Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 54fdc71..e5790b8 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1096,8 +1096,7 @@ static int ath10k_core_probe_fw(struct ath10k *ar) ret = ath10k_bmi_get_target_info(ar, &target_info); if (ret) { ath10k_err(ar, "could not get target info (%d)\n", ret); - ath10k_hif_power_down(ar); - return ret; + goto err_power_down; } ar->target_version = target_info.version; @@ -1106,15 +1105,13 @@ static int ath10k_core_probe_fw(struct ath10k *ar) ret = ath10k_init_hw_params(ar); if (ret) { ath10k_err(ar, "could not get hw params (%d)\n", ret); - ath10k_hif_power_down(ar); - return ret; + goto err_power_down; } ret = ath10k_core_fetch_firmware_files(ar); if (ret) { ath10k_err(ar, "could not fetch firmware files (%d)\n", ret); - ath10k_hif_power_down(ar); - return ret; + goto err_power_down; } ath10k_core_init_max_sta_count(ar); @@ -1124,10 +1121,7 @@ static int ath10k_core_probe_fw(struct ath10k *ar) ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_NORMAL); if (ret) { ath10k_err(ar, "could not init core (%d)\n", ret); - ath10k_core_free_firmware_files(ar); - ath10k_hif_power_down(ar); - mutex_unlock(&ar->conf_mutex); - return ret; + goto err_unlock; } ath10k_print_driver_info(ar); @@ -1137,6 +1131,16 @@ static int ath10k_core_probe_fw(struct ath10k *ar) ath10k_hif_power_down(ar); return 0; + +err_unlock: + mutex_unlock(&ar->conf_mutex); + + ath10k_core_free_firmware_files(ar); + +err_power_down: + ath10k_hif_power_down(ar); + + return ret; } static void ath10k_core_register_work(struct work_struct *work) -- cgit v0.10.2 From 5f2144d9b2ea297aa75f0f952be96af7f02360f1 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 3 Dec 2014 10:09:59 +0200 Subject: ath10k: create ath10k_core_init_features() It's easier to manage firmware version differences when we configure them in one place. Rename ath10k_core_init_max_sta_count() to ath10k_core_init_firmware_features() and start moving most of the firmware version ("features") handling to that function. Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index e5790b8..6c47c1e 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -643,13 +643,6 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) goto err; } - if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features) && - !test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - ath10k_err(ar, "feature bits corrupted: 10.2 feature requires 10.x feature to be set as well"); - ret = -EINVAL; - goto err; - } - /* now fetch the board file */ if (ar->hw_params.fw.board == NULL) { ath10k_err(ar, "board data file not defined"); @@ -870,8 +863,14 @@ static void ath10k_core_restart(struct work_struct *work) mutex_unlock(&ar->conf_mutex); } -static void ath10k_core_init_max_sta_count(struct ath10k *ar) +static int ath10k_core_init_firmware_features(struct ath10k *ar) { + if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features) && + !test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + ath10k_err(ar, "feature bits corrupted: 10.2 feature requires 10.x feature to be set as well"); + return -EINVAL; + } + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { ar->max_num_peers = TARGET_10X_NUM_PEERS; ar->max_num_stations = TARGET_10X_NUM_STATIONS; @@ -879,6 +878,8 @@ static void ath10k_core_init_max_sta_count(struct ath10k *ar) ar->max_num_peers = TARGET_NUM_PEERS; ar->max_num_stations = TARGET_NUM_STATIONS; } + + return 0; } int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode) @@ -1114,7 +1115,12 @@ static int ath10k_core_probe_fw(struct ath10k *ar) goto err_power_down; } - ath10k_core_init_max_sta_count(ar); + ret = ath10k_core_init_firmware_features(ar); + if (ret) { + ath10k_err(ar, "fatal problem with firmware features: %d\n", + ret); + goto err_free_firmware_files; + } mutex_lock(&ar->conf_mutex); @@ -1135,6 +1141,7 @@ static int ath10k_core_probe_fw(struct ath10k *ar) err_unlock: mutex_unlock(&ar->conf_mutex); +err_free_firmware_files: ath10k_core_free_firmware_files(ar); err_power_down: -- cgit v0.10.2 From 202e86e60646d6987e3a3e63871453401e72d451 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 3 Dec 2014 10:10:08 +0200 Subject: ath10k: add ATH10K_FW_IE_WMI_OP_VERSION Instead of using feature flags, add new 32 bit variable for managing different WMI versions. This makes it firmware interface tests a bit less convoluted, especially when we add one more interface. Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 6c47c1e..16f210e 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -508,7 +508,7 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) int ie_id, i, index, bit, ret; struct ath10k_fw_ie *hdr; const u8 *data; - __le32 *timestamp; + __le32 *timestamp, *version; /* first fetch the firmware file (firmware-*.bin) */ ar->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, name); @@ -623,6 +623,17 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) ar->otp_len = ie_len; break; + case ATH10K_FW_IE_WMI_OP_VERSION: + if (ie_len != sizeof(u32)) + break; + + version = (__le32 *)data; + + ar->wmi.op_version = le32_to_cpup(version); + + ath10k_dbg(ar, ATH10K_DBG_BOOT, "found fw ie wmi op version %d\n", + ar->wmi.op_version); + break; default: ath10k_warn(ar, "Unknown FW IE: %u\n", le32_to_cpu(hdr->id)); @@ -871,12 +882,40 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) return -EINVAL; } - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - ar->max_num_peers = TARGET_10X_NUM_PEERS; - ar->max_num_stations = TARGET_10X_NUM_STATIONS; - } else { + if (ar->wmi.op_version >= ATH10K_FW_WMI_OP_VERSION_MAX) { + ath10k_err(ar, "unsupported WMI OP version (max %d): %d\n", + ATH10K_FW_WMI_OP_VERSION_MAX, ar->wmi.op_version); + return -EINVAL; + } + + /* Backwards compatibility for firmwares without + * ATH10K_FW_IE_WMI_OP_VERSION. + */ + if (ar->wmi.op_version == ATH10K_FW_WMI_OP_VERSION_UNSET) { + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) + ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_10_2; + else + ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_10_1; + } else { + ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_MAIN; + } + } + + switch (ar->wmi.op_version) { + case ATH10K_FW_WMI_OP_VERSION_MAIN: ar->max_num_peers = TARGET_NUM_PEERS; ar->max_num_stations = TARGET_NUM_STATIONS; + break; + case ATH10K_FW_WMI_OP_VERSION_10_1: + case ATH10K_FW_WMI_OP_VERSION_10_2: + ar->max_num_peers = TARGET_10X_NUM_PEERS; + ar->max_num_stations = TARGET_10X_NUM_STATIONS; + break; + case ATH10K_FW_WMI_OP_VERSION_UNSET: + case ATH10K_FW_WMI_OP_VERSION_MAX: + WARN_ON(1); + return -EINVAL; } return 0; diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 44bee88..0ae1df6 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -120,6 +120,7 @@ struct ath10k_mem_chunk { }; struct ath10k_wmi { + enum ath10k_fw_wmi_op_version op_version; enum ath10k_htc_ep_id eid; struct completion service_ready; struct completion unified_ready; @@ -369,7 +370,7 @@ enum ath10k_fw_features { /* wmi_mgmt_rx_hdr contains extra RSSI information */ ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX = 0, - /* firmware from 10X branch */ + /* Firmware from 10X branch. Deprecated, don't use in new code. */ ATH10K_FW_FEATURE_WMI_10X = 1, /* firmware support tx frame management over WMI, otherwise it's HTT */ @@ -378,8 +379,9 @@ enum ath10k_fw_features { /* Firmware does not support P2P */ ATH10K_FW_FEATURE_NO_P2P = 3, - /* Firmware 10.2 feature bit. The ATH10K_FW_FEATURE_WMI_10X feature bit - * is required to be set as well. + /* Firmware 10.2 feature bit. The ATH10K_FW_FEATURE_WMI_10X feature + * bit is required to be set as well. Deprecated, don't use in new + * code. */ ATH10K_FW_FEATURE_WMI_10_2 = 4, diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index dfedfd0..5bae90c 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -58,6 +58,22 @@ enum ath10k_fw_ie_type { ATH10K_FW_IE_FEATURES = 2, ATH10K_FW_IE_FW_IMAGE = 3, ATH10K_FW_IE_OTP_IMAGE = 4, + + /* WMI "operations" interface version, 32 bit value. Supported from + * FW API 4 and above. + */ + ATH10K_FW_IE_WMI_OP_VERSION = 5, +}; + +enum ath10k_fw_wmi_op_version { + ATH10K_FW_WMI_OP_VERSION_UNSET = 0, + + ATH10K_FW_WMI_OP_VERSION_MAIN = 1, + ATH10K_FW_WMI_OP_VERSION_10_1 = 2, + ATH10K_FW_WMI_OP_VERSION_10_2 = 3, + + /* keep last */ + ATH10K_FW_WMI_OP_VERSION_MAX, }; /* Known pecularities: -- cgit v0.10.2 From 91ad5f56f62c0a5582f79fa6306ac189f0ef41bd Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 3 Dec 2014 10:10:17 +0200 Subject: ath10k: set max_num_pending_tx in ath10k_core_init_firmware_features() Better to have this in same place as other firmware interface handling. Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 16f210e..54a1257 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -906,11 +906,13 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) case ATH10K_FW_WMI_OP_VERSION_MAIN: ar->max_num_peers = TARGET_NUM_PEERS; ar->max_num_stations = TARGET_NUM_STATIONS; + ar->htt.max_num_pending_tx = TARGET_NUM_MSDU_DESC; break; case ATH10K_FW_WMI_OP_VERSION_10_1: case ATH10K_FW_WMI_OP_VERSION_10_2: ar->max_num_peers = TARGET_10X_NUM_PEERS; ar->max_num_stations = TARGET_10X_NUM_STATIONS; + ar->htt.max_num_pending_tx = TARGET_10X_NUM_MSDU_DESC; break; case ATH10K_FW_WMI_OP_VERSION_UNSET: case ATH10K_FW_WMI_OP_VERSION_MAX: diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 4bc51d8..a1bda41 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -93,11 +93,6 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt) spin_lock_init(&htt->tx_lock); - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, htt->ar->fw_features)) - htt->max_num_pending_tx = TARGET_10X_NUM_MSDU_DESC; - else - htt->max_num_pending_tx = TARGET_NUM_MSDU_DESC; - ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n", htt->max_num_pending_tx); -- cgit v0.10.2 From 32653cf19554ddb77e9528851df3eed3ea35619d Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 3 Dec 2014 10:10:26 +0200 Subject: ath10k: implement intermediate event args This splits the actual event parsing into intermediary structures to facilitate future support of vastly different ABI WMI backends. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index c0f3e4d..4a9468e 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -977,22 +977,48 @@ ath10k_wmi_event_scan_type_str(enum wmi_scan_event_type type, } } +static int ath10k_wmi_pull_scan_ev(struct sk_buff *skb, + struct wmi_scan_ev_arg *arg) +{ + struct wmi_scan_event *ev = (void *)skb->data; + + if (skb->len < sizeof(*ev)) + return -EPROTO; + + skb_pull(skb, sizeof(*ev)); + arg->event_type = ev->event_type; + arg->reason = ev->reason; + arg->channel_freq = ev->channel_freq; + arg->scan_req_id = ev->scan_req_id; + arg->scan_id = ev->scan_id; + arg->vdev_id = ev->vdev_id; + + return 0; +} + static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb) { - struct wmi_scan_event *event = (struct wmi_scan_event *)skb->data; + struct wmi_scan_ev_arg arg = {}; enum wmi_scan_event_type event_type; enum wmi_scan_completion_reason reason; u32 freq; u32 req_id; u32 scan_id; u32 vdev_id; + int ret; - event_type = __le32_to_cpu(event->event_type); - reason = __le32_to_cpu(event->reason); - freq = __le32_to_cpu(event->channel_freq); - req_id = __le32_to_cpu(event->scan_req_id); - scan_id = __le32_to_cpu(event->scan_id); - vdev_id = __le32_to_cpu(event->vdev_id); + ret = ath10k_wmi_pull_scan_ev(skb, &arg); + if (ret) { + ath10k_warn(ar, "failed to parse scan event: %d\n", ret); + return ret; + } + + event_type = __le32_to_cpu(arg.event_type); + reason = __le32_to_cpu(arg.reason); + freq = __le32_to_cpu(arg.channel_freq); + req_id = __le32_to_cpu(arg.scan_req_id); + scan_id = __le32_to_cpu(arg.scan_id); + vdev_id = __le32_to_cpu(arg.vdev_id); spin_lock_bh(&ar->data_lock); @@ -1147,11 +1173,52 @@ static void ath10k_wmi_handle_wep_reauth(struct ath10k *ar, } } -static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) +static int ath10k_wmi_pull_mgmt_rx_ev(struct sk_buff *skb, + struct wmi_mgmt_rx_ev_arg *arg, + struct ath10k *ar) { struct wmi_mgmt_rx_event_v1 *ev_v1; struct wmi_mgmt_rx_event_v2 *ev_v2; struct wmi_mgmt_rx_hdr_v1 *ev_hdr; + size_t pull_len; + u32 msdu_len; + + if (test_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features)) { + ev_v2 = (struct wmi_mgmt_rx_event_v2 *)skb->data; + ev_hdr = &ev_v2->hdr.v1; + pull_len = sizeof(*ev_v2); + } else { + ev_v1 = (struct wmi_mgmt_rx_event_v1 *)skb->data; + ev_hdr = &ev_v1->hdr; + pull_len = sizeof(*ev_v1); + } + + if (skb->len < pull_len) + return -EPROTO; + + skb_pull(skb, pull_len); + arg->channel = ev_hdr->channel; + arg->buf_len = ev_hdr->buf_len; + arg->status = ev_hdr->status; + arg->snr = ev_hdr->snr; + arg->phy_mode = ev_hdr->phy_mode; + arg->rate = ev_hdr->rate; + + msdu_len = __le32_to_cpu(arg->buf_len); + if (skb->len < msdu_len) + return -EPROTO; + + /* the WMI buffer might've ended up being padded to 4 bytes due to HTC + * trailer with credit update. Trim the excess garbage. + */ + skb_trim(skb, msdu_len); + + return 0; +} + +static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_mgmt_rx_ev_arg arg = {}; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr; u32 rx_status; @@ -1161,24 +1228,20 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) u32 rate; u32 buf_len; u16 fc; - int pull_len; + int ret; - if (test_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features)) { - ev_v2 = (struct wmi_mgmt_rx_event_v2 *)skb->data; - ev_hdr = &ev_v2->hdr.v1; - pull_len = sizeof(*ev_v2); - } else { - ev_v1 = (struct wmi_mgmt_rx_event_v1 *)skb->data; - ev_hdr = &ev_v1->hdr; - pull_len = sizeof(*ev_v1); + ret = ath10k_wmi_pull_mgmt_rx_ev(skb, &arg, ar); + if (ret) { + ath10k_warn(ar, "failed to parse mgmt rx event: %d\n", ret); + return ret; } - channel = __le32_to_cpu(ev_hdr->channel); - buf_len = __le32_to_cpu(ev_hdr->buf_len); - rx_status = __le32_to_cpu(ev_hdr->status); - snr = __le32_to_cpu(ev_hdr->snr); - phy_mode = __le32_to_cpu(ev_hdr->phy_mode); - rate = __le32_to_cpu(ev_hdr->rate); + channel = __le32_to_cpu(arg.channel); + buf_len = __le32_to_cpu(arg.buf_len); + rx_status = __le32_to_cpu(arg.status); + snr = __le32_to_cpu(arg.snr); + phy_mode = __le32_to_cpu(arg.phy_mode); + rate = __le32_to_cpu(arg.rate); memset(status, 0, sizeof(*status)); @@ -1232,8 +1295,6 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR; status->rate_idx = get_rate_idx(rate, status->band); - skb_pull(skb, pull_len); - hdr = (struct ieee80211_hdr *)skb->data; fc = le16_to_cpu(hdr->frame_control); @@ -1266,12 +1327,6 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) status->freq, status->band, status->signal, status->rate_idx); - /* - * packets from HTC come aligned to 4byte boundaries - * because they can originally come in along with a trailer - */ - skb_trim(skb, buf_len); - ieee80211_rx(ar->hw, skb); return 0; } @@ -1295,21 +1350,44 @@ exit: return idx; } +static int ath10k_wmi_pull_ch_info_ev(struct sk_buff *skb, + struct wmi_ch_info_ev_arg *arg) +{ + struct wmi_chan_info_event *ev = (void *)skb->data; + + if (skb->len < sizeof(*ev)) + return -EPROTO; + + skb_pull(skb, sizeof(*ev)); + arg->err_code = ev->err_code; + arg->freq = ev->freq; + arg->cmd_flags = ev->cmd_flags; + arg->noise_floor = ev->noise_floor; + arg->rx_clear_count = ev->rx_clear_count; + arg->cycle_count = ev->cycle_count; + + return 0; +} + static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) { - struct wmi_chan_info_event *ev; + struct wmi_ch_info_ev_arg arg = {}; struct survey_info *survey; u32 err_code, freq, cmd_flags, noise_floor, rx_clear_count, cycle_count; - int idx; + int idx, ret; - ev = (struct wmi_chan_info_event *)skb->data; + ret = ath10k_wmi_pull_ch_info_ev(skb, &arg); + if (ret) { + ath10k_warn(ar, "failed to parse chan info event: %d\n", ret); + return; + } - err_code = __le32_to_cpu(ev->err_code); - freq = __le32_to_cpu(ev->freq); - cmd_flags = __le32_to_cpu(ev->cmd_flags); - noise_floor = __le32_to_cpu(ev->noise_floor); - rx_clear_count = __le32_to_cpu(ev->rx_clear_count); - cycle_count = __le32_to_cpu(ev->cycle_count); + err_code = __le32_to_cpu(arg.err_code); + freq = __le32_to_cpu(arg.freq); + cmd_flags = __le32_to_cpu(arg.cmd_flags); + noise_floor = __le32_to_cpu(arg.noise_floor); + rx_clear_count = __le32_to_cpu(arg.rx_clear_count); + cycle_count = __le32_to_cpu(arg.cycle_count); ath10k_dbg(ar, ATH10K_DBG_WMI, "chan info err_code %d freq %d cmd_flags %d noise_floor %d rx_clear_count %d cycle_count %d\n", @@ -1566,16 +1644,38 @@ static void ath10k_wmi_event_update_stats(struct ath10k *ar, ath10k_debug_fw_stats_process(ar, skb); } +static int ath10k_wmi_pull_vdev_start_ev(struct sk_buff *skb, + struct wmi_vdev_start_ev_arg *arg) +{ + struct wmi_vdev_start_response_event *ev = (void *)skb->data; + + if (skb->len < sizeof(*ev)) + return -EPROTO; + + skb_pull(skb, sizeof(*ev)); + arg->vdev_id = ev->vdev_id; + arg->req_id = ev->req_id; + arg->resp_type = ev->resp_type; + arg->status = ev->status; + + return 0; +} + static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, struct sk_buff *skb) { - struct wmi_vdev_start_response_event *ev; + struct wmi_vdev_start_ev_arg arg = {}; + int ret; ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_START_RESP_EVENTID\n"); - ev = (struct wmi_vdev_start_response_event *)skb->data; + ret = ath10k_wmi_pull_vdev_start_ev(skb, &arg); + if (ret) { + ath10k_warn(ar, "failed to parse vdev start event: %d\n", ret); + return; + } - if (WARN_ON(__le32_to_cpu(ev->status))) + if (WARN_ON(__le32_to_cpu(arg.status))) return; complete(&ar->vdev_setup_done); @@ -1588,23 +1688,43 @@ static void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, complete(&ar->vdev_setup_done); } +static int ath10k_wmi_pull_peer_kick_ev(struct sk_buff *skb, + struct wmi_peer_kick_ev_arg *arg) +{ + struct wmi_peer_sta_kickout_event *ev = (void *)skb->data; + + if (skb->len < sizeof(*ev)) + return -EPROTO; + + skb_pull(skb, sizeof(*ev)); + arg->mac_addr = ev->peer_macaddr.addr; + + return 0; +} + static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, struct sk_buff *skb) { - struct wmi_peer_sta_kickout_event *ev; + struct wmi_peer_kick_ev_arg arg = {}; struct ieee80211_sta *sta; + int ret; - ev = (struct wmi_peer_sta_kickout_event *)skb->data; + ret = ath10k_wmi_pull_peer_kick_ev(skb, &arg); + if (ret) { + ath10k_warn(ar, "failed to parse peer kickout event: %d\n", + ret); + return; + } ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event peer sta kickout %pM\n", - ev->peer_macaddr.addr); + arg.mac_addr); rcu_read_lock(); - sta = ieee80211_find_sta_by_ifaddr(ar->hw, ev->peer_macaddr.addr, NULL); + sta = ieee80211_find_sta_by_ifaddr(ar->hw, arg.mac_addr, NULL); if (!sta) { ath10k_warn(ar, "Spurious quick kickout for STA %pM\n", - ev->peer_macaddr.addr); + arg.mac_addr); goto exit; } @@ -1641,7 +1761,7 @@ exit: static void ath10k_wmi_update_tim(struct ath10k *ar, struct ath10k_vif *arvif, struct sk_buff *bcn, - struct wmi_bcn_info *bcn_info) + const struct wmi_tim_info *tim_info) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)bcn->data; struct ieee80211_tim_ie *tim; @@ -1652,14 +1772,14 @@ static void ath10k_wmi_update_tim(struct ath10k *ar, /* if next SWBA has no tim_changed the tim_bitmap is garbage. * we must copy the bitmap upon change and reuse it later */ - if (__le32_to_cpu(bcn_info->tim_info.tim_changed)) { + if (__le32_to_cpu(tim_info->tim_changed)) { int i; BUILD_BUG_ON(sizeof(arvif->u.ap.tim_bitmap) != - sizeof(bcn_info->tim_info.tim_bitmap)); + sizeof(tim_info->tim_bitmap)); for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++) { - t = bcn_info->tim_info.tim_bitmap[i / 4]; + t = tim_info->tim_bitmap[i / 4]; v = __le32_to_cpu(t); arvif->u.ap.tim_bitmap[i] = (v >> ((i % 4) * 8)) & 0xFF; } @@ -1711,13 +1831,13 @@ static void ath10k_wmi_update_tim(struct ath10k *ar, return; } - tim->bitmap_ctrl = !!__le32_to_cpu(bcn_info->tim_info.tim_mcast); + tim->bitmap_ctrl = !!__le32_to_cpu(tim_info->tim_mcast); memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len); if (tim->dtim_count == 0) { ATH10K_SKB_CB(bcn)->bcn.dtim_zero = true; - if (__le32_to_cpu(bcn_info->tim_info.tim_mcast) == 1) + if (__le32_to_cpu(tim_info->tim_mcast) == 1) ATH10K_SKB_CB(bcn)->bcn.deliver_cab = true; } @@ -1727,7 +1847,7 @@ static void ath10k_wmi_update_tim(struct ath10k *ar, } static void ath10k_p2p_fill_noa_ie(u8 *data, u32 len, - struct wmi_p2p_noa_info *noa) + const struct wmi_p2p_noa_info *noa) { struct ieee80211_p2p_noa_attr *noa_attr; u8 ctwindow_oppps = noa->ctwindow_oppps; @@ -1769,7 +1889,7 @@ static void ath10k_p2p_fill_noa_ie(u8 *data, u32 len, *noa_attr_len = __cpu_to_le16(attr_len); } -static u32 ath10k_p2p_calc_noa_ie_len(struct wmi_p2p_noa_info *noa) +static u32 ath10k_p2p_calc_noa_ie_len(const struct wmi_p2p_noa_info *noa) { u32 len = 0; u8 noa_descriptors = noa->num_descriptors; @@ -1789,9 +1909,8 @@ static u32 ath10k_p2p_calc_noa_ie_len(struct wmi_p2p_noa_info *noa) static void ath10k_wmi_update_noa(struct ath10k *ar, struct ath10k_vif *arvif, struct sk_buff *bcn, - struct wmi_bcn_info *bcn_info) + const struct wmi_p2p_noa_info *noa) { - struct wmi_p2p_noa_info *noa = &bcn_info->p2p_noa_info; u8 *new_data, *old_data = arvif->u.ap.noa_data; u32 new_len; @@ -1832,22 +1951,59 @@ cleanup: kfree(old_data); } +static int ath10k_wmi_pull_swba_ev(struct sk_buff *skb, + struct wmi_swba_ev_arg *arg) +{ + struct wmi_host_swba_event *ev = (void *)skb->data; + u32 map; + size_t i; + + if (skb->len < sizeof(*ev)) + return -EPROTO; + + skb_pull(skb, sizeof(*ev)); + arg->vdev_map = ev->vdev_map; + + for (i = 0, map = __le32_to_cpu(ev->vdev_map); map; map >>= 1) { + if (!(map & BIT(0))) + continue; + + /* If this happens there were some changes in firmware and + * ath10k should update the max size of tim_info array. + */ + if (WARN_ON_ONCE(i == ARRAY_SIZE(arg->tim_info))) + break; + + arg->tim_info[i] = &ev->bcn_info[i].tim_info; + arg->noa_info[i] = &ev->bcn_info[i].p2p_noa_info; + i++; + } + + return 0; +} + static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) { - struct wmi_host_swba_event *ev; + struct wmi_swba_ev_arg arg = {}; u32 map; int i = -1; - struct wmi_bcn_info *bcn_info; + const struct wmi_tim_info *tim_info; + const struct wmi_p2p_noa_info *noa_info; struct ath10k_vif *arvif; struct sk_buff *bcn; dma_addr_t paddr; int ret, vdev_id = 0; - ev = (struct wmi_host_swba_event *)skb->data; - map = __le32_to_cpu(ev->vdev_map); + ret = ath10k_wmi_pull_swba_ev(skb, &arg); + if (ret) { + ath10k_warn(ar, "failed to parse swba event: %d\n", ret); + return; + } + + map = __le32_to_cpu(arg.vdev_map); ath10k_dbg(ar, ATH10K_DBG_MGMT, "mgmt swba vdev_map 0x%x\n", - ev->vdev_map); + map); for (; map; map >>= 1, vdev_id++) { if (!(map & 0x1)) @@ -1860,19 +2016,20 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) break; } - bcn_info = &ev->bcn_info[i]; + tim_info = arg.tim_info[i]; + noa_info = arg.noa_info[i]; ath10k_dbg(ar, ATH10K_DBG_MGMT, "mgmt event bcn_info %d tim_len %d mcast %d changed %d num_ps_pending %d bitmap 0x%08x%08x%08x%08x\n", i, - __le32_to_cpu(bcn_info->tim_info.tim_len), - __le32_to_cpu(bcn_info->tim_info.tim_mcast), - __le32_to_cpu(bcn_info->tim_info.tim_changed), - __le32_to_cpu(bcn_info->tim_info.tim_num_ps_pending), - __le32_to_cpu(bcn_info->tim_info.tim_bitmap[3]), - __le32_to_cpu(bcn_info->tim_info.tim_bitmap[2]), - __le32_to_cpu(bcn_info->tim_info.tim_bitmap[1]), - __le32_to_cpu(bcn_info->tim_info.tim_bitmap[0])); + __le32_to_cpu(tim_info->tim_len), + __le32_to_cpu(tim_info->tim_mcast), + __le32_to_cpu(tim_info->tim_changed), + __le32_to_cpu(tim_info->tim_num_ps_pending), + __le32_to_cpu(tim_info->tim_bitmap[3]), + __le32_to_cpu(tim_info->tim_bitmap[2]), + __le32_to_cpu(tim_info->tim_bitmap[1]), + __le32_to_cpu(tim_info->tim_bitmap[0])); arvif = ath10k_get_arvif(ar, vdev_id); if (arvif == NULL) { @@ -1899,8 +2056,8 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) } ath10k_tx_h_seq_no(arvif->vif, bcn); - ath10k_wmi_update_tim(ar, arvif, bcn, bcn_info); - ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info); + ath10k_wmi_update_tim(ar, arvif, bcn, tim_info); + ath10k_wmi_update_noa(ar, arvif, bcn, noa_info); spin_lock_bh(&ar->data_lock); @@ -2188,37 +2345,53 @@ ath10k_wmi_event_spectral_scan(struct ath10k *ar, } } +static int ath10k_wmi_pull_phyerr_ev(struct sk_buff *skb, + struct wmi_phyerr_ev_arg *arg) +{ + struct wmi_phyerr_event *ev = (void *)skb->data; + + if (skb->len < sizeof(*ev)) + return -EPROTO; + + arg->num_phyerrs = ev->num_phyerrs; + arg->tsf_l32 = ev->tsf_l32; + arg->tsf_u32 = ev->tsf_u32; + arg->buf_len = __cpu_to_le32(skb->len - sizeof(*ev)); + arg->phyerrs = ev->phyerrs; + + return 0; +} + static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb) { - const struct wmi_phyerr_event *ev; + struct wmi_phyerr_ev_arg arg = {}; const struct wmi_phyerr *phyerr; u32 count, i, buf_len, phy_err_code; u64 tsf; - int left_len = skb->len; + int left_len, ret; ATH10K_DFS_STAT_INC(ar, phy_errors); - /* Check if combined event available */ - if (left_len < sizeof(*ev)) { - ath10k_warn(ar, "wmi phyerr combined event wrong len\n"); + ret = ath10k_wmi_pull_phyerr_ev(skb, &arg); + if (ret) { + ath10k_warn(ar, "failed to parse phyerr event: %d\n", ret); return; } - left_len -= sizeof(*ev); + left_len = __le32_to_cpu(arg.buf_len); /* Check number of included events */ - ev = (const struct wmi_phyerr_event *)skb->data; - count = __le32_to_cpu(ev->num_phyerrs); + count = __le32_to_cpu(arg.num_phyerrs); - tsf = __le32_to_cpu(ev->tsf_u32); + tsf = __le32_to_cpu(arg.tsf_u32); tsf <<= 32; - tsf |= __le32_to_cpu(ev->tsf_l32); + tsf |= __le32_to_cpu(arg.tsf_l32); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event phyerr count %d tsf64 0x%llX\n", count, tsf); - phyerr = ev->phyerrs; + phyerr = arg.phyerrs; for (i = 0; i < count; i++) { /* Check if we can read event header */ if (left_len < sizeof(*phyerr)) { @@ -2622,22 +2795,42 @@ static void ath10k_wmi_event_service_ready(struct ath10k *ar, complete(&ar->wmi.service_ready); } -static int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb) +static int ath10k_wmi_pull_rdy_ev(struct sk_buff *skb, + struct wmi_rdy_ev_arg *arg) { - struct wmi_ready_event *ev = (struct wmi_ready_event *)skb->data; + struct wmi_ready_event *ev = (void *)skb->data; - if (WARN_ON(skb->len < sizeof(*ev))) - return -EINVAL; + if (skb->len < sizeof(*ev)) + return -EPROTO; + + skb_pull(skb, sizeof(*ev)); + arg->sw_version = ev->sw_version; + arg->abi_version = ev->abi_version; + arg->status = ev->status; + arg->mac_addr = ev->mac_addr.addr; + + return 0; +} - ether_addr_copy(ar->mac_addr, ev->mac_addr.addr); +static int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_rdy_ev_arg arg = {}; + int ret; + + ret = ath10k_wmi_pull_rdy_ev(skb, &arg); + if (ret) { + ath10k_warn(ar, "failed to parse ready event: %d\n", ret); + return ret; + } ath10k_dbg(ar, ATH10K_DBG_WMI, - "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d skb->len %i ev-sz %zu\n", - __le32_to_cpu(ev->sw_version), - __le32_to_cpu(ev->abi_version), - ev->mac_addr.addr, - __le32_to_cpu(ev->status), skb->len, sizeof(*ev)); + "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d\n", + __le32_to_cpu(arg.sw_version), + __le32_to_cpu(arg.abi_version), + arg.mac_addr, + __le32_to_cpu(arg.status)); + ether_addr_copy(ar->mac_addr, arg.mac_addr); complete(&ar->wmi.unified_ready); return 0; } diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 2139192..0a38798 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -4120,7 +4120,7 @@ struct wmi_bcn_info { struct wmi_host_swba_event { __le32 vdev_map; - struct wmi_bcn_info bcn_info[1]; + struct wmi_bcn_info bcn_info[0]; } __packed; #define WMI_MAX_AP_VDEV 16 @@ -4567,6 +4567,58 @@ struct wmi_dbglog_cfg_cmd { #define WMI_MAX_MEM_REQS 16 +struct wmi_scan_ev_arg { + __le32 event_type; /* %WMI_SCAN_EVENT_ */ + __le32 reason; /* %WMI_SCAN_REASON_ */ + __le32 channel_freq; /* only valid for WMI_SCAN_EVENT_FOREIGN_CHANNEL */ + __le32 scan_req_id; + __le32 scan_id; + __le32 vdev_id; +}; + +struct wmi_mgmt_rx_ev_arg { + __le32 channel; + __le32 snr; + __le32 rate; + __le32 phy_mode; + __le32 buf_len; + __le32 status; /* %WMI_RX_STATUS_ */ +}; + +struct wmi_ch_info_ev_arg { + __le32 err_code; + __le32 freq; + __le32 cmd_flags; + __le32 noise_floor; + __le32 rx_clear_count; + __le32 cycle_count; +}; + +struct wmi_vdev_start_ev_arg { + __le32 vdev_id; + __le32 req_id; + __le32 resp_type; /* %WMI_VDEV_RESP_ */ + __le32 status; +}; + +struct wmi_peer_kick_ev_arg { + const u8 *mac_addr; +}; + +struct wmi_swba_ev_arg { + __le32 vdev_map; + const struct wmi_tim_info *tim_info[WMI_MAX_AP_VDEV]; + const struct wmi_p2p_noa_info *noa_info[WMI_MAX_AP_VDEV]; +}; + +struct wmi_phyerr_ev_arg { + __le32 num_phyerrs; + __le32 tsf_l32; + __le32 tsf_u32; + __le32 buf_len; + const struct wmi_phyerr *phyerrs; +}; + struct wmi_svc_rdy_ev_arg { __le32 min_tx_power; __le32 max_tx_power; @@ -4583,6 +4635,13 @@ struct wmi_svc_rdy_ev_arg { const struct wlan_host_mem_req *mem_reqs[WMI_MAX_MEM_REQS]; }; +struct wmi_rdy_ev_arg { + __le32 sw_version; + __le32 abi_version; + __le32 status; + const u8 *mac_addr; +}; + struct ath10k; struct ath10k_vif; struct ath10k_fw_stats; -- cgit v0.10.2 From d7579d12c33f87de9975d17880d708b50e959bbb Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 3 Dec 2014 10:10:54 +0200 Subject: ath10k: introduce wmi ops Since the 10.x fw branch support was introduced it became apparent ath10k will need to be able to deal with different fw ABIs eventually. The patch creates an abstraction for dealing with command and event structures across different ABIs and mostly gets rid of the ATH10K_FW_FEATURE_WMI_10X flag usage. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 54a1257..d83d9a7 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -28,6 +28,7 @@ #include "debug.h" #include "htt.h" #include "testmode.h" +#include "wmi-ops.h" unsigned int ath10k_debug_mask; static bool uart_print; diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 0ae1df6..3f15c13 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -129,6 +129,7 @@ struct ath10k_wmi { struct wmi_cmd_map *cmd; struct wmi_vdev_param_map *vdev_param; struct wmi_pdev_param_map *pdev_param; + const struct wmi_ops *ops; u32 num_mem_chunks; struct ath10k_mem_chunk mem_chunks[WMI_MAX_MEM_REQS]; @@ -618,6 +619,7 @@ struct ath10k { /* protected by conf_mutex */ const struct firmware *utf; DECLARE_BITMAP(orig_fw_features, ATH10K_FW_FEATURE_COUNT); + enum ath10k_fw_wmi_op_version orig_wmi_op_version; /* protected by data_lock */ bool utf_monitor; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index a716758..c15b5774 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -23,6 +23,7 @@ #include "core.h" #include "debug.h" #include "hif.h" +#include "wmi-ops.h" /* ms */ #define ATH10K_DEBUG_HTT_STATS_INTERVAL 1000 diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index c400567..5475f0f 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -27,6 +27,8 @@ #include "htt.h" #include "txrx.h" #include "testmode.h" +#include "wmi.h" +#include "wmi-ops.h" /**********/ /* Crypto */ diff --git a/drivers/net/wireless/ath/ath10k/spectral.c b/drivers/net/wireless/ath/ath10k/spectral.c index 63ce61f..d22addf 100644 --- a/drivers/net/wireless/ath/ath10k/spectral.c +++ b/drivers/net/wireless/ath/ath10k/spectral.c @@ -17,6 +17,7 @@ #include #include "core.h" #include "debug.h" +#include "wmi-ops.h" static void send_fft_sample(struct ath10k *ar, const struct fft_sample_tlv *fft_sample_tlv) diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c index 483db9c..b084f88 100644 --- a/drivers/net/wireless/ath/ath10k/testmode.c +++ b/drivers/net/wireless/ath/ath10k/testmode.c @@ -187,13 +187,14 @@ static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[]) memcpy(ar->testmode.orig_fw_features, ar->fw_features, sizeof(ar->fw_features)); + ar->testmode.orig_wmi_op_version = ar->wmi.op_version; /* utf.bin firmware image does not advertise firmware features. Do * an ugly hack where we force the firmware features so that wmi.c * will use the correct WMI interface. */ memset(ar->fw_features, 0, sizeof(ar->fw_features)); - __set_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features); + ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_10_1; ret = ath10k_hif_power_up(ar); if (ret) { @@ -224,6 +225,7 @@ err_fw_features: /* return the original firmware features */ memcpy(ar->fw_features, ar->testmode.orig_fw_features, sizeof(ar->fw_features)); + ar->wmi.op_version = ar->testmode.orig_wmi_op_version; release_firmware(ar->testmode.utf); ar->testmode.utf = NULL; @@ -250,6 +252,7 @@ static void __ath10k_tm_cmd_utf_stop(struct ath10k *ar) /* return the original firmware features */ memcpy(ar->fw_features, ar->testmode.orig_fw_features, sizeof(ar->fw_features)); + ar->wmi.op_version = ar->testmode.orig_wmi_op_version; release_firmware(ar->testmode.utf); ar->testmode.utf = NULL; diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h new file mode 100644 index 0000000..1fbc520 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h @@ -0,0 +1,821 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2014 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WMI_OPS_H_ +#define _WMI_OPS_H_ + +struct ath10k; +struct sk_buff; + +struct wmi_ops { + void (*rx)(struct ath10k *ar, struct sk_buff *skb); + void (*map_svc)(const __le32 *in, unsigned long *out, size_t len); + + int (*pull_scan)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_scan_ev_arg *arg); + int (*pull_mgmt_rx)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_mgmt_rx_ev_arg *arg); + int (*pull_ch_info)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_ch_info_ev_arg *arg); + int (*pull_vdev_start)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_vdev_start_ev_arg *arg); + int (*pull_peer_kick)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_peer_kick_ev_arg *arg); + int (*pull_swba)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_swba_ev_arg *arg); + int (*pull_phyerr)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_phyerr_ev_arg *arg); + int (*pull_svc_rdy)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_svc_rdy_ev_arg *arg); + int (*pull_rdy)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_rdy_ev_arg *arg); + int (*pull_fw_stats)(struct ath10k *ar, struct sk_buff *skb, + struct ath10k_fw_stats *stats); + + struct sk_buff *(*gen_pdev_suspend)(struct ath10k *ar, u32 suspend_opt); + struct sk_buff *(*gen_pdev_resume)(struct ath10k *ar); + struct sk_buff *(*gen_pdev_set_rd)(struct ath10k *ar, u16 rd, u16 rd2g, + u16 rd5g, u16 ctl2g, u16 ctl5g, + enum wmi_dfs_region dfs_reg); + struct sk_buff *(*gen_pdev_set_param)(struct ath10k *ar, u32 id, + u32 value); + struct sk_buff *(*gen_init)(struct ath10k *ar); + struct sk_buff *(*gen_start_scan)(struct ath10k *ar, + const struct wmi_start_scan_arg *arg); + struct sk_buff *(*gen_stop_scan)(struct ath10k *ar, + const struct wmi_stop_scan_arg *arg); + struct sk_buff *(*gen_vdev_create)(struct ath10k *ar, u32 vdev_id, + enum wmi_vdev_type type, + enum wmi_vdev_subtype subtype, + const u8 macaddr[ETH_ALEN]); + struct sk_buff *(*gen_vdev_delete)(struct ath10k *ar, u32 vdev_id); + struct sk_buff *(*gen_vdev_start)(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *arg, + bool restart); + struct sk_buff *(*gen_vdev_stop)(struct ath10k *ar, u32 vdev_id); + struct sk_buff *(*gen_vdev_up)(struct ath10k *ar, u32 vdev_id, u32 aid, + const u8 *bssid); + struct sk_buff *(*gen_vdev_down)(struct ath10k *ar, u32 vdev_id); + struct sk_buff *(*gen_vdev_set_param)(struct ath10k *ar, u32 vdev_id, + u32 param_id, u32 param_value); + struct sk_buff *(*gen_vdev_install_key)(struct ath10k *ar, + const struct wmi_vdev_install_key_arg *arg); + struct sk_buff *(*gen_vdev_spectral_conf)(struct ath10k *ar, + const struct wmi_vdev_spectral_conf_arg *arg); + struct sk_buff *(*gen_vdev_spectral_enable)(struct ath10k *ar, u32 vdev_id, + u32 trigger, u32 enable); + struct sk_buff *(*gen_peer_create)(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]); + struct sk_buff *(*gen_peer_delete)(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]); + struct sk_buff *(*gen_peer_flush)(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN], + u32 tid_bitmap); + struct sk_buff *(*gen_peer_set_param)(struct ath10k *ar, u32 vdev_id, + const u8 *peer_addr, + enum wmi_peer_param param_id, + u32 param_value); + struct sk_buff *(*gen_peer_assoc)(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg); + struct sk_buff *(*gen_set_psmode)(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_ps_mode psmode); + struct sk_buff *(*gen_set_sta_ps)(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_powersave_param param_id, + u32 value); + struct sk_buff *(*gen_set_ap_ps)(struct ath10k *ar, u32 vdev_id, + const u8 *mac, + enum wmi_ap_ps_peer_param param_id, + u32 value); + struct sk_buff *(*gen_scan_chan_list)(struct ath10k *ar, + const struct wmi_scan_chan_list_arg *arg); + struct sk_buff *(*gen_beacon_dma)(struct ath10k_vif *arvif); + struct sk_buff *(*gen_pdev_set_wmm)(struct ath10k *ar, + const struct wmi_pdev_set_wmm_params_arg *arg); + struct sk_buff *(*gen_request_stats)(struct ath10k *ar, + enum wmi_stats_id stats_id); + struct sk_buff *(*gen_force_fw_hang)(struct ath10k *ar, + enum wmi_force_fw_hang_type type, + u32 delay_ms); + struct sk_buff *(*gen_mgmt_tx)(struct ath10k *ar, struct sk_buff *skb); + struct sk_buff *(*gen_dbglog_cfg)(struct ath10k *ar, u32 module_enable); + struct sk_buff *(*gen_pktlog_enable)(struct ath10k *ar, u32 filter); + struct sk_buff *(*gen_pktlog_disable)(struct ath10k *ar); +}; + +int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id); + +static inline int +ath10k_wmi_rx(struct ath10k *ar, struct sk_buff *skb) +{ + if (WARN_ON_ONCE(!ar->wmi.ops->rx)) + return -EOPNOTSUPP; + + ar->wmi.ops->rx(ar, skb); + return 0; +} + +static inline int +ath10k_wmi_map_svc(struct ath10k *ar, const __le32 *in, unsigned long *out, + size_t len) +{ + if (!ar->wmi.ops->map_svc) + return -EOPNOTSUPP; + + ar->wmi.ops->map_svc(in, out, len); + return 0; +} + +static inline int +ath10k_wmi_pull_scan(struct ath10k *ar, struct sk_buff *skb, + struct wmi_scan_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_scan) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_scan(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_mgmt_rx(struct ath10k *ar, struct sk_buff *skb, + struct wmi_mgmt_rx_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_mgmt_rx) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_mgmt_rx(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_ch_info(struct ath10k *ar, struct sk_buff *skb, + struct wmi_ch_info_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_ch_info) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_ch_info(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_vdev_start(struct ath10k *ar, struct sk_buff *skb, + struct wmi_vdev_start_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_vdev_start) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_vdev_start(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_peer_kick(struct ath10k *ar, struct sk_buff *skb, + struct wmi_peer_kick_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_peer_kick) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_peer_kick(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_swba(struct ath10k *ar, struct sk_buff *skb, + struct wmi_swba_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_swba) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_swba(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_phyerr(struct ath10k *ar, struct sk_buff *skb, + struct wmi_phyerr_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_phyerr) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_phyerr(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_svc_rdy(struct ath10k *ar, struct sk_buff *skb, + struct wmi_svc_rdy_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_svc_rdy) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_svc_rdy(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_rdy(struct ath10k *ar, struct sk_buff *skb, + struct wmi_rdy_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_rdy) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_rdy(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb, + struct ath10k_fw_stats *stats) +{ + if (!ar->wmi.ops->pull_fw_stats) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_fw_stats(ar, skb, stats); +} + +static inline int +ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(msdu); + struct sk_buff *skb; + int ret; + + if (!ar->wmi.ops->gen_mgmt_tx) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_mgmt_tx(ar, msdu); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + ret = ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->mgmt_tx_cmdid); + if (ret) + return ret; + + /* FIXME There's no ACK event for Management Tx. This probably + * shouldn't be called here either. */ + info->flags |= IEEE80211_TX_STAT_ACK; + ieee80211_tx_status_irqsafe(ar->hw, msdu); + + return 0; +} + +static inline int +ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, u16 rd5g, + u16 ctl2g, u16 ctl5g, + enum wmi_dfs_region dfs_reg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pdev_set_rd) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pdev_set_rd(ar, rd, rd2g, rd5g, ctl2g, ctl5g, + dfs_reg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_set_regdomain_cmdid); +} + +static inline int +ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pdev_suspend) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pdev_suspend(ar, suspend_opt); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid); +} + +static inline int +ath10k_wmi_pdev_resume_target(struct ath10k *ar) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pdev_resume) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pdev_resume(ar); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_resume_cmdid); +} + +static inline int +ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pdev_set_param) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pdev_set_param(ar, id, value); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid); +} + +static inline int +ath10k_wmi_cmd_init(struct ath10k *ar) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_init) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_init(ar); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->init_cmdid); +} + +static inline int +ath10k_wmi_start_scan(struct ath10k *ar, + const struct wmi_start_scan_arg *arg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_start_scan) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_start_scan(ar, arg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->start_scan_cmdid); +} + +static inline int +ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_stop_scan) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_stop_scan(ar, arg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->stop_scan_cmdid); +} + +static inline int +ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, + enum wmi_vdev_type type, + enum wmi_vdev_subtype subtype, + const u8 macaddr[ETH_ALEN]) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_create) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_create(ar, vdev_id, type, subtype, macaddr); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_create_cmdid); +} + +static inline int +ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_delete) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_delete(ar, vdev_id); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_delete_cmdid); +} + +static inline int +ath10k_wmi_vdev_start(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *arg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_start) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_start(ar, arg, false); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->vdev_start_request_cmdid); +} + +static inline int +ath10k_wmi_vdev_restart(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *arg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_start) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_start(ar, arg, true); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->vdev_restart_request_cmdid); +} + +static inline int +ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_stop) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_stop(ar, vdev_id); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_stop_cmdid); +} + +static inline int +ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_up) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_up(ar, vdev_id, aid, bssid); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_up_cmdid); +} + +static inline int +ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_down) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_down(ar, vdev_id); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_down_cmdid); +} + +static inline int +ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, u32 param_id, + u32 param_value) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_set_param) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_set_param(ar, vdev_id, param_id, + param_value); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_set_param_cmdid); +} + +static inline int +ath10k_wmi_vdev_install_key(struct ath10k *ar, + const struct wmi_vdev_install_key_arg *arg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_install_key) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_install_key(ar, arg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->vdev_install_key_cmdid); +} + +static inline int +ath10k_wmi_vdev_spectral_conf(struct ath10k *ar, + const struct wmi_vdev_spectral_conf_arg *arg) +{ + struct sk_buff *skb; + u32 cmd_id; + + skb = ar->wmi.ops->gen_vdev_spectral_conf(ar, arg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + cmd_id = ar->wmi.cmd->vdev_spectral_scan_configure_cmdid; + return ath10k_wmi_cmd_send(ar, skb, cmd_id); +} + +static inline int +ath10k_wmi_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, u32 trigger, + u32 enable) +{ + struct sk_buff *skb; + u32 cmd_id; + + skb = ar->wmi.ops->gen_vdev_spectral_enable(ar, vdev_id, trigger, + enable); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + cmd_id = ar->wmi.cmd->vdev_spectral_scan_enable_cmdid; + return ath10k_wmi_cmd_send(ar, skb, cmd_id); +} + +static inline int +ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_peer_create) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_peer_create(ar, vdev_id, peer_addr); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_create_cmdid); +} + +static inline int +ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_peer_delete) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_peer_delete(ar, vdev_id, peer_addr); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_delete_cmdid); +} + +static inline int +ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN], u32 tid_bitmap) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_peer_flush) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_peer_flush(ar, vdev_id, peer_addr, tid_bitmap); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_flush_tids_cmdid); +} + +static inline int +ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, const u8 *peer_addr, + enum wmi_peer_param param_id, u32 param_value) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_peer_set_param) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_peer_set_param(ar, vdev_id, peer_addr, param_id, + param_value); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_set_param_cmdid); +} + +static inline int +ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_ps_mode psmode) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_set_psmode) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_set_psmode(ar, vdev_id, psmode); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->sta_powersave_mode_cmdid); +} + +static inline int +ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_powersave_param param_id, u32 value) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_set_sta_ps) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_set_sta_ps(ar, vdev_id, param_id, value); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->sta_powersave_param_cmdid); +} + +static inline int +ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, + enum wmi_ap_ps_peer_param param_id, u32 value) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_set_ap_ps) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_set_ap_ps(ar, vdev_id, mac, param_id, value); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->ap_ps_peer_param_cmdid); +} + +static inline int +ath10k_wmi_scan_chan_list(struct ath10k *ar, + const struct wmi_scan_chan_list_arg *arg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_scan_chan_list) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_scan_chan_list(ar, arg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid); +} + +static inline int +ath10k_wmi_peer_assoc(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_peer_assoc) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_peer_assoc(ar, arg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid); +} + +static inline int +ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + struct sk_buff *skb; + int ret; + + if (!ar->wmi.ops->gen_beacon_dma) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_beacon_dma(arvif); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + ret = ath10k_wmi_cmd_send_nowait(ar, skb, + ar->wmi.cmd->pdev_send_bcn_cmdid); + if (ret) { + dev_kfree_skb(skb); + return ret; + } + + return 0; +} + +static inline int +ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, + const struct wmi_pdev_set_wmm_params_arg *arg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pdev_set_wmm) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pdev_set_wmm(ar, arg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_set_wmm_params_cmdid); +} + +static inline int +ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_request_stats) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_request_stats(ar, stats_id); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->request_stats_cmdid); +} + +static inline int +ath10k_wmi_force_fw_hang(struct ath10k *ar, + enum wmi_force_fw_hang_type type, u32 delay_ms) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_force_fw_hang) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_force_fw_hang(ar, type, delay_ms); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid); +} + +static inline int +ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_dbglog_cfg) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_dbglog_cfg(ar, module_enable); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->dbglog_cfg_cmdid); +} + +static inline int +ath10k_wmi_pdev_pktlog_enable(struct ath10k *ar, u32 filter) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pktlog_enable) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pktlog_enable(ar, filter); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_pktlog_enable_cmdid); +} + +static inline int +ath10k_wmi_pdev_pktlog_disable(struct ath10k *ar) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pktlog_disable) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pktlog_disable(ar); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_pktlog_disable_cmdid); +} + +#endif diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 4a9468e..24bb97f 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -24,6 +24,7 @@ #include "wmi.h" #include "mac.h" #include "testmode.h" +#include "wmi-ops.h" /* MAIN WMI cmd track */ static struct wmi_cmd_map wmi_cmd_map = { @@ -685,8 +686,8 @@ static void ath10k_wmi_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) dev_kfree_skb(skb); } -static int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb, - u32 cmd_id) +int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb, + u32 cmd_id) { struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); struct wmi_cmd_hdr *cmd_hdr; @@ -792,24 +793,23 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id) return ret; } -int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) +static struct sk_buff * +ath10k_wmi_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu) { - int ret = 0; struct wmi_mgmt_tx_cmd *cmd; struct ieee80211_hdr *hdr; - struct sk_buff *wmi_skb; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct sk_buff *skb; int len; - u32 buf_len = skb->len; + u32 buf_len = msdu->len; u16 fc; - hdr = (struct ieee80211_hdr *)skb->data; + hdr = (struct ieee80211_hdr *)msdu->data; fc = le16_to_cpu(hdr->frame_control); if (WARN_ON_ONCE(!ieee80211_is_mgmt(hdr->frame_control))) - return -EINVAL; + return ERR_PTR(-EINVAL); - len = sizeof(cmd->hdr) + skb->len; + len = sizeof(cmd->hdr) + msdu->len; if ((ieee80211_is_action(hdr->frame_control) || ieee80211_is_deauth(hdr->frame_control) || @@ -821,36 +821,27 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) len = round_up(len, 4); - wmi_skb = ath10k_wmi_alloc_skb(ar, len); - if (!wmi_skb) - return -ENOMEM; + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); - cmd = (struct wmi_mgmt_tx_cmd *)wmi_skb->data; + cmd = (struct wmi_mgmt_tx_cmd *)skb->data; - cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(skb)->vdev_id); + cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(msdu)->vdev_id); cmd->hdr.tx_rate = 0; cmd->hdr.tx_power = 0; cmd->hdr.buf_len = __cpu_to_le32(buf_len); ether_addr_copy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr)); - memcpy(cmd->buf, skb->data, skb->len); + memcpy(cmd->buf, msdu->data, msdu->len); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n", - wmi_skb, wmi_skb->len, fc & IEEE80211_FCTL_FTYPE, + msdu, skb->len, fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE); trace_ath10k_tx_hdr(ar, skb->data, skb->len); trace_ath10k_tx_payload(ar, skb->data, skb->len); - /* Send the management frame buffer to the target */ - ret = ath10k_wmi_cmd_send(ar, wmi_skb, ar->wmi.cmd->mgmt_tx_cmdid); - if (ret) - return ret; - - /* TODO: report tx status to mac80211 - temporary just ACK */ - info->flags |= IEEE80211_TX_STAT_ACK; - ieee80211_tx_status_irqsafe(ar->hw, skb); - - return ret; + return skb; } static void ath10k_wmi_event_scan_started(struct ath10k *ar) @@ -977,8 +968,8 @@ ath10k_wmi_event_scan_type_str(enum wmi_scan_event_type type, } } -static int ath10k_wmi_pull_scan_ev(struct sk_buff *skb, - struct wmi_scan_ev_arg *arg) +static int ath10k_wmi_op_pull_scan_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_scan_ev_arg *arg) { struct wmi_scan_event *ev = (void *)skb->data; @@ -1007,7 +998,7 @@ static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb) u32 vdev_id; int ret; - ret = ath10k_wmi_pull_scan_ev(skb, &arg); + ret = ath10k_wmi_pull_scan(ar, skb, &arg); if (ret) { ath10k_warn(ar, "failed to parse scan event: %d\n", ret); return ret; @@ -1173,9 +1164,8 @@ static void ath10k_wmi_handle_wep_reauth(struct ath10k *ar, } } -static int ath10k_wmi_pull_mgmt_rx_ev(struct sk_buff *skb, - struct wmi_mgmt_rx_ev_arg *arg, - struct ath10k *ar) +static int ath10k_wmi_op_pull_mgmt_rx_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_mgmt_rx_ev_arg *arg) { struct wmi_mgmt_rx_event_v1 *ev_v1; struct wmi_mgmt_rx_event_v2 *ev_v2; @@ -1230,7 +1220,7 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) u16 fc; int ret; - ret = ath10k_wmi_pull_mgmt_rx_ev(skb, &arg, ar); + ret = ath10k_wmi_pull_mgmt_rx(ar, skb, &arg); if (ret) { ath10k_warn(ar, "failed to parse mgmt rx event: %d\n", ret); return ret; @@ -1350,8 +1340,8 @@ exit: return idx; } -static int ath10k_wmi_pull_ch_info_ev(struct sk_buff *skb, - struct wmi_ch_info_ev_arg *arg) +static int ath10k_wmi_op_pull_ch_info_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_ch_info_ev_arg *arg) { struct wmi_chan_info_event *ev = (void *)skb->data; @@ -1376,7 +1366,7 @@ static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) u32 err_code, freq, cmd_flags, noise_floor, rx_clear_count, cycle_count; int idx, ret; - ret = ath10k_wmi_pull_ch_info_ev(skb, &arg); + ret = ath10k_wmi_pull_ch_info(ar, skb, &arg); if (ret) { ath10k_warn(ar, "failed to parse chan info event: %d\n", ret); return; @@ -1513,9 +1503,9 @@ static void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src, dst->peer_tx_rate = __le32_to_cpu(src->peer_tx_rate); } -static int ath10k_wmi_main_pull_fw_stats(struct ath10k *ar, - struct sk_buff *skb, - struct ath10k_fw_stats *stats) +static int ath10k_wmi_main_op_pull_fw_stats(struct ath10k *ar, + struct sk_buff *skb, + struct ath10k_fw_stats *stats) { const struct wmi_stats_event *ev = (void *)skb->data; u32 num_pdev_stats, num_vdev_stats, num_peer_stats; @@ -1565,9 +1555,9 @@ static int ath10k_wmi_main_pull_fw_stats(struct ath10k *ar, return 0; } -static int ath10k_wmi_10x_pull_fw_stats(struct ath10k *ar, - struct sk_buff *skb, - struct ath10k_fw_stats *stats) +static int ath10k_wmi_10x_op_pull_fw_stats(struct ath10k *ar, + struct sk_buff *skb, + struct ath10k_fw_stats *stats) { const struct wmi_stats_event *ev = (void *)skb->data; u32 num_pdev_stats, num_vdev_stats, num_peer_stats; @@ -1628,15 +1618,6 @@ static int ath10k_wmi_10x_pull_fw_stats(struct ath10k *ar, return 0; } -int ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb, - struct ath10k_fw_stats *stats) -{ - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) - return ath10k_wmi_10x_pull_fw_stats(ar, skb, stats); - else - return ath10k_wmi_main_pull_fw_stats(ar, skb, stats); -} - static void ath10k_wmi_event_update_stats(struct ath10k *ar, struct sk_buff *skb) { @@ -1644,8 +1625,9 @@ static void ath10k_wmi_event_update_stats(struct ath10k *ar, ath10k_debug_fw_stats_process(ar, skb); } -static int ath10k_wmi_pull_vdev_start_ev(struct sk_buff *skb, - struct wmi_vdev_start_ev_arg *arg) +static int +ath10k_wmi_op_pull_vdev_start_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_vdev_start_ev_arg *arg) { struct wmi_vdev_start_response_event *ev = (void *)skb->data; @@ -1669,7 +1651,7 @@ static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_START_RESP_EVENTID\n"); - ret = ath10k_wmi_pull_vdev_start_ev(skb, &arg); + ret = ath10k_wmi_pull_vdev_start(ar, skb, &arg); if (ret) { ath10k_warn(ar, "failed to parse vdev start event: %d\n", ret); return; @@ -1688,8 +1670,9 @@ static void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, complete(&ar->vdev_setup_done); } -static int ath10k_wmi_pull_peer_kick_ev(struct sk_buff *skb, - struct wmi_peer_kick_ev_arg *arg) +static int +ath10k_wmi_op_pull_peer_kick_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_peer_kick_ev_arg *arg) { struct wmi_peer_sta_kickout_event *ev = (void *)skb->data; @@ -1709,7 +1692,7 @@ static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, struct ieee80211_sta *sta; int ret; - ret = ath10k_wmi_pull_peer_kick_ev(skb, &arg); + ret = ath10k_wmi_pull_peer_kick(ar, skb, &arg); if (ret) { ath10k_warn(ar, "failed to parse peer kickout event: %d\n", ret); @@ -1951,8 +1934,8 @@ cleanup: kfree(old_data); } -static int ath10k_wmi_pull_swba_ev(struct sk_buff *skb, - struct wmi_swba_ev_arg *arg) +static int ath10k_wmi_op_pull_swba_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_swba_ev_arg *arg) { struct wmi_host_swba_event *ev = (void *)skb->data; u32 map; @@ -1994,7 +1977,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) dma_addr_t paddr; int ret, vdev_id = 0; - ret = ath10k_wmi_pull_swba_ev(skb, &arg); + ret = ath10k_wmi_pull_swba(ar, skb, &arg); if (ret) { ath10k_warn(ar, "failed to parse swba event: %d\n", ret); return; @@ -2345,8 +2328,8 @@ ath10k_wmi_event_spectral_scan(struct ath10k *ar, } } -static int ath10k_wmi_pull_phyerr_ev(struct sk_buff *skb, - struct wmi_phyerr_ev_arg *arg) +static int ath10k_wmi_op_pull_phyerr_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_phyerr_ev_arg *arg) { struct wmi_phyerr_event *ev = (void *)skb->data; @@ -2372,7 +2355,7 @@ static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb) ATH10K_DFS_STAT_INC(ar, phy_errors); - ret = ath10k_wmi_pull_phyerr_ev(skb, &arg); + ret = ath10k_wmi_pull_phyerr(ar, skb, &arg); if (ret) { ath10k_warn(ar, "failed to parse phyerr event: %d\n", ret); return; @@ -2608,8 +2591,9 @@ static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id, return 0; } -static int ath10k_wmi_main_pull_svc_rdy_ev(struct sk_buff *skb, - struct wmi_svc_rdy_ev_arg *arg) +static int +ath10k_wmi_main_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_svc_rdy_ev_arg *arg) { struct wmi_service_ready_event *ev; size_t i, n; @@ -2644,8 +2628,9 @@ static int ath10k_wmi_main_pull_svc_rdy_ev(struct sk_buff *skb, return 0; } -static int ath10k_wmi_10x_pull_svc_rdy_ev(struct sk_buff *skb, - struct wmi_svc_rdy_ev_arg *arg) +static int +ath10k_wmi_10x_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_svc_rdy_ev_arg *arg) { struct wmi_10x_service_ready_event *ev; int i, n; @@ -2686,23 +2671,16 @@ static void ath10k_wmi_event_service_ready(struct ath10k *ar, u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i; int ret; - memset(&ar->wmi.svc_map, 0, sizeof(ar->wmi.svc_map)); - - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - ret = ath10k_wmi_10x_pull_svc_rdy_ev(skb, &arg); - wmi_10x_svc_map(arg.service_map, ar->wmi.svc_map, - arg.service_map_len); - } else { - ret = ath10k_wmi_main_pull_svc_rdy_ev(skb, &arg); - wmi_main_svc_map(arg.service_map, ar->wmi.svc_map, - arg.service_map_len); - } - + ret = ath10k_wmi_pull_svc_rdy(ar, skb, &arg); if (ret) { ath10k_warn(ar, "failed to parse service ready: %d\n", ret); return; } + memset(&ar->wmi.svc_map, 0, sizeof(ar->wmi.svc_map)); + ath10k_wmi_map_svc(ar, arg.service_map, ar->wmi.svc_map, + arg.service_map_len); + ar->hw_min_tx_power = __le32_to_cpu(arg.min_tx_power); ar->hw_max_tx_power = __le32_to_cpu(arg.max_tx_power); ar->ht_cap_info = __le32_to_cpu(arg.ht_cap); @@ -2795,8 +2773,8 @@ static void ath10k_wmi_event_service_ready(struct ath10k *ar, complete(&ar->wmi.service_ready); } -static int ath10k_wmi_pull_rdy_ev(struct sk_buff *skb, - struct wmi_rdy_ev_arg *arg) +static int ath10k_wmi_op_pull_rdy_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_rdy_ev_arg *arg) { struct wmi_ready_event *ev = (void *)skb->data; @@ -2817,7 +2795,7 @@ static int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb) struct wmi_rdy_ev_arg arg = {}; int ret; - ret = ath10k_wmi_pull_rdy_ev(skb, &arg); + ret = ath10k_wmi_pull_rdy(ar, skb, &arg); if (ret) { ath10k_warn(ar, "failed to parse ready event: %d\n", ret); return ret; @@ -2835,7 +2813,7 @@ static int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb) return 0; } -static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb) +static void ath10k_wmi_op_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; enum wmi_event_id id; @@ -2951,7 +2929,7 @@ static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb) dev_kfree_skb(skb); } -static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb) +static void ath10k_wmi_10_1_op_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; enum wmi_10x_event_id id; @@ -3075,7 +3053,7 @@ out: dev_kfree_skb(skb); } -static void ath10k_wmi_10_2_process_rx(struct ath10k *ar, struct sk_buff *skb) +static void ath10k_wmi_10_2_op_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; enum wmi_10_2_event_id id; @@ -3194,14 +3172,11 @@ static void ath10k_wmi_10_2_process_rx(struct ath10k *ar, struct sk_buff *skb) static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) - ath10k_wmi_10_2_process_rx(ar, skb); - else - ath10k_wmi_10x_process_rx(ar, skb); - } else { - ath10k_wmi_main_process_rx(ar, skb); - } + int ret; + + ret = ath10k_wmi_rx(ar, skb); + if (ret) + ath10k_warn(ar, "failed to process wmi rx: %d\n", ret); } int ath10k_wmi_connect(struct ath10k *ar) @@ -3232,16 +3207,17 @@ int ath10k_wmi_connect(struct ath10k *ar) return 0; } -static int ath10k_wmi_main_pdev_set_regdomain(struct ath10k *ar, u16 rd, - u16 rd2g, u16 rd5g, u16 ctl2g, - u16 ctl5g) +static struct sk_buff * +ath10k_wmi_op_gen_pdev_set_rd(struct ath10k *ar, u16 rd, u16 rd2g, u16 rd5g, + u16 ctl2g, u16 ctl5g, + enum wmi_dfs_region dfs_reg) { struct wmi_pdev_set_regdomain_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_pdev_set_regdomain_cmd *)skb->data; cmd->reg_domain = __cpu_to_le32(rd); @@ -3253,22 +3229,20 @@ static int ath10k_wmi_main_pdev_set_regdomain(struct ath10k *ar, u16 rd, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x\n", rd, rd2g, rd5g, ctl2g, ctl5g); - - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->pdev_set_regdomain_cmdid); + return skb; } -static int ath10k_wmi_10x_pdev_set_regdomain(struct ath10k *ar, u16 rd, - u16 rd2g, u16 rd5g, - u16 ctl2g, u16 ctl5g, - enum wmi_dfs_region dfs_reg) +static struct sk_buff * +ath10k_wmi_10x_op_gen_pdev_set_rd(struct ath10k *ar, u16 rd, u16 rd2g, u16 + rd5g, u16 ctl2g, u16 ctl5g, + enum wmi_dfs_region dfs_reg) { struct wmi_pdev_set_regdomain_cmd_10x *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_pdev_set_regdomain_cmd_10x *)skb->data; cmd->reg_domain = __cpu_to_le32(rd); @@ -3281,50 +3255,39 @@ static int ath10k_wmi_10x_pdev_set_regdomain(struct ath10k *ar, u16 rd, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x dfs_region %x\n", rd, rd2g, rd5g, ctl2g, ctl5g, dfs_reg); - - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->pdev_set_regdomain_cmdid); -} - -int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, - u16 rd5g, u16 ctl2g, u16 ctl5g, - enum wmi_dfs_region dfs_reg) -{ - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) - return ath10k_wmi_10x_pdev_set_regdomain(ar, rd, rd2g, rd5g, - ctl2g, ctl5g, dfs_reg); - else - return ath10k_wmi_main_pdev_set_regdomain(ar, rd, rd2g, rd5g, - ctl2g, ctl5g); + return skb; } -int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt) +static struct sk_buff * +ath10k_wmi_op_gen_pdev_suspend(struct ath10k *ar, u32 suspend_opt) { struct wmi_pdev_suspend_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_pdev_suspend_cmd *)skb->data; cmd->suspend_opt = __cpu_to_le32(suspend_opt); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid); + return skb; } -int ath10k_wmi_pdev_resume_target(struct ath10k *ar) +static struct sk_buff * +ath10k_wmi_op_gen_pdev_resume(struct ath10k *ar) { struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, 0); - if (skb == NULL) - return -ENOMEM; + if (!skb) + return ERR_PTR(-ENOMEM); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_resume_cmdid); + return skb; } -int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value) +static struct sk_buff * +ath10k_wmi_op_gen_pdev_set_param(struct ath10k *ar, u32 id, u32 value) { struct wmi_pdev_set_param_cmd *cmd; struct sk_buff *skb; @@ -3332,12 +3295,12 @@ int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value) if (id == WMI_PDEV_PARAM_UNSUPPORTED) { ath10k_warn(ar, "pdev param %d not supported by firmware\n", id); - return -EOPNOTSUPP; + return ERR_PTR(-EOPNOTSUPP); } skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_pdev_set_param_cmd *)skb->data; cmd->param_id = __cpu_to_le32(id); @@ -3345,7 +3308,7 @@ int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value) ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n", id, value); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid); + return skb; } static void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar, @@ -3370,7 +3333,7 @@ static void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar, } } -static int ath10k_wmi_main_cmd_init(struct ath10k *ar) +static struct sk_buff *ath10k_wmi_op_gen_init(struct ath10k *ar) { struct wmi_init_cmd *cmd; struct sk_buff *buf; @@ -3433,7 +3396,7 @@ static int ath10k_wmi_main_cmd_init(struct ath10k *ar) buf = ath10k_wmi_alloc_skb(ar, len); if (!buf) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_init_cmd *)buf->data; @@ -3441,10 +3404,10 @@ static int ath10k_wmi_main_cmd_init(struct ath10k *ar) ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init\n"); - return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid); + return buf; } -static int ath10k_wmi_10x_cmd_init(struct ath10k *ar) +static struct sk_buff *ath10k_wmi_10_1_op_gen_init(struct ath10k *ar) { struct wmi_init_cmd_10x *cmd; struct sk_buff *buf; @@ -3499,7 +3462,7 @@ static int ath10k_wmi_10x_cmd_init(struct ath10k *ar) buf = ath10k_wmi_alloc_skb(ar, len); if (!buf) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_init_cmd_10x *)buf->data; @@ -3507,10 +3470,10 @@ static int ath10k_wmi_10x_cmd_init(struct ath10k *ar) ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init 10x\n"); - return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid); + return buf; } -static int ath10k_wmi_10_2_cmd_init(struct ath10k *ar) +static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar) { struct wmi_init_cmd_10_2 *cmd; struct sk_buff *buf; @@ -3565,7 +3528,7 @@ static int ath10k_wmi_10_2_cmd_init(struct ath10k *ar) buf = ath10k_wmi_alloc_skb(ar, len); if (!buf) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_init_cmd_10_2 *)buf->data; @@ -3573,23 +3536,7 @@ static int ath10k_wmi_10_2_cmd_init(struct ath10k *ar) ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init 10.2\n"); - return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid); -} - -int ath10k_wmi_cmd_init(struct ath10k *ar) -{ - int ret; - - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) - ret = ath10k_wmi_10_2_cmd_init(ar); - else - ret = ath10k_wmi_10x_cmd_init(ar); - } else { - ret = ath10k_wmi_main_cmd_init(ar); - } - - return ret; + return buf; } static int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg) @@ -3739,46 +3686,60 @@ ath10k_wmi_put_start_scan_tlvs(struct wmi_start_scan_tlvs *tlvs, } } -int ath10k_wmi_start_scan(struct ath10k *ar, - const struct wmi_start_scan_arg *arg) +static struct sk_buff * +ath10k_wmi_op_gen_start_scan(struct ath10k *ar, + const struct wmi_start_scan_arg *arg) { + struct wmi_start_scan_cmd *cmd; struct sk_buff *skb; size_t len; int ret; ret = ath10k_wmi_start_scan_verify(arg); if (ret) - return ret; - - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) - len = sizeof(struct wmi_10x_start_scan_cmd) + - ath10k_wmi_start_scan_tlvs_len(arg); - else - len = sizeof(struct wmi_start_scan_cmd) + - ath10k_wmi_start_scan_tlvs_len(arg); + return ERR_PTR(ret); + len = sizeof(*cmd) + ath10k_wmi_start_scan_tlvs_len(arg); skb = ath10k_wmi_alloc_skb(ar, len); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - struct wmi_10x_start_scan_cmd *cmd; - - cmd = (struct wmi_10x_start_scan_cmd *)skb->data; - ath10k_wmi_put_start_scan_common(&cmd->common, arg); - ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg); - } else { - struct wmi_start_scan_cmd *cmd; + cmd = (struct wmi_start_scan_cmd *)skb->data; - cmd = (struct wmi_start_scan_cmd *)skb->data; - cmd->burst_duration_ms = __cpu_to_le32(0); + ath10k_wmi_put_start_scan_common(&cmd->common, arg); + ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg); - ath10k_wmi_put_start_scan_common(&cmd->common, arg); - ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg); - } + cmd->burst_duration_ms = __cpu_to_le32(0); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi start scan\n"); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->start_scan_cmdid); + return skb; +} + +static struct sk_buff * +ath10k_wmi_10x_op_gen_start_scan(struct ath10k *ar, + const struct wmi_start_scan_arg *arg) +{ + struct wmi_10x_start_scan_cmd *cmd; + struct sk_buff *skb; + size_t len; + int ret; + + ret = ath10k_wmi_start_scan_verify(arg); + if (ret) + return ERR_PTR(ret); + + len = sizeof(*cmd) + ath10k_wmi_start_scan_tlvs_len(arg); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + cmd = (struct wmi_10x_start_scan_cmd *)skb->data; + + ath10k_wmi_put_start_scan_common(&cmd->common, arg); + ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi 10x start scan\n"); + return skb; } void ath10k_wmi_start_scan_init(struct ath10k *ar, @@ -3807,7 +3768,9 @@ void ath10k_wmi_start_scan_init(struct ath10k *ar, arg->bssids[0].bssid = "\xFF\xFF\xFF\xFF\xFF\xFF"; } -int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg) +static struct sk_buff * +ath10k_wmi_op_gen_stop_scan(struct ath10k *ar, + const struct wmi_stop_scan_arg *arg) { struct wmi_stop_scan_cmd *cmd; struct sk_buff *skb; @@ -3815,13 +3778,13 @@ int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg) u32 req_id; if (arg->req_id > 0xFFF) - return -EINVAL; + return ERR_PTR(-EINVAL); if (arg->req_type == WMI_SCAN_STOP_ONE && arg->u.scan_id > 0xFFF) - return -EINVAL; + return ERR_PTR(-EINVAL); skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); scan_id = arg->u.scan_id; scan_id |= WMI_HOST_SCAN_REQ_ID_PREFIX; @@ -3838,20 +3801,21 @@ int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg) ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi stop scan reqid %d req_type %d vdev/scan_id %d\n", arg->req_id, arg->req_type, arg->u.scan_id); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->stop_scan_cmdid); + return skb; } -int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, - enum wmi_vdev_type type, - enum wmi_vdev_subtype subtype, - const u8 macaddr[ETH_ALEN]) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_create(struct ath10k *ar, u32 vdev_id, + enum wmi_vdev_type type, + enum wmi_vdev_subtype subtype, + const u8 macaddr[ETH_ALEN]) { struct wmi_vdev_create_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_create_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -3862,58 +3826,52 @@ int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI vdev create: id %d type %d subtype %d macaddr %pM\n", vdev_id, type, subtype, macaddr); - - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_create_cmdid); + return skb; } -int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_delete(struct ath10k *ar, u32 vdev_id) { struct wmi_vdev_delete_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_delete_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI vdev delete id %d\n", vdev_id); - - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_delete_cmdid); + return skb; } -static int -ath10k_wmi_vdev_start_restart(struct ath10k *ar, - const struct wmi_vdev_start_request_arg *arg, - u32 cmd_id) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_start(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *arg, + bool restart) { struct wmi_vdev_start_request_cmd *cmd; struct sk_buff *skb; const char *cmdname; u32 flags = 0; - if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid && - cmd_id != ar->wmi.cmd->vdev_restart_request_cmdid) - return -EINVAL; if (WARN_ON(arg->ssid && arg->ssid_len == 0)) - return -EINVAL; + return ERR_PTR(-EINVAL); if (WARN_ON(arg->hidden_ssid && !arg->ssid)) - return -EINVAL; + return ERR_PTR(-EINVAL); if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid))) - return -EINVAL; + return ERR_PTR(-EINVAL); - if (cmd_id == ar->wmi.cmd->vdev_start_request_cmdid) - cmdname = "start"; - else if (cmd_id == ar->wmi.cmd->vdev_restart_request_cmdid) + if (restart) cmdname = "restart"; else - return -EINVAL; /* should not happen, we already check cmd_id */ + cmdname = "start"; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); if (arg->hidden_ssid) flags |= WMI_VDEV_START_HIDDEN_SSID; @@ -3942,50 +3900,36 @@ ath10k_wmi_vdev_start_restart(struct ath10k *ar, flags, arg->channel.freq, arg->channel.mode, cmd->chan.flags, arg->channel.max_power); - return ath10k_wmi_cmd_send(ar, skb, cmd_id); -} - -int ath10k_wmi_vdev_start(struct ath10k *ar, - const struct wmi_vdev_start_request_arg *arg) -{ - u32 cmd_id = ar->wmi.cmd->vdev_start_request_cmdid; - - return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id); -} - -int ath10k_wmi_vdev_restart(struct ath10k *ar, - const struct wmi_vdev_start_request_arg *arg) -{ - u32 cmd_id = ar->wmi.cmd->vdev_restart_request_cmdid; - - return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id); + return skb; } -int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_stop(struct ath10k *ar, u32 vdev_id) { struct wmi_vdev_stop_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_stop_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id); - - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_stop_cmdid); + return skb; } -int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, + const u8 *bssid) { struct wmi_vdev_up_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_up_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -3995,30 +3939,30 @@ int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid) ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n", vdev_id, aid, bssid); - - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_up_cmdid); + return skb; } -int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_down(struct ath10k *ar, u32 vdev_id) { struct wmi_vdev_down_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_down_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt vdev down id 0x%x\n", vdev_id); - - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_down_cmdid); + return skb; } -int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, - u32 param_id, u32 param_value) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_set_param(struct ath10k *ar, u32 vdev_id, + u32 param_id, u32 param_value) { struct wmi_vdev_set_param_cmd *cmd; struct sk_buff *skb; @@ -4027,12 +3971,12 @@ int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "vdev param %d not supported by firmware\n", param_id); - return -EOPNOTSUPP; + return ERR_PTR(-EOPNOTSUPP); } skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_set_param_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -4042,24 +3986,24 @@ int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi vdev id 0x%x set param %d value %d\n", vdev_id, param_id, param_value); - - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_set_param_cmdid); + return skb; } -int ath10k_wmi_vdev_install_key(struct ath10k *ar, - const struct wmi_vdev_install_key_arg *arg) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_install_key(struct ath10k *ar, + const struct wmi_vdev_install_key_arg *arg) { struct wmi_vdev_install_key_cmd *cmd; struct sk_buff *skb; if (arg->key_cipher == WMI_CIPHER_NONE && arg->key_data != NULL) - return -EINVAL; + return ERR_PTR(-EINVAL); if (arg->key_cipher != WMI_CIPHER_NONE && arg->key_data == NULL) - return -EINVAL; + return ERR_PTR(-EINVAL); skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd) + arg->key_len); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_install_key_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(arg->vdev_id); @@ -4078,20 +4022,19 @@ int ath10k_wmi_vdev_install_key(struct ath10k *ar, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi vdev install key idx %d cipher %d len %d\n", arg->key_idx, arg->key_cipher, arg->key_len); - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->vdev_install_key_cmdid); + return skb; } -int ath10k_wmi_vdev_spectral_conf(struct ath10k *ar, - const struct wmi_vdev_spectral_conf_arg *arg) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_spectral_conf(struct ath10k *ar, + const struct wmi_vdev_spectral_conf_arg *arg) { struct wmi_vdev_spectral_conf_cmd *cmd; struct sk_buff *skb; - u32 cmdid; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_spectral_conf_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(arg->vdev_id); @@ -4114,39 +4057,38 @@ int ath10k_wmi_vdev_spectral_conf(struct ath10k *ar, cmd->scan_dbm_adj = __cpu_to_le32(arg->scan_dbm_adj); cmd->scan_chn_mask = __cpu_to_le32(arg->scan_chn_mask); - cmdid = ar->wmi.cmd->vdev_spectral_scan_configure_cmdid; - return ath10k_wmi_cmd_send(ar, skb, cmdid); + return skb; } -int ath10k_wmi_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, u32 trigger, - u32 enable) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, + u32 trigger, u32 enable) { struct wmi_vdev_spectral_enable_cmd *cmd; struct sk_buff *skb; - u32 cmdid; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_spectral_enable_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); cmd->trigger_cmd = __cpu_to_le32(trigger); cmd->enable_cmd = __cpu_to_le32(enable); - cmdid = ar->wmi.cmd->vdev_spectral_scan_enable_cmdid; - return ath10k_wmi_cmd_send(ar, skb, cmdid); + return skb; } -int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, - const u8 peer_addr[ETH_ALEN]) +static struct sk_buff * +ath10k_wmi_op_gen_peer_create(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]) { struct wmi_peer_create_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_peer_create_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -4155,18 +4097,19 @@ int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi peer create vdev_id %d peer_addr %pM\n", vdev_id, peer_addr); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_create_cmdid); + return skb; } -int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, - const u8 peer_addr[ETH_ALEN]) +static struct sk_buff * +ath10k_wmi_op_gen_peer_delete(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]) { struct wmi_peer_delete_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_peer_delete_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -4175,18 +4118,19 @@ int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi peer delete vdev_id %d peer_addr %pM\n", vdev_id, peer_addr); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_delete_cmdid); + return skb; } -int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, - const u8 peer_addr[ETH_ALEN], u32 tid_bitmap) +static struct sk_buff * +ath10k_wmi_op_gen_peer_flush(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN], u32 tid_bitmap) { struct wmi_peer_flush_tids_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_peer_flush_tids_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -4196,19 +4140,21 @@ int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi peer flush vdev_id %d peer_addr %pM tids %08x\n", vdev_id, peer_addr, tid_bitmap); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_flush_tids_cmdid); + return skb; } -int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, - const u8 *peer_addr, enum wmi_peer_param param_id, - u32 param_value) +static struct sk_buff * +ath10k_wmi_op_gen_peer_set_param(struct ath10k *ar, u32 vdev_id, + const u8 *peer_addr, + enum wmi_peer_param param_id, + u32 param_value) { struct wmi_peer_set_param_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_peer_set_param_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -4219,19 +4165,19 @@ int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi vdev %d peer 0x%pM set param %d value %d\n", vdev_id, peer_addr, param_id, param_value); - - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_set_param_cmdid); + return skb; } -int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, - enum wmi_sta_ps_mode psmode) +static struct sk_buff * +ath10k_wmi_op_gen_set_psmode(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_ps_mode psmode) { struct wmi_sta_powersave_mode_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_sta_powersave_mode_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -4240,21 +4186,20 @@ int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi set powersave id 0x%x mode %d\n", vdev_id, psmode); - - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->sta_powersave_mode_cmdid); + return skb; } -int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, - enum wmi_sta_powersave_param param_id, - u32 value) +static struct sk_buff * +ath10k_wmi_op_gen_set_sta_ps(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_powersave_param param_id, + u32 value) { struct wmi_sta_powersave_param_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_sta_powersave_param_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -4264,22 +4209,22 @@ int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi sta ps param vdev_id 0x%x param %d value %d\n", vdev_id, param_id, value); - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->sta_powersave_param_cmdid); + return skb; } -int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, - enum wmi_ap_ps_peer_param param_id, u32 value) +static struct sk_buff * +ath10k_wmi_op_gen_set_ap_ps(struct ath10k *ar, u32 vdev_id, const u8 *mac, + enum wmi_ap_ps_peer_param param_id, u32 value) { struct wmi_ap_ps_peer_cmd *cmd; struct sk_buff *skb; if (!mac) - return -EINVAL; + return ERR_PTR(-EINVAL); skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_ap_ps_peer_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -4290,13 +4235,12 @@ int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi ap ps param vdev_id 0x%X param %d value %d mac_addr %pM\n", vdev_id, param_id, value, mac); - - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->ap_ps_peer_param_cmdid); + return skb; } -int ath10k_wmi_scan_chan_list(struct ath10k *ar, - const struct wmi_scan_chan_list_arg *arg) +static struct sk_buff * +ath10k_wmi_op_gen_scan_chan_list(struct ath10k *ar, + const struct wmi_scan_chan_list_arg *arg) { struct wmi_scan_chan_list_cmd *cmd; struct sk_buff *skb; @@ -4309,7 +4253,7 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar, skb = ath10k_wmi_alloc_skb(ar, len); if (!skb) - return -EINVAL; + return ERR_PTR(-EINVAL); cmd = (struct wmi_scan_chan_list_cmd *)skb->data; cmd->num_scan_chans = __cpu_to_le32(arg->n_channels); @@ -4321,7 +4265,7 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar, ath10k_wmi_put_wmi_channel(ci, ch); } - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid); + return skb; } static void @@ -4402,12 +4346,9 @@ ath10k_wmi_peer_assoc_fill_10_2(struct ath10k *ar, void *buf, cmd->info0 = __cpu_to_le32(info0); } -int ath10k_wmi_peer_assoc(struct ath10k *ar, - const struct wmi_peer_assoc_complete_arg *arg) +static int +ath10k_wmi_peer_assoc_check_arg(const struct wmi_peer_assoc_complete_arg *arg) { - struct sk_buff *skb; - int len; - if (arg->peer_mpdu_density > 16) return -EINVAL; if (arg->peer_legacy_rates.num_rates > MAX_SUPPORTED_RATES) @@ -4415,49 +4356,98 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar, if (arg->peer_ht_rates.num_rates > MAX_SUPPORTED_RATES) return -EINVAL; - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) - len = sizeof(struct wmi_10_2_peer_assoc_complete_cmd); - else - len = sizeof(struct wmi_10_1_peer_assoc_complete_cmd); - } else { - len = sizeof(struct wmi_main_peer_assoc_complete_cmd); - } + return 0; +} + +static struct sk_buff * +ath10k_wmi_op_gen_peer_assoc(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg) +{ + size_t len = sizeof(struct wmi_main_peer_assoc_complete_cmd); + struct sk_buff *skb; + int ret; + + ret = ath10k_wmi_peer_assoc_check_arg(arg); + if (ret) + return ERR_PTR(ret); skb = ath10k_wmi_alloc_skb(ar, len); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) - ath10k_wmi_peer_assoc_fill_10_2(ar, skb->data, arg); - else - ath10k_wmi_peer_assoc_fill_10_1(ar, skb->data, arg); - } else { - ath10k_wmi_peer_assoc_fill_main(ar, skb->data, arg); - } + ath10k_wmi_peer_assoc_fill_main(ar, skb->data, arg); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi peer assoc vdev %d addr %pM (%s)\n", + arg->vdev_id, arg->addr, + arg->peer_reassoc ? "reassociate" : "new"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_10_1_op_gen_peer_assoc(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg) +{ + size_t len = sizeof(struct wmi_10_1_peer_assoc_complete_cmd); + struct sk_buff *skb; + int ret; + + ret = ath10k_wmi_peer_assoc_check_arg(arg); + if (ret) + return ERR_PTR(ret); + + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ath10k_wmi_peer_assoc_fill_10_1(ar, skb->data, arg); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi peer assoc vdev %d addr %pM (%s)\n", + arg->vdev_id, arg->addr, + arg->peer_reassoc ? "reassociate" : "new"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_10_2_op_gen_peer_assoc(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg) +{ + size_t len = sizeof(struct wmi_10_2_peer_assoc_complete_cmd); + struct sk_buff *skb; + int ret; + + ret = ath10k_wmi_peer_assoc_check_arg(arg); + if (ret) + return ERR_PTR(ret); + + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ath10k_wmi_peer_assoc_fill_10_2(ar, skb->data, arg); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi peer assoc vdev %d addr %pM (%s)\n", arg->vdev_id, arg->addr, arg->peer_reassoc ? "reassociate" : "new"); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid); + return skb; } /* This function assumes the beacon is already DMA mapped */ -int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif) +static struct sk_buff * +ath10k_wmi_op_gen_beacon_dma(struct ath10k_vif *arvif) { + struct ath10k *ar = arvif->ar; struct wmi_bcn_tx_ref_cmd *cmd; struct sk_buff *skb; struct sk_buff *beacon = arvif->beacon; - struct ath10k *ar = arvif->ar; struct ieee80211_hdr *hdr; - int ret; u16 fc; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); hdr = (struct ieee80211_hdr *)beacon->data; fc = le16_to_cpu(hdr->frame_control); @@ -4477,13 +4467,7 @@ int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif) if (ATH10K_SKB_CB(beacon)->bcn.deliver_cab) cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DELIVER_CAB); - ret = ath10k_wmi_cmd_send_nowait(ar, skb, - ar->wmi.cmd->pdev_send_bcn_cmdid); - - if (ret) - dev_kfree_skb(skb); - - return ret; + return skb; } static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params, @@ -4497,15 +4481,16 @@ static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params, params->no_ack = __cpu_to_le32(arg->no_ack); } -int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, - const struct wmi_pdev_set_wmm_params_arg *arg) +static struct sk_buff * +ath10k_wmi_op_gen_pdev_set_wmm(struct ath10k *ar, + const struct wmi_pdev_set_wmm_params_arg *arg) { struct wmi_pdev_set_wmm_params *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_pdev_set_wmm_params *)skb->data; ath10k_wmi_pdev_set_wmm_param(&cmd->ac_be, &arg->ac_be); @@ -4514,35 +4499,36 @@ int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev set wmm params\n"); - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->pdev_set_wmm_params_cmdid); + return skb; } -int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) +static struct sk_buff * +ath10k_wmi_op_gen_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) { struct wmi_request_stats_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_request_stats_cmd *)skb->data; cmd->stats_id = __cpu_to_le32(stats_id); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->request_stats_cmdid); + return skb; } -int ath10k_wmi_force_fw_hang(struct ath10k *ar, - enum wmi_force_fw_hang_type type, u32 delay_ms) +static struct sk_buff * +ath10k_wmi_op_gen_force_fw_hang(struct ath10k *ar, + enum wmi_force_fw_hang_type type, u32 delay_ms) { struct wmi_force_fw_hang_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_force_fw_hang_cmd *)skb->data; cmd->type = __cpu_to_le32(type); @@ -4550,10 +4536,11 @@ int ath10k_wmi_force_fw_hang(struct ath10k *ar, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n", type, delay_ms); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid); + return skb; } -int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable) +static struct sk_buff * +ath10k_wmi_op_gen_dbglog_cfg(struct ath10k *ar, u32 module_enable) { struct wmi_dbglog_cfg_cmd *cmd; struct sk_buff *skb; @@ -4561,7 +4548,7 @@ int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable) skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_dbglog_cfg_cmd *)skb->data; @@ -4586,57 +4573,224 @@ int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable) __le32_to_cpu(cmd->module_valid), __le32_to_cpu(cmd->config_enable), __le32_to_cpu(cmd->config_valid)); - - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->dbglog_cfg_cmdid); + return skb; } -int ath10k_wmi_pdev_pktlog_enable(struct ath10k *ar, u32 ev_bitmap) +static struct sk_buff * +ath10k_wmi_op_gen_pktlog_enable(struct ath10k *ar, u32 ev_bitmap) { struct wmi_pdev_pktlog_enable_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); ev_bitmap &= ATH10K_PKTLOG_ANY; - ath10k_dbg(ar, ATH10K_DBG_WMI, - "wmi enable pktlog filter:%x\n", ev_bitmap); cmd = (struct wmi_pdev_pktlog_enable_cmd *)skb->data; cmd->ev_bitmap = __cpu_to_le32(ev_bitmap); - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->pdev_pktlog_enable_cmdid); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi enable pktlog filter 0x%08x\n", + ev_bitmap); + return skb; } -int ath10k_wmi_pdev_pktlog_disable(struct ath10k *ar) +static struct sk_buff * +ath10k_wmi_op_gen_pktlog_disable(struct ath10k *ar) { struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, 0); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi disable pktlog\n"); - - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->pdev_pktlog_disable_cmdid); + return skb; } +static const struct wmi_ops wmi_ops = { + .rx = ath10k_wmi_op_rx, + .map_svc = wmi_main_svc_map, + + .pull_scan = ath10k_wmi_op_pull_scan_ev, + .pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev, + .pull_ch_info = ath10k_wmi_op_pull_ch_info_ev, + .pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev, + .pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev, + .pull_swba = ath10k_wmi_op_pull_swba_ev, + .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev, + .pull_svc_rdy = ath10k_wmi_main_op_pull_svc_rdy_ev, + .pull_rdy = ath10k_wmi_op_pull_rdy_ev, + .pull_fw_stats = ath10k_wmi_main_op_pull_fw_stats, + + .gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend, + .gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume, + .gen_pdev_set_rd = ath10k_wmi_op_gen_pdev_set_rd, + .gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param, + .gen_init = ath10k_wmi_op_gen_init, + .gen_start_scan = ath10k_wmi_op_gen_start_scan, + .gen_stop_scan = ath10k_wmi_op_gen_stop_scan, + .gen_vdev_create = ath10k_wmi_op_gen_vdev_create, + .gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete, + .gen_vdev_start = ath10k_wmi_op_gen_vdev_start, + .gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop, + .gen_vdev_up = ath10k_wmi_op_gen_vdev_up, + .gen_vdev_down = ath10k_wmi_op_gen_vdev_down, + .gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param, + .gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key, + .gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf, + .gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable, + .gen_peer_create = ath10k_wmi_op_gen_peer_create, + .gen_peer_delete = ath10k_wmi_op_gen_peer_delete, + .gen_peer_flush = ath10k_wmi_op_gen_peer_flush, + .gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param, + .gen_peer_assoc = ath10k_wmi_op_gen_peer_assoc, + .gen_set_psmode = ath10k_wmi_op_gen_set_psmode, + .gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps, + .gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps, + .gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list, + .gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma, + .gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm, + .gen_request_stats = ath10k_wmi_op_gen_request_stats, + .gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang, + .gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx, + .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg, + .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable, + .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable, +}; + +static const struct wmi_ops wmi_10_1_ops = { + .rx = ath10k_wmi_10_1_op_rx, + .map_svc = wmi_10x_svc_map, + .pull_svc_rdy = ath10k_wmi_10x_op_pull_svc_rdy_ev, + .pull_fw_stats = ath10k_wmi_10x_op_pull_fw_stats, + .gen_init = ath10k_wmi_10_1_op_gen_init, + .gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd, + .gen_start_scan = ath10k_wmi_10x_op_gen_start_scan, + .gen_peer_assoc = ath10k_wmi_10_1_op_gen_peer_assoc, + + /* shared with main branch */ + .pull_scan = ath10k_wmi_op_pull_scan_ev, + .pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev, + .pull_ch_info = ath10k_wmi_op_pull_ch_info_ev, + .pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev, + .pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev, + .pull_swba = ath10k_wmi_op_pull_swba_ev, + .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev, + .pull_rdy = ath10k_wmi_op_pull_rdy_ev, + + .gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend, + .gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume, + .gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param, + .gen_stop_scan = ath10k_wmi_op_gen_stop_scan, + .gen_vdev_create = ath10k_wmi_op_gen_vdev_create, + .gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete, + .gen_vdev_start = ath10k_wmi_op_gen_vdev_start, + .gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop, + .gen_vdev_up = ath10k_wmi_op_gen_vdev_up, + .gen_vdev_down = ath10k_wmi_op_gen_vdev_down, + .gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param, + .gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key, + .gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf, + .gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable, + .gen_peer_create = ath10k_wmi_op_gen_peer_create, + .gen_peer_delete = ath10k_wmi_op_gen_peer_delete, + .gen_peer_flush = ath10k_wmi_op_gen_peer_flush, + .gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param, + .gen_set_psmode = ath10k_wmi_op_gen_set_psmode, + .gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps, + .gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps, + .gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list, + .gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma, + .gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm, + .gen_request_stats = ath10k_wmi_op_gen_request_stats, + .gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang, + .gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx, + .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg, + .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable, + .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable, +}; + +static const struct wmi_ops wmi_10_2_ops = { + .rx = ath10k_wmi_10_2_op_rx, + .gen_init = ath10k_wmi_10_2_op_gen_init, + .gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc, + + /* shared with 10.1 */ + .map_svc = wmi_10x_svc_map, + .pull_svc_rdy = ath10k_wmi_10x_op_pull_svc_rdy_ev, + .pull_fw_stats = ath10k_wmi_10x_op_pull_fw_stats, + .gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd, + .gen_start_scan = ath10k_wmi_10x_op_gen_start_scan, + + .pull_scan = ath10k_wmi_op_pull_scan_ev, + .pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev, + .pull_ch_info = ath10k_wmi_op_pull_ch_info_ev, + .pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev, + .pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev, + .pull_swba = ath10k_wmi_op_pull_swba_ev, + .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev, + .pull_rdy = ath10k_wmi_op_pull_rdy_ev, + + .gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend, + .gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume, + .gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param, + .gen_stop_scan = ath10k_wmi_op_gen_stop_scan, + .gen_vdev_create = ath10k_wmi_op_gen_vdev_create, + .gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete, + .gen_vdev_start = ath10k_wmi_op_gen_vdev_start, + .gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop, + .gen_vdev_up = ath10k_wmi_op_gen_vdev_up, + .gen_vdev_down = ath10k_wmi_op_gen_vdev_down, + .gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param, + .gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key, + .gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf, + .gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable, + .gen_peer_create = ath10k_wmi_op_gen_peer_create, + .gen_peer_delete = ath10k_wmi_op_gen_peer_delete, + .gen_peer_flush = ath10k_wmi_op_gen_peer_flush, + .gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param, + .gen_set_psmode = ath10k_wmi_op_gen_set_psmode, + .gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps, + .gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps, + .gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list, + .gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma, + .gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm, + .gen_request_stats = ath10k_wmi_op_gen_request_stats, + .gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang, + .gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx, + .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg, + .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable, + .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable, +}; + int ath10k_wmi_attach(struct ath10k *ar) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) - ar->wmi.cmd = &wmi_10_2_cmd_map; - else - ar->wmi.cmd = &wmi_10x_cmd_map; - + switch (ar->wmi.op_version) { + case ATH10K_FW_WMI_OP_VERSION_10_2: + ar->wmi.cmd = &wmi_10_2_cmd_map; + ar->wmi.ops = &wmi_10_2_ops; ar->wmi.vdev_param = &wmi_10x_vdev_param_map; ar->wmi.pdev_param = &wmi_10x_pdev_param_map; - } else { + break; + case ATH10K_FW_WMI_OP_VERSION_10_1: + ar->wmi.cmd = &wmi_10x_cmd_map; + ar->wmi.ops = &wmi_10_1_ops; + ar->wmi.vdev_param = &wmi_10x_vdev_param_map; + ar->wmi.pdev_param = &wmi_10x_pdev_param_map; + break; + case ATH10K_FW_WMI_OP_VERSION_MAIN: ar->wmi.cmd = &wmi_cmd_map; + ar->wmi.ops = &wmi_ops; ar->wmi.vdev_param = &wmi_vdev_param_map; ar->wmi.pdev_param = &wmi_pdev_param_map; + break; + case ATH10K_FW_WMI_OP_VERSION_UNSET: + case ATH10K_FW_WMI_OP_VERSION_MAX: + ath10k_err(ar, "unsupported WMI op version: %d\n", + ar->wmi.op_version); + return -EINVAL; } init_completion(&ar->wmi.service_ready); diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 0a38798..7172f3c 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -4644,7 +4644,6 @@ struct wmi_rdy_ev_arg { struct ath10k; struct ath10k_vif; -struct ath10k_fw_stats; int ath10k_wmi_attach(struct ath10k *ar); void ath10k_wmi_detach(struct ath10k *ar); @@ -4655,70 +4654,8 @@ int ath10k_wmi_connect(struct ath10k *ar); struct sk_buff *ath10k_wmi_alloc_skb(struct ath10k *ar, u32 len); int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id); - -int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt); -int ath10k_wmi_pdev_resume_target(struct ath10k *ar); -int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, - u16 rd5g, u16 ctl2g, u16 ctl5g, - enum wmi_dfs_region dfs_reg); -int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value); -int ath10k_wmi_cmd_init(struct ath10k *ar); -int ath10k_wmi_start_scan(struct ath10k *ar, const struct wmi_start_scan_arg *); +int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb, + u32 cmd_id); void ath10k_wmi_start_scan_init(struct ath10k *ar, struct wmi_start_scan_arg *); -int ath10k_wmi_stop_scan(struct ath10k *ar, - const struct wmi_stop_scan_arg *arg); -int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, - enum wmi_vdev_type type, - enum wmi_vdev_subtype subtype, - const u8 macaddr[ETH_ALEN]); -int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id); -int ath10k_wmi_vdev_start(struct ath10k *ar, - const struct wmi_vdev_start_request_arg *); -int ath10k_wmi_vdev_restart(struct ath10k *ar, - const struct wmi_vdev_start_request_arg *); -int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id); -int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, - const u8 *bssid); -int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id); -int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, - u32 param_id, u32 param_value); -int ath10k_wmi_vdev_install_key(struct ath10k *ar, - const struct wmi_vdev_install_key_arg *arg); -int ath10k_wmi_vdev_spectral_conf(struct ath10k *ar, - const struct wmi_vdev_spectral_conf_arg *arg); -int ath10k_wmi_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, u32 trigger, - u32 enable); -int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, - const u8 peer_addr[ETH_ALEN]); -int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, - const u8 peer_addr[ETH_ALEN]); -int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, - const u8 peer_addr[ETH_ALEN], u32 tid_bitmap); -int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, - const u8 *peer_addr, - enum wmi_peer_param param_id, u32 param_value); -int ath10k_wmi_peer_assoc(struct ath10k *ar, - const struct wmi_peer_assoc_complete_arg *arg); -int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, - enum wmi_sta_ps_mode psmode); -int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, - enum wmi_sta_powersave_param param_id, - u32 value); -int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, - enum wmi_ap_ps_peer_param param_id, u32 value); -int ath10k_wmi_scan_chan_list(struct ath10k *ar, - const struct wmi_scan_chan_list_arg *arg); -int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif); -int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, - const struct wmi_pdev_set_wmm_params_arg *arg); -int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id); -int ath10k_wmi_force_fw_hang(struct ath10k *ar, - enum wmi_force_fw_hang_type type, u32 delay_ms); -int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb); -int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable); -int ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb, - struct ath10k_fw_stats *stats); -int ath10k_wmi_pdev_pktlog_enable(struct ath10k *ar, u32 ev_list); -int ath10k_wmi_pdev_pktlog_disable(struct ath10k *ar); #endif /* _WMI_H_ */ -- cgit v0.10.2 From 0226d6025845d0e93a7a37e0c521a33b546018d9 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 3 Dec 2014 10:11:22 +0200 Subject: ath10k: make some wmi functions public Some functions can be shared across different WMI ABIs. Make them public so different WMI backends can use them from different source files in the future. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 24bb97f..cc0c9e0 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -610,9 +610,8 @@ static struct wmi_cmd_map wmi_10_2_cmd_map = { .gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID, }; -static void -ath10k_wmi_put_wmi_channel(struct wmi_channel *ch, - const struct wmi_channel_arg *arg) +void ath10k_wmi_put_wmi_channel(struct wmi_channel *ch, + const struct wmi_channel_arg *arg) { u32 flags = 0; @@ -987,7 +986,7 @@ static int ath10k_wmi_op_pull_scan_ev(struct ath10k *ar, struct sk_buff *skb, return 0; } -static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb) +int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb) { struct wmi_scan_ev_arg arg = {}; enum wmi_scan_event_type event_type; @@ -1206,7 +1205,7 @@ static int ath10k_wmi_op_pull_mgmt_rx_ev(struct ath10k *ar, struct sk_buff *skb, return 0; } -static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) +int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_mgmt_rx_ev_arg arg = {}; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); @@ -1359,7 +1358,7 @@ static int ath10k_wmi_op_pull_ch_info_ev(struct ath10k *ar, struct sk_buff *skb, return 0; } -static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) +void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) { struct wmi_ch_info_ev_arg arg = {}; struct survey_info *survey; @@ -1427,12 +1426,12 @@ exit: spin_unlock_bh(&ar->data_lock); } -static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb) +void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_ECHO_EVENTID\n"); } -static int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb) +int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event debug mesg len %d\n", skb->len); @@ -1442,8 +1441,8 @@ static int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb) return 0; } -static void ath10k_wmi_pull_pdev_stats(const struct wmi_pdev_stats *src, - struct ath10k_fw_stats_pdev *dst) +void ath10k_wmi_pull_pdev_stats(const struct wmi_pdev_stats *src, + struct ath10k_fw_stats_pdev *dst) { const struct wal_dbg_tx_stats *tx = &src->wal.tx; const struct wal_dbg_rx_stats *rx = &src->wal.rx; @@ -1495,8 +1494,8 @@ static void ath10k_wmi_pull_pdev_stats(const struct wmi_pdev_stats *src, dst->mpdu_errs = __le32_to_cpu(rx->mpdu_errs); } -static void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src, - struct ath10k_fw_stats_peer *dst) +void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src, + struct ath10k_fw_stats_peer *dst) { ether_addr_copy(dst->peer_macaddr, src->peer_macaddr.addr); dst->peer_rssi = __le32_to_cpu(src->peer_rssi); @@ -1618,8 +1617,7 @@ static int ath10k_wmi_10x_op_pull_fw_stats(struct ath10k *ar, return 0; } -static void ath10k_wmi_event_update_stats(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_update_stats(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n"); ath10k_debug_fw_stats_process(ar, skb); @@ -1643,8 +1641,7 @@ ath10k_wmi_op_pull_vdev_start_ev(struct ath10k *ar, struct sk_buff *skb, return 0; } -static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, struct sk_buff *skb) { struct wmi_vdev_start_ev_arg arg = {}; int ret; @@ -1663,8 +1660,7 @@ static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, complete(&ar->vdev_setup_done); } -static void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_STOPPED_EVENTID\n"); complete(&ar->vdev_setup_done); @@ -1685,8 +1681,7 @@ ath10k_wmi_op_pull_peer_kick_ev(struct ath10k *ar, struct sk_buff *skb, return 0; } -static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, struct sk_buff *skb) { struct wmi_peer_kick_ev_arg arg = {}; struct ieee80211_sta *sta; @@ -1965,7 +1960,7 @@ static int ath10k_wmi_op_pull_swba_ev(struct ath10k *ar, struct sk_buff *skb, return 0; } -static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) +void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) { struct wmi_swba_ev_arg arg = {}; u32 map; @@ -2086,8 +2081,7 @@ skip: } } -static void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n"); } @@ -2208,9 +2202,9 @@ static int ath10k_dfs_fft_report(struct ath10k *ar, return 0; } -static void ath10k_wmi_event_dfs(struct ath10k *ar, - const struct wmi_phyerr *phyerr, - u64 tsf) +void ath10k_wmi_event_dfs(struct ath10k *ar, + const struct wmi_phyerr *phyerr, + u64 tsf) { int buf_len, tlv_len, res, i = 0; const struct phyerr_tlv *tlv; @@ -2273,10 +2267,9 @@ static void ath10k_wmi_event_dfs(struct ath10k *ar, } } -static void -ath10k_wmi_event_spectral_scan(struct ath10k *ar, - const struct wmi_phyerr *phyerr, - u64 tsf) +void ath10k_wmi_event_spectral_scan(struct ath10k *ar, + const struct wmi_phyerr *phyerr, + u64 tsf) { int buf_len, tlv_len, res, i = 0; struct phyerr_tlv *tlv; @@ -2345,7 +2338,7 @@ static int ath10k_wmi_op_pull_phyerr_ev(struct ath10k *ar, struct sk_buff *skb, return 0; } -static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb) +void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb) { struct wmi_phyerr_ev_arg arg = {}; const struct wmi_phyerr *phyerr; @@ -2414,19 +2407,17 @@ static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb) } } -static void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb) +void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_ROAM_EVENTID\n"); } -static void ath10k_wmi_event_profile_match(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_profile_match(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PROFILE_MATCH\n"); } -static void ath10k_wmi_event_debug_print(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_debug_print(struct ath10k *ar, struct sk_buff *skb) { char buf[101], c; int i; @@ -2459,103 +2450,90 @@ static void ath10k_wmi_event_debug_print(struct ath10k *ar, ath10k_dbg(ar, ATH10K_DBG_WMI_PRINT, "wmi print '%s'\n", buf); } -static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb) +void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_QVIT_EVENTID\n"); } -static void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_WLAN_PROFILE_DATA_EVENTID\n"); } -static void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar, + struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_RTT_MEASUREMENT_REPORT_EVENTID\n"); } -static void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar, + struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TSF_MEASUREMENT_REPORT_EVENTID\n"); } -static void ath10k_wmi_event_rtt_error_report(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_rtt_error_report(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_RTT_ERROR_REPORT_EVENTID\n"); } -static void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_WOW_WAKEUP_HOST_EVENTID\n"); } -static void ath10k_wmi_event_dcs_interference(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_dcs_interference(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_DCS_INTERFERENCE_EVENTID\n"); } -static void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_TPC_CONFIG_EVENTID\n"); } -static void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_FTM_INTG_EVENTID\n"); } -static void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_GTK_OFFLOAD_STATUS_EVENTID\n"); } -static void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_GTK_REKEY_FAIL_EVENTID\n"); } -static void ath10k_wmi_event_delba_complete(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_delba_complete(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TX_DELBA_COMPLETE_EVENTID\n"); } -static void ath10k_wmi_event_addba_complete(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_addba_complete(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TX_ADDBA_COMPLETE_EVENTID\n"); } -static void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar, + struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n"); } -static void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_INST_RSSI_STATS_EVENTID\n"); } -static void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_STANDBY_REQ_EVENTID\n"); } -static void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n"); } @@ -2664,8 +2642,7 @@ ath10k_wmi_10x_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb, return 0; } -static void ath10k_wmi_event_service_ready(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_service_ready(struct ath10k *ar, struct sk_buff *skb) { struct wmi_svc_rdy_ev_arg arg = {}; u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i; @@ -2790,7 +2767,7 @@ static int ath10k_wmi_op_pull_rdy_ev(struct ath10k *ar, struct sk_buff *skb, return 0; } -static int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb) +int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb) { struct wmi_rdy_ev_arg arg = {}; int ret; @@ -3311,8 +3288,8 @@ ath10k_wmi_op_gen_pdev_set_param(struct ath10k *ar, u32 id, u32 value) return skb; } -static void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar, - struct wmi_host_mem_chunks *chunks) +void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar, + struct wmi_host_mem_chunks *chunks) { struct host_memory_chunk *chunk; int i; @@ -3539,7 +3516,7 @@ static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar) return buf; } -static int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg) +int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg) { if (arg->ie_len && !arg->ie) return -EINVAL; @@ -3590,9 +3567,8 @@ ath10k_wmi_start_scan_tlvs_len(const struct wmi_start_scan_arg *arg) return len; } -static void -ath10k_wmi_put_start_scan_common(struct wmi_start_scan_common *cmn, - const struct wmi_start_scan_arg *arg) +void ath10k_wmi_put_start_scan_common(struct wmi_start_scan_common *cmn, + const struct wmi_start_scan_arg *arg) { u32 scan_id; u32 scan_req_id; @@ -4470,8 +4446,8 @@ ath10k_wmi_op_gen_beacon_dma(struct ath10k_vif *arvif) return skb; } -static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params, - const struct wmi_wmm_params_arg *arg) +void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params, + const struct wmi_wmm_params_arg *arg) { params->cwmin = __cpu_to_le32(arg->cwmin); params->cwmax = __cpu_to_le32(arg->cwmax); diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 7172f3c..032a8bd 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -4644,12 +4644,15 @@ struct wmi_rdy_ev_arg { struct ath10k; struct ath10k_vif; +struct ath10k_fw_stats_pdev; +struct ath10k_fw_stats_peer; int ath10k_wmi_attach(struct ath10k *ar); void ath10k_wmi_detach(struct ath10k *ar); int ath10k_wmi_wait_for_service_ready(struct ath10k *ar); int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar); +struct sk_buff *ath10k_wmi_alloc_skb(struct ath10k *ar, u32 len); int ath10k_wmi_connect(struct ath10k *ar); struct sk_buff *ath10k_wmi_alloc_skb(struct ath10k *ar, u32 len); @@ -4658,4 +4661,62 @@ int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id); void ath10k_wmi_start_scan_init(struct ath10k *ar, struct wmi_start_scan_arg *); +void ath10k_wmi_pull_pdev_stats(const struct wmi_pdev_stats *src, + struct ath10k_fw_stats_pdev *dst); +void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src, + struct ath10k_fw_stats_peer *dst); +void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar, + struct wmi_host_mem_chunks *chunks); +void ath10k_wmi_put_start_scan_common(struct wmi_start_scan_common *cmn, + const struct wmi_start_scan_arg *arg); +void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params, + const struct wmi_wmm_params_arg *arg); +void ath10k_wmi_put_wmi_channel(struct wmi_channel *ch, + const struct wmi_channel_arg *arg); +int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg); + +int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb); +int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb); +int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_update_stats(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_dfs(struct ath10k *ar, + const struct wmi_phyerr *phyerr, u64 tsf); +void ath10k_wmi_event_spectral_scan(struct ath10k *ar, + const struct wmi_phyerr *phyerr, + u64 tsf); +void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_profile_match(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_debug_print(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar, + struct sk_buff *skb); +void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar, + struct sk_buff *skb); +void ath10k_wmi_event_rtt_error_report(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_dcs_interference(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar, + struct sk_buff *skb); +void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_delba_complete(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_addba_complete(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar, + struct sk_buff *skb); +void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_service_ready(struct ath10k *ar, struct sk_buff *skb); +int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb); + #endif /* _WMI_H_ */ -- cgit v0.10.2 From ca996ec5660874edaf7fab6939beaaa9e023d347 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 3 Dec 2014 10:11:32 +0200 Subject: ath10k: implement wmi-tlv backend Latest main firmware branch introduced a new WMI ABI called wmi-tlv. It is not a tlv strictly speaking but something that resembles it because it is ordered and may have duplicate id entries. This prepares ath10k to support new hw. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile index 8b1b1ad..8abb66c 100644 --- a/drivers/net/wireless/ath/ath10k/Makefile +++ b/drivers/net/wireless/ath/ath10k/Makefile @@ -8,6 +8,7 @@ ath10k_core-y += mac.o \ htt_tx.o \ txrx.o \ wmi.o \ + wmi-tlv.o \ bmi.o ath10k_core-$(CONFIG_ATH10K_DEBUGFS) += spectral.o diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index a156e6e..42ec793 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -1093,6 +1093,8 @@ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, (CE_HTT_H2T_MSG_SRC_NENTRIES - 1)); BUILD_BUG_ON(2*TARGET_10X_NUM_MSDU_DESC > (CE_HTT_H2T_MSG_SRC_NENTRIES - 1)); + BUILD_BUG_ON(2*TARGET_TLV_NUM_MSDU_DESC > + (CE_HTT_H2T_MSG_SRC_NENTRIES - 1)); ce_state->ar = ar; ce_state->id = ce_id; diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index d83d9a7..577a3d7 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -915,6 +915,11 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) ar->max_num_stations = TARGET_10X_NUM_STATIONS; ar->htt.max_num_pending_tx = TARGET_10X_NUM_MSDU_DESC; break; + case ATH10K_FW_WMI_OP_VERSION_TLV: + ar->max_num_peers = TARGET_TLV_NUM_PEERS; + ar->max_num_stations = TARGET_TLV_NUM_STATIONS; + ar->htt.max_num_pending_tx = TARGET_TLV_NUM_MSDU_DESC; + break; case ATH10K_FW_WMI_OP_VERSION_UNSET: case ATH10K_FW_WMI_OP_VERSION_MAX: WARN_ON(1); diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 5bae90c..809c252 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -71,6 +71,7 @@ enum ath10k_fw_wmi_op_version { ATH10K_FW_WMI_OP_VERSION_MAIN = 1, ATH10K_FW_WMI_OP_VERSION_10_1 = 2, ATH10K_FW_WMI_OP_VERSION_10_2 = 3, + ATH10K_FW_WMI_OP_VERSION_TLV = 4, /* keep last */ ATH10K_FW_WMI_OP_VERSION_MAX, @@ -178,6 +179,15 @@ struct ath10k_pktlog_hdr { #define TARGET_10X_NUM_MSDU_DESC (1024 + 400) #define TARGET_10X_MAX_FRAG_ENTRIES 0 +/* Target specific defines for WMI-TLV firmware */ +#define TARGET_TLV_NUM_VDEVS 3 +#define TARGET_TLV_NUM_STATIONS 32 +#define TARGET_TLV_NUM_PEERS ((TARGET_TLV_NUM_STATIONS) + \ + (TARGET_TLV_NUM_VDEVS) + \ + 2) +#define TARGET_TLV_NUM_TIDS ((TARGET_TLV_NUM_PEERS) * 2) +#define TARGET_TLV_NUM_MSDU_DESC (1024 + 32) + /* Number of Copy Engines supported */ #define CE_COUNT 8 diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c new file mode 100644 index 0000000..b21048c --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -0,0 +1,2218 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2014 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include "core.h" +#include "debug.h" +#include "hw.h" +#include "wmi.h" +#include "wmi-ops.h" +#include "wmi-tlv.h" + +/***************/ +/* TLV helpers */ +/**************/ + +struct wmi_tlv_policy { + size_t min_len; +}; + +static const struct wmi_tlv_policy wmi_tlv_policies[] = { + [WMI_TLV_TAG_ARRAY_BYTE] + = { .min_len = sizeof(u8) }, + [WMI_TLV_TAG_ARRAY_UINT32] + = { .min_len = sizeof(u32) }, + [WMI_TLV_TAG_STRUCT_SCAN_EVENT] + = { .min_len = sizeof(struct wmi_scan_event) }, + [WMI_TLV_TAG_STRUCT_MGMT_RX_HDR] + = { .min_len = sizeof(struct wmi_tlv_mgmt_rx_ev) }, + [WMI_TLV_TAG_STRUCT_CHAN_INFO_EVENT] + = { .min_len = sizeof(struct wmi_chan_info_event) }, + [WMI_TLV_TAG_STRUCT_VDEV_START_RESPONSE_EVENT] + = { .min_len = sizeof(struct wmi_vdev_start_response_event) }, + [WMI_TLV_TAG_STRUCT_PEER_STA_KICKOUT_EVENT] + = { .min_len = sizeof(struct wmi_peer_sta_kickout_event) }, + [WMI_TLV_TAG_STRUCT_HOST_SWBA_EVENT] + = { .min_len = sizeof(struct wmi_host_swba_event) }, + [WMI_TLV_TAG_STRUCT_TIM_INFO] + = { .min_len = sizeof(struct wmi_tim_info) }, + [WMI_TLV_TAG_STRUCT_P2P_NOA_INFO] + = { .min_len = sizeof(struct wmi_p2p_noa_info) }, + [WMI_TLV_TAG_STRUCT_SERVICE_READY_EVENT] + = { .min_len = sizeof(struct wmi_tlv_svc_rdy_ev) }, + [WMI_TLV_TAG_STRUCT_HAL_REG_CAPABILITIES] + = { .min_len = sizeof(struct hal_reg_capabilities) }, + [WMI_TLV_TAG_STRUCT_WLAN_HOST_MEM_REQ] + = { .min_len = sizeof(struct wlan_host_mem_req) }, + [WMI_TLV_TAG_STRUCT_READY_EVENT] + = { .min_len = sizeof(struct wmi_tlv_rdy_ev) }, +}; + +static int +ath10k_wmi_tlv_iter(struct ath10k *ar, const void *ptr, size_t len, + int (*iter)(struct ath10k *ar, u16 tag, u16 len, + const void *ptr, void *data), + void *data) +{ + const void *begin = ptr; + const struct wmi_tlv *tlv; + u16 tlv_tag, tlv_len; + int ret; + + while (len > 0) { + if (len < sizeof(*tlv)) { + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n", + ptr - begin, len, sizeof(*tlv)); + return -EINVAL; + } + + tlv = ptr; + tlv_tag = __le16_to_cpu(tlv->tag); + tlv_len = __le16_to_cpu(tlv->len); + ptr += sizeof(*tlv); + len -= sizeof(*tlv); + + if (tlv_len > len) { + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi tlv parse failure of tag %hhu at byte %zd (%zu bytes left, %hhu expected)\n", + tlv_tag, ptr - begin, len, tlv_len); + return -EINVAL; + } + + if (tlv_tag < ARRAY_SIZE(wmi_tlv_policies) && + wmi_tlv_policies[tlv_tag].min_len && + wmi_tlv_policies[tlv_tag].min_len > tlv_len) { + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi tlv parse failure of tag %hhu at byte %zd (%hhu bytes is less than min length %zu)\n", + tlv_tag, ptr - begin, tlv_len, + wmi_tlv_policies[tlv_tag].min_len); + return -EINVAL; + } + + ret = iter(ar, tlv_tag, tlv_len, ptr, data); + if (ret) + return ret; + + ptr += tlv_len; + len -= tlv_len; + } + + return 0; +} + +static int ath10k_wmi_tlv_iter_parse(struct ath10k *ar, u16 tag, u16 len, + const void *ptr, void *data) +{ + const void **tb = data; + + if (tag < WMI_TLV_TAG_MAX) + tb[tag] = ptr; + + return 0; +} + +static int ath10k_wmi_tlv_parse(struct ath10k *ar, const void **tb, + const void *ptr, size_t len) +{ + return ath10k_wmi_tlv_iter(ar, ptr, len, ath10k_wmi_tlv_iter_parse, + (void *)tb); +} + +static const void ** +ath10k_wmi_tlv_parse_alloc(struct ath10k *ar, const void *ptr, + size_t len, gfp_t gfp) +{ + const void **tb; + int ret; + + tb = kzalloc(sizeof(*tb) * WMI_TLV_TAG_MAX, gfp); + if (!tb) + return ERR_PTR(-ENOMEM); + + ret = ath10k_wmi_tlv_parse(ar, tb, ptr, len); + if (ret) { + kfree(tb); + return ERR_PTR(ret); + } + + return tb; +} + +static u16 ath10k_wmi_tlv_len(const void *ptr) +{ + return __le16_to_cpu((((const struct wmi_tlv *)ptr) - 1)->len); +} + +/***********/ +/* TLV ops */ +/***********/ + +static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_cmd_hdr *cmd_hdr; + enum wmi_tlv_event_id id; + + cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID); + + if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL) + return; + + trace_ath10k_wmi_event(ar, id, skb->data, skb->len); + + switch (id) { + case WMI_TLV_MGMT_RX_EVENTID: + ath10k_wmi_event_mgmt_rx(ar, skb); + /* mgmt_rx() owns the skb now! */ + return; + case WMI_TLV_SCAN_EVENTID: + ath10k_wmi_event_scan(ar, skb); + break; + case WMI_TLV_CHAN_INFO_EVENTID: + ath10k_wmi_event_chan_info(ar, skb); + break; + case WMI_TLV_ECHO_EVENTID: + ath10k_wmi_event_echo(ar, skb); + break; + case WMI_TLV_DEBUG_MESG_EVENTID: + ath10k_wmi_event_debug_mesg(ar, skb); + break; + case WMI_TLV_UPDATE_STATS_EVENTID: + ath10k_wmi_event_update_stats(ar, skb); + break; + case WMI_TLV_VDEV_START_RESP_EVENTID: + ath10k_wmi_event_vdev_start_resp(ar, skb); + break; + case WMI_TLV_VDEV_STOPPED_EVENTID: + ath10k_wmi_event_vdev_stopped(ar, skb); + break; + case WMI_TLV_PEER_STA_KICKOUT_EVENTID: + ath10k_wmi_event_peer_sta_kickout(ar, skb); + break; + case WMI_TLV_HOST_SWBA_EVENTID: + ath10k_wmi_event_host_swba(ar, skb); + break; + case WMI_TLV_TBTTOFFSET_UPDATE_EVENTID: + ath10k_wmi_event_tbttoffset_update(ar, skb); + break; + case WMI_TLV_PHYERR_EVENTID: + ath10k_wmi_event_phyerr(ar, skb); + break; + case WMI_TLV_ROAM_EVENTID: + ath10k_wmi_event_roam(ar, skb); + break; + case WMI_TLV_PROFILE_MATCH: + ath10k_wmi_event_profile_match(ar, skb); + break; + case WMI_TLV_DEBUG_PRINT_EVENTID: + ath10k_wmi_event_debug_print(ar, skb); + break; + case WMI_TLV_PDEV_QVIT_EVENTID: + ath10k_wmi_event_pdev_qvit(ar, skb); + break; + case WMI_TLV_WLAN_PROFILE_DATA_EVENTID: + ath10k_wmi_event_wlan_profile_data(ar, skb); + break; + case WMI_TLV_RTT_MEASUREMENT_REPORT_EVENTID: + ath10k_wmi_event_rtt_measurement_report(ar, skb); + break; + case WMI_TLV_TSF_MEASUREMENT_REPORT_EVENTID: + ath10k_wmi_event_tsf_measurement_report(ar, skb); + break; + case WMI_TLV_RTT_ERROR_REPORT_EVENTID: + ath10k_wmi_event_rtt_error_report(ar, skb); + break; + case WMI_TLV_WOW_WAKEUP_HOST_EVENTID: + ath10k_wmi_event_wow_wakeup_host(ar, skb); + break; + case WMI_TLV_DCS_INTERFERENCE_EVENTID: + ath10k_wmi_event_dcs_interference(ar, skb); + break; + case WMI_TLV_PDEV_TPC_CONFIG_EVENTID: + ath10k_wmi_event_pdev_tpc_config(ar, skb); + break; + case WMI_TLV_PDEV_FTM_INTG_EVENTID: + ath10k_wmi_event_pdev_ftm_intg(ar, skb); + break; + case WMI_TLV_GTK_OFFLOAD_STATUS_EVENTID: + ath10k_wmi_event_gtk_offload_status(ar, skb); + break; + case WMI_TLV_GTK_REKEY_FAIL_EVENTID: + ath10k_wmi_event_gtk_rekey_fail(ar, skb); + break; + case WMI_TLV_TX_DELBA_COMPLETE_EVENTID: + ath10k_wmi_event_delba_complete(ar, skb); + break; + case WMI_TLV_TX_ADDBA_COMPLETE_EVENTID: + ath10k_wmi_event_addba_complete(ar, skb); + break; + case WMI_TLV_VDEV_INSTALL_KEY_COMPLETE_EVENTID: + ath10k_wmi_event_vdev_install_key_complete(ar, skb); + break; + case WMI_TLV_SERVICE_READY_EVENTID: + ath10k_wmi_event_service_ready(ar, skb); + break; + case WMI_TLV_READY_EVENTID: + ath10k_wmi_event_ready(ar, skb); + break; + default: + ath10k_warn(ar, "Unknown eventid: %d\n", id); + break; + } + + dev_kfree_skb(skb); +} + +static int ath10k_wmi_tlv_op_pull_scan_ev(struct ath10k *ar, + struct sk_buff *skb, + struct wmi_scan_ev_arg *arg) +{ + const void **tb; + const struct wmi_scan_event *ev; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_SCAN_EVENT]; + if (!ev) { + kfree(tb); + return -EPROTO; + } + + arg->event_type = ev->event_type; + arg->reason = ev->reason; + arg->channel_freq = ev->channel_freq; + arg->scan_req_id = ev->scan_req_id; + arg->scan_id = ev->scan_id; + arg->vdev_id = ev->vdev_id; + + kfree(tb); + return 0; +} + +static int ath10k_wmi_tlv_op_pull_mgmt_rx_ev(struct ath10k *ar, + struct sk_buff *skb, + struct wmi_mgmt_rx_ev_arg *arg) +{ + const void **tb; + const struct wmi_tlv_mgmt_rx_ev *ev; + const u8 *frame; + u32 msdu_len; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_MGMT_RX_HDR]; + frame = tb[WMI_TLV_TAG_ARRAY_BYTE]; + + if (!ev || !frame) { + kfree(tb); + return -EPROTO; + } + + arg->channel = ev->channel; + arg->buf_len = ev->buf_len; + arg->status = ev->status; + arg->snr = ev->snr; + arg->phy_mode = ev->phy_mode; + arg->rate = ev->rate; + + msdu_len = __le32_to_cpu(arg->buf_len); + + if (skb->len < (frame - skb->data) + msdu_len) { + kfree(tb); + return -EPROTO; + } + + /* shift the sk_buff to point to `frame` */ + skb_trim(skb, 0); + skb_put(skb, frame - skb->data); + skb_pull(skb, frame - skb->data); + skb_put(skb, msdu_len); + + kfree(tb); + return 0; +} + +static int ath10k_wmi_tlv_op_pull_ch_info_ev(struct ath10k *ar, + struct sk_buff *skb, + struct wmi_ch_info_ev_arg *arg) +{ + const void **tb; + const struct wmi_chan_info_event *ev; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_CHAN_INFO_EVENT]; + if (!ev) { + kfree(tb); + return -EPROTO; + } + + arg->err_code = ev->err_code; + arg->freq = ev->freq; + arg->cmd_flags = ev->cmd_flags; + arg->noise_floor = ev->noise_floor; + arg->rx_clear_count = ev->rx_clear_count; + arg->cycle_count = ev->cycle_count; + + kfree(tb); + return 0; +} + +static int +ath10k_wmi_tlv_op_pull_vdev_start_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_vdev_start_ev_arg *arg) +{ + const void **tb; + const struct wmi_vdev_start_response_event *ev; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_VDEV_START_RESPONSE_EVENT]; + if (!ev) { + kfree(tb); + return -EPROTO; + } + + skb_pull(skb, sizeof(*ev)); + arg->vdev_id = ev->vdev_id; + arg->req_id = ev->req_id; + arg->resp_type = ev->resp_type; + arg->status = ev->status; + + kfree(tb); + return 0; +} + +static int ath10k_wmi_tlv_op_pull_peer_kick_ev(struct ath10k *ar, + struct sk_buff *skb, + struct wmi_peer_kick_ev_arg *arg) +{ + const void **tb; + const struct wmi_peer_sta_kickout_event *ev; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_PEER_STA_KICKOUT_EVENT]; + if (!ev) { + kfree(tb); + return -EPROTO; + } + + arg->mac_addr = ev->peer_macaddr.addr; + + kfree(tb); + return 0; +} + +struct wmi_tlv_swba_parse { + const struct wmi_host_swba_event *ev; + bool tim_done; + bool noa_done; + size_t n_tim; + size_t n_noa; + struct wmi_swba_ev_arg *arg; +}; + +static int ath10k_wmi_tlv_swba_tim_parse(struct ath10k *ar, u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_swba_parse *swba = data; + + if (tag != WMI_TLV_TAG_STRUCT_TIM_INFO) + return -EPROTO; + + if (swba->n_tim >= ARRAY_SIZE(swba->arg->tim_info)) + return -ENOBUFS; + + swba->arg->tim_info[swba->n_tim++] = ptr; + return 0; +} + +static int ath10k_wmi_tlv_swba_noa_parse(struct ath10k *ar, u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_swba_parse *swba = data; + + if (tag != WMI_TLV_TAG_STRUCT_P2P_NOA_INFO) + return -EPROTO; + + if (swba->n_noa >= ARRAY_SIZE(swba->arg->noa_info)) + return -ENOBUFS; + + swba->arg->noa_info[swba->n_noa++] = ptr; + return 0; +} + +static int ath10k_wmi_tlv_swba_parse(struct ath10k *ar, u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_swba_parse *swba = data; + int ret; + + switch (tag) { + case WMI_TLV_TAG_STRUCT_HOST_SWBA_EVENT: + swba->ev = ptr; + break; + case WMI_TLV_TAG_ARRAY_STRUCT: + if (!swba->tim_done) { + swba->tim_done = true; + ret = ath10k_wmi_tlv_iter(ar, ptr, len, + ath10k_wmi_tlv_swba_tim_parse, + swba); + if (ret) + return ret; + } else if (!swba->noa_done) { + swba->noa_done = true; + ret = ath10k_wmi_tlv_iter(ar, ptr, len, + ath10k_wmi_tlv_swba_noa_parse, + swba); + if (ret) + return ret; + } + break; + default: + break; + } + return 0; +} + +static int ath10k_wmi_tlv_op_pull_swba_ev(struct ath10k *ar, + struct sk_buff *skb, + struct wmi_swba_ev_arg *arg) +{ + struct wmi_tlv_swba_parse swba = { .arg = arg }; + u32 map; + size_t n_vdevs; + int ret; + + ret = ath10k_wmi_tlv_iter(ar, skb->data, skb->len, + ath10k_wmi_tlv_swba_parse, &swba); + if (ret) { + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + if (!swba.ev) + return -EPROTO; + + arg->vdev_map = swba.ev->vdev_map; + + for (map = __le32_to_cpu(arg->vdev_map), n_vdevs = 0; map; map >>= 1) + if (map & BIT(0)) + n_vdevs++; + + if (n_vdevs != swba.n_tim || + n_vdevs != swba.n_noa) + return -EPROTO; + + return 0; +} + +static int ath10k_wmi_tlv_op_pull_phyerr_ev(struct ath10k *ar, + struct sk_buff *skb, + struct wmi_phyerr_ev_arg *arg) +{ + const void **tb; + const struct wmi_tlv_phyerr_ev *ev; + const void *phyerrs; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_COMB_PHYERR_RX_HDR]; + phyerrs = tb[WMI_TLV_TAG_ARRAY_BYTE]; + + if (!ev || !phyerrs) { + kfree(tb); + return -EPROTO; + } + + arg->num_phyerrs = ev->num_phyerrs; + arg->tsf_l32 = ev->tsf_l32; + arg->tsf_u32 = ev->tsf_u32; + arg->buf_len = ev->buf_len; + arg->phyerrs = phyerrs; + + kfree(tb); + return 0; +} + +#define WMI_TLV_ABI_VER_NS0 0x5F414351 +#define WMI_TLV_ABI_VER_NS1 0x00004C4D +#define WMI_TLV_ABI_VER_NS2 0x00000000 +#define WMI_TLV_ABI_VER_NS3 0x00000000 + +#define WMI_TLV_ABI_VER0_MAJOR 1 +#define WMI_TLV_ABI_VER0_MINOR 0 +#define WMI_TLV_ABI_VER0 ((((WMI_TLV_ABI_VER0_MAJOR) << 24) & 0xFF000000) | \ + (((WMI_TLV_ABI_VER0_MINOR) << 0) & 0x00FFFFFF)) +#define WMI_TLV_ABI_VER1 53 + +static int +ath10k_wmi_tlv_parse_mem_reqs(struct ath10k *ar, u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_svc_rdy_ev_arg *arg = data; + int i; + + if (tag != WMI_TLV_TAG_STRUCT_WLAN_HOST_MEM_REQ) + return -EPROTO; + + for (i = 0; i < ARRAY_SIZE(arg->mem_reqs); i++) { + if (!arg->mem_reqs[i]) { + arg->mem_reqs[i] = ptr; + return 0; + } + } + + return -ENOMEM; +} + +static int ath10k_wmi_tlv_op_pull_svc_rdy_ev(struct ath10k *ar, + struct sk_buff *skb, + struct wmi_svc_rdy_ev_arg *arg) +{ + const void **tb; + const struct hal_reg_capabilities *reg; + const struct wmi_tlv_svc_rdy_ev *ev; + const __le32 *svc_bmap; + const struct wlan_host_mem_req *mem_reqs; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_SERVICE_READY_EVENT]; + reg = tb[WMI_TLV_TAG_STRUCT_HAL_REG_CAPABILITIES]; + svc_bmap = tb[WMI_TLV_TAG_ARRAY_UINT32]; + mem_reqs = tb[WMI_TLV_TAG_ARRAY_STRUCT]; + + if (!ev || !reg || !svc_bmap || !mem_reqs) { + kfree(tb); + return -EPROTO; + } + + /* This is an internal ABI compatibility check for WMI TLV so check it + * here instead of the generic WMI code. + */ + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi tlv abi 0x%08x ?= 0x%08x, 0x%08x ?= 0x%08x, 0x%08x ?= 0x%08x, 0x%08x ?= 0x%08x, 0x%08x ?= 0x%08x\n", + __le32_to_cpu(ev->abi.abi_ver0), WMI_TLV_ABI_VER0, + __le32_to_cpu(ev->abi.abi_ver_ns0), WMI_TLV_ABI_VER_NS0, + __le32_to_cpu(ev->abi.abi_ver_ns1), WMI_TLV_ABI_VER_NS1, + __le32_to_cpu(ev->abi.abi_ver_ns2), WMI_TLV_ABI_VER_NS2, + __le32_to_cpu(ev->abi.abi_ver_ns3), WMI_TLV_ABI_VER_NS3); + + if (__le32_to_cpu(ev->abi.abi_ver0) != WMI_TLV_ABI_VER0 || + __le32_to_cpu(ev->abi.abi_ver_ns0) != WMI_TLV_ABI_VER_NS0 || + __le32_to_cpu(ev->abi.abi_ver_ns1) != WMI_TLV_ABI_VER_NS1 || + __le32_to_cpu(ev->abi.abi_ver_ns2) != WMI_TLV_ABI_VER_NS2 || + __le32_to_cpu(ev->abi.abi_ver_ns3) != WMI_TLV_ABI_VER_NS3) { + kfree(tb); + return -ENOTSUPP; + } + + arg->min_tx_power = ev->hw_min_tx_power; + arg->max_tx_power = ev->hw_max_tx_power; + arg->ht_cap = ev->ht_cap_info; + arg->vht_cap = ev->vht_cap_info; + arg->sw_ver0 = ev->abi.abi_ver0; + arg->sw_ver1 = ev->abi.abi_ver1; + arg->fw_build = ev->fw_build_vers; + arg->phy_capab = ev->phy_capability; + arg->num_rf_chains = ev->num_rf_chains; + arg->eeprom_rd = reg->eeprom_rd; + arg->num_mem_reqs = ev->num_mem_reqs; + arg->service_map = svc_bmap; + arg->service_map_len = ath10k_wmi_tlv_len(svc_bmap); + + ret = ath10k_wmi_tlv_iter(ar, mem_reqs, ath10k_wmi_tlv_len(mem_reqs), + ath10k_wmi_tlv_parse_mem_reqs, arg); + if (ret) { + kfree(tb); + ath10k_warn(ar, "failed to parse mem_reqs tlv: %d\n", ret); + return ret; + } + + kfree(tb); + return 0; +} + +static int ath10k_wmi_tlv_op_pull_rdy_ev(struct ath10k *ar, + struct sk_buff *skb, + struct wmi_rdy_ev_arg *arg) +{ + const void **tb; + const struct wmi_tlv_rdy_ev *ev; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_READY_EVENT]; + if (!ev) { + kfree(tb); + return -EPROTO; + } + + arg->sw_version = ev->abi.abi_ver0; + arg->abi_version = ev->abi.abi_ver1; + arg->status = ev->status; + arg->mac_addr = ev->mac_addr.addr; + + kfree(tb); + return 0; +} + +static int ath10k_wmi_tlv_op_pull_fw_stats(struct ath10k *ar, + struct sk_buff *skb, + struct ath10k_fw_stats *stats) +{ + const void **tb; + const struct wmi_stats_event *ev; + const void *data; + u32 num_pdev_stats, num_vdev_stats, num_peer_stats; + size_t data_len; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_STATS_EVENT]; + data = tb[WMI_TLV_TAG_ARRAY_BYTE]; + + if (!ev || !data) { + kfree(tb); + return -EPROTO; + } + + data_len = ath10k_wmi_tlv_len(data); + num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats); + num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats); + num_peer_stats = __le32_to_cpu(ev->num_peer_stats); + + WARN_ON(1); /* FIXME: not implemented yet */ + + kfree(tb); + return 0; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_pdev_suspend(struct ath10k *ar, u32 opt) +{ + struct wmi_tlv_pdev_suspend *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_SUSPEND_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->opt = __cpu_to_le32(opt); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev suspend\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_pdev_resume(struct ath10k *ar) +{ + struct wmi_tlv_resume_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_RESUME_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->reserved = __cpu_to_le32(0); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev resume\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_pdev_set_rd(struct ath10k *ar, + u16 rd, u16 rd2g, u16 rd5g, + u16 ctl2g, u16 ctl5g, + enum wmi_dfs_region dfs_reg) +{ + struct wmi_tlv_pdev_set_rd_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_SET_REGDOMAIN_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->regd = __cpu_to_le32(rd); + cmd->regd_2ghz = __cpu_to_le32(rd2g); + cmd->regd_5ghz = __cpu_to_le32(rd5g); + cmd->conform_limit_2ghz = __cpu_to_le32(rd2g); + cmd->conform_limit_5ghz = __cpu_to_le32(rd5g); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev set rd\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_pdev_set_param(struct ath10k *ar, u32 param_id, + u32 param_value) +{ + struct wmi_tlv_pdev_set_param_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_SET_PARAM_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(param_value); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev set param\n"); + return skb; +} + +static struct sk_buff *ath10k_wmi_tlv_op_gen_init(struct ath10k *ar) +{ + struct sk_buff *skb; + struct wmi_tlv *tlv; + struct wmi_tlv_init_cmd *cmd; + struct wmi_tlv_resource_config *cfg; + struct wmi_host_mem_chunks *chunks; + size_t len, chunks_len; + void *ptr; + + chunks_len = ar->wmi.num_mem_chunks * sizeof(struct host_memory_chunk); + len = (sizeof(*tlv) + sizeof(*cmd)) + + (sizeof(*tlv) + sizeof(*cfg)) + + (sizeof(*tlv) + chunks_len); + + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = skb->data; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_INIT_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_RESOURCE_CONFIG); + tlv->len = __cpu_to_le16(sizeof(*cfg)); + cfg = (void *)tlv->value; + ptr += sizeof(*tlv); + ptr += sizeof(*cfg); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT); + tlv->len = __cpu_to_le16(chunks_len); + chunks = (void *)tlv->value; + + ptr += sizeof(*tlv); + ptr += chunks_len; + + cmd->abi.abi_ver0 = __cpu_to_le32(WMI_TLV_ABI_VER0); + cmd->abi.abi_ver1 = __cpu_to_le32(WMI_TLV_ABI_VER1); + cmd->abi.abi_ver_ns0 = __cpu_to_le32(WMI_TLV_ABI_VER_NS0); + cmd->abi.abi_ver_ns1 = __cpu_to_le32(WMI_TLV_ABI_VER_NS1); + cmd->abi.abi_ver_ns2 = __cpu_to_le32(WMI_TLV_ABI_VER_NS2); + cmd->abi.abi_ver_ns3 = __cpu_to_le32(WMI_TLV_ABI_VER_NS3); + cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks); + + cfg->num_vdevs = __cpu_to_le32(TARGET_TLV_NUM_VDEVS); + cfg->num_peers = __cpu_to_le32(TARGET_TLV_NUM_PEERS); + cfg->num_offload_peers = __cpu_to_le32(0); + cfg->num_offload_reorder_bufs = __cpu_to_le32(0); + cfg->num_peer_keys = __cpu_to_le32(2); + cfg->num_tids = __cpu_to_le32(TARGET_TLV_NUM_TIDS); + cfg->ast_skid_limit = __cpu_to_le32(0x10); + cfg->tx_chain_mask = __cpu_to_le32(0x7); + cfg->rx_chain_mask = __cpu_to_le32(0x7); + cfg->rx_timeout_pri[0] = __cpu_to_le32(0x64); + cfg->rx_timeout_pri[1] = __cpu_to_le32(0x64); + cfg->rx_timeout_pri[2] = __cpu_to_le32(0x64); + cfg->rx_timeout_pri[3] = __cpu_to_le32(0x28); + cfg->rx_decap_mode = __cpu_to_le32(1); + cfg->scan_max_pending_reqs = __cpu_to_le32(4); + cfg->bmiss_offload_max_vdev = __cpu_to_le32(3); + cfg->roam_offload_max_vdev = __cpu_to_le32(3); + cfg->roam_offload_max_ap_profiles = __cpu_to_le32(8); + cfg->num_mcast_groups = __cpu_to_le32(0); + cfg->num_mcast_table_elems = __cpu_to_le32(0); + cfg->mcast2ucast_mode = __cpu_to_le32(0); + cfg->tx_dbg_log_size = __cpu_to_le32(0x400); + cfg->num_wds_entries = __cpu_to_le32(0x20); + cfg->dma_burst_size = __cpu_to_le32(0); + cfg->mac_aggr_delim = __cpu_to_le32(0); + cfg->rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(0); + cfg->vow_config = __cpu_to_le32(0); + cfg->gtk_offload_max_vdev = __cpu_to_le32(2); + cfg->num_msdu_desc = __cpu_to_le32(TARGET_TLV_NUM_MSDU_DESC); + cfg->max_frag_entries = __cpu_to_le32(2); + cfg->num_tdls_vdevs = __cpu_to_le32(1); + cfg->num_tdls_conn_table_entries = __cpu_to_le32(0x20); + cfg->beacon_tx_offload_max_vdev = __cpu_to_le32(2); + cfg->num_multicast_filter_entries = __cpu_to_le32(5); + cfg->num_wow_filters = __cpu_to_le32(0x16); + cfg->num_keep_alive_pattern = __cpu_to_le32(6); + cfg->keep_alive_pattern_size = __cpu_to_le32(0); + cfg->max_tdls_concurrent_sleep_sta = __cpu_to_le32(1); + cfg->max_tdls_concurrent_buffer_sta = __cpu_to_le32(1); + + ath10k_wmi_put_host_mem_chunks(ar, chunks); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv init\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_start_scan(struct ath10k *ar, + const struct wmi_start_scan_arg *arg) +{ + struct wmi_tlv_start_scan_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + size_t len, chan_len, ssid_len, bssid_len, ie_len; + __le32 *chans; + struct wmi_ssid *ssids; + struct wmi_mac_addr *addrs; + void *ptr; + int i, ret; + + ret = ath10k_wmi_start_scan_verify(arg); + if (ret) + return ERR_PTR(ret); + + chan_len = arg->n_channels * sizeof(__le32); + ssid_len = arg->n_ssids * sizeof(struct wmi_ssid); + bssid_len = arg->n_bssids * sizeof(struct wmi_mac_addr); + ie_len = roundup(arg->ie_len, 4); + len = (sizeof(*tlv) + sizeof(*cmd)) + + (arg->n_channels ? sizeof(*tlv) + chan_len : 0) + + (arg->n_ssids ? sizeof(*tlv) + ssid_len : 0) + + (arg->n_bssids ? sizeof(*tlv) + bssid_len : 0) + + (arg->ie_len ? sizeof(*tlv) + ie_len : 0); + + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_START_SCAN_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + + ath10k_wmi_put_start_scan_common(&cmd->common, arg); + cmd->burst_duration_ms = __cpu_to_le32(0); + cmd->num_channels = __cpu_to_le32(arg->n_channels); + cmd->num_ssids = __cpu_to_le32(arg->n_ssids); + cmd->num_bssids = __cpu_to_le32(arg->n_bssids); + cmd->ie_len = __cpu_to_le32(arg->ie_len); + cmd->num_probes = __cpu_to_le32(3); + + /* FIXME: There are some scan flag inconsistencies across firmwares, + * e.g. WMI-TLV inverts the logic behind the following flag. + */ + cmd->common.scan_ctrl_flags ^= __cpu_to_le32(WMI_SCAN_FILTER_PROBE_REQ); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_UINT32); + tlv->len = __cpu_to_le16(chan_len); + chans = (void *)tlv->value; + for (i = 0; i < arg->n_channels; i++) + chans[i] = __cpu_to_le32(arg->channels[i]); + + ptr += sizeof(*tlv); + ptr += chan_len; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_FIXED_STRUCT); + tlv->len = __cpu_to_le16(ssid_len); + ssids = (void *)tlv->value; + for (i = 0; i < arg->n_ssids; i++) { + ssids[i].ssid_len = __cpu_to_le32(arg->ssids[i].len); + memcpy(ssids[i].ssid, arg->ssids[i].ssid, arg->ssids[i].len); + } + + ptr += sizeof(*tlv); + ptr += ssid_len; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_FIXED_STRUCT); + tlv->len = __cpu_to_le16(bssid_len); + addrs = (void *)tlv->value; + for (i = 0; i < arg->n_bssids; i++) + ether_addr_copy(addrs[i].addr, arg->bssids[i].bssid); + + ptr += sizeof(*tlv); + ptr += bssid_len; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE); + tlv->len = __cpu_to_le16(ie_len); + memcpy(tlv->value, arg->ie, arg->ie_len); + + ptr += sizeof(*tlv); + ptr += ie_len; + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv start scan\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_stop_scan(struct ath10k *ar, + const struct wmi_stop_scan_arg *arg) +{ + struct wmi_stop_scan_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + u32 scan_id; + u32 req_id; + + if (arg->req_id > 0xFFF) + return ERR_PTR(-EINVAL); + if (arg->req_type == WMI_SCAN_STOP_ONE && arg->u.scan_id > 0xFFF) + return ERR_PTR(-EINVAL); + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + scan_id = arg->u.scan_id; + scan_id |= WMI_HOST_SCAN_REQ_ID_PREFIX; + + req_id = arg->req_id; + req_id |= WMI_HOST_SCAN_REQUESTOR_ID_PREFIX; + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STOP_SCAN_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->req_type = __cpu_to_le32(arg->req_type); + cmd->vdev_id = __cpu_to_le32(arg->u.vdev_id); + cmd->scan_id = __cpu_to_le32(scan_id); + cmd->scan_req_id = __cpu_to_le32(req_id); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv stop scan\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_create(struct ath10k *ar, + u32 vdev_id, + enum wmi_vdev_type vdev_type, + enum wmi_vdev_subtype vdev_subtype, + const u8 mac_addr[ETH_ALEN]) +{ + struct wmi_vdev_create_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_CREATE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->vdev_type = __cpu_to_le32(vdev_type); + cmd->vdev_subtype = __cpu_to_le32(vdev_subtype); + ether_addr_copy(cmd->vdev_macaddr.addr, mac_addr); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev create\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_delete(struct ath10k *ar, u32 vdev_id) +{ + struct wmi_vdev_delete_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_DELETE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev delete\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_start(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *arg, + bool restart) +{ + struct wmi_tlv_vdev_start_cmd *cmd; + struct wmi_channel *ch; + struct wmi_p2p_noa_descriptor *noa; + struct wmi_tlv *tlv; + struct sk_buff *skb; + size_t len; + void *ptr; + u32 flags = 0; + + if (WARN_ON(arg->ssid && arg->ssid_len == 0)) + return ERR_PTR(-EINVAL); + if (WARN_ON(arg->hidden_ssid && !arg->ssid)) + return ERR_PTR(-EINVAL); + if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid))) + return ERR_PTR(-EINVAL); + + len = (sizeof(*tlv) + sizeof(*cmd)) + + (sizeof(*tlv) + sizeof(*ch)) + + (sizeof(*tlv) + 0); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + if (arg->hidden_ssid) + flags |= WMI_VDEV_START_HIDDEN_SSID; + if (arg->pmf_enabled) + flags |= WMI_VDEV_START_PMF_ENABLED; + + ptr = (void *)skb->data; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_START_REQUEST_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->bcn_intval = __cpu_to_le32(arg->bcn_intval); + cmd->dtim_period = __cpu_to_le32(arg->dtim_period); + cmd->flags = __cpu_to_le32(flags); + cmd->bcn_tx_rate = __cpu_to_le32(arg->bcn_tx_rate); + cmd->bcn_tx_power = __cpu_to_le32(arg->bcn_tx_power); + cmd->disable_hw_ack = __cpu_to_le32(arg->disable_hw_ack); + + if (arg->ssid) { + cmd->ssid.ssid_len = __cpu_to_le32(arg->ssid_len); + memcpy(cmd->ssid.ssid, arg->ssid, arg->ssid_len); + } + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_CHANNEL); + tlv->len = __cpu_to_le16(sizeof(*ch)); + ch = (void *)tlv->value; + ath10k_wmi_put_wmi_channel(ch, &arg->channel); + + ptr += sizeof(*tlv); + ptr += sizeof(*ch); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT); + tlv->len = 0; + noa = (void *)tlv->value; + + /* Note: This is a nested TLV containing: + * [wmi_tlv][wmi_p2p_noa_descriptor][wmi_tlv].. + */ + + ptr += sizeof(*tlv); + ptr += 0; + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev start\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_stop(struct ath10k *ar, u32 vdev_id) +{ + struct wmi_vdev_stop_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_STOP_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev stop\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, + const u8 *bssid) + +{ + struct wmi_vdev_up_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_UP_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->vdev_assoc_id = __cpu_to_le32(aid); + ether_addr_copy(cmd->vdev_bssid.addr, bssid); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev up\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_down(struct ath10k *ar, u32 vdev_id) +{ + struct wmi_vdev_down_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_DOWN_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev down\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_set_param(struct ath10k *ar, u32 vdev_id, + u32 param_id, u32 param_value) +{ + struct wmi_vdev_set_param_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_SET_PARAM_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(param_value); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev set param\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_install_key(struct ath10k *ar, + const struct wmi_vdev_install_key_arg *arg) +{ + struct wmi_vdev_install_key_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + size_t len; + void *ptr; + + if (arg->key_cipher == WMI_CIPHER_NONE && arg->key_data != NULL) + return ERR_PTR(-EINVAL); + if (arg->key_cipher != WMI_CIPHER_NONE && arg->key_data == NULL) + return ERR_PTR(-EINVAL); + + len = sizeof(*tlv) + sizeof(*cmd) + + sizeof(*tlv) + roundup(arg->key_len, sizeof(__le32)); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_INSTALL_KEY_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->key_idx = __cpu_to_le32(arg->key_idx); + cmd->key_flags = __cpu_to_le32(arg->key_flags); + cmd->key_cipher = __cpu_to_le32(arg->key_cipher); + cmd->key_len = __cpu_to_le32(arg->key_len); + cmd->key_txmic_len = __cpu_to_le32(arg->key_txmic_len); + cmd->key_rxmic_len = __cpu_to_le32(arg->key_rxmic_len); + + if (arg->macaddr) + ether_addr_copy(cmd->peer_macaddr.addr, arg->macaddr); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE); + tlv->len = __cpu_to_le16(roundup(arg->key_len, sizeof(__le32))); + if (arg->key_data) + memcpy(tlv->value, arg->key_data, arg->key_len); + + ptr += sizeof(*tlv); + ptr += roundup(arg->key_len, sizeof(__le32)); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev install key\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_peer_create(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]) +{ + struct wmi_tlv_peer_create_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_CREATE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->peer_type = __cpu_to_le32(WMI_TLV_PEER_TYPE_DEFAULT); /* FIXME */ + ether_addr_copy(cmd->peer_addr.addr, peer_addr); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer create\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_peer_delete(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]) +{ + struct wmi_peer_delete_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_DELETE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + ether_addr_copy(cmd->peer_macaddr.addr, peer_addr); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer delete\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_peer_flush(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN], u32 tid_bitmap) +{ + struct wmi_peer_flush_tids_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_FLUSH_TIDS_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->peer_tid_bitmap = __cpu_to_le32(tid_bitmap); + ether_addr_copy(cmd->peer_macaddr.addr, peer_addr); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer flush\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_peer_set_param(struct ath10k *ar, u32 vdev_id, + const u8 *peer_addr, + enum wmi_peer_param param_id, + u32 param_value) +{ + struct wmi_peer_set_param_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_SET_PARAM_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(param_value); + ether_addr_copy(cmd->peer_macaddr.addr, peer_addr); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer set param\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_peer_assoc(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg) +{ + struct wmi_tlv_peer_assoc_cmd *cmd; + struct wmi_vht_rate_set *vht_rate; + struct wmi_tlv *tlv; + struct sk_buff *skb; + size_t len, legacy_rate_len, ht_rate_len; + void *ptr; + + if (arg->peer_mpdu_density > 16) + return ERR_PTR(-EINVAL); + if (arg->peer_legacy_rates.num_rates > MAX_SUPPORTED_RATES) + return ERR_PTR(-EINVAL); + if (arg->peer_ht_rates.num_rates > MAX_SUPPORTED_RATES) + return ERR_PTR(-EINVAL); + + legacy_rate_len = roundup(arg->peer_legacy_rates.num_rates, + sizeof(__le32)); + ht_rate_len = roundup(arg->peer_ht_rates.num_rates, sizeof(__le32)); + len = (sizeof(*tlv) + sizeof(*cmd)) + + (sizeof(*tlv) + legacy_rate_len) + + (sizeof(*tlv) + ht_rate_len) + + (sizeof(*tlv) + sizeof(*vht_rate)); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_ASSOC_COMPLETE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->new_assoc = __cpu_to_le32(arg->peer_reassoc ? 0 : 1); + cmd->assoc_id = __cpu_to_le32(arg->peer_aid); + cmd->flags = __cpu_to_le32(arg->peer_flags); + cmd->caps = __cpu_to_le32(arg->peer_caps); + cmd->listen_intval = __cpu_to_le32(arg->peer_listen_intval); + cmd->ht_caps = __cpu_to_le32(arg->peer_ht_caps); + cmd->max_mpdu = __cpu_to_le32(arg->peer_max_mpdu); + cmd->mpdu_density = __cpu_to_le32(arg->peer_mpdu_density); + cmd->rate_caps = __cpu_to_le32(arg->peer_rate_caps); + cmd->nss = __cpu_to_le32(arg->peer_num_spatial_streams); + cmd->vht_caps = __cpu_to_le32(arg->peer_vht_caps); + cmd->phy_mode = __cpu_to_le32(arg->peer_phymode); + cmd->num_legacy_rates = __cpu_to_le32(arg->peer_legacy_rates.num_rates); + cmd->num_ht_rates = __cpu_to_le32(arg->peer_ht_rates.num_rates); + ether_addr_copy(cmd->mac_addr.addr, arg->addr); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE); + tlv->len = __cpu_to_le16(legacy_rate_len); + memcpy(tlv->value, arg->peer_legacy_rates.rates, + arg->peer_legacy_rates.num_rates); + + ptr += sizeof(*tlv); + ptr += legacy_rate_len; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE); + tlv->len = __cpu_to_le16(ht_rate_len); + memcpy(tlv->value, arg->peer_ht_rates.rates, + arg->peer_ht_rates.num_rates); + + ptr += sizeof(*tlv); + ptr += ht_rate_len; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VHT_RATE_SET); + tlv->len = __cpu_to_le16(sizeof(*vht_rate)); + vht_rate = (void *)tlv->value; + + vht_rate->rx_max_rate = __cpu_to_le32(arg->peer_vht_rates.rx_max_rate); + vht_rate->rx_mcs_set = __cpu_to_le32(arg->peer_vht_rates.rx_mcs_set); + vht_rate->tx_max_rate = __cpu_to_le32(arg->peer_vht_rates.tx_max_rate); + vht_rate->tx_mcs_set = __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set); + + ptr += sizeof(*tlv); + ptr += sizeof(*vht_rate); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer assoc\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_set_psmode(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_ps_mode psmode) +{ + struct wmi_sta_powersave_mode_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_POWERSAVE_MODE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->sta_ps_mode = __cpu_to_le32(psmode); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv set psmode\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_set_sta_ps(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_powersave_param param_id, + u32 param_value) +{ + struct wmi_sta_powersave_param_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_POWERSAVE_PARAM_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(param_value); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv set sta ps\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_set_ap_ps(struct ath10k *ar, u32 vdev_id, const u8 *mac, + enum wmi_ap_ps_peer_param param_id, u32 value) +{ + struct wmi_ap_ps_peer_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + if (!mac) + return ERR_PTR(-EINVAL); + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_AP_PS_PEER_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(value); + ether_addr_copy(cmd->peer_macaddr.addr, mac); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv ap ps param\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_scan_chan_list(struct ath10k *ar, + const struct wmi_scan_chan_list_arg *arg) +{ + struct wmi_tlv_scan_chan_list_cmd *cmd; + struct wmi_channel *ci; + struct wmi_channel_arg *ch; + struct wmi_tlv *tlv; + struct sk_buff *skb; + size_t chans_len, len; + int i; + void *ptr, *chans; + + chans_len = arg->n_channels * (sizeof(*tlv) + sizeof(*ci)); + len = (sizeof(*tlv) + sizeof(*cmd)) + + (sizeof(*tlv) + chans_len); + + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_SCAN_CHAN_LIST_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->num_scan_chans = __cpu_to_le32(arg->n_channels); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT); + tlv->len = __cpu_to_le16(chans_len); + chans = (void *)tlv->value; + + for (i = 0; i < arg->n_channels; i++) { + ch = &arg->channels[i]; + + tlv = chans; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_CHANNEL); + tlv->len = __cpu_to_le16(sizeof(*ci)); + ci = (void *)tlv->value; + + ath10k_wmi_put_wmi_channel(ci, ch); + + chans += sizeof(*tlv); + chans += sizeof(*ci); + } + + ptr += sizeof(*tlv); + ptr += chans_len; + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv scan chan list\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_beacon_dma(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + struct wmi_bcn_tx_ref_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + struct sk_buff *beacon = arvif->beacon; + struct ieee80211_hdr *hdr; + u16 fc; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + hdr = (struct ieee80211_hdr *)beacon->data; + fc = le16_to_cpu(hdr->frame_control); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_BCN_SEND_FROM_HOST_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(arvif->vdev_id); + cmd->data_len = __cpu_to_le32(beacon->len); + cmd->data_ptr = __cpu_to_le32(ATH10K_SKB_CB(beacon)->paddr); + cmd->msdu_id = 0; + cmd->frame_control = __cpu_to_le32(fc); + cmd->flags = 0; + + if (ATH10K_SKB_CB(beacon)->bcn.dtim_zero) + cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DTIM_ZERO); + + if (ATH10K_SKB_CB(beacon)->bcn.deliver_cab) + cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DELIVER_CAB); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv beacon dma\n"); + return skb; +} + +static void *ath10k_wmi_tlv_put_wmm(void *ptr, + const struct wmi_wmm_params_arg *arg) +{ + struct wmi_wmm_params *wmm; + struct wmi_tlv *tlv; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_WMM_PARAMS); + tlv->len = __cpu_to_le16(sizeof(*wmm)); + wmm = (void *)tlv->value; + ath10k_wmi_pdev_set_wmm_param(wmm, arg); + + return ptr + sizeof(*tlv) + sizeof(*wmm); +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_pdev_set_wmm(struct ath10k *ar, + const struct wmi_pdev_set_wmm_params_arg *arg) +{ + struct wmi_tlv_pdev_set_wmm_cmd *cmd; + struct wmi_wmm_params *wmm; + struct wmi_tlv *tlv; + struct sk_buff *skb; + size_t len; + void *ptr; + + len = (sizeof(*tlv) + sizeof(*cmd)) + + (4 * (sizeof(*tlv) + sizeof(*wmm))); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_SET_WMM_PARAMS_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + + /* nothing to set here */ + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_be); + ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_bk); + ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_vi); + ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_vo); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev set wmm\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_request_stats(struct ath10k *ar, + enum wmi_stats_id stats_id) +{ + struct wmi_request_stats_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_REQUEST_STATS_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->stats_id = __cpu_to_le32(stats_id); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv request stats\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_force_fw_hang(struct ath10k *ar, + enum wmi_force_fw_hang_type type, + u32 delay_ms) +{ + struct wmi_force_fw_hang_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_FORCE_FW_HANG_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->type = __cpu_to_le32(type); + cmd->delay_ms = __cpu_to_le32(delay_ms); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv force fw hang\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_dbglog_cfg(struct ath10k *ar, u32 module_enable) +{ + struct wmi_tlv_dbglog_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + size_t len, bmap_len; + u32 value; + void *ptr; + + if (module_enable) { + value = WMI_TLV_DBGLOG_LOG_LEVEL_VALUE( + module_enable, + WMI_TLV_DBGLOG_LOG_LEVEL_VERBOSE); + } else { + value = WMI_TLV_DBGLOG_LOG_LEVEL_VALUE( + WMI_TLV_DBGLOG_ALL_MODULES, + WMI_TLV_DBGLOG_LOG_LEVEL_WARN); + } + + bmap_len = 0; + len = sizeof(*tlv) + sizeof(*cmd) + sizeof(*tlv) + bmap_len; + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_DEBUG_LOG_CONFIG_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->param = __cpu_to_le32(WMI_TLV_DBGLOG_PARAM_LOG_LEVEL); + cmd->value = __cpu_to_le32(value); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_UINT32); + tlv->len = __cpu_to_le16(bmap_len); + + /* nothing to do here */ + + ptr += sizeof(*tlv); + ptr += sizeof(bmap_len); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv dbglog value 0x%08x\n", value); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_pktlog_enable(struct ath10k *ar, u32 filter) +{ + struct wmi_tlv_pktlog_enable *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + void *ptr; + size_t len; + + len = sizeof(*tlv) + sizeof(*cmd); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_PKTLOG_ENABLE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->filter = __cpu_to_le32(filter); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pktlog enable filter 0x%08x\n", + filter); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_pktlog_disable(struct ath10k *ar) +{ + struct wmi_tlv_pktlog_disable *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + void *ptr; + size_t len; + + len = sizeof(*tlv) + sizeof(*cmd); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_PKTLOG_DISABLE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pktlog disable\n"); + return skb; +} + +/****************/ +/* TLV mappings */ +/****************/ + +static struct wmi_cmd_map wmi_tlv_cmd_map = { + .init_cmdid = WMI_TLV_INIT_CMDID, + .start_scan_cmdid = WMI_TLV_START_SCAN_CMDID, + .stop_scan_cmdid = WMI_TLV_STOP_SCAN_CMDID, + .scan_chan_list_cmdid = WMI_TLV_SCAN_CHAN_LIST_CMDID, + .scan_sch_prio_tbl_cmdid = WMI_TLV_SCAN_SCH_PRIO_TBL_CMDID, + .pdev_set_regdomain_cmdid = WMI_TLV_PDEV_SET_REGDOMAIN_CMDID, + .pdev_set_channel_cmdid = WMI_TLV_PDEV_SET_CHANNEL_CMDID, + .pdev_set_param_cmdid = WMI_TLV_PDEV_SET_PARAM_CMDID, + .pdev_pktlog_enable_cmdid = WMI_TLV_PDEV_PKTLOG_ENABLE_CMDID, + .pdev_pktlog_disable_cmdid = WMI_TLV_PDEV_PKTLOG_DISABLE_CMDID, + .pdev_set_wmm_params_cmdid = WMI_TLV_PDEV_SET_WMM_PARAMS_CMDID, + .pdev_set_ht_cap_ie_cmdid = WMI_TLV_PDEV_SET_HT_CAP_IE_CMDID, + .pdev_set_vht_cap_ie_cmdid = WMI_TLV_PDEV_SET_VHT_CAP_IE_CMDID, + .pdev_set_dscp_tid_map_cmdid = WMI_TLV_PDEV_SET_DSCP_TID_MAP_CMDID, + .pdev_set_quiet_mode_cmdid = WMI_TLV_PDEV_SET_QUIET_MODE_CMDID, + .pdev_green_ap_ps_enable_cmdid = WMI_TLV_PDEV_GREEN_AP_PS_ENABLE_CMDID, + .pdev_get_tpc_config_cmdid = WMI_TLV_PDEV_GET_TPC_CONFIG_CMDID, + .pdev_set_base_macaddr_cmdid = WMI_TLV_PDEV_SET_BASE_MACADDR_CMDID, + .vdev_create_cmdid = WMI_TLV_VDEV_CREATE_CMDID, + .vdev_delete_cmdid = WMI_TLV_VDEV_DELETE_CMDID, + .vdev_start_request_cmdid = WMI_TLV_VDEV_START_REQUEST_CMDID, + .vdev_restart_request_cmdid = WMI_TLV_VDEV_RESTART_REQUEST_CMDID, + .vdev_up_cmdid = WMI_TLV_VDEV_UP_CMDID, + .vdev_stop_cmdid = WMI_TLV_VDEV_STOP_CMDID, + .vdev_down_cmdid = WMI_TLV_VDEV_DOWN_CMDID, + .vdev_set_param_cmdid = WMI_TLV_VDEV_SET_PARAM_CMDID, + .vdev_install_key_cmdid = WMI_TLV_VDEV_INSTALL_KEY_CMDID, + .peer_create_cmdid = WMI_TLV_PEER_CREATE_CMDID, + .peer_delete_cmdid = WMI_TLV_PEER_DELETE_CMDID, + .peer_flush_tids_cmdid = WMI_TLV_PEER_FLUSH_TIDS_CMDID, + .peer_set_param_cmdid = WMI_TLV_PEER_SET_PARAM_CMDID, + .peer_assoc_cmdid = WMI_TLV_PEER_ASSOC_CMDID, + .peer_add_wds_entry_cmdid = WMI_TLV_PEER_ADD_WDS_ENTRY_CMDID, + .peer_remove_wds_entry_cmdid = WMI_TLV_PEER_REMOVE_WDS_ENTRY_CMDID, + .peer_mcast_group_cmdid = WMI_TLV_PEER_MCAST_GROUP_CMDID, + .bcn_tx_cmdid = WMI_TLV_BCN_TX_CMDID, + .pdev_send_bcn_cmdid = WMI_TLV_PDEV_SEND_BCN_CMDID, + .bcn_tmpl_cmdid = WMI_TLV_BCN_TMPL_CMDID, + .bcn_filter_rx_cmdid = WMI_TLV_BCN_FILTER_RX_CMDID, + .prb_req_filter_rx_cmdid = WMI_TLV_PRB_REQ_FILTER_RX_CMDID, + .mgmt_tx_cmdid = WMI_TLV_MGMT_TX_CMDID, + .prb_tmpl_cmdid = WMI_TLV_PRB_TMPL_CMDID, + .addba_clear_resp_cmdid = WMI_TLV_ADDBA_CLEAR_RESP_CMDID, + .addba_send_cmdid = WMI_TLV_ADDBA_SEND_CMDID, + .addba_status_cmdid = WMI_TLV_ADDBA_STATUS_CMDID, + .delba_send_cmdid = WMI_TLV_DELBA_SEND_CMDID, + .addba_set_resp_cmdid = WMI_TLV_ADDBA_SET_RESP_CMDID, + .send_singleamsdu_cmdid = WMI_TLV_SEND_SINGLEAMSDU_CMDID, + .sta_powersave_mode_cmdid = WMI_TLV_STA_POWERSAVE_MODE_CMDID, + .sta_powersave_param_cmdid = WMI_TLV_STA_POWERSAVE_PARAM_CMDID, + .sta_mimo_ps_mode_cmdid = WMI_TLV_STA_MIMO_PS_MODE_CMDID, + .pdev_dfs_enable_cmdid = WMI_TLV_PDEV_DFS_ENABLE_CMDID, + .pdev_dfs_disable_cmdid = WMI_TLV_PDEV_DFS_DISABLE_CMDID, + .roam_scan_mode = WMI_TLV_ROAM_SCAN_MODE, + .roam_scan_rssi_threshold = WMI_TLV_ROAM_SCAN_RSSI_THRESHOLD, + .roam_scan_period = WMI_TLV_ROAM_SCAN_PERIOD, + .roam_scan_rssi_change_threshold = + WMI_TLV_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + .roam_ap_profile = WMI_TLV_ROAM_AP_PROFILE, + .ofl_scan_add_ap_profile = WMI_TLV_ROAM_AP_PROFILE, + .ofl_scan_remove_ap_profile = WMI_TLV_OFL_SCAN_REMOVE_AP_PROFILE, + .ofl_scan_period = WMI_TLV_OFL_SCAN_PERIOD, + .p2p_dev_set_device_info = WMI_TLV_P2P_DEV_SET_DEVICE_INFO, + .p2p_dev_set_discoverability = WMI_TLV_P2P_DEV_SET_DISCOVERABILITY, + .p2p_go_set_beacon_ie = WMI_TLV_P2P_GO_SET_BEACON_IE, + .p2p_go_set_probe_resp_ie = WMI_TLV_P2P_GO_SET_PROBE_RESP_IE, + .p2p_set_vendor_ie_data_cmdid = WMI_TLV_P2P_SET_VENDOR_IE_DATA_CMDID, + .ap_ps_peer_param_cmdid = WMI_TLV_AP_PS_PEER_PARAM_CMDID, + .ap_ps_peer_uapsd_coex_cmdid = WMI_TLV_AP_PS_PEER_UAPSD_COEX_CMDID, + .peer_rate_retry_sched_cmdid = WMI_TLV_PEER_RATE_RETRY_SCHED_CMDID, + .wlan_profile_trigger_cmdid = WMI_TLV_WLAN_PROFILE_TRIGGER_CMDID, + .wlan_profile_set_hist_intvl_cmdid = + WMI_TLV_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + .wlan_profile_get_profile_data_cmdid = + WMI_TLV_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + .wlan_profile_enable_profile_id_cmdid = + WMI_TLV_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + .wlan_profile_list_profile_id_cmdid = + WMI_TLV_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + .pdev_suspend_cmdid = WMI_TLV_PDEV_SUSPEND_CMDID, + .pdev_resume_cmdid = WMI_TLV_PDEV_RESUME_CMDID, + .add_bcn_filter_cmdid = WMI_TLV_ADD_BCN_FILTER_CMDID, + .rmv_bcn_filter_cmdid = WMI_TLV_RMV_BCN_FILTER_CMDID, + .wow_add_wake_pattern_cmdid = WMI_TLV_WOW_ADD_WAKE_PATTERN_CMDID, + .wow_del_wake_pattern_cmdid = WMI_TLV_WOW_DEL_WAKE_PATTERN_CMDID, + .wow_enable_disable_wake_event_cmdid = + WMI_TLV_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + .wow_enable_cmdid = WMI_TLV_WOW_ENABLE_CMDID, + .wow_hostwakeup_from_sleep_cmdid = + WMI_TLV_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + .rtt_measreq_cmdid = WMI_TLV_RTT_MEASREQ_CMDID, + .rtt_tsf_cmdid = WMI_TLV_RTT_TSF_CMDID, + .vdev_spectral_scan_configure_cmdid = WMI_TLV_SPECTRAL_SCAN_CONF_CMDID, + .vdev_spectral_scan_enable_cmdid = WMI_TLV_SPECTRAL_SCAN_ENABLE_CMDID, + .request_stats_cmdid = WMI_TLV_REQUEST_STATS_CMDID, + .set_arp_ns_offload_cmdid = WMI_TLV_SET_ARP_NS_OFFLOAD_CMDID, + .network_list_offload_config_cmdid = + WMI_TLV_NETWORK_LIST_OFFLOAD_CONFIG_CMDID, + .gtk_offload_cmdid = WMI_TLV_GTK_OFFLOAD_CMDID, + .csa_offload_enable_cmdid = WMI_TLV_CSA_OFFLOAD_ENABLE_CMDID, + .csa_offload_chanswitch_cmdid = WMI_TLV_CSA_OFFLOAD_CHANSWITCH_CMDID, + .chatter_set_mode_cmdid = WMI_TLV_CHATTER_SET_MODE_CMDID, + .peer_tid_addba_cmdid = WMI_TLV_PEER_TID_ADDBA_CMDID, + .peer_tid_delba_cmdid = WMI_TLV_PEER_TID_DELBA_CMDID, + .sta_dtim_ps_method_cmdid = WMI_TLV_STA_DTIM_PS_METHOD_CMDID, + .sta_uapsd_auto_trig_cmdid = WMI_TLV_STA_UAPSD_AUTO_TRIG_CMDID, + .sta_keepalive_cmd = WMI_TLV_STA_KEEPALIVE_CMDID, + .echo_cmdid = WMI_TLV_ECHO_CMDID, + .pdev_utf_cmdid = WMI_TLV_PDEV_UTF_CMDID, + .dbglog_cfg_cmdid = WMI_TLV_DBGLOG_CFG_CMDID, + .pdev_qvit_cmdid = WMI_TLV_PDEV_QVIT_CMDID, + .pdev_ftm_intg_cmdid = WMI_TLV_PDEV_FTM_INTG_CMDID, + .vdev_set_keepalive_cmdid = WMI_TLV_VDEV_SET_KEEPALIVE_CMDID, + .vdev_get_keepalive_cmdid = WMI_TLV_VDEV_GET_KEEPALIVE_CMDID, + .force_fw_hang_cmdid = WMI_TLV_FORCE_FW_HANG_CMDID, + .gpio_config_cmdid = WMI_TLV_GPIO_CONFIG_CMDID, + .gpio_output_cmdid = WMI_TLV_GPIO_OUTPUT_CMDID, +}; + +static struct wmi_pdev_param_map wmi_tlv_pdev_param_map = { + .tx_chain_mask = WMI_TLV_PDEV_PARAM_TX_CHAIN_MASK, + .rx_chain_mask = WMI_TLV_PDEV_PARAM_RX_CHAIN_MASK, + .txpower_limit2g = WMI_TLV_PDEV_PARAM_TXPOWER_LIMIT2G, + .txpower_limit5g = WMI_TLV_PDEV_PARAM_TXPOWER_LIMIT5G, + .txpower_scale = WMI_TLV_PDEV_PARAM_TXPOWER_SCALE, + .beacon_gen_mode = WMI_TLV_PDEV_PARAM_BEACON_GEN_MODE, + .beacon_tx_mode = WMI_TLV_PDEV_PARAM_BEACON_TX_MODE, + .resmgr_offchan_mode = WMI_TLV_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + .protection_mode = WMI_TLV_PDEV_PARAM_PROTECTION_MODE, + .dynamic_bw = WMI_TLV_PDEV_PARAM_DYNAMIC_BW, + .non_agg_sw_retry_th = WMI_TLV_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + .agg_sw_retry_th = WMI_TLV_PDEV_PARAM_AGG_SW_RETRY_TH, + .sta_kickout_th = WMI_TLV_PDEV_PARAM_STA_KICKOUT_TH, + .ac_aggrsize_scaling = WMI_TLV_PDEV_PARAM_AC_AGGRSIZE_SCALING, + .ltr_enable = WMI_TLV_PDEV_PARAM_LTR_ENABLE, + .ltr_ac_latency_be = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_BE, + .ltr_ac_latency_bk = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_BK, + .ltr_ac_latency_vi = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_VI, + .ltr_ac_latency_vo = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_VO, + .ltr_ac_latency_timeout = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + .ltr_sleep_override = WMI_TLV_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + .ltr_rx_override = WMI_TLV_PDEV_PARAM_LTR_RX_OVERRIDE, + .ltr_tx_activity_timeout = WMI_TLV_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + .l1ss_enable = WMI_TLV_PDEV_PARAM_L1SS_ENABLE, + .dsleep_enable = WMI_TLV_PDEV_PARAM_DSLEEP_ENABLE, + .pcielp_txbuf_flush = WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_FLUSH, + .pcielp_txbuf_watermark = WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_EN, + .pcielp_txbuf_tmo_en = WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_EN, + .pcielp_txbuf_tmo_value = WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE, + .pdev_stats_update_period = WMI_TLV_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + .vdev_stats_update_period = WMI_TLV_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + .peer_stats_update_period = WMI_TLV_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + .bcnflt_stats_update_period = + WMI_TLV_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + .pmf_qos = WMI_TLV_PDEV_PARAM_PMF_QOS, + .arp_ac_override = WMI_TLV_PDEV_PARAM_ARP_AC_OVERRIDE, + .dcs = WMI_TLV_PDEV_PARAM_DCS, + .ani_enable = WMI_TLV_PDEV_PARAM_ANI_ENABLE, + .ani_poll_period = WMI_TLV_PDEV_PARAM_ANI_POLL_PERIOD, + .ani_listen_period = WMI_TLV_PDEV_PARAM_ANI_LISTEN_PERIOD, + .ani_ofdm_level = WMI_TLV_PDEV_PARAM_ANI_OFDM_LEVEL, + .ani_cck_level = WMI_TLV_PDEV_PARAM_ANI_CCK_LEVEL, + .dyntxchain = WMI_TLV_PDEV_PARAM_DYNTXCHAIN, + .proxy_sta = WMI_TLV_PDEV_PARAM_PROXY_STA, + .idle_ps_config = WMI_TLV_PDEV_PARAM_IDLE_PS_CONFIG, + .power_gating_sleep = WMI_TLV_PDEV_PARAM_POWER_GATING_SLEEP, + .fast_channel_reset = WMI_TLV_PDEV_PARAM_UNSUPPORTED, + .burst_dur = WMI_TLV_PDEV_PARAM_BURST_DUR, + .burst_enable = WMI_TLV_PDEV_PARAM_BURST_ENABLE, +}; + +static struct wmi_vdev_param_map wmi_tlv_vdev_param_map = { + .rts_threshold = WMI_TLV_VDEV_PARAM_RTS_THRESHOLD, + .fragmentation_threshold = WMI_TLV_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + .beacon_interval = WMI_TLV_VDEV_PARAM_BEACON_INTERVAL, + .listen_interval = WMI_TLV_VDEV_PARAM_LISTEN_INTERVAL, + .multicast_rate = WMI_TLV_VDEV_PARAM_MULTICAST_RATE, + .mgmt_tx_rate = WMI_TLV_VDEV_PARAM_MGMT_TX_RATE, + .slot_time = WMI_TLV_VDEV_PARAM_SLOT_TIME, + .preamble = WMI_TLV_VDEV_PARAM_PREAMBLE, + .swba_time = WMI_TLV_VDEV_PARAM_SWBA_TIME, + .wmi_vdev_stats_update_period = WMI_TLV_VDEV_STATS_UPDATE_PERIOD, + .wmi_vdev_pwrsave_ageout_time = WMI_TLV_VDEV_PWRSAVE_AGEOUT_TIME, + .wmi_vdev_host_swba_interval = WMI_TLV_VDEV_HOST_SWBA_INTERVAL, + .dtim_period = WMI_TLV_VDEV_PARAM_DTIM_PERIOD, + .wmi_vdev_oc_scheduler_air_time_limit = + WMI_TLV_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + .wds = WMI_TLV_VDEV_PARAM_WDS, + .atim_window = WMI_TLV_VDEV_PARAM_ATIM_WINDOW, + .bmiss_count_max = WMI_TLV_VDEV_PARAM_BMISS_COUNT_MAX, + .bmiss_first_bcnt = WMI_TLV_VDEV_PARAM_BMISS_FIRST_BCNT, + .bmiss_final_bcnt = WMI_TLV_VDEV_PARAM_BMISS_FINAL_BCNT, + .feature_wmm = WMI_TLV_VDEV_PARAM_FEATURE_WMM, + .chwidth = WMI_TLV_VDEV_PARAM_CHWIDTH, + .chextoffset = WMI_TLV_VDEV_PARAM_CHEXTOFFSET, + .disable_htprotection = WMI_TLV_VDEV_PARAM_DISABLE_HTPROTECTION, + .sta_quickkickout = WMI_TLV_VDEV_PARAM_STA_QUICKKICKOUT, + .mgmt_rate = WMI_TLV_VDEV_PARAM_MGMT_RATE, + .protection_mode = WMI_TLV_VDEV_PARAM_PROTECTION_MODE, + .fixed_rate = WMI_TLV_VDEV_PARAM_FIXED_RATE, + .sgi = WMI_TLV_VDEV_PARAM_SGI, + .ldpc = WMI_TLV_VDEV_PARAM_LDPC, + .tx_stbc = WMI_TLV_VDEV_PARAM_TX_STBC, + .rx_stbc = WMI_TLV_VDEV_PARAM_RX_STBC, + .intra_bss_fwd = WMI_TLV_VDEV_PARAM_INTRA_BSS_FWD, + .def_keyid = WMI_TLV_VDEV_PARAM_DEF_KEYID, + .nss = WMI_TLV_VDEV_PARAM_NSS, + .bcast_data_rate = WMI_TLV_VDEV_PARAM_BCAST_DATA_RATE, + .mcast_data_rate = WMI_TLV_VDEV_PARAM_MCAST_DATA_RATE, + .mcast_indicate = WMI_TLV_VDEV_PARAM_MCAST_INDICATE, + .dhcp_indicate = WMI_TLV_VDEV_PARAM_DHCP_INDICATE, + .unknown_dest_indicate = WMI_TLV_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + .ap_keepalive_min_idle_inactive_time_secs = + WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_idle_inactive_time_secs = + WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_unresponsive_time_secs = + WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + .ap_enable_nawds = WMI_TLV_VDEV_PARAM_AP_ENABLE_NAWDS, + .mcast2ucast_set = WMI_TLV_VDEV_PARAM_UNSUPPORTED, + .enable_rtscts = WMI_TLV_VDEV_PARAM_ENABLE_RTSCTS, + .txbf = WMI_TLV_VDEV_PARAM_TXBF, + .packet_powersave = WMI_TLV_VDEV_PARAM_PACKET_POWERSAVE, + .drop_unencry = WMI_TLV_VDEV_PARAM_DROP_UNENCRY, + .tx_encap_type = WMI_TLV_VDEV_PARAM_TX_ENCAP_TYPE, + .ap_detect_out_of_sync_sleeping_sta_time_secs = + WMI_TLV_VDEV_PARAM_UNSUPPORTED, +}; + +static const struct wmi_ops wmi_tlv_ops = { + .rx = ath10k_wmi_tlv_op_rx, + .map_svc = wmi_tlv_svc_map, + + .pull_scan = ath10k_wmi_tlv_op_pull_scan_ev, + .pull_mgmt_rx = ath10k_wmi_tlv_op_pull_mgmt_rx_ev, + .pull_ch_info = ath10k_wmi_tlv_op_pull_ch_info_ev, + .pull_vdev_start = ath10k_wmi_tlv_op_pull_vdev_start_ev, + .pull_peer_kick = ath10k_wmi_tlv_op_pull_peer_kick_ev, + .pull_swba = ath10k_wmi_tlv_op_pull_swba_ev, + .pull_phyerr = ath10k_wmi_tlv_op_pull_phyerr_ev, + .pull_svc_rdy = ath10k_wmi_tlv_op_pull_svc_rdy_ev, + .pull_rdy = ath10k_wmi_tlv_op_pull_rdy_ev, + .pull_fw_stats = ath10k_wmi_tlv_op_pull_fw_stats, + + .gen_pdev_suspend = ath10k_wmi_tlv_op_gen_pdev_suspend, + .gen_pdev_resume = ath10k_wmi_tlv_op_gen_pdev_resume, + .gen_pdev_set_rd = ath10k_wmi_tlv_op_gen_pdev_set_rd, + .gen_pdev_set_param = ath10k_wmi_tlv_op_gen_pdev_set_param, + .gen_init = ath10k_wmi_tlv_op_gen_init, + .gen_start_scan = ath10k_wmi_tlv_op_gen_start_scan, + .gen_stop_scan = ath10k_wmi_tlv_op_gen_stop_scan, + .gen_vdev_create = ath10k_wmi_tlv_op_gen_vdev_create, + .gen_vdev_delete = ath10k_wmi_tlv_op_gen_vdev_delete, + .gen_vdev_start = ath10k_wmi_tlv_op_gen_vdev_start, + .gen_vdev_stop = ath10k_wmi_tlv_op_gen_vdev_stop, + .gen_vdev_up = ath10k_wmi_tlv_op_gen_vdev_up, + .gen_vdev_down = ath10k_wmi_tlv_op_gen_vdev_down, + .gen_vdev_set_param = ath10k_wmi_tlv_op_gen_vdev_set_param, + .gen_vdev_install_key = ath10k_wmi_tlv_op_gen_vdev_install_key, + .gen_peer_create = ath10k_wmi_tlv_op_gen_peer_create, + .gen_peer_delete = ath10k_wmi_tlv_op_gen_peer_delete, + .gen_peer_flush = ath10k_wmi_tlv_op_gen_peer_flush, + .gen_peer_set_param = ath10k_wmi_tlv_op_gen_peer_set_param, + .gen_peer_assoc = ath10k_wmi_tlv_op_gen_peer_assoc, + .gen_set_psmode = ath10k_wmi_tlv_op_gen_set_psmode, + .gen_set_sta_ps = ath10k_wmi_tlv_op_gen_set_sta_ps, + .gen_set_ap_ps = ath10k_wmi_tlv_op_gen_set_ap_ps, + .gen_scan_chan_list = ath10k_wmi_tlv_op_gen_scan_chan_list, + .gen_beacon_dma = ath10k_wmi_tlv_op_gen_beacon_dma, + .gen_pdev_set_wmm = ath10k_wmi_tlv_op_gen_pdev_set_wmm, + .gen_request_stats = ath10k_wmi_tlv_op_gen_request_stats, + .gen_force_fw_hang = ath10k_wmi_tlv_op_gen_force_fw_hang, + /* .gen_mgmt_tx = not implemented; HTT is used */ + .gen_dbglog_cfg = ath10k_wmi_tlv_op_gen_dbglog_cfg, + .gen_pktlog_enable = ath10k_wmi_tlv_op_gen_pktlog_enable, + .gen_pktlog_disable = ath10k_wmi_tlv_op_gen_pktlog_disable, +}; + +/************/ +/* TLV init */ +/************/ + +void ath10k_wmi_tlv_attach(struct ath10k *ar) +{ + ar->wmi.cmd = &wmi_tlv_cmd_map; + ar->wmi.vdev_param = &wmi_tlv_vdev_param_map; + ar->wmi.pdev_param = &wmi_tlv_pdev_param_map; + ar->wmi.ops = &wmi_tlv_ops; +} diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h new file mode 100644 index 0000000..54ffa12 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h @@ -0,0 +1,1380 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2014 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef _WMI_TLV_H +#define _WMI_TLV_H + +#define WMI_TLV_CMD(grp_id) (((grp_id) << 12) | 0x1) +#define WMI_TLV_EV(grp_id) (((grp_id) << 12) | 0x1) +#define WMI_TLV_CMD_UNSUPPORTED 0 +#define WMI_TLV_PDEV_PARAM_UNSUPPORTED 0 +#define WMI_TLV_VDEV_PARAM_UNSUPPORTED 0 + +enum wmi_tlv_grp_id { + WMI_TLV_GRP_START = 0x3, + WMI_TLV_GRP_SCAN = WMI_TLV_GRP_START, + WMI_TLV_GRP_PDEV, + WMI_TLV_GRP_VDEV, + WMI_TLV_GRP_PEER, + WMI_TLV_GRP_MGMT, + WMI_TLV_GRP_BA_NEG, + WMI_TLV_GRP_STA_PS, + WMI_TLV_GRP_DFS, + WMI_TLV_GRP_ROAM, + WMI_TLV_GRP_OFL_SCAN, + WMI_TLV_GRP_P2P, + WMI_TLV_GRP_AP_PS, + WMI_TLV_GRP_RATECTL, + WMI_TLV_GRP_PROFILE, + WMI_TLV_GRP_SUSPEND, + WMI_TLV_GRP_BCN_FILTER, + WMI_TLV_GRP_WOW, + WMI_TLV_GRP_RTT, + WMI_TLV_GRP_SPECTRAL, + WMI_TLV_GRP_STATS, + WMI_TLV_GRP_ARP_NS_OFL, + WMI_TLV_GRP_NLO_OFL, + WMI_TLV_GRP_GTK_OFL, + WMI_TLV_GRP_CSA_OFL, + WMI_TLV_GRP_CHATTER, + WMI_TLV_GRP_TID_ADDBA, + WMI_TLV_GRP_MISC, + WMI_TLV_GRP_GPIO, + WMI_TLV_GRP_FWTEST, + WMI_TLV_GRP_TDLS, + WMI_TLV_GRP_RESMGR, + WMI_TLV_GRP_STA_SMPS, + WMI_TLV_GRP_WLAN_HB, + WMI_TLV_GRP_RMC, + WMI_TLV_GRP_MHF_OFL, + WMI_TLV_GRP_LOCATION_SCAN, + WMI_TLV_GRP_OEM, + WMI_TLV_GRP_NAN, + WMI_TLV_GRP_COEX, + WMI_TLV_GRP_OBSS_OFL, + WMI_TLV_GRP_LPI, + WMI_TLV_GRP_EXTSCAN, + WMI_TLV_GRP_DHCP_OFL, + WMI_TLV_GRP_IPA, + WMI_TLV_GRP_MDNS_OFL, + WMI_TLV_GRP_SAP_OFL, +}; + +enum wmi_tlv_cmd_id { + WMI_TLV_INIT_CMDID = 0x1, + WMI_TLV_START_SCAN_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_SCAN), + WMI_TLV_STOP_SCAN_CMDID, + WMI_TLV_SCAN_CHAN_LIST_CMDID, + WMI_TLV_SCAN_SCH_PRIO_TBL_CMDID, + WMI_TLV_SCAN_UPDATE_REQUEST_CMDID, + WMI_TLV_SCAN_PROB_REQ_OUI_CMDID, + WMI_TLV_PDEV_SET_REGDOMAIN_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_PDEV), + WMI_TLV_PDEV_SET_CHANNEL_CMDID, + WMI_TLV_PDEV_SET_PARAM_CMDID, + WMI_TLV_PDEV_PKTLOG_ENABLE_CMDID, + WMI_TLV_PDEV_PKTLOG_DISABLE_CMDID, + WMI_TLV_PDEV_SET_WMM_PARAMS_CMDID, + WMI_TLV_PDEV_SET_HT_CAP_IE_CMDID, + WMI_TLV_PDEV_SET_VHT_CAP_IE_CMDID, + WMI_TLV_PDEV_SET_DSCP_TID_MAP_CMDID, + WMI_TLV_PDEV_SET_QUIET_MODE_CMDID, + WMI_TLV_PDEV_GREEN_AP_PS_ENABLE_CMDID, + WMI_TLV_PDEV_GET_TPC_CONFIG_CMDID, + WMI_TLV_PDEV_SET_BASE_MACADDR_CMDID, + WMI_TLV_PDEV_DUMP_CMDID, + WMI_TLV_PDEV_SET_LED_CONFIG_CMDID, + WMI_TLV_PDEV_GET_TEMPERATURE_CMDID, + WMI_TLV_PDEV_SET_LED_FLASHING_CMDID, + WMI_TLV_VDEV_CREATE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_VDEV), + WMI_TLV_VDEV_DELETE_CMDID, + WMI_TLV_VDEV_START_REQUEST_CMDID, + WMI_TLV_VDEV_RESTART_REQUEST_CMDID, + WMI_TLV_VDEV_UP_CMDID, + WMI_TLV_VDEV_STOP_CMDID, + WMI_TLV_VDEV_DOWN_CMDID, + WMI_TLV_VDEV_SET_PARAM_CMDID, + WMI_TLV_VDEV_INSTALL_KEY_CMDID, + WMI_TLV_VDEV_WNM_SLEEPMODE_CMDID, + WMI_TLV_VDEV_WMM_ADDTS_CMDID, + WMI_TLV_VDEV_WMM_DELTS_CMDID, + WMI_TLV_VDEV_SET_WMM_PARAMS_CMDID, + WMI_TLV_VDEV_SET_GTX_PARAMS_CMDID, + WMI_TLV_VDEV_IPSEC_NATKEEPALIVE_FILTER_CMDID, + WMI_TLV_VDEV_PLMREQ_START_CMDID, + WMI_TLV_VDEV_PLMREQ_STOP_CMDID, + WMI_TLV_PEER_CREATE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_PEER), + WMI_TLV_PEER_DELETE_CMDID, + WMI_TLV_PEER_FLUSH_TIDS_CMDID, + WMI_TLV_PEER_SET_PARAM_CMDID, + WMI_TLV_PEER_ASSOC_CMDID, + WMI_TLV_PEER_ADD_WDS_ENTRY_CMDID, + WMI_TLV_PEER_REMOVE_WDS_ENTRY_CMDID, + WMI_TLV_PEER_MCAST_GROUP_CMDID, + WMI_TLV_PEER_INFO_REQ_CMDID, + WMI_TLV_PEER_GET_ESTIMATED_LINKSPEED_CMDID, + WMI_TLV_BCN_TX_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_MGMT), + WMI_TLV_PDEV_SEND_BCN_CMDID, + WMI_TLV_BCN_TMPL_CMDID, + WMI_TLV_BCN_FILTER_RX_CMDID, + WMI_TLV_PRB_REQ_FILTER_RX_CMDID, + WMI_TLV_MGMT_TX_CMDID, + WMI_TLV_PRB_TMPL_CMDID, + WMI_TLV_ADDBA_CLEAR_RESP_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_BA_NEG), + WMI_TLV_ADDBA_SEND_CMDID, + WMI_TLV_ADDBA_STATUS_CMDID, + WMI_TLV_DELBA_SEND_CMDID, + WMI_TLV_ADDBA_SET_RESP_CMDID, + WMI_TLV_SEND_SINGLEAMSDU_CMDID, + WMI_TLV_STA_POWERSAVE_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_STA_PS), + WMI_TLV_STA_POWERSAVE_PARAM_CMDID, + WMI_TLV_STA_MIMO_PS_MODE_CMDID, + WMI_TLV_PDEV_DFS_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_DFS), + WMI_TLV_PDEV_DFS_DISABLE_CMDID, + WMI_TLV_DFS_PHYERR_FILTER_ENA_CMDID, + WMI_TLV_DFS_PHYERR_FILTER_DIS_CMDID, + WMI_TLV_ROAM_SCAN_MODE = WMI_TLV_CMD(WMI_TLV_GRP_ROAM), + WMI_TLV_ROAM_SCAN_RSSI_THRESHOLD, + WMI_TLV_ROAM_SCAN_PERIOD, + WMI_TLV_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + WMI_TLV_ROAM_AP_PROFILE, + WMI_TLV_ROAM_CHAN_LIST, + WMI_TLV_ROAM_SCAN_CMD, + WMI_TLV_ROAM_SYNCH_COMPLETE, + WMI_TLV_ROAM_SET_RIC_REQUEST_CMDID, + WMI_TLV_ROAM_INVOKE_CMDID, + WMI_TLV_OFL_SCAN_ADD_AP_PROFILE = WMI_TLV_CMD(WMI_TLV_GRP_OFL_SCAN), + WMI_TLV_OFL_SCAN_REMOVE_AP_PROFILE, + WMI_TLV_OFL_SCAN_PERIOD, + WMI_TLV_P2P_DEV_SET_DEVICE_INFO = WMI_TLV_CMD(WMI_TLV_GRP_P2P), + WMI_TLV_P2P_DEV_SET_DISCOVERABILITY, + WMI_TLV_P2P_GO_SET_BEACON_IE, + WMI_TLV_P2P_GO_SET_PROBE_RESP_IE, + WMI_TLV_P2P_SET_VENDOR_IE_DATA_CMDID, + WMI_TLV_P2P_DISC_OFFLOAD_CONFIG_CMDID, + WMI_TLV_P2P_DISC_OFFLOAD_APPIE_CMDID, + WMI_TLV_P2P_DISC_OFFLOAD_PATTERN_CMDID, + WMI_TLV_P2P_SET_OPPPS_PARAM_CMDID, + WMI_TLV_AP_PS_PEER_PARAM_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_AP_PS), + WMI_TLV_AP_PS_PEER_UAPSD_COEX_CMDID, + WMI_TLV_PEER_RATE_RETRY_SCHED_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_RATECTL), + WMI_TLV_WLAN_PROFILE_TRIGGER_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_PROFILE), + WMI_TLV_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + WMI_TLV_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + WMI_TLV_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + WMI_TLV_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + WMI_TLV_PDEV_SUSPEND_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_SUSPEND), + WMI_TLV_PDEV_RESUME_CMDID, + WMI_TLV_ADD_BCN_FILTER_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_BCN_FILTER), + WMI_TLV_RMV_BCN_FILTER_CMDID, + WMI_TLV_WOW_ADD_WAKE_PATTERN_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_WOW), + WMI_TLV_WOW_DEL_WAKE_PATTERN_CMDID, + WMI_TLV_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + WMI_TLV_WOW_ENABLE_CMDID, + WMI_TLV_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + WMI_TLV_WOW_ACER_IOAC_ADD_KEEPALIVE_CMDID, + WMI_TLV_WOW_ACER_IOAC_DEL_KEEPALIVE_CMDID, + WMI_TLV_WOW_ACER_IOAC_ADD_WAKE_PATTERN_CMDID, + WMI_TLV_WOW_ACER_IOAC_DEL_WAKE_PATTERN_CMDID, + WMI_TLV_D0_WOW_ENABLE_DISABLE_CMDID, + WMI_TLV_EXTWOW_ENABLE_CMDID, + WMI_TLV_EXTWOW_SET_APP_TYPE1_PARAMS_CMDID, + WMI_TLV_EXTWOW_SET_APP_TYPE2_PARAMS_CMDID, + WMI_TLV_RTT_MEASREQ_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_RTT), + WMI_TLV_RTT_TSF_CMDID, + WMI_TLV_SPECTRAL_SCAN_CONF_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_SPECTRAL), + WMI_TLV_SPECTRAL_SCAN_ENABLE_CMDID, + WMI_TLV_REQUEST_STATS_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_STATS), + WMI_TLV_MCC_SCHED_TRAFFIC_STATS_CMDID, + WMI_TLV_REQUEST_STATS_EXT_CMDID, + WMI_TLV_REQUEST_LINK_STATS_CMDID, + WMI_TLV_START_LINK_STATS_CMDID, + WMI_TLV_CLEAR_LINK_STATS_CMDID, + WMI_TLV_SET_ARP_NS_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_ARP_NS_OFL), + WMI_TLV_ADD_PROACTIVE_ARP_RSP_PATTERN_CMDID, + WMI_TLV_DEL_PROACTIVE_ARP_RSP_PATTERN_CMDID, + WMI_TLV_NETWORK_LIST_OFFLOAD_CONFIG_CMDID = + WMI_TLV_CMD(WMI_TLV_GRP_NLO_OFL), + WMI_TLV_APFIND_CMDID, + WMI_TLV_GTK_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_GTK_OFL), + WMI_TLV_CSA_OFFLOAD_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_CSA_OFL), + WMI_TLV_CSA_OFFLOAD_CHANSWITCH_CMDID, + WMI_TLV_CHATTER_SET_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_CHATTER), + WMI_TLV_CHATTER_ADD_COALESCING_FILTER_CMDID, + WMI_TLV_CHATTER_DELETE_COALESCING_FILTER_CMDID, + WMI_TLV_CHATTER_COALESCING_QUERY_CMDID, + WMI_TLV_PEER_TID_ADDBA_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_TID_ADDBA), + WMI_TLV_PEER_TID_DELBA_CMDID, + WMI_TLV_STA_DTIM_PS_METHOD_CMDID, + WMI_TLV_STA_UAPSD_AUTO_TRIG_CMDID, + WMI_TLV_STA_KEEPALIVE_CMDID, + WMI_TLV_BA_REQ_SSN_CMDID, + WMI_TLV_ECHO_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_MISC), + WMI_TLV_PDEV_UTF_CMDID, + WMI_TLV_DBGLOG_CFG_CMDID, + WMI_TLV_PDEV_QVIT_CMDID, + WMI_TLV_PDEV_FTM_INTG_CMDID, + WMI_TLV_VDEV_SET_KEEPALIVE_CMDID, + WMI_TLV_VDEV_GET_KEEPALIVE_CMDID, + WMI_TLV_FORCE_FW_HANG_CMDID, + WMI_TLV_SET_MCASTBCAST_FILTER_CMDID, + WMI_TLV_THERMAL_MGMT_CMDID, + WMI_TLV_HOST_AUTO_SHUTDOWN_CFG_CMDID, + WMI_TLV_TPC_CHAINMASK_CONFIG_CMDID, + WMI_TLV_SET_ANTENNA_DIVERSITY_CMDID, + WMI_TLV_GPIO_CONFIG_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_GPIO), + WMI_TLV_GPIO_OUTPUT_CMDID, + WMI_TLV_TXBF_CMDID, + WMI_TLV_FWTEST_VDEV_MCC_SET_TBTT_MODE_CMDID = + WMI_TLV_CMD(WMI_TLV_GRP_FWTEST), + WMI_TLV_FWTEST_P2P_SET_NOA_PARAM_CMDID, + WMI_TLV_UNIT_TEST_CMDID, + WMI_TLV_TDLS_SET_STATE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_TDLS), + WMI_TLV_TDLS_PEER_UPDATE_CMDID, + WMI_TLV_TDLS_SET_OFFCHAN_MODE_CMDID, + WMI_TLV_RESMGR_ADAPTIVE_OCS_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_RESMGR), + WMI_TLV_RESMGR_SET_CHAN_TIME_QUOTA_CMDID, + WMI_TLV_RESMGR_SET_CHAN_LATENCY_CMDID, + WMI_TLV_STA_SMPS_FORCE_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_STA_SMPS), + WMI_TLV_STA_SMPS_PARAM_CMDID, + WMI_TLV_HB_SET_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_WLAN_HB), + WMI_TLV_HB_SET_TCP_PARAMS_CMDID, + WMI_TLV_HB_SET_TCP_PKT_FILTER_CMDID, + WMI_TLV_HB_SET_UDP_PARAMS_CMDID, + WMI_TLV_HB_SET_UDP_PKT_FILTER_CMDID, + WMI_TLV_RMC_SET_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_RMC), + WMI_TLV_RMC_SET_ACTION_PERIOD_CMDID, + WMI_TLV_RMC_CONFIG_CMDID, + WMI_TLV_MHF_OFFLOAD_SET_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_MHF_OFL), + WMI_TLV_MHF_OFFLOAD_PLUMB_ROUTING_TBL_CMDID, + WMI_TLV_BATCH_SCAN_ENABLE_CMDID = + WMI_TLV_CMD(WMI_TLV_GRP_LOCATION_SCAN), + WMI_TLV_BATCH_SCAN_DISABLE_CMDID, + WMI_TLV_BATCH_SCAN_TRIGGER_RESULT_CMDID, + WMI_TLV_OEM_REQ_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_OEM), + WMI_TLV_NAN_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_NAN), + WMI_TLV_MODEM_POWER_STATE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_COEX), + WMI_TLV_CHAN_AVOID_UPDATE_CMDID, + WMI_TLV_OBSS_SCAN_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_OBSS_OFL), + WMI_TLV_OBSS_SCAN_DISABLE_CMDID, + WMI_TLV_LPI_MGMT_SNOOPING_CONFIG_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_LPI), + WMI_TLV_LPI_START_SCAN_CMDID, + WMI_TLV_LPI_STOP_SCAN_CMDID, + WMI_TLV_EXTSCAN_START_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_EXTSCAN), + WMI_TLV_EXTSCAN_STOP_CMDID, + WMI_TLV_EXTSCAN_CONFIGURE_WLAN_CHANGE_MONITOR_CMDID, + WMI_TLV_EXTSCAN_CONFIGURE_HOTLIST_MONITOR_CMDID, + WMI_TLV_EXTSCAN_GET_CACHED_RESULTS_CMDID, + WMI_TLV_EXTSCAN_GET_WLAN_CHANGE_RESULTS_CMDID, + WMI_TLV_EXTSCAN_SET_CAPABILITIES_CMDID, + WMI_TLV_EXTSCAN_GET_CAPABILITIES_CMDID, + WMI_TLV_SET_DHCP_SERVER_OFFLOAD_CMDID = + WMI_TLV_CMD(WMI_TLV_GRP_DHCP_OFL), + WMI_TLV_IPA_OFFLOAD_ENABLE_DISABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_IPA), + WMI_TLV_MDNS_OFFLOAD_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_MDNS_OFL), + WMI_TLV_MDNS_SET_FQDN_CMDID, + WMI_TLV_MDNS_SET_RESPONSE_CMDID, + WMI_TLV_MDNS_GET_STATS_CMDID, + WMI_TLV_SAP_OFL_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_SAP_OFL), +}; + +enum wmi_tlv_event_id { + WMI_TLV_SERVICE_READY_EVENTID = 0x1, + WMI_TLV_READY_EVENTID, + WMI_TLV_SCAN_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_SCAN), + WMI_TLV_PDEV_TPC_CONFIG_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_PDEV), + WMI_TLV_CHAN_INFO_EVENTID, + WMI_TLV_PHYERR_EVENTID, + WMI_TLV_PDEV_DUMP_EVENTID, + WMI_TLV_TX_PAUSE_EVENTID, + WMI_TLV_DFS_RADAR_EVENTID, + WMI_TLV_PDEV_L1SS_TRACK_EVENTID, + WMI_TLV_PDEV_TEMPERATURE_EVENTID, + WMI_TLV_VDEV_START_RESP_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_VDEV), + WMI_TLV_VDEV_STOPPED_EVENTID, + WMI_TLV_VDEV_INSTALL_KEY_COMPLETE_EVENTID, + WMI_TLV_VDEV_MCC_BCN_INTERVAL_CHANGE_REQ_EVENTID, + WMI_TLV_PEER_STA_KICKOUT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_PEER), + WMI_TLV_PEER_INFO_EVENTID, + WMI_TLV_PEER_TX_FAIL_CNT_THR_EVENTID, + WMI_TLV_PEER_ESTIMATED_LINKSPEED_EVENTID, + WMI_TLV_PEER_STATE_EVENTID, + WMI_TLV_MGMT_RX_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_MGMT), + WMI_TLV_HOST_SWBA_EVENTID, + WMI_TLV_TBTTOFFSET_UPDATE_EVENTID, + WMI_TLV_OFFLOAD_BCN_TX_STATUS_EVENTID, + WMI_TLV_OFFLOAD_PROB_RESP_TX_STATUS_EVENTID, + WMI_TLV_TX_DELBA_COMPLETE_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_BA_NEG), + WMI_TLV_TX_ADDBA_COMPLETE_EVENTID, + WMI_TLV_BA_RSP_SSN_EVENTID, + WMI_TLV_AGGR_STATE_TRIG_EVENTID, + WMI_TLV_ROAM_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_ROAM), + WMI_TLV_PROFILE_MATCH, + WMI_TLV_ROAM_SYNCH_EVENTID, + WMI_TLV_P2P_DISC_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_P2P), + WMI_TLV_P2P_NOA_EVENTID, + WMI_TLV_PDEV_RESUME_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_SUSPEND), + WMI_TLV_WOW_WAKEUP_HOST_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_WOW), + WMI_TLV_D0_WOW_DISABLE_ACK_EVENTID, + WMI_TLV_RTT_MEASUREMENT_REPORT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_RTT), + WMI_TLV_TSF_MEASUREMENT_REPORT_EVENTID, + WMI_TLV_RTT_ERROR_REPORT_EVENTID, + WMI_TLV_STATS_EXT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_STATS), + WMI_TLV_IFACE_LINK_STATS_EVENTID, + WMI_TLV_PEER_LINK_STATS_EVENTID, + WMI_TLV_RADIO_LINK_STATS_EVENTID, + WMI_TLV_NLO_MATCH_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_NLO_OFL), + WMI_TLV_NLO_SCAN_COMPLETE_EVENTID, + WMI_TLV_APFIND_EVENTID, + WMI_TLV_GTK_OFFLOAD_STATUS_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_GTK_OFL), + WMI_TLV_GTK_REKEY_FAIL_EVENTID, + WMI_TLV_CSA_HANDLING_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_CSA_OFL), + WMI_TLV_CHATTER_PC_QUERY_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_CHATTER), + WMI_TLV_ECHO_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_MISC), + WMI_TLV_PDEV_UTF_EVENTID, + WMI_TLV_DEBUG_MESG_EVENTID, + WMI_TLV_UPDATE_STATS_EVENTID, + WMI_TLV_DEBUG_PRINT_EVENTID, + WMI_TLV_DCS_INTERFERENCE_EVENTID, + WMI_TLV_PDEV_QVIT_EVENTID, + WMI_TLV_WLAN_PROFILE_DATA_EVENTID, + WMI_TLV_PDEV_FTM_INTG_EVENTID, + WMI_TLV_WLAN_FREQ_AVOID_EVENTID, + WMI_TLV_VDEV_GET_KEEPALIVE_EVENTID, + WMI_TLV_THERMAL_MGMT_EVENTID, + WMI_TLV_DIAG_DATA_CONTAINER_EVENTID, + WMI_TLV_HOST_AUTO_SHUTDOWN_EVENTID, + WMI_TLV_UPDATE_WHAL_MIB_STATS_EVENTID, + WMI_TLV_UPDATE_VDEV_RATE_STATS_EVENTID, + WMI_TLV_DIAG_EVENTID, + WMI_TLV_GPIO_INPUT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_GPIO), + WMI_TLV_UPLOADH_EVENTID, + WMI_TLV_CAPTUREH_EVENTID, + WMI_TLV_RFKILL_STATE_CHANGE_EVENTID, + WMI_TLV_TDLS_PEER_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_TDLS), + WMI_TLV_BATCH_SCAN_ENABLED_EVENTID = + WMI_TLV_EV(WMI_TLV_GRP_LOCATION_SCAN), + WMI_TLV_BATCH_SCAN_RESULT_EVENTID, + WMI_TLV_OEM_CAPABILITY_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_OEM), + WMI_TLV_OEM_MEASUREMENT_REPORT_EVENTID, + WMI_TLV_OEM_ERROR_REPORT_EVENTID, + WMI_TLV_NAN_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_NAN), + WMI_TLV_LPI_RESULT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_LPI), + WMI_TLV_LPI_STATUS_EVENTID, + WMI_TLV_LPI_HANDOFF_EVENTID, + WMI_TLV_EXTSCAN_START_STOP_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_EXTSCAN), + WMI_TLV_EXTSCAN_OPERATION_EVENTID, + WMI_TLV_EXTSCAN_TABLE_USAGE_EVENTID, + WMI_TLV_EXTSCAN_CACHED_RESULTS_EVENTID, + WMI_TLV_EXTSCAN_WLAN_CHANGE_RESULTS_EVENTID, + WMI_TLV_EXTSCAN_HOTLIST_MATCH_EVENTID, + WMI_TLV_EXTSCAN_CAPABILITIES_EVENTID, + WMI_TLV_MDNS_STATS_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_MDNS_OFL), + WMI_TLV_SAP_OFL_ADD_STA_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_SAP_OFL), + WMI_TLV_SAP_OFL_DEL_STA_EVENTID, +}; + +enum wmi_tlv_pdev_param { + WMI_TLV_PDEV_PARAM_TX_CHAIN_MASK = 0x1, + WMI_TLV_PDEV_PARAM_RX_CHAIN_MASK, + WMI_TLV_PDEV_PARAM_TXPOWER_LIMIT2G, + WMI_TLV_PDEV_PARAM_TXPOWER_LIMIT5G, + WMI_TLV_PDEV_PARAM_TXPOWER_SCALE, + WMI_TLV_PDEV_PARAM_BEACON_GEN_MODE, + WMI_TLV_PDEV_PARAM_BEACON_TX_MODE, + WMI_TLV_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + WMI_TLV_PDEV_PARAM_PROTECTION_MODE, + WMI_TLV_PDEV_PARAM_DYNAMIC_BW, + WMI_TLV_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + WMI_TLV_PDEV_PARAM_AGG_SW_RETRY_TH, + WMI_TLV_PDEV_PARAM_STA_KICKOUT_TH, + WMI_TLV_PDEV_PARAM_AC_AGGRSIZE_SCALING, + WMI_TLV_PDEV_PARAM_LTR_ENABLE, + WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_BE, + WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_BK, + WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_VI, + WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_VO, + WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + WMI_TLV_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + WMI_TLV_PDEV_PARAM_LTR_RX_OVERRIDE, + WMI_TLV_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + WMI_TLV_PDEV_PARAM_L1SS_ENABLE, + WMI_TLV_PDEV_PARAM_DSLEEP_ENABLE, + WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_FLUSH, + WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_WATERMARK, + WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_EN, + WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE, + WMI_TLV_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + WMI_TLV_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + WMI_TLV_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + WMI_TLV_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + WMI_TLV_PDEV_PARAM_PMF_QOS, + WMI_TLV_PDEV_PARAM_ARP_AC_OVERRIDE, + WMI_TLV_PDEV_PARAM_DCS, + WMI_TLV_PDEV_PARAM_ANI_ENABLE, + WMI_TLV_PDEV_PARAM_ANI_POLL_PERIOD, + WMI_TLV_PDEV_PARAM_ANI_LISTEN_PERIOD, + WMI_TLV_PDEV_PARAM_ANI_OFDM_LEVEL, + WMI_TLV_PDEV_PARAM_ANI_CCK_LEVEL, + WMI_TLV_PDEV_PARAM_DYNTXCHAIN, + WMI_TLV_PDEV_PARAM_PROXY_STA, + WMI_TLV_PDEV_PARAM_IDLE_PS_CONFIG, + WMI_TLV_PDEV_PARAM_POWER_GATING_SLEEP, + WMI_TLV_PDEV_PARAM_RFKILL_ENABLE, + WMI_TLV_PDEV_PARAM_BURST_DUR, + WMI_TLV_PDEV_PARAM_BURST_ENABLE, + WMI_TLV_PDEV_PARAM_HW_RFKILL_CONFIG, + WMI_TLV_PDEV_PARAM_LOW_POWER_RF_ENABLE, + WMI_TLV_PDEV_PARAM_L1SS_TRACK, + WMI_TLV_PDEV_PARAM_HYST_EN, + WMI_TLV_PDEV_PARAM_POWER_COLLAPSE_ENABLE, + WMI_TLV_PDEV_PARAM_LED_SYS_STATE, + WMI_TLV_PDEV_PARAM_LED_ENABLE, + WMI_TLV_PDEV_PARAM_AUDIO_OVER_WLAN_LATENCY, + WMI_TLV_PDEV_PARAM_AUDIO_OVER_WLAN_ENABLE, + WMI_TLV_PDEV_PARAM_WHAL_MIB_STATS_UPDATE_ENABLE, + WMI_TLV_PDEV_PARAM_VDEV_RATE_STATS_UPDATE_PERIOD, + WMI_TLV_PDEV_PARAM_TXPOWER_REASON_NONE, + WMI_TLV_PDEV_PARAM_TXPOWER_REASON_SAR, + WMI_TLV_PDEV_PARAM_TXPOWER_REASON_MAX, +}; + +enum wmi_tlv_vdev_param { + WMI_TLV_VDEV_PARAM_RTS_THRESHOLD = 0x1, + WMI_TLV_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + WMI_TLV_VDEV_PARAM_BEACON_INTERVAL, + WMI_TLV_VDEV_PARAM_LISTEN_INTERVAL, + WMI_TLV_VDEV_PARAM_MULTICAST_RATE, + WMI_TLV_VDEV_PARAM_MGMT_TX_RATE, + WMI_TLV_VDEV_PARAM_SLOT_TIME, + WMI_TLV_VDEV_PARAM_PREAMBLE, + WMI_TLV_VDEV_PARAM_SWBA_TIME, + WMI_TLV_VDEV_STATS_UPDATE_PERIOD, + WMI_TLV_VDEV_PWRSAVE_AGEOUT_TIME, + WMI_TLV_VDEV_HOST_SWBA_INTERVAL, + WMI_TLV_VDEV_PARAM_DTIM_PERIOD, + WMI_TLV_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + WMI_TLV_VDEV_PARAM_WDS, + WMI_TLV_VDEV_PARAM_ATIM_WINDOW, + WMI_TLV_VDEV_PARAM_BMISS_COUNT_MAX, + WMI_TLV_VDEV_PARAM_BMISS_FIRST_BCNT, + WMI_TLV_VDEV_PARAM_BMISS_FINAL_BCNT, + WMI_TLV_VDEV_PARAM_FEATURE_WMM, + WMI_TLV_VDEV_PARAM_CHWIDTH, + WMI_TLV_VDEV_PARAM_CHEXTOFFSET, + WMI_TLV_VDEV_PARAM_DISABLE_HTPROTECTION, + WMI_TLV_VDEV_PARAM_STA_QUICKKICKOUT, + WMI_TLV_VDEV_PARAM_MGMT_RATE, + WMI_TLV_VDEV_PARAM_PROTECTION_MODE, + WMI_TLV_VDEV_PARAM_FIXED_RATE, + WMI_TLV_VDEV_PARAM_SGI, + WMI_TLV_VDEV_PARAM_LDPC, + WMI_TLV_VDEV_PARAM_TX_STBC, + WMI_TLV_VDEV_PARAM_RX_STBC, + WMI_TLV_VDEV_PARAM_INTRA_BSS_FWD, + WMI_TLV_VDEV_PARAM_DEF_KEYID, + WMI_TLV_VDEV_PARAM_NSS, + WMI_TLV_VDEV_PARAM_BCAST_DATA_RATE, + WMI_TLV_VDEV_PARAM_MCAST_DATA_RATE, + WMI_TLV_VDEV_PARAM_MCAST_INDICATE, + WMI_TLV_VDEV_PARAM_DHCP_INDICATE, + WMI_TLV_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + WMI_TLV_VDEV_PARAM_AP_ENABLE_NAWDS, + WMI_TLV_VDEV_PARAM_ENABLE_RTSCTS, + WMI_TLV_VDEV_PARAM_TXBF, + WMI_TLV_VDEV_PARAM_PACKET_POWERSAVE, + WMI_TLV_VDEV_PARAM_DROP_UNENCRY, + WMI_TLV_VDEV_PARAM_TX_ENCAP_TYPE, + WMI_TLV_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS, + WMI_TLV_VDEV_PARAM_EARLY_RX_ADJUST_ENABLE, + WMI_TLV_VDEV_PARAM_EARLY_RX_TGT_BMISS_NUM, + WMI_TLV_VDEV_PARAM_EARLY_RX_BMISS_SAMPLE_CYCLE, + WMI_TLV_VDEV_PARAM_EARLY_RX_SLOP_STEP, + WMI_TLV_VDEV_PARAM_EARLY_RX_INIT_SLOP, + WMI_TLV_VDEV_PARAM_EARLY_RX_ADJUST_PAUSE, + WMI_TLV_VDEV_PARAM_TX_PWRLIMIT, + WMI_TLV_VDEV_PARAM_SNR_NUM_FOR_CAL, + WMI_TLV_VDEV_PARAM_ROAM_FW_OFFLOAD, + WMI_TLV_VDEV_PARAM_ENABLE_RMC, + WMI_TLV_VDEV_PARAM_IBSS_MAX_BCN_LOST_MS, + WMI_TLV_VDEV_PARAM_MAX_RATE, + WMI_TLV_VDEV_PARAM_EARLY_RX_DRIFT_SAMPLE, + WMI_TLV_VDEV_PARAM_SET_IBSS_TX_FAIL_CNT_THR, + WMI_TLV_VDEV_PARAM_EBT_RESYNC_TIMEOUT, + WMI_TLV_VDEV_PARAM_AGGR_TRIG_EVENT_ENABLE, + WMI_TLV_VDEV_PARAM_IS_IBSS_POWER_SAVE_ALLOWED, + WMI_TLV_VDEV_PARAM_IS_POWER_COLLAPSE_ALLOWED, + WMI_TLV_VDEV_PARAM_IS_AWAKE_ON_TXRX_ENABLED, + WMI_TLV_VDEV_PARAM_INACTIVITY_CNT, + WMI_TLV_VDEV_PARAM_TXSP_END_INACTIVITY_TIME_MS, + WMI_TLV_VDEV_PARAM_DTIM_POLICY, + WMI_TLV_VDEV_PARAM_IBSS_PS_WARMUP_TIME_SECS, + WMI_TLV_VDEV_PARAM_IBSS_PS_1RX_CHAIN_IN_ATIM_WINDOW_ENABLE, +}; + +enum wmi_tlv_tag { + WMI_TLV_TAG_LAST_RESERVED = 15, + + WMI_TLV_TAG_FIRST_ARRAY_ENUM, + WMI_TLV_TAG_ARRAY_UINT32 = WMI_TLV_TAG_FIRST_ARRAY_ENUM, + WMI_TLV_TAG_ARRAY_BYTE, + WMI_TLV_TAG_ARRAY_STRUCT, + WMI_TLV_TAG_ARRAY_FIXED_STRUCT, + WMI_TLV_TAG_LAST_ARRAY_ENUM = 31, + + WMI_TLV_TAG_STRUCT_SERVICE_READY_EVENT, + WMI_TLV_TAG_STRUCT_HAL_REG_CAPABILITIES, + WMI_TLV_TAG_STRUCT_WLAN_HOST_MEM_REQ, + WMI_TLV_TAG_STRUCT_READY_EVENT, + WMI_TLV_TAG_STRUCT_SCAN_EVENT, + WMI_TLV_TAG_STRUCT_PDEV_TPC_CONFIG_EVENT, + WMI_TLV_TAG_STRUCT_CHAN_INFO_EVENT, + WMI_TLV_TAG_STRUCT_COMB_PHYERR_RX_HDR, + WMI_TLV_TAG_STRUCT_VDEV_START_RESPONSE_EVENT, + WMI_TLV_TAG_STRUCT_VDEV_STOPPED_EVENT, + WMI_TLV_TAG_STRUCT_VDEV_INSTALL_KEY_COMPLETE_EVENT, + WMI_TLV_TAG_STRUCT_PEER_STA_KICKOUT_EVENT, + WMI_TLV_TAG_STRUCT_MGMT_RX_HDR, + WMI_TLV_TAG_STRUCT_TBTT_OFFSET_EVENT, + WMI_TLV_TAG_STRUCT_TX_DELBA_COMPLETE_EVENT, + WMI_TLV_TAG_STRUCT_TX_ADDBA_COMPLETE_EVENT, + WMI_TLV_TAG_STRUCT_ROAM_EVENT, + WMI_TLV_TAG_STRUCT_WOW_EVENT_INFO, + WMI_TLV_TAG_STRUCT_WOW_EVENT_INFO_SECTION_BITMAP, + WMI_TLV_TAG_STRUCT_RTT_EVENT_HEADER, + WMI_TLV_TAG_STRUCT_RTT_ERROR_REPORT_EVENT, + WMI_TLV_TAG_STRUCT_RTT_MEAS_EVENT, + WMI_TLV_TAG_STRUCT_ECHO_EVENT, + WMI_TLV_TAG_STRUCT_FTM_INTG_EVENT, + WMI_TLV_TAG_STRUCT_VDEV_GET_KEEPALIVE_EVENT, + WMI_TLV_TAG_STRUCT_GPIO_INPUT_EVENT, + WMI_TLV_TAG_STRUCT_CSA_EVENT, + WMI_TLV_TAG_STRUCT_GTK_OFFLOAD_STATUS_EVENT, + WMI_TLV_TAG_STRUCT_IGTK_INFO, + WMI_TLV_TAG_STRUCT_DCS_INTERFERENCE_EVENT, + WMI_TLV_TAG_STRUCT_ATH_DCS_CW_INT, + WMI_TLV_TAG_STRUCT_ATH_DCS_WLAN_INT_STAT, + WMI_TLV_TAG_STRUCT_WLAN_PROFILE_CTX_T, + WMI_TLV_TAG_STRUCT_WLAN_PROFILE_T, + WMI_TLV_TAG_STRUCT_PDEV_QVIT_EVENT, + WMI_TLV_TAG_STRUCT_HOST_SWBA_EVENT, + WMI_TLV_TAG_STRUCT_TIM_INFO, + WMI_TLV_TAG_STRUCT_P2P_NOA_INFO, + WMI_TLV_TAG_STRUCT_STATS_EVENT, + WMI_TLV_TAG_STRUCT_AVOID_FREQ_RANGES_EVENT, + WMI_TLV_TAG_STRUCT_AVOID_FREQ_RANGE_DESC, + WMI_TLV_TAG_STRUCT_GTK_REKEY_FAIL_EVENT, + WMI_TLV_TAG_STRUCT_INIT_CMD, + WMI_TLV_TAG_STRUCT_RESOURCE_CONFIG, + WMI_TLV_TAG_STRUCT_WLAN_HOST_MEMORY_CHUNK, + WMI_TLV_TAG_STRUCT_START_SCAN_CMD, + WMI_TLV_TAG_STRUCT_STOP_SCAN_CMD, + WMI_TLV_TAG_STRUCT_SCAN_CHAN_LIST_CMD, + WMI_TLV_TAG_STRUCT_CHANNEL, + WMI_TLV_TAG_STRUCT_PDEV_SET_REGDOMAIN_CMD, + WMI_TLV_TAG_STRUCT_PDEV_SET_PARAM_CMD, + WMI_TLV_TAG_STRUCT_PDEV_SET_WMM_PARAMS_CMD, + WMI_TLV_TAG_STRUCT_WMM_PARAMS, + WMI_TLV_TAG_STRUCT_PDEV_SET_QUIET_CMD, + WMI_TLV_TAG_STRUCT_VDEV_CREATE_CMD, + WMI_TLV_TAG_STRUCT_VDEV_DELETE_CMD, + WMI_TLV_TAG_STRUCT_VDEV_START_REQUEST_CMD, + WMI_TLV_TAG_STRUCT_P2P_NOA_DESCRIPTOR, + WMI_TLV_TAG_STRUCT_P2P_GO_SET_BEACON_IE, + WMI_TLV_TAG_STRUCT_GTK_OFFLOAD_CMD, + WMI_TLV_TAG_STRUCT_VDEV_UP_CMD, + WMI_TLV_TAG_STRUCT_VDEV_STOP_CMD, + WMI_TLV_TAG_STRUCT_VDEV_DOWN_CMD, + WMI_TLV_TAG_STRUCT_VDEV_SET_PARAM_CMD, + WMI_TLV_TAG_STRUCT_VDEV_INSTALL_KEY_CMD, + WMI_TLV_TAG_STRUCT_PEER_CREATE_CMD, + WMI_TLV_TAG_STRUCT_PEER_DELETE_CMD, + WMI_TLV_TAG_STRUCT_PEER_FLUSH_TIDS_CMD, + WMI_TLV_TAG_STRUCT_PEER_SET_PARAM_CMD, + WMI_TLV_TAG_STRUCT_PEER_ASSOC_COMPLETE_CMD, + WMI_TLV_TAG_STRUCT_VHT_RATE_SET, + WMI_TLV_TAG_STRUCT_BCN_TMPL_CMD, + WMI_TLV_TAG_STRUCT_PRB_TMPL_CMD, + WMI_TLV_TAG_STRUCT_BCN_PRB_INFO, + WMI_TLV_TAG_STRUCT_PEER_TID_ADDBA_CMD, + WMI_TLV_TAG_STRUCT_PEER_TID_DELBA_CMD, + WMI_TLV_TAG_STRUCT_STA_POWERSAVE_MODE_CMD, + WMI_TLV_TAG_STRUCT_STA_POWERSAVE_PARAM_CMD, + WMI_TLV_TAG_STRUCT_STA_DTIM_PS_METHOD_CMD, + WMI_TLV_TAG_STRUCT_ROAM_SCAN_MODE, + WMI_TLV_TAG_STRUCT_ROAM_SCAN_RSSI_THRESHOLD, + WMI_TLV_TAG_STRUCT_ROAM_SCAN_PERIOD, + WMI_TLV_TAG_STRUCT_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + WMI_TLV_TAG_STRUCT_PDEV_SUSPEND_CMD, + WMI_TLV_TAG_STRUCT_PDEV_RESUME_CMD, + WMI_TLV_TAG_STRUCT_ADD_BCN_FILTER_CMD, + WMI_TLV_TAG_STRUCT_RMV_BCN_FILTER_CMD, + WMI_TLV_TAG_STRUCT_WOW_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_WOW_HOSTWAKEUP_FROM_SLEEP_CMD, + WMI_TLV_TAG_STRUCT_STA_UAPSD_AUTO_TRIG_CMD, + WMI_TLV_TAG_STRUCT_STA_UAPSD_AUTO_TRIG_PARAM, + WMI_TLV_TAG_STRUCT_SET_ARP_NS_OFFLOAD_CMD, + WMI_TLV_TAG_STRUCT_ARP_OFFLOAD_TUPLE, + WMI_TLV_TAG_STRUCT_NS_OFFLOAD_TUPLE, + WMI_TLV_TAG_STRUCT_FTM_INTG_CMD, + WMI_TLV_TAG_STRUCT_STA_KEEPALIVE_CMD, + WMI_TLV_TAG_STRUCT_STA_KEEPALVE_ARP_RESPONSE, + WMI_TLV_TAG_STRUCT_P2P_SET_VENDOR_IE_DATA_CMD, + WMI_TLV_TAG_STRUCT_AP_PS_PEER_CMD, + WMI_TLV_TAG_STRUCT_PEER_RATE_RETRY_SCHED_CMD, + WMI_TLV_TAG_STRUCT_WLAN_PROFILE_TRIGGER_CMD, + WMI_TLV_TAG_STRUCT_WLAN_PROFILE_SET_HIST_INTVL_CMD, + WMI_TLV_TAG_STRUCT_WLAN_PROFILE_GET_PROF_DATA_CMD, + WMI_TLV_TAG_STRUCT_WLAN_PROFILE_ENABLE_PROFILE_ID_CMD, + WMI_TLV_TAG_STRUCT_WOW_DEL_PATTERN_CMD, + WMI_TLV_TAG_STRUCT_WOW_ADD_DEL_EVT_CMD, + WMI_TLV_TAG_STRUCT_RTT_MEASREQ_HEAD, + WMI_TLV_TAG_STRUCT_RTT_MEASREQ_BODY, + WMI_TLV_TAG_STRUCT_RTT_TSF_CMD, + WMI_TLV_TAG_STRUCT_VDEV_SPECTRAL_CONFIGURE_CMD, + WMI_TLV_TAG_STRUCT_VDEV_SPECTRAL_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_REQUEST_STATS_CMD, + WMI_TLV_TAG_STRUCT_NLO_CONFIG_CMD, + WMI_TLV_TAG_STRUCT_NLO_CONFIGURED_PARAMETERS, + WMI_TLV_TAG_STRUCT_CSA_OFFLOAD_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_CSA_OFFLOAD_CHANSWITCH_CMD, + WMI_TLV_TAG_STRUCT_CHATTER_SET_MODE_CMD, + WMI_TLV_TAG_STRUCT_ECHO_CMD, + WMI_TLV_TAG_STRUCT_VDEV_SET_KEEPALIVE_CMD, + WMI_TLV_TAG_STRUCT_VDEV_GET_KEEPALIVE_CMD, + WMI_TLV_TAG_STRUCT_FORCE_FW_HANG_CMD, + WMI_TLV_TAG_STRUCT_GPIO_CONFIG_CMD, + WMI_TLV_TAG_STRUCT_GPIO_OUTPUT_CMD, + WMI_TLV_TAG_STRUCT_PEER_ADD_WDS_ENTRY_CMD, + WMI_TLV_TAG_STRUCT_PEER_REMOVE_WDS_ENTRY_CMD, + WMI_TLV_TAG_STRUCT_BCN_TX_HDR, + WMI_TLV_TAG_STRUCT_BCN_SEND_FROM_HOST_CMD, + WMI_TLV_TAG_STRUCT_MGMT_TX_HDR, + WMI_TLV_TAG_STRUCT_ADDBA_CLEAR_RESP_CMD, + WMI_TLV_TAG_STRUCT_ADDBA_SEND_CMD, + WMI_TLV_TAG_STRUCT_DELBA_SEND_CMD, + WMI_TLV_TAG_STRUCT_ADDBA_SETRESPONSE_CMD, + WMI_TLV_TAG_STRUCT_SEND_SINGLEAMSDU_CMD, + WMI_TLV_TAG_STRUCT_PDEV_PKTLOG_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_PKTLOG_DISABLE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_SET_HT_IE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_SET_VHT_IE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_SET_DSCP_TID_MAP_CMD, + WMI_TLV_TAG_STRUCT_PDEV_GREEN_AP_PS_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_GET_TPC_CONFIG_CMD, + WMI_TLV_TAG_STRUCT_PDEV_SET_BASE_MACADDR_CMD, + WMI_TLV_TAG_STRUCT_PEER_MCAST_GROUP_CMD, + WMI_TLV_TAG_STRUCT_ROAM_AP_PROFILE, + WMI_TLV_TAG_STRUCT_AP_PROFILE, + WMI_TLV_TAG_STRUCT_SCAN_SCH_PRIORITY_TABLE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_DFS_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_DFS_DISABLE_CMD, + WMI_TLV_TAG_STRUCT_WOW_ADD_PATTERN_CMD, + WMI_TLV_TAG_STRUCT_WOW_BITMAP_PATTERN_T, + WMI_TLV_TAG_STRUCT_WOW_IPV4_SYNC_PATTERN_T, + WMI_TLV_TAG_STRUCT_WOW_IPV6_SYNC_PATTERN_T, + WMI_TLV_TAG_STRUCT_WOW_MAGIC_PATTERN_CMD, + WMI_TLV_TAG_STRUCT_SCAN_UPDATE_REQUEST_CMD, + WMI_TLV_TAG_STRUCT_CHATTER_PKT_COALESCING_FILTER, + WMI_TLV_TAG_STRUCT_CHATTER_COALESCING_ADD_FILTER_CMD, + WMI_TLV_TAG_STRUCT_CHATTER_COALESCING_DELETE_FILTER_CMD, + WMI_TLV_TAG_STRUCT_CHATTER_COALESCING_QUERY_CMD, + WMI_TLV_TAG_STRUCT_TXBF_CMD, + WMI_TLV_TAG_STRUCT_DEBUG_LOG_CONFIG_CMD, + WMI_TLV_TAG_STRUCT_NLO_EVENT, + WMI_TLV_TAG_STRUCT_CHATTER_QUERY_REPLY_EVENT, + WMI_TLV_TAG_STRUCT_UPLOAD_H_HDR, + WMI_TLV_TAG_STRUCT_CAPTURE_H_EVENT_HDR, + WMI_TLV_TAG_STRUCT_VDEV_WNM_SLEEPMODE_CMD, + WMI_TLV_TAG_STRUCT_VDEV_IPSEC_NATKEEPALIVE_FILTER_CMD, + WMI_TLV_TAG_STRUCT_VDEV_WMM_ADDTS_CMD, + WMI_TLV_TAG_STRUCT_VDEV_WMM_DELTS_CMD, + WMI_TLV_TAG_STRUCT_VDEV_SET_WMM_PARAMS_CMD, + WMI_TLV_TAG_STRUCT_TDLS_SET_STATE_CMD, + WMI_TLV_TAG_STRUCT_TDLS_PEER_UPDATE_CMD, + WMI_TLV_TAG_STRUCT_TDLS_PEER_EVENT, + WMI_TLV_TAG_STRUCT_TDLS_PEER_CAPABILITIES, + WMI_TLV_TAG_STRUCT_VDEV_MCC_SET_TBTT_MODE_CMD, + WMI_TLV_TAG_STRUCT_ROAM_CHAN_LIST, + WMI_TLV_TAG_STRUCT_VDEV_MCC_BCN_INTVL_CHANGE_EVENT, + WMI_TLV_TAG_STRUCT_RESMGR_ADAPTIVE_OCS_CMD, + WMI_TLV_TAG_STRUCT_RESMGR_SET_CHAN_TIME_QUOTA_CMD, + WMI_TLV_TAG_STRUCT_RESMGR_SET_CHAN_LATENCY_CMD, + WMI_TLV_TAG_STRUCT_BA_REQ_SSN_CMD, + WMI_TLV_TAG_STRUCT_BA_RSP_SSN_EVENT, + WMI_TLV_TAG_STRUCT_STA_SMPS_FORCE_MODE_CMD, + WMI_TLV_TAG_STRUCT_SET_MCASTBCAST_FILTER_CMD, + WMI_TLV_TAG_STRUCT_P2P_SET_OPPPS_CMD, + WMI_TLV_TAG_STRUCT_P2P_SET_NOA_CMD, + WMI_TLV_TAG_STRUCT_BA_REQ_SSN_CMD_SUB_STRUCT_PARAM, + WMI_TLV_TAG_STRUCT_BA_REQ_SSN_EVENT_SUB_STRUCT_PARAM, + WMI_TLV_TAG_STRUCT_STA_SMPS_PARAM_CMD, + WMI_TLV_TAG_STRUCT_VDEV_SET_GTX_PARAMS_CMD, + WMI_TLV_TAG_STRUCT_MCC_SCHED_TRAFFIC_STATS_CMD, + WMI_TLV_TAG_STRUCT_MCC_SCHED_STA_TRAFFIC_STATS, + WMI_TLV_TAG_STRUCT_OFFLOAD_BCN_TX_STATUS_EVENT, + WMI_TLV_TAG_STRUCT_P2P_NOA_EVENT, + WMI_TLV_TAG_STRUCT_HB_SET_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_HB_SET_TCP_PARAMS_CMD, + WMI_TLV_TAG_STRUCT_HB_SET_TCP_PKT_FILTER_CMD, + WMI_TLV_TAG_STRUCT_HB_SET_UDP_PARAMS_CMD, + WMI_TLV_TAG_STRUCT_HB_SET_UDP_PKT_FILTER_CMD, + WMI_TLV_TAG_STRUCT_HB_IND_EVENT, + WMI_TLV_TAG_STRUCT_TX_PAUSE_EVENT, + WMI_TLV_TAG_STRUCT_RFKILL_EVENT, + WMI_TLV_TAG_STRUCT_DFS_RADAR_EVENT, + WMI_TLV_TAG_STRUCT_DFS_PHYERR_FILTER_ENA_CMD, + WMI_TLV_TAG_STRUCT_DFS_PHYERR_FILTER_DIS_CMD, + WMI_TLV_TAG_STRUCT_BATCH_SCAN_RESULT_SCAN_LIST, + WMI_TLV_TAG_STRUCT_BATCH_SCAN_RESULT_NETWORK_INFO, + WMI_TLV_TAG_STRUCT_BATCH_SCAN_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_BATCH_SCAN_DISABLE_CMD, + WMI_TLV_TAG_STRUCT_BATCH_SCAN_TRIGGER_RESULT_CMD, + WMI_TLV_TAG_STRUCT_BATCH_SCAN_ENABLED_EVENT, + WMI_TLV_TAG_STRUCT_BATCH_SCAN_RESULT_EVENT, + WMI_TLV_TAG_STRUCT_VDEV_PLMREQ_START_CMD, + WMI_TLV_TAG_STRUCT_VDEV_PLMREQ_STOP_CMD, + WMI_TLV_TAG_STRUCT_THERMAL_MGMT_CMD, + WMI_TLV_TAG_STRUCT_THERMAL_MGMT_EVENT, + WMI_TLV_TAG_STRUCT_PEER_INFO_REQ_CMD, + WMI_TLV_TAG_STRUCT_PEER_INFO_EVENT, + WMI_TLV_TAG_STRUCT_PEER_INFO, + WMI_TLV_TAG_STRUCT_PEER_TX_FAIL_CNT_THR_EVENT, + WMI_TLV_TAG_STRUCT_RMC_SET_MODE_CMD, + WMI_TLV_TAG_STRUCT_RMC_SET_ACTION_PERIOD_CMD, + WMI_TLV_TAG_STRUCT_RMC_CONFIG_CMD, + WMI_TLV_TAG_STRUCT_MHF_OFFLOAD_SET_MODE_CMD, + WMI_TLV_TAG_STRUCT_MHF_OFFLOAD_PLUMB_ROUTING_TABLE_CMD, + WMI_TLV_TAG_STRUCT_ADD_PROACTIVE_ARP_RSP_PATTERN_CMD, + WMI_TLV_TAG_STRUCT_DEL_PROACTIVE_ARP_RSP_PATTERN_CMD, + WMI_TLV_TAG_STRUCT_NAN_CMD_PARAM, + WMI_TLV_TAG_STRUCT_NAN_EVENT_HDR, + WMI_TLV_TAG_STRUCT_PDEV_L1SS_TRACK_EVENT, + WMI_TLV_TAG_STRUCT_DIAG_DATA_CONTAINER_EVENT, + WMI_TLV_TAG_STRUCT_MODEM_POWER_STATE_CMD_PARAM, + WMI_TLV_TAG_STRUCT_PEER_GET_ESTIMATED_LINKSPEED_CMD, + WMI_TLV_TAG_STRUCT_PEER_ESTIMATED_LINKSPEED_EVENT, + WMI_TLV_TAG_STRUCT_AGGR_STATE_TRIG_EVENT, + WMI_TLV_TAG_STRUCT_MHF_OFFLOAD_ROUTING_TABLE_ENTRY, + WMI_TLV_TAG_STRUCT_ROAM_SCAN_CMD, + WMI_TLV_TAG_STRUCT_REQ_STATS_EXT_CMD, + WMI_TLV_TAG_STRUCT_STATS_EXT_EVENT, + WMI_TLV_TAG_STRUCT_OBSS_SCAN_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_OBSS_SCAN_DISABLE_CMD, + WMI_TLV_TAG_STRUCT_OFFLOAD_PRB_RSP_TX_STATUS_EVENT, + WMI_TLV_TAG_STRUCT_PDEV_SET_LED_CONFIG_CMD, + WMI_TLV_TAG_STRUCT_HOST_AUTO_SHUTDOWN_CFG_CMD, + WMI_TLV_TAG_STRUCT_HOST_AUTO_SHUTDOWN_EVENT, + WMI_TLV_TAG_STRUCT_UPDATE_WHAL_MIB_STATS_EVENT, + WMI_TLV_TAG_STRUCT_CHAN_AVOID_UPDATE_CMD_PARAM, + WMI_TLV_TAG_STRUCT_WOW_ACER_IOAC_PKT_PATTERN_T, + WMI_TLV_TAG_STRUCT_WOW_ACER_IOAC_TMR_PATTERN_T, + WMI_TLV_TAG_STRUCT_WOW_IOAC_ADD_KEEPALIVE_CMD, + WMI_TLV_TAG_STRUCT_WOW_IOAC_DEL_KEEPALIVE_CMD, + WMI_TLV_TAG_STRUCT_WOW_IOAC_KEEPALIVE_T, + WMI_TLV_TAG_STRUCT_WOW_ACER_IOAC_ADD_PATTERN_CMD, + WMI_TLV_TAG_STRUCT_WOW_ACER_IOAC_DEL_PATTERN_CMD, + WMI_TLV_TAG_STRUCT_START_LINK_STATS_CMD, + WMI_TLV_TAG_STRUCT_CLEAR_LINK_STATS_CMD, + WMI_TLV_TAG_STRUCT_REQUEST_LINK_STATS_CMD, + WMI_TLV_TAG_STRUCT_IFACE_LINK_STATS_EVENT, + WMI_TLV_TAG_STRUCT_RADIO_LINK_STATS_EVENT, + WMI_TLV_TAG_STRUCT_PEER_STATS_EVENT, + WMI_TLV_TAG_STRUCT_CHANNEL_STATS, + WMI_TLV_TAG_STRUCT_RADIO_LINK_STATS, + WMI_TLV_TAG_STRUCT_RATE_STATS, + WMI_TLV_TAG_STRUCT_PEER_LINK_STATS, + WMI_TLV_TAG_STRUCT_WMM_AC_STATS, + WMI_TLV_TAG_STRUCT_IFACE_LINK_STATS, + WMI_TLV_TAG_STRUCT_LPI_MGMT_SNOOPING_CONFIG_CMD, + WMI_TLV_TAG_STRUCT_LPI_START_SCAN_CMD, + WMI_TLV_TAG_STRUCT_LPI_STOP_SCAN_CMD, + WMI_TLV_TAG_STRUCT_LPI_RESULT_EVENT, + WMI_TLV_TAG_STRUCT_PEER_STATE_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_BUCKET_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_BUCKET_CHANNEL_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_START_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_STOP_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_CONFIGURE_WLAN_CHANGE_MONITOR_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_CHANGE_BSSID_PARAM_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_CONFIGURE_HOTLIST_MONITOR_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_GET_CACHED_RESULTS_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_GET_WLAN_CHANGE_RESULTS_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_SET_CAPABILITIES_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_GET_CAPABILITIES_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_OPERATION_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_START_STOP_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_TABLE_USAGE_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_DESCRIPTOR_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_RSSI_INFO_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_CACHED_RESULTS_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_CHANGE_RESULTS_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_CHANGE_RESULT_BSSID_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_HOTLIST_MATCH_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_CAPABILITIES_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_CACHE_CAPABILITIES_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_CHANGE_MONITOR_CAPABILITIES_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_HOTLIST_MONITOR_CAPABILITIES_EVENT, + WMI_TLV_TAG_STRUCT_D0_WOW_ENABLE_DISABLE_CMD, + WMI_TLV_TAG_STRUCT_D0_WOW_DISABLE_ACK_EVENT, + WMI_TLV_TAG_STRUCT_UNIT_TEST_CMD, + WMI_TLV_TAG_STRUCT_ROAM_OFFLOAD_TLV_PARAM, + WMI_TLV_TAG_STRUCT_ROAM_11I_OFFLOAD_TLV_PARAM, + WMI_TLV_TAG_STRUCT_ROAM_11R_OFFLOAD_TLV_PARAM, + WMI_TLV_TAG_STRUCT_ROAM_ESE_OFFLOAD_TLV_PARAM, + WMI_TLV_TAG_STRUCT_ROAM_SYNCH_EVENT, + WMI_TLV_TAG_STRUCT_ROAM_SYNCH_COMPLETE, + WMI_TLV_TAG_STRUCT_EXTWOW_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_EXTWOW_SET_APP_TYPE1_PARAMS_CMD, + WMI_TLV_TAG_STRUCT_EXTWOW_SET_APP_TYPE2_PARAMS_CMD, + WMI_TLV_TAG_STRUCT_LPI_STATUS_EVENT, + WMI_TLV_TAG_STRUCT_LPI_HANDOFF_EVENT, + WMI_TLV_TAG_STRUCT_VDEV_RATE_STATS_EVENT, + WMI_TLV_TAG_STRUCT_VDEV_RATE_HT_INFO, + WMI_TLV_TAG_STRUCT_RIC_REQUEST, + WMI_TLV_TAG_STRUCT_PDEV_GET_TEMPERATURE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_TEMPERATURE_EVENT, + WMI_TLV_TAG_STRUCT_SET_DHCP_SERVER_OFFLOAD_CMD, + WMI_TLV_TAG_STRUCT_TPC_CHAINMASK_CONFIG_CMD, + WMI_TLV_TAG_STRUCT_RIC_TSPEC, + WMI_TLV_TAG_STRUCT_TPC_CHAINMASK_CONFIG, + WMI_TLV_TAG_STRUCT_IPA_OFFLOAD_CMD, + WMI_TLV_TAG_STRUCT_SCAN_PROB_REQ_OUI_CMD, + WMI_TLV_TAG_STRUCT_KEY_MATERIAL, + WMI_TLV_TAG_STRUCT_TDLS_SET_OFFCHAN_MODE_CMD, + WMI_TLV_TAG_STRUCT_SET_LED_FLASHING_CMD, + WMI_TLV_TAG_STRUCT_MDNS_OFFLOAD_CMD, + WMI_TLV_TAG_STRUCT_MDNS_SET_FQDN_CMD, + WMI_TLV_TAG_STRUCT_MDNS_SET_RESP_CMD, + WMI_TLV_TAG_STRUCT_MDNS_GET_STATS_CMD, + WMI_TLV_TAG_STRUCT_MDNS_STATS_EVENT, + WMI_TLV_TAG_STRUCT_ROAM_INVOKE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_RESUME_EVENT, + WMI_TLV_TAG_STRUCT_PDEV_SET_ANTENNA_DIVERSITY_CMD, + WMI_TLV_TAG_STRUCT_SAP_OFL_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_SAP_OFL_ADD_STA_EVENT, + WMI_TLV_TAG_STRUCT_SAP_OFL_DEL_STA_EVENT, + WMI_TLV_TAG_STRUCT_APFIND_CMD_PARAM, + WMI_TLV_TAG_STRUCT_APFIND_EVENT_HDR, + + WMI_TLV_TAG_MAX +}; + +enum wmi_tlv_service { + WMI_TLV_SERVICE_BEACON_OFFLOAD = 0, + WMI_TLV_SERVICE_SCAN_OFFLOAD, + WMI_TLV_SERVICE_ROAM_SCAN_OFFLOAD, + WMI_TLV_SERVICE_BCN_MISS_OFFLOAD, + WMI_TLV_SERVICE_STA_PWRSAVE, + WMI_TLV_SERVICE_STA_ADVANCED_PWRSAVE, + WMI_TLV_SERVICE_AP_UAPSD, + WMI_TLV_SERVICE_AP_DFS, + WMI_TLV_SERVICE_11AC, + WMI_TLV_SERVICE_BLOCKACK, + WMI_TLV_SERVICE_PHYERR, + WMI_TLV_SERVICE_BCN_FILTER, + WMI_TLV_SERVICE_RTT, + WMI_TLV_SERVICE_WOW, + WMI_TLV_SERVICE_RATECTRL_CACHE, + WMI_TLV_SERVICE_IRAM_TIDS, + WMI_TLV_SERVICE_ARPNS_OFFLOAD, + WMI_TLV_SERVICE_NLO, + WMI_TLV_SERVICE_GTK_OFFLOAD, + WMI_TLV_SERVICE_SCAN_SCH, + WMI_TLV_SERVICE_CSA_OFFLOAD, + WMI_TLV_SERVICE_CHATTER, + WMI_TLV_SERVICE_COEX_FREQAVOID, + WMI_TLV_SERVICE_PACKET_POWER_SAVE, + WMI_TLV_SERVICE_FORCE_FW_HANG, + WMI_TLV_SERVICE_GPIO, + WMI_TLV_SERVICE_STA_DTIM_PS_MODULATED_DTIM, + WMI_TLV_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, + WMI_TLV_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, + WMI_TLV_SERVICE_STA_KEEP_ALIVE, + WMI_TLV_SERVICE_TX_ENCAP, + WMI_TLV_SERVICE_AP_PS_DETECT_OUT_OF_SYNC, + WMI_TLV_SERVICE_EARLY_RX, + WMI_TLV_SERVICE_STA_SMPS, + WMI_TLV_SERVICE_FWTEST, + WMI_TLV_SERVICE_STA_WMMAC, + WMI_TLV_SERVICE_TDLS, + WMI_TLV_SERVICE_BURST, + WMI_TLV_SERVICE_MCC_BCN_INTERVAL_CHANGE, + WMI_TLV_SERVICE_ADAPTIVE_OCS, + WMI_TLV_SERVICE_BA_SSN_SUPPORT, + WMI_TLV_SERVICE_FILTER_IPSEC_NATKEEPALIVE, + WMI_TLV_SERVICE_WLAN_HB, + WMI_TLV_SERVICE_LTE_ANT_SHARE_SUPPORT, + WMI_TLV_SERVICE_BATCH_SCAN, + WMI_TLV_SERVICE_QPOWER, + WMI_TLV_SERVICE_PLMREQ, + WMI_TLV_SERVICE_THERMAL_MGMT, + WMI_TLV_SERVICE_RMC, + WMI_TLV_SERVICE_MHF_OFFLOAD, + WMI_TLV_SERVICE_COEX_SAR, + WMI_TLV_SERVICE_BCN_TXRATE_OVERRIDE, + WMI_TLV_SERVICE_NAN, + WMI_TLV_SERVICE_L1SS_STAT, + WMI_TLV_SERVICE_ESTIMATE_LINKSPEED, + WMI_TLV_SERVICE_OBSS_SCAN, + WMI_TLV_SERVICE_TDLS_OFFCHAN, + WMI_TLV_SERVICE_TDLS_UAPSD_BUFFER_STA, + WMI_TLV_SERVICE_TDLS_UAPSD_SLEEP_STA, + WMI_TLV_SERVICE_IBSS_PWRSAVE, + WMI_TLV_SERVICE_LPASS, + WMI_TLV_SERVICE_EXTSCAN, + WMI_TLV_SERVICE_D0WOW, + WMI_TLV_SERVICE_HSOFFLOAD, + WMI_TLV_SERVICE_ROAM_HO_OFFLOAD, + WMI_TLV_SERVICE_RX_FULL_REORDER, + WMI_TLV_SERVICE_DHCP_OFFLOAD, + WMI_TLV_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT, + WMI_TLV_SERVICE_MDNS_OFFLOAD, + WMI_TLV_SERVICE_SAP_AUTH_OFFLOAD, +}; + +#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id, len) \ + ((svc_id) < (len) && \ + __le32_to_cpu((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \ + BIT((svc_id)%(sizeof(u32)))) + +#define SVCMAP(x, y, len) \ + do { \ + if (WMI_SERVICE_IS_ENABLED((in), (x), (len))) \ + __set_bit(y, out); \ + } while (0) + +static inline void +wmi_tlv_svc_map(const __le32 *in, unsigned long *out, size_t len) +{ + SVCMAP(WMI_TLV_SERVICE_BEACON_OFFLOAD, + WMI_SERVICE_BEACON_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_SCAN_OFFLOAD, + WMI_SERVICE_SCAN_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_ROAM_SCAN_OFFLOAD, + WMI_SERVICE_ROAM_SCAN_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_BCN_MISS_OFFLOAD, + WMI_SERVICE_BCN_MISS_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_STA_PWRSAVE, + WMI_SERVICE_STA_PWRSAVE, len); + SVCMAP(WMI_TLV_SERVICE_STA_ADVANCED_PWRSAVE, + WMI_SERVICE_STA_ADVANCED_PWRSAVE, len); + SVCMAP(WMI_TLV_SERVICE_AP_UAPSD, + WMI_SERVICE_AP_UAPSD, len); + SVCMAP(WMI_TLV_SERVICE_AP_DFS, + WMI_SERVICE_AP_DFS, len); + SVCMAP(WMI_TLV_SERVICE_11AC, + WMI_SERVICE_11AC, len); + SVCMAP(WMI_TLV_SERVICE_BLOCKACK, + WMI_SERVICE_BLOCKACK, len); + SVCMAP(WMI_TLV_SERVICE_PHYERR, + WMI_SERVICE_PHYERR, len); + SVCMAP(WMI_TLV_SERVICE_BCN_FILTER, + WMI_SERVICE_BCN_FILTER, len); + SVCMAP(WMI_TLV_SERVICE_RTT, + WMI_SERVICE_RTT, len); + SVCMAP(WMI_TLV_SERVICE_WOW, + WMI_SERVICE_WOW, len); + SVCMAP(WMI_TLV_SERVICE_RATECTRL_CACHE, + WMI_SERVICE_RATECTRL_CACHE, len); + SVCMAP(WMI_TLV_SERVICE_IRAM_TIDS, + WMI_SERVICE_IRAM_TIDS, len); + SVCMAP(WMI_TLV_SERVICE_ARPNS_OFFLOAD, + WMI_SERVICE_ARPNS_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_NLO, + WMI_SERVICE_NLO, len); + SVCMAP(WMI_TLV_SERVICE_GTK_OFFLOAD, + WMI_SERVICE_GTK_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_SCAN_SCH, + WMI_SERVICE_SCAN_SCH, len); + SVCMAP(WMI_TLV_SERVICE_CSA_OFFLOAD, + WMI_SERVICE_CSA_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_CHATTER, + WMI_SERVICE_CHATTER, len); + SVCMAP(WMI_TLV_SERVICE_COEX_FREQAVOID, + WMI_SERVICE_COEX_FREQAVOID, len); + SVCMAP(WMI_TLV_SERVICE_PACKET_POWER_SAVE, + WMI_SERVICE_PACKET_POWER_SAVE, len); + SVCMAP(WMI_TLV_SERVICE_FORCE_FW_HANG, + WMI_SERVICE_FORCE_FW_HANG, len); + SVCMAP(WMI_TLV_SERVICE_GPIO, + WMI_SERVICE_GPIO, len); + SVCMAP(WMI_TLV_SERVICE_STA_DTIM_PS_MODULATED_DTIM, + WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, len); + SVCMAP(WMI_TLV_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, + WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, len); + SVCMAP(WMI_TLV_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, + WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, len); + SVCMAP(WMI_TLV_SERVICE_STA_KEEP_ALIVE, + WMI_SERVICE_STA_KEEP_ALIVE, len); + SVCMAP(WMI_TLV_SERVICE_TX_ENCAP, + WMI_SERVICE_TX_ENCAP, len); + SVCMAP(WMI_TLV_SERVICE_AP_PS_DETECT_OUT_OF_SYNC, + WMI_SERVICE_AP_PS_DETECT_OUT_OF_SYNC, len); + SVCMAP(WMI_TLV_SERVICE_EARLY_RX, + WMI_SERVICE_EARLY_RX, len); + SVCMAP(WMI_TLV_SERVICE_STA_SMPS, + WMI_SERVICE_STA_SMPS, len); + SVCMAP(WMI_TLV_SERVICE_FWTEST, + WMI_SERVICE_FWTEST, len); + SVCMAP(WMI_TLV_SERVICE_STA_WMMAC, + WMI_SERVICE_STA_WMMAC, len); + SVCMAP(WMI_TLV_SERVICE_TDLS, + WMI_SERVICE_TDLS, len); + SVCMAP(WMI_TLV_SERVICE_BURST, + WMI_SERVICE_BURST, len); + SVCMAP(WMI_TLV_SERVICE_MCC_BCN_INTERVAL_CHANGE, + WMI_SERVICE_MCC_BCN_INTERVAL_CHANGE, len); + SVCMAP(WMI_TLV_SERVICE_ADAPTIVE_OCS, + WMI_SERVICE_ADAPTIVE_OCS, len); + SVCMAP(WMI_TLV_SERVICE_BA_SSN_SUPPORT, + WMI_SERVICE_BA_SSN_SUPPORT, len); + SVCMAP(WMI_TLV_SERVICE_FILTER_IPSEC_NATKEEPALIVE, + WMI_SERVICE_FILTER_IPSEC_NATKEEPALIVE, len); + SVCMAP(WMI_TLV_SERVICE_WLAN_HB, + WMI_SERVICE_WLAN_HB, len); + SVCMAP(WMI_TLV_SERVICE_LTE_ANT_SHARE_SUPPORT, + WMI_SERVICE_LTE_ANT_SHARE_SUPPORT, len); + SVCMAP(WMI_TLV_SERVICE_BATCH_SCAN, + WMI_SERVICE_BATCH_SCAN, len); + SVCMAP(WMI_TLV_SERVICE_QPOWER, + WMI_SERVICE_QPOWER, len); + SVCMAP(WMI_TLV_SERVICE_PLMREQ, + WMI_SERVICE_PLMREQ, len); + SVCMAP(WMI_TLV_SERVICE_THERMAL_MGMT, + WMI_SERVICE_THERMAL_MGMT, len); + SVCMAP(WMI_TLV_SERVICE_RMC, + WMI_SERVICE_RMC, len); + SVCMAP(WMI_TLV_SERVICE_MHF_OFFLOAD, + WMI_SERVICE_MHF_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_COEX_SAR, + WMI_SERVICE_COEX_SAR, len); + SVCMAP(WMI_TLV_SERVICE_BCN_TXRATE_OVERRIDE, + WMI_SERVICE_BCN_TXRATE_OVERRIDE, len); + SVCMAP(WMI_TLV_SERVICE_NAN, + WMI_SERVICE_NAN, len); + SVCMAP(WMI_TLV_SERVICE_L1SS_STAT, + WMI_SERVICE_L1SS_STAT, len); + SVCMAP(WMI_TLV_SERVICE_ESTIMATE_LINKSPEED, + WMI_SERVICE_ESTIMATE_LINKSPEED, len); + SVCMAP(WMI_TLV_SERVICE_OBSS_SCAN, + WMI_SERVICE_OBSS_SCAN, len); + SVCMAP(WMI_TLV_SERVICE_TDLS_OFFCHAN, + WMI_SERVICE_TDLS_OFFCHAN, len); + SVCMAP(WMI_TLV_SERVICE_TDLS_UAPSD_BUFFER_STA, + WMI_SERVICE_TDLS_UAPSD_BUFFER_STA, len); + SVCMAP(WMI_TLV_SERVICE_TDLS_UAPSD_SLEEP_STA, + WMI_SERVICE_TDLS_UAPSD_SLEEP_STA, len); + SVCMAP(WMI_TLV_SERVICE_IBSS_PWRSAVE, + WMI_SERVICE_IBSS_PWRSAVE, len); + SVCMAP(WMI_TLV_SERVICE_LPASS, + WMI_SERVICE_LPASS, len); + SVCMAP(WMI_TLV_SERVICE_EXTSCAN, + WMI_SERVICE_EXTSCAN, len); + SVCMAP(WMI_TLV_SERVICE_D0WOW, + WMI_SERVICE_D0WOW, len); + SVCMAP(WMI_TLV_SERVICE_HSOFFLOAD, + WMI_SERVICE_HSOFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_ROAM_HO_OFFLOAD, + WMI_SERVICE_ROAM_HO_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_RX_FULL_REORDER, + WMI_SERVICE_RX_FULL_REORDER, len); + SVCMAP(WMI_TLV_SERVICE_DHCP_OFFLOAD, + WMI_SERVICE_DHCP_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT, + WMI_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT, len); + SVCMAP(WMI_TLV_SERVICE_MDNS_OFFLOAD, + WMI_SERVICE_MDNS_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_SAP_AUTH_OFFLOAD, + WMI_SERVICE_SAP_AUTH_OFFLOAD, len); +} + +#undef SVCMAP + +struct wmi_tlv { + __le16 len; + __le16 tag; + u8 value[0]; +} __packed; + +#define WMI_TLV_MGMT_RX_NUM_RSSI 4 + +struct wmi_tlv_mgmt_rx_ev { + __le32 channel; + __le32 snr; + __le32 rate; + __le32 phy_mode; + __le32 buf_len; + __le32 status; + __le32 rssi[WMI_TLV_MGMT_RX_NUM_RSSI]; +} __packed; + +struct wmi_tlv_abi_version { + __le32 abi_ver0; + __le32 abi_ver1; + __le32 abi_ver_ns0; + __le32 abi_ver_ns1; + __le32 abi_ver_ns2; + __le32 abi_ver_ns3; +} __packed; + +enum wmi_tlv_hw_bd_id { + WMI_TLV_HW_BD_LEGACY = 0, + WMI_TLV_HW_BD_QCA6174 = 1, + WMI_TLV_HW_BD_QCA2582 = 2, +}; + +struct wmi_tlv_hw_bd_info { + u8 rev; + u8 project_id; + u8 custom_id; + u8 reference_design_id; +} __packed; + +struct wmi_tlv_svc_rdy_ev { + __le32 fw_build_vers; + struct wmi_tlv_abi_version abi; + __le32 phy_capability; + __le32 max_frag_entry; + __le32 num_rf_chains; + __le32 ht_cap_info; + __le32 vht_cap_info; + __le32 vht_supp_mcs; + __le32 hw_min_tx_power; + __le32 hw_max_tx_power; + __le32 sys_cap_info; + __le32 min_pkt_size_enable; + __le32 max_bcn_ie_size; + __le32 num_mem_reqs; + __le32 max_num_scan_chans; + __le32 hw_bd_id; /* 0 means hw_bd_info is invalid */ + struct wmi_tlv_hw_bd_info hw_bd_info[5]; +} __packed; + +struct wmi_tlv_rdy_ev { + struct wmi_tlv_abi_version abi; + struct wmi_mac_addr mac_addr; + __le32 status; +} __packed; + +struct wmi_tlv_resource_config { + __le32 num_vdevs; + __le32 num_peers; + __le32 num_offload_peers; + __le32 num_offload_reorder_bufs; + __le32 num_peer_keys; + __le32 num_tids; + __le32 ast_skid_limit; + __le32 tx_chain_mask; + __le32 rx_chain_mask; + __le32 rx_timeout_pri[4]; + __le32 rx_decap_mode; + __le32 scan_max_pending_reqs; + __le32 bmiss_offload_max_vdev; + __le32 roam_offload_max_vdev; + __le32 roam_offload_max_ap_profiles; + __le32 num_mcast_groups; + __le32 num_mcast_table_elems; + __le32 mcast2ucast_mode; + __le32 tx_dbg_log_size; + __le32 num_wds_entries; + __le32 dma_burst_size; + __le32 mac_aggr_delim; + __le32 rx_skip_defrag_timeout_dup_detection_check; + __le32 vow_config; + __le32 gtk_offload_max_vdev; + __le32 num_msdu_desc; + __le32 max_frag_entries; + __le32 num_tdls_vdevs; + __le32 num_tdls_conn_table_entries; + __le32 beacon_tx_offload_max_vdev; + __le32 num_multicast_filter_entries; + __le32 num_wow_filters; + __le32 num_keep_alive_pattern; + __le32 keep_alive_pattern_size; + __le32 max_tdls_concurrent_sleep_sta; + __le32 max_tdls_concurrent_buffer_sta; +} __packed; + +struct wmi_tlv_init_cmd { + struct wmi_tlv_abi_version abi; + __le32 num_host_mem_chunks; +} __packed; + +struct wmi_tlv_pdev_set_param_cmd { + __le32 pdev_id; /* not used yet */ + __le32 param_id; + __le32 param_value; +} __packed; + +struct wmi_tlv_pdev_set_rd_cmd { + __le32 pdev_id; /* not used yet */ + __le32 regd; + __le32 regd_2ghz; + __le32 regd_5ghz; + __le32 conform_limit_2ghz; + __le32 conform_limit_5ghz; +} __packed; + +struct wmi_tlv_scan_chan_list_cmd { + __le32 num_scan_chans; +} __packed; + +struct wmi_tlv_start_scan_cmd { + struct wmi_start_scan_common common; + __le32 burst_duration_ms; + __le32 num_channels; + __le32 num_bssids; + __le32 num_ssids; + __le32 ie_len; + __le32 num_probes; +} __packed; + +struct wmi_tlv_vdev_start_cmd { + __le32 vdev_id; + __le32 requestor_id; + __le32 bcn_intval; + __le32 dtim_period; + __le32 flags; + struct wmi_ssid ssid; + __le32 bcn_tx_rate; + __le32 bcn_tx_power; + __le32 num_noa_descr; + __le32 disable_hw_ack; +} __packed; + +enum { + WMI_TLV_PEER_TYPE_DEFAULT = 0, /* generic / non-BSS / self-peer */ + WMI_TLV_PEER_TYPE_BSS = 1, + WMI_TLV_PEER_TYPE_TDLS = 2, + WMI_TLV_PEER_TYPE_HOST_MAX = 127, + WMI_TLV_PEER_TYPE_ROAMOFFLOAD_TMP = 128, +}; + +struct wmi_tlv_peer_create_cmd { + __le32 vdev_id; + struct wmi_mac_addr peer_addr; + __le32 peer_type; +} __packed; + +struct wmi_tlv_peer_assoc_cmd { + struct wmi_mac_addr mac_addr; + __le32 vdev_id; + __le32 new_assoc; + __le32 assoc_id; + __le32 flags; + __le32 caps; + __le32 listen_intval; + __le32 ht_caps; + __le32 max_mpdu; + __le32 mpdu_density; + __le32 rate_caps; + __le32 nss; + __le32 vht_caps; + __le32 phy_mode; + __le32 ht_info[2]; + __le32 num_legacy_rates; + __le32 num_ht_rates; +} __packed; + +struct wmi_tlv_pdev_suspend { + __le32 pdev_id; /* not used yet */ + __le32 opt; +} __packed; + +struct wmi_tlv_pdev_set_wmm_cmd { + __le32 pdev_id; /* not used yet */ + __le32 dg_type; /* no idea.. */ +} __packed; + +struct wmi_tlv_phyerr_ev { + __le32 num_phyerrs; + __le32 tsf_l32; + __le32 tsf_u32; + __le32 buf_len; +} __packed; + +enum wmi_tlv_dbglog_param { + WMI_TLV_DBGLOG_PARAM_LOG_LEVEL = 1, + WMI_TLV_DBGLOG_PARAM_VDEV_ENABLE, + WMI_TLV_DBGLOG_PARAM_VDEV_DISABLE, + WMI_TLV_DBGLOG_PARAM_VDEV_ENABLE_BITMAP, + WMI_TLV_DBGLOG_PARAM_VDEV_DISABLE_BITMAP, +}; + +enum wmi_tlv_dbglog_log_level { + WMI_TLV_DBGLOG_LOG_LEVEL_VERBOSE = 0, + WMI_TLV_DBGLOG_LOG_LEVEL_INFO, + WMI_TLV_DBGLOG_LOG_LEVEL_INFO_LVL_1, + WMI_TLV_DBGLOG_LOG_LEVEL_INFO_LVL_2, + WMI_TLV_DBGLOG_LOG_LEVEL_WARN, + WMI_TLV_DBGLOG_LOG_LEVEL_ERR, +}; + +#define WMI_TLV_DBGLOG_BITMAP_MAX_IDS 512 +#define WMI_TLV_DBGLOG_BITMAP_MAX_WORDS (WMI_TLV_DBGLOG_BITMAP_MAX_IDS / \ + sizeof(__le32)) +#define WMI_TLV_DBGLOG_ALL_MODULES 0xffff +#define WMI_TLV_DBGLOG_LOG_LEVEL_VALUE(module_id, log_level) \ + (((module_id << 16) & 0xffff0000) | \ + ((log_level << 0) & 0x000000ff)) + +struct wmi_tlv_dbglog_cmd { + __le32 param; + __le32 value; +} __packed; + +struct wmi_tlv_resume_cmd { + __le32 reserved; +} __packed; + +struct wmi_tlv_req_stats_cmd { + __le32 stats_id; /* wmi_stats_id */ + __le32 vdev_id; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_tlv_vdev_stats { + __le32 vdev_id; + __le32 beacon_snr; + __le32 data_snr; + __le32 num_tx_frames[4]; /* per-AC */ + __le32 num_rx_frames; + __le32 num_tx_frames_retries[4]; + __le32 num_tx_frames_failures[4]; + __le32 num_rts_fail; + __le32 num_rts_success; + __le32 num_rx_err; + __le32 num_rx_discard; + __le32 num_tx_not_acked; + __le32 tx_rate_history[10]; + __le32 beacon_rssi_history[10]; +} __packed; + +struct wmi_tlv_pktlog_enable { + __le32 reserved; + __le32 filter; +} __packed; + +struct wmi_tlv_pktlog_disable { + __le32 reserved; +} __packed; + +void ath10k_wmi_tlv_attach(struct ath10k *ar); + +#endif diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index cc0c9e0..69f7935 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -22,6 +22,7 @@ #include "htc.h" #include "debug.h" #include "wmi.h" +#include "wmi-tlv.h" #include "mac.h" #include "testmode.h" #include "wmi-ops.h" @@ -2735,13 +2736,14 @@ void ath10k_wmi_event_service_ready(struct ath10k *ar, struct sk_buff *skb) } ath10k_dbg(ar, ATH10K_DBG_WMI, - "wmi event service ready min_tx_power 0x%08x max_tx_power 0x%08x ht_cap 0x%08x vht_cap 0x%08x sw_ver0 0x%08x sw_ver1 0x%08x phy_capab 0x%08x num_rf_chains 0x%08x eeprom_rd 0x%08x num_mem_reqs 0x%08x\n", + "wmi event service ready min_tx_power 0x%08x max_tx_power 0x%08x ht_cap 0x%08x vht_cap 0x%08x sw_ver0 0x%08x sw_ver1 0x%08x fw_build 0x%08x phy_capab 0x%08x num_rf_chains 0x%08x eeprom_rd 0x%08x num_mem_reqs 0x%08x\n", __le32_to_cpu(arg.min_tx_power), __le32_to_cpu(arg.max_tx_power), __le32_to_cpu(arg.ht_cap), __le32_to_cpu(arg.vht_cap), __le32_to_cpu(arg.sw_ver0), __le32_to_cpu(arg.sw_ver1), + __le32_to_cpu(arg.fw_build), __le32_to_cpu(arg.phy_capab), __le32_to_cpu(arg.num_rf_chains), __le32_to_cpu(arg.eeprom_rd), @@ -4762,6 +4764,9 @@ int ath10k_wmi_attach(struct ath10k *ar) ar->wmi.vdev_param = &wmi_vdev_param_map; ar->wmi.pdev_param = &wmi_pdev_param_map; break; + case ATH10K_FW_WMI_OP_VERSION_TLV: + ath10k_wmi_tlv_attach(ar); + break; case ATH10K_FW_WMI_OP_VERSION_UNSET: case ATH10K_FW_WMI_OP_VERSION_MAX: ath10k_err(ar, "unsupported WMI op version: %d\n", diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 032a8bd..7d091a7 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -109,6 +109,45 @@ enum wmi_service { WMI_SERVICE_BURST, WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT, WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT, + WMI_SERVICE_ROAM_SCAN_OFFLOAD, + WMI_SERVICE_AP_PS_DETECT_OUT_OF_SYNC, + WMI_SERVICE_EARLY_RX, + WMI_SERVICE_STA_SMPS, + WMI_SERVICE_FWTEST, + WMI_SERVICE_STA_WMMAC, + WMI_SERVICE_TDLS, + WMI_SERVICE_MCC_BCN_INTERVAL_CHANGE, + WMI_SERVICE_ADAPTIVE_OCS, + WMI_SERVICE_BA_SSN_SUPPORT, + WMI_SERVICE_FILTER_IPSEC_NATKEEPALIVE, + WMI_SERVICE_WLAN_HB, + WMI_SERVICE_LTE_ANT_SHARE_SUPPORT, + WMI_SERVICE_BATCH_SCAN, + WMI_SERVICE_QPOWER, + WMI_SERVICE_PLMREQ, + WMI_SERVICE_THERMAL_MGMT, + WMI_SERVICE_RMC, + WMI_SERVICE_MHF_OFFLOAD, + WMI_SERVICE_COEX_SAR, + WMI_SERVICE_BCN_TXRATE_OVERRIDE, + WMI_SERVICE_NAN, + WMI_SERVICE_L1SS_STAT, + WMI_SERVICE_ESTIMATE_LINKSPEED, + WMI_SERVICE_OBSS_SCAN, + WMI_SERVICE_TDLS_OFFCHAN, + WMI_SERVICE_TDLS_UAPSD_BUFFER_STA, + WMI_SERVICE_TDLS_UAPSD_SLEEP_STA, + WMI_SERVICE_IBSS_PWRSAVE, + WMI_SERVICE_LPASS, + WMI_SERVICE_EXTSCAN, + WMI_SERVICE_D0WOW, + WMI_SERVICE_HSOFFLOAD, + WMI_SERVICE_ROAM_HO_OFFLOAD, + WMI_SERVICE_RX_FULL_REORDER, + WMI_SERVICE_DHCP_OFFLOAD, + WMI_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT, + WMI_SERVICE_MDNS_OFFLOAD, + WMI_SERVICE_SAP_AUTH_OFFLOAD, /* keep last */ WMI_SERVICE_MAX, @@ -215,6 +254,45 @@ static inline char *wmi_service_name(int service_id) SVCSTR(WMI_SERVICE_BURST); SVCSTR(WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT); SVCSTR(WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT); + SVCSTR(WMI_SERVICE_ROAM_SCAN_OFFLOAD); + SVCSTR(WMI_SERVICE_AP_PS_DETECT_OUT_OF_SYNC); + SVCSTR(WMI_SERVICE_EARLY_RX); + SVCSTR(WMI_SERVICE_STA_SMPS); + SVCSTR(WMI_SERVICE_FWTEST); + SVCSTR(WMI_SERVICE_STA_WMMAC); + SVCSTR(WMI_SERVICE_TDLS); + SVCSTR(WMI_SERVICE_MCC_BCN_INTERVAL_CHANGE); + SVCSTR(WMI_SERVICE_ADAPTIVE_OCS); + SVCSTR(WMI_SERVICE_BA_SSN_SUPPORT); + SVCSTR(WMI_SERVICE_FILTER_IPSEC_NATKEEPALIVE); + SVCSTR(WMI_SERVICE_WLAN_HB); + SVCSTR(WMI_SERVICE_LTE_ANT_SHARE_SUPPORT); + SVCSTR(WMI_SERVICE_BATCH_SCAN); + SVCSTR(WMI_SERVICE_QPOWER); + SVCSTR(WMI_SERVICE_PLMREQ); + SVCSTR(WMI_SERVICE_THERMAL_MGMT); + SVCSTR(WMI_SERVICE_RMC); + SVCSTR(WMI_SERVICE_MHF_OFFLOAD); + SVCSTR(WMI_SERVICE_COEX_SAR); + SVCSTR(WMI_SERVICE_BCN_TXRATE_OVERRIDE); + SVCSTR(WMI_SERVICE_NAN); + SVCSTR(WMI_SERVICE_L1SS_STAT); + SVCSTR(WMI_SERVICE_ESTIMATE_LINKSPEED); + SVCSTR(WMI_SERVICE_OBSS_SCAN); + SVCSTR(WMI_SERVICE_TDLS_OFFCHAN); + SVCSTR(WMI_SERVICE_TDLS_UAPSD_BUFFER_STA); + SVCSTR(WMI_SERVICE_TDLS_UAPSD_SLEEP_STA); + SVCSTR(WMI_SERVICE_IBSS_PWRSAVE); + SVCSTR(WMI_SERVICE_LPASS); + SVCSTR(WMI_SERVICE_EXTSCAN); + SVCSTR(WMI_SERVICE_D0WOW); + SVCSTR(WMI_SERVICE_HSOFFLOAD); + SVCSTR(WMI_SERVICE_ROAM_HO_OFFLOAD); + SVCSTR(WMI_SERVICE_RX_FULL_REORDER); + SVCSTR(WMI_SERVICE_DHCP_OFFLOAD); + SVCSTR(WMI_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT); + SVCSTR(WMI_SERVICE_MDNS_OFFLOAD); + SVCSTR(WMI_SERVICE_SAP_AUTH_OFFLOAD); default: return NULL; } @@ -4626,6 +4704,7 @@ struct wmi_svc_rdy_ev_arg { __le32 vht_cap; __le32 sw_ver0; __le32 sw_ver1; + __le32 fw_build; __le32 phy_capab; __le32 num_rf_chains; __le32 eeprom_rd; -- cgit v0.10.2 From b43bf97ef786aed3daae4c63a6abb50ea913802e Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Tue, 2 Dec 2014 13:06:53 +0200 Subject: ath10k: add new pdev parameters for fw 10.2 New pdev paramters have been added to firmware 10.2, hence update wmi interfaces to sync with. Signed-off-by: Peter Oh Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 7d091a7..4b31da5 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -2793,6 +2793,9 @@ enum wmi_10x_pdev_param { WMI_10X_PDEV_PARAM_SET_MCAST2UCAST_MODE, WMI_10X_PDEV_PARAM_SET_MCAST2UCAST_BUFFER, WMI_10X_PDEV_PARAM_REMOVE_MCAST2UCAST_BUFFER, + WMI_10X_PDEV_PARAM_PEER_STA_PS_STATECHG_ENABLE, + WMI_10X_PDEV_PARAM_RTS_FIXED_RATE, + WMI_10X_PDEV_PARAM_CAL_PERIOD }; struct wmi_pdev_set_param_cmd { -- cgit v0.10.2 From a7bd3e9901634b1185eb91c3f986d64cb3506e19 Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Tue, 2 Dec 2014 13:07:14 +0200 Subject: ath10k: add new wmi interface of NF cal period Introduce a new wmi interface controls noise floor (NF) calibration period via debugfs as firmware has introduced it on v10.2. It allows users to modify frequency of NF calibration in millisecond and changes RSSI reporting frequency consequently. Short calibration period will trigger more frequent NF calibration, so that RSSI reported in receive frames is more realistic. Till now calibration was done at 30 seconds. Signed-off-by: Peter Oh Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 3f15c13..613355c 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -327,6 +327,7 @@ struct ath10k_debug { u32 fw_dbglog_mask; u32 pktlog_filter; u32 reg_addr; + u32 nf_cal_period; u8 htt_max_amsdu; u8 htt_max_ampdu; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index c15b5774..70a9599 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -1608,6 +1608,73 @@ static const struct file_operations fops_cal_data = { .llseek = default_llseek, }; +static ssize_t ath10k_read_nf_cal_period(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned int len; + char buf[32]; + + len = scnprintf(buf, sizeof(buf), "%d\n", + ar->debug.nf_cal_period); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath10k_write_nf_cal_period(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned long period; + int ret; + + ret = kstrtoul_from_user(user_buf, count, 0, &period); + if (ret) + return ret; + + if (period > WMI_PDEV_PARAM_CAL_PERIOD_MAX) + return -EINVAL; + + /* there's no way to switch back to the firmware default */ + if (period == 0) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + ar->debug.nf_cal_period = period; + + if (ar->state != ATH10K_STATE_ON) { + /* firmware is not running, nothing else to do */ + ret = count; + goto exit; + } + + ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->cal_period, + ar->debug.nf_cal_period); + if (ret) { + ath10k_warn(ar, "cal period cfg failed from debugfs: %d\n", + ret); + goto exit; + } + + ret = count; + +exit: + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static const struct file_operations fops_nf_cal_period = { + .read = ath10k_read_nf_cal_period, + .write = ath10k_write_nf_cal_period, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath10k_debug_start(struct ath10k *ar) { int ret; @@ -1643,6 +1710,16 @@ int ath10k_debug_start(struct ath10k *ar) ath10k_warn(ar, "failed to disable pktlog: %d\n", ret); } + if (ar->debug.nf_cal_period) { + ret = ath10k_wmi_pdev_set_param(ar, + ar->wmi.pdev_param->cal_period, + ar->debug.nf_cal_period); + if (ret) + /* not serious */ + ath10k_warn(ar, "cal period cfg failed from debug start: %d\n", + ret); + } + return ret; } @@ -1881,6 +1958,9 @@ int ath10k_debug_register(struct ath10k *ar) debugfs_create_file("cal_data", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_cal_data); + debugfs_create_file("nf_cal_period", S_IRUSR | S_IWUSR, + ar->debug.debugfs_phy, ar, &fops_nf_cal_period); + if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED)) { debugfs_create_file("dfs_simulate_radar", S_IWUSR, ar->debug.debugfs_phy, ar, diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index b21048c..1627ec5 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -2096,6 +2096,7 @@ static struct wmi_pdev_param_map wmi_tlv_pdev_param_map = { .fast_channel_reset = WMI_TLV_PDEV_PARAM_UNSUPPORTED, .burst_dur = WMI_TLV_PDEV_PARAM_BURST_DUR, .burst_enable = WMI_TLV_PDEV_PARAM_BURST_ENABLE, + .cal_period = WMI_PDEV_PARAM_UNSUPPORTED, }; static struct wmi_vdev_param_map wmi_tlv_vdev_param_map = { diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 69f7935..fa486f6 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -436,6 +436,7 @@ static struct wmi_pdev_param_map wmi_pdev_param_map = { .fast_channel_reset = WMI_PDEV_PARAM_UNSUPPORTED, .burst_dur = WMI_PDEV_PARAM_UNSUPPORTED, .burst_enable = WMI_PDEV_PARAM_UNSUPPORTED, + .cal_period = WMI_PDEV_PARAM_UNSUPPORTED, }; static struct wmi_pdev_param_map wmi_10x_pdev_param_map = { @@ -488,6 +489,7 @@ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = { .fast_channel_reset = WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET, .burst_dur = WMI_10X_PDEV_PARAM_BURST_DUR, .burst_enable = WMI_10X_PDEV_PARAM_BURST_ENABLE, + .cal_period = WMI_10X_PDEV_PARAM_CAL_PERIOD, }; /* firmware 10.2 specific mappings */ diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 4b31da5..97f902f 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -2583,6 +2583,7 @@ struct wmi_pdev_param_map { u32 fast_channel_reset; u32 burst_dur; u32 burst_enable; + u32 cal_period; }; #define WMI_PDEV_PARAM_UNSUPPORTED 0 @@ -2803,6 +2804,9 @@ struct wmi_pdev_set_param_cmd { __le32 param_value; } __packed; +/* valid period is 1 ~ 60000ms, unit in millisecond */ +#define WMI_PDEV_PARAM_CAL_PERIOD_MAX 60000 + struct wmi_pdev_get_tpc_config_cmd { /* parameter */ __le32 param; -- cgit v0.10.2 From 5cb2f03c67265922c66e20270d4fd669a661da82 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 9 Dec 2014 21:19:51 +0800 Subject: regulator: rk808: Fix sparse non static symbol warnings Fixes the following sparse warnings: drivers/regulator/rk808-regulator.c:100:5: warning: symbol 'rk808_set_suspend_voltage' was not declared. Should it be static? drivers/regulator/rk808-regulator.c:115:5: warning: symbol 'rk808_set_suspend_enable' was not declared. Should it be static? drivers/regulator/rk808-regulator.c:126:5: warning: symbol 'rk808_set_suspend_disable' was not declared. Should it be static? Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c index 8d1dc48..33042eb 100644 --- a/drivers/regulator/rk808-regulator.c +++ b/drivers/regulator/rk808-regulator.c @@ -97,7 +97,7 @@ static int rk808_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) RK808_RAMP_RATE_MASK, ramp_value); } -int rk808_set_suspend_voltage(struct regulator_dev *rdev, int uv) +static int rk808_set_suspend_voltage(struct regulator_dev *rdev, int uv) { unsigned int reg; int sel = regulator_map_voltage_linear_range(rdev, uv, uv); @@ -112,7 +112,7 @@ int rk808_set_suspend_voltage(struct regulator_dev *rdev, int uv) sel); } -int rk808_set_suspend_enable(struct regulator_dev *rdev) +static int rk808_set_suspend_enable(struct regulator_dev *rdev) { unsigned int reg; @@ -123,7 +123,7 @@ int rk808_set_suspend_enable(struct regulator_dev *rdev) 0); } -int rk808_set_suspend_disable(struct regulator_dev *rdev) +static int rk808_set_suspend_disable(struct regulator_dev *rdev) { unsigned int reg; -- cgit v0.10.2 From 5424d43e4daddf71aaa60d6565c8816af9150adf Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 10 Dec 2014 17:40:53 +0000 Subject: spi: Move queue data structure initialisation to main master init Since most devices now do use the standard queue and in order to avoid initialisation ordering issues being introduced by further refactorings to improve performance move the initialisation of the queue and the lock for it to the main master allocation. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index da7e622..b81ccdb 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -989,9 +989,6 @@ static int spi_init_queue(struct spi_master *master) { struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; - INIT_LIST_HEAD(&master->queue); - spin_lock_init(&master->queue_lock); - master->running = false; master->busy = false; @@ -1595,6 +1592,8 @@ int spi_register_master(struct spi_master *master) dynamic = 1; } + INIT_LIST_HEAD(&master->queue); + spin_lock_init(&master->queue_lock); spin_lock_init(&master->bus_lock_spinlock); mutex_init(&master->bus_lock_mutex); master->bus_lock_flag = 0; -- cgit v0.10.2 From 983aee5d7090cf12b624f18533777caa09d067b1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 9 Dec 2014 19:46:56 +0000 Subject: spi: Check to see if the device is processing a message before we idle cur_msg is updated under the queue lock and holds the message we are currently processing. Since currently we only ever do removals in the pump kthread it doesn't matter in what order we do things but we want to be able to push things out from the submitting thread so pull the check to see if we're currently handling a message before we check to see if the queue is idle. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b81ccdb..0bc752d 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -891,8 +891,16 @@ static void spi_pump_messages(struct kthread_work *work) bool was_busy = false; int ret; - /* Lock queue and check for queue work */ + /* Lock queue */ spin_lock_irqsave(&master->queue_lock, flags); + + /* Make sure we are not already running a message */ + if (master->cur_msg) { + spin_unlock_irqrestore(&master->queue_lock, flags); + return; + } + + /* Check if the queue is idle */ if (list_empty(&master->queue) || !master->running) { if (!master->busy) { spin_unlock_irqrestore(&master->queue_lock, flags); @@ -916,11 +924,6 @@ static void spi_pump_messages(struct kthread_work *work) return; } - /* Make sure we are not already running a message */ - if (master->cur_msg) { - spin_unlock_irqrestore(&master->queue_lock, flags); - return; - } /* Extract head of queue */ master->cur_msg = list_first_entry(&master->queue, struct spi_message, queue); -- cgit v0.10.2 From 0461a4149836c792d186027c8c859637a4cfb11a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 9 Dec 2014 21:38:05 +0000 Subject: spi: Pump transfers inside calling context for spi_sync() If we are using the standard SPI message pump (which all drivers should be transitioning over to) then special case the message enqueue and instead of starting the worker thread to push messages to the hardware do so in the context of the caller if the controller is idle. This avoids a context switch in the common case where the controller has a single user in a single thread, for short PIO transfers there may be no need to context switch away from the calling context to complete the transfer. The code is a bit more complex than is desirable in part due to the need to handle drivers not using the standard queue and in part due to handling the various combinations of bus locking and asynchronous submission in interrupt context. It is still suboptimal since it will still wake the message pump for each transfer in order to schedule idling of the hardware and if multiple contexts are using the controller simultaneously a caller may end up pumping a message for some random other thread rather than for itself, and if the thread ends up deferring due to another context idling the hardware then it will just busy wait. It can, however, have the benefit of aggregating power up and down of the hardware when a caller performs a series of transfers back to back without any need for the use of spi_async(). Signed-off-by: Mark Brown diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 0bc752d..e1bf257 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -882,6 +882,9 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_transfer); * needs processing and if so call out to the driver to initialize hardware * and transfer each message. * + * Note that it is called both from the kthread itself and also from + * inside spi_sync(); the queue extraction handling at the top of the + * function should deal with this safely. */ static void spi_pump_messages(struct kthread_work *work) { @@ -900,6 +903,13 @@ static void spi_pump_messages(struct kthread_work *work) return; } + /* If another context is idling the device then defer */ + if (master->idling) { + queue_kthread_work(&master->kworker, &master->pump_messages); + spin_unlock_irqrestore(&master->queue_lock, flags); + return; + } + /* Check if the queue is idle */ if (list_empty(&master->queue) || !master->running) { if (!master->busy) { @@ -907,7 +917,9 @@ static void spi_pump_messages(struct kthread_work *work) return; } master->busy = false; + master->idling = true; spin_unlock_irqrestore(&master->queue_lock, flags); + kfree(master->dummy_rx); master->dummy_rx = NULL; kfree(master->dummy_tx); @@ -921,6 +933,10 @@ static void spi_pump_messages(struct kthread_work *work) pm_runtime_put_autosuspend(master->dev.parent); } trace_spi_master_idle(master); + + spin_lock_irqsave(&master->queue_lock, flags); + master->idling = false; + spin_unlock_irqrestore(&master->queue_lock, flags); return; } @@ -1161,12 +1177,9 @@ static int spi_destroy_queue(struct spi_master *master) return 0; } -/** - * spi_queued_transfer - transfer function for queued transfers - * @spi: spi device which is requesting transfer - * @msg: spi message which is to handled is queued to driver queue - */ -static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg) +static int __spi_queued_transfer(struct spi_device *spi, + struct spi_message *msg, + bool need_pump) { struct spi_master *master = spi->master; unsigned long flags; @@ -1181,13 +1194,23 @@ static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg) msg->status = -EINPROGRESS; list_add_tail(&msg->queue, &master->queue); - if (!master->busy) + if (!master->busy && need_pump) queue_kthread_work(&master->kworker, &master->pump_messages); spin_unlock_irqrestore(&master->queue_lock, flags); return 0; } +/** + * spi_queued_transfer - transfer function for queued transfers + * @spi: spi device which is requesting transfer + * @msg: spi message which is to handled is queued to driver queue + */ +static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg) +{ + return __spi_queued_transfer(spi, msg, true); +} + static int spi_master_initialize_queue(struct spi_master *master) { int ret; @@ -2102,19 +2125,46 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, DECLARE_COMPLETION_ONSTACK(done); int status; struct spi_master *master = spi->master; + unsigned long flags; + + status = __spi_validate(spi, message); + if (status != 0) + return status; message->complete = spi_complete; message->context = &done; + message->spi = spi; if (!bus_locked) mutex_lock(&master->bus_lock_mutex); - status = spi_async_locked(spi, message); + /* If we're not using the legacy transfer method then we will + * try to transfer in the calling context so special case. + * This code would be less tricky if we could remove the + * support for driver implemented message queues. + */ + if (master->transfer == spi_queued_transfer) { + spin_lock_irqsave(&master->bus_lock_spinlock, flags); + + trace_spi_message_submit(message); + + status = __spi_queued_transfer(spi, message, false); + + spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); + } else { + status = spi_async_locked(spi, message); + } if (!bus_locked) mutex_unlock(&master->bus_lock_mutex); if (status == 0) { + /* Push out the messages in the calling context if we + * can. + */ + if (master->transfer == spi_queued_transfer) + spi_pump_messages(&master->pump_messages); + wait_for_completion(&done); status = message->status; } diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index a6ef2a8..4e6db75 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -260,6 +260,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @pump_messages: work struct for scheduling work to the message pump * @queue_lock: spinlock to syncronise access to message queue * @queue: message queue + * @idling: the device is entering idle state * @cur_msg: the currently in-flight message * @cur_msg_prepared: spi_prepare_message was called for the currently * in-flight message @@ -425,6 +426,7 @@ struct spi_master { spinlock_t queue_lock; struct list_head queue; struct spi_message *cur_msg; + bool idling; bool busy; bool running; bool rt; -- cgit v0.10.2 From fc9e0f71f2d7ea43fa3ba0bea68586d1462cb5a7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 10 Dec 2014 13:46:33 +0000 Subject: spi: Only idle the message pump in the worker kthread In order to avoid the situation where the kthread is waiting for another context to make the hardware idle let the message pump know if it's being called from the worker thread context and if it isn't then defer to the worker thread instead of idling the hardware immediately. This will ensure that if this situation happens we block rather than busy waiting. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index e1bf257..3ac188f 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -875,8 +875,9 @@ void spi_finalize_current_transfer(struct spi_master *master) EXPORT_SYMBOL_GPL(spi_finalize_current_transfer); /** - * spi_pump_messages - kthread work function which processes spi message queue - * @work: pointer to kthread work struct contained in the master struct + * __spi_pump_messages - function which processes spi message queue + * @master: master to process queue for + * @in_kthread: true if we are in the context of the message pump thread * * This function checks if there is any spi message in the queue that * needs processing and if so call out to the driver to initialize hardware @@ -886,10 +887,8 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_transfer); * inside spi_sync(); the queue extraction handling at the top of the * function should deal with this safely. */ -static void spi_pump_messages(struct kthread_work *work) +static void __spi_pump_messages(struct spi_master *master, bool in_kthread) { - struct spi_master *master = - container_of(work, struct spi_master, pump_messages); unsigned long flags; bool was_busy = false; int ret; @@ -916,6 +915,15 @@ static void spi_pump_messages(struct kthread_work *work) spin_unlock_irqrestore(&master->queue_lock, flags); return; } + + /* Only do teardown in the thread */ + if (!in_kthread) { + queue_kthread_work(&master->kworker, + &master->pump_messages); + spin_unlock_irqrestore(&master->queue_lock, flags); + return; + } + master->busy = false; master->idling = true; spin_unlock_irqrestore(&master->queue_lock, flags); @@ -1004,6 +1012,18 @@ static void spi_pump_messages(struct kthread_work *work) } } +/** + * spi_pump_messages - kthread work function which processes spi message queue + * @work: pointer to kthread work struct contained in the master struct + */ +static void spi_pump_messages(struct kthread_work *work) +{ + struct spi_master *master = + container_of(work, struct spi_master, pump_messages); + + __spi_pump_messages(master, true); +} + static int spi_init_queue(struct spi_master *master) { struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; @@ -2163,7 +2183,7 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, * can. */ if (master->transfer == spi_queued_transfer) - spi_pump_messages(&master->pump_messages); + __spi_pump_messages(master, false); wait_for_completion(&done); status = message->status; -- cgit v0.10.2 From 7f6d62a2adc6c3a8eb7bbdceea2630634eb2d535 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 9 Dec 2014 22:18:03 +0000 Subject: spi/falcon: Remove hardware prepare and unprepare functions They are completely empty and therefore serve no function. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c index f73b300..973ded3 100644 --- a/drivers/spi/spi-falcon.c +++ b/drivers/spi/spi-falcon.c @@ -353,16 +353,6 @@ static int falcon_sflash_setup(struct spi_device *spi) return 0; } -static int falcon_sflash_prepare_xfer(struct spi_master *master) -{ - return 0; -} - -static int falcon_sflash_unprepare_xfer(struct spi_master *master) -{ - return 0; -} - static int falcon_sflash_xfer_one(struct spi_master *master, struct spi_message *m) { @@ -420,9 +410,7 @@ static int falcon_sflash_probe(struct platform_device *pdev) master->mode_bits = SPI_MODE_3; master->flags = SPI_MASTER_HALF_DUPLEX; master->setup = falcon_sflash_setup; - master->prepare_transfer_hardware = falcon_sflash_prepare_xfer; master->transfer_one_message = falcon_sflash_xfer_one; - master->unprepare_transfer_hardware = falcon_sflash_unprepare_xfer; master->dev.of_node = pdev->dev.of_node; ret = devm_spi_register_master(&pdev->dev, master); -- cgit v0.10.2 From cec3f0ed7de78c02ac4bc719a95915367d68c778 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 12 Dec 2014 12:26:25 +0100 Subject: cfg80211: use __force __rcu to suppress sparse warning The code assigns a constant value (a pointer to a static variable) to an RCU pointer, which results in a sparse warning: reg.c:112:10: warning: cast adds address space to expression () Suppress this warning by using __force. Signed-off-by: Johannes Berg diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 47be616..d83480b6 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -109,7 +109,7 @@ static struct regulatory_request core_request_world = { * protected by RTNL (and can be accessed with RCU protection) */ static struct regulatory_request __rcu *last_request = - (void __rcu *)&core_request_world; + (void __force __rcu *)&core_request_world; /* To trigger userspace events */ static struct platform_device *reg_pdev; -- cgit v0.10.2 From ba1debdfed974f25aa598c283567878657b292ee Mon Sep 17 00:00:00 2001 From: Vadim Kochan Date: Wed, 10 Dec 2014 00:21:06 +0200 Subject: wireless: Support of IFLA_INFO_KIND rtnl attribute It allows to identify the wlan kind of device for the user application, e.g.: # ip -d link 1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0 2: enp0s25: mtu 1500 qdisc pfifo_fast state DOWN mode DEFAULT group default qlen 1000 link/ether XX:XX:XX:XX:XX:XX brd ff:ff:ff:ff:ff:ff promiscuity 0 3: wlp3s0: mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000 link/ether XX:XX:XX:XX:XX:XX brd ff:ff:ff:ff:ff:ff promiscuity 0 wlan Signed-off-by: Vadim Kochan Acked-by: Marcel Holtmann [make wireless_link_ops const] Signed-off-by: Johannes Berg diff --git a/net/wireless/core.c b/net/wireless/core.c index 53dda77..4910758 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "nl80211.h" #include "core.h" #include "sysfs.h" @@ -937,6 +938,10 @@ void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev, } EXPORT_SYMBOL(cfg80211_stop_iface); +static const struct rtnl_link_ops wireless_link_ops = { + .kind = "wlan", +}; + static int cfg80211_netdev_notifier_call(struct notifier_block *nb, unsigned long state, void *ptr) { @@ -954,6 +959,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, switch (state) { case NETDEV_POST_INIT: SET_NETDEV_DEVTYPE(dev, &wiphy_type); + dev->rtnl_link_ops = &wireless_link_ops; break; case NETDEV_REGISTER: /* -- cgit v0.10.2 From 6b127c71fbdd3daacfd8b9f80b8e6ebfb70a889e Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 10 Dec 2014 21:26:10 +0530 Subject: mac80211: Move IEEE80211_TX_CTL_PS_RESPONSE Move IEEE80211_TX_CTL_PS_RESPONSE to info->control.flags since this is used only in the TX path (by ath9k). This frees up a bit which can be used for other purposes. Signed-off-by: Sujith Manoharan Signed-off-by: Johannes Berg diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index e9bd02c..4caee66 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2259,7 +2259,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath_txq *txq = txctl->txq; struct ath_atx_tid *tid = NULL; struct ath_buf *bf; - bool queue, skip_uapsd = false; + bool queue, skip_uapsd = false, ps_resp; int q, ret; if (vif) @@ -2268,6 +2268,8 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) txctl->force_channel = true; + ps_resp = !!(info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE); + ret = ath_tx_prepare(hw, skb, txctl); if (ret) return ret; @@ -2310,7 +2312,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, if (txctl->an && queue) tid = ath_get_skb_tid(sc, txctl->an, skb); - if (!skip_uapsd && (info->flags & IEEE80211_TX_CTL_PS_RESPONSE)) { + if (!skip_uapsd && ps_resp) { ath_txq_unlock(sc, txq); txq = sc->tx.uapsdq; ath_txq_lock(sc, txq); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 58d719d..b36e60d 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -505,8 +505,6 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_CTL_DONTFRAG: Don't fragment this packet even if it * would be fragmented by size (this is optional, only used for * monitor injection). - * @IEEE80211_TX_CTL_PS_RESPONSE: This frame is a response to a poll - * frame (PS-Poll or uAPSD). * * Note: If you have to add new flags to the enumeration, then don't * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary. @@ -542,7 +540,6 @@ enum mac80211_tx_info_flags { IEEE80211_TX_STATUS_EOSP = BIT(28), IEEE80211_TX_CTL_USE_MINRATE = BIT(29), IEEE80211_TX_CTL_DONTFRAG = BIT(30), - IEEE80211_TX_CTL_PS_RESPONSE = BIT(31), }; #define IEEE80211_TX_CTL_STBC_SHIFT 23 @@ -552,11 +549,14 @@ enum mac80211_tx_info_flags { * * @IEEE80211_TX_CTRL_PORT_CTRL_PROTO: this frame is a port control * protocol frame (e.g. EAP) + * @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll + * frame (PS-Poll or uAPSD). * * These flags are used in tx_info->control.flags. */ enum mac80211_tx_control_flags { IEEE80211_TX_CTRL_PORT_CTRL_PROTO = BIT(0), + IEEE80211_TX_CTRL_PS_RESPONSE = BIT(1), }; /* diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index a42f5b2..db8b07a 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1243,10 +1243,11 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, * ends the poll/service period. */ info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER | - IEEE80211_TX_CTL_PS_RESPONSE | IEEE80211_TX_STATUS_EOSP | IEEE80211_TX_CTL_REQ_TX_STATUS; + info->control.flags |= IEEE80211_TX_CTRL_PS_RESPONSE; + if (call_driver) drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false); @@ -1395,8 +1396,8 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, * STA may still remain is PS mode after this frame * exchange. */ - info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER | - IEEE80211_TX_CTL_PS_RESPONSE; + info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER; + info->control.flags |= IEEE80211_TX_CTRL_PS_RESPONSE; /* * Use MoreData flag to indicate whether there are -- cgit v0.10.2 From 5cf16616e152dd5c274a65954c77f64892d025a8 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 10 Dec 2014 21:26:11 +0530 Subject: mac80211: Fix accounting of multicast frames Since multicast frames are marked as no-ack, using IEEE80211_TX_STAT_ACK to check if they have been successfully transmitted by the driver is incorrect since a driver can choose to ignore transmission status for no-ack frames. This results in incorrect accounting for such frames. To fix this issue, this patch introduces a new flag that can be used by drivers to indicate error-free transmission of no-ack frames. Signed-off-by: Sujith Manoharan [add a note about not setting the flag for non-no-ack frames] Signed-off-by: Johannes Berg diff --git a/include/net/mac80211.h b/include/net/mac80211.h index b36e60d..b24ef57 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -505,6 +505,11 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_CTL_DONTFRAG: Don't fragment this packet even if it * would be fragmented by size (this is optional, only used for * monitor injection). + * @IEEE80211_TX_STAT_NOACK_TRANSMITTED: A frame that was marked with + * IEEE80211_TX_CTL_NO_ACK has been successfully transmitted without + * any errors (like issues specific to the driver/HW). + * This flag must not be set for frames that don't request no-ack + * behaviour with IEEE80211_TX_CTL_NO_ACK. * * Note: If you have to add new flags to the enumeration, then don't * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary. @@ -540,6 +545,7 @@ enum mac80211_tx_info_flags { IEEE80211_TX_STATUS_EOSP = BIT(28), IEEE80211_TX_CTL_USE_MINRATE = BIT(29), IEEE80211_TX_CTL_DONTFRAG = BIT(30), + IEEE80211_TX_STAT_NOACK_TRANSMITTED = BIT(31), }; #define IEEE80211_TX_CTL_STBC_SHIFT 23 diff --git a/net/mac80211/status.c b/net/mac80211/status.c index bb146f3..d64037c 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -664,13 +664,15 @@ void ieee80211_tx_status_noskb(struct ieee80211_hw *hw, struct ieee80211_supported_band *sband; int retry_count; int rates_idx; - bool acked; + bool acked, noack_success; rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count); sband = hw->wiphy->bands[info->band]; acked = !!(info->flags & IEEE80211_TX_STAT_ACK); + noack_success = !!(info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED); + if (pubsta) { struct sta_info *sta; @@ -696,7 +698,7 @@ void ieee80211_tx_status_noskb(struct ieee80211_hw *hw, rate_control_tx_status_noskb(local, sband, sta, info); } - if (acked) { + if (acked || noack_success) { local->dot11TransmittedFrameCount++; if (!pubsta) local->dot11MulticastTransmittedFrameCount++; @@ -856,7 +858,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) * Fragments are passed to low-level drivers as separate skbs, so these * are actually fragments, not frames. Update frame counters only for * the first fragment of the frame. */ - if (info->flags & IEEE80211_TX_STAT_ACK) { + if ((info->flags & IEEE80211_TX_STAT_ACK) || + (info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED)) { if (ieee80211_is_first_frag(hdr->seq_ctrl)) { local->dot11TransmittedFrameCount++; if (is_multicast_ether_addr(hdr->addr1)) -- cgit v0.10.2 From 5ede19c58a1ec274b28e03a76b855c3c55ab6f11 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 8 Dec 2014 11:23:11 +0100 Subject: ASoC: wm8750: Make VREF a VMID instead of OUTPUT widget Commit 04489eeb02a4 ("ALSA: wm8750: add missing VREF output") added the previously missing VREF widget. And while adding the widget as OUTPUT widget silences the error of the missing widget it should have rather been a VMID widget. The VREF widget is only used internally as a source for the VREF input of the Out3 Mux. Selecting that input means that the path should be kept powered up at the VREF/VMID voltage, but that there is no actual audio signal. Using a VMID widget will do this, whereas using a OUTPUT widget will simply cause the path to completely power down. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 67653a2..d9e2626 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -323,7 +323,7 @@ static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("ROUT2"), SND_SOC_DAPM_OUTPUT("MONO1"), SND_SOC_DAPM_OUTPUT("OUT3"), - SND_SOC_DAPM_OUTPUT("VREF"), + SND_SOC_DAPM_VMID("VREF"), SND_SOC_DAPM_INPUT("LINPUT1"), SND_SOC_DAPM_INPUT("LINPUT2"), -- cgit v0.10.2 From 4bcc56bb3a9622e877772598d1cac124266b2b8a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Nov 2014 11:23:53 +0100 Subject: mac80211: ask driver to look at power level when starting AP The power level might have been set, but as the interface was idle it might not have taken effect yet. Ask the driver to check the power level when starting up an AP so that in this case the correct power level is used in case the device/driver can only set it when the interface is actually active. Signed-off-by: Johannes Berg diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index e75d5c5..1696658 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -678,7 +678,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON | BSS_CHANGED_SSID | - BSS_CHANGED_P2P_PS; + BSS_CHANGED_P2P_PS | + BSS_CHANGED_TXPOWER; int err; old = sdata_dereference(sdata->u.ap.beacon, sdata); -- cgit v0.10.2 From 848955ccf0bdf42fff33e021a76f6daec98fe59b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 11 Nov 2014 12:48:42 +0100 Subject: mac80211: move U-APSD enablement to vif flags In order to let drivers have more dynamic U-APSD support, move the enablement flag to the virtual interface driver flags. This lets drivers not only set it up differently for different interfaces, but also enable/disable on the fly if needed. Signed-off-by: Johannes Berg diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index c400567..2619db1 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2871,6 +2871,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, int bit; u32 vdev_param; + vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; + mutex_lock(&ar->conf_mutex); memset(arvif, 0, sizeof(*arvif)); @@ -5024,7 +5026,6 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | - IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_MFP_CAPABLE | IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_HAS_RATE_CONTROL | diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c index 3e78cc3..0da6e42 100644 --- a/drivers/net/wireless/cw1200/main.c +++ b/drivers/net/wireless/cw1200/main.c @@ -282,7 +282,6 @@ static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr, IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | IEEE80211_HW_REPORTS_TX_ACK_STATUS | - IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_TX_AMPDU_SETUP_IN_HW | diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c index 5b84664..a1e3237 100644 --- a/drivers/net/wireless/cw1200/sta.c +++ b/drivers/net/wireless/cw1200/sta.c @@ -213,6 +213,7 @@ int cw1200_add_interface(struct ieee80211_hw *dev, /* __le32 auto_calibration_mode = __cpu_to_le32(1); */ vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_UAPSD | IEEE80211_VIF_SUPPORTS_CQM_RSSI; mutex_lock(&priv->conf_mutex); diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 31a5b3f..346331d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -326,6 +326,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC | IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED; hw->rate_control_algorithm = "iwl-mvm-rs"; + hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES; + hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; /* * Enable 11w if advertised by firmware and software crypto @@ -336,13 +338,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) !iwlwifi_mod_params.sw_crypto) hw->flags |= IEEE80211_HW_MFP_CAPABLE; - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT && - !iwlwifi_mod_params.uapsd_disable) { - hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD; - hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES; - hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; - } - if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN || mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) { hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS; @@ -1147,6 +1142,10 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, mvm->bf_allowed_vif = mvmvif; vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | IEEE80211_VIF_SUPPORTS_CQM_RSSI; + if (mvm->fw->ucode_capa.flags & + IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT && + !iwlwifi_mod_params.uapsd_disable) + vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; } /* diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 0b30a7b..d4ba009 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -500,6 +500,7 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw, int ret = 0; vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_UAPSD | IEEE80211_VIF_SUPPORTS_CQM_RSSI; wl1251_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", @@ -1480,9 +1481,7 @@ int wl1251_init_ieee80211(struct wl1251 *wl) /* unit us */ /* FIXME: find a proper value */ - wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_SUPPORTS_PS | - IEEE80211_HW_SUPPORTS_UAPSD; + wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS; wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 6ad3fce..2a99456 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2508,6 +2508,7 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, } vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_UAPSD | IEEE80211_VIF_SUPPORTS_CQM_RSSI; wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", @@ -5776,7 +5777,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | - IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_HAS_RATE_CONTROL | IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_REPORTS_TX_ACK_STATUS | diff --git a/include/net/mac80211.h b/include/net/mac80211.h index b24ef57..4913c00 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1187,10 +1187,15 @@ struct ieee80211_channel_switch { * monitoring on this virtual interface -- i.e. it can monitor * connection quality related parameters, such as the RSSI level and * provide notifications if configured trigger levels are reached. + * @IEEE80211_VIF_SUPPORTS_UAPSD: The device can do U-APSD for this + * interface. This flag should be set during interface addition, + * but may be set/cleared as late as authentication to an AP. It is + * only valid for managed/station mode interfaces. */ enum ieee80211_vif_flags { IEEE80211_VIF_BEACON_FILTER = BIT(0), IEEE80211_VIF_SUPPORTS_CQM_RSSI = BIT(1), + IEEE80211_VIF_SUPPORTS_UAPSD = BIT(2), }; /** @@ -1589,11 +1594,6 @@ struct ieee80211_tx_control { * @IEEE80211_HW_MFP_CAPABLE: * Hardware supports management frame protection (MFP, IEEE 802.11w). * - * @IEEE80211_HW_SUPPORTS_UAPSD: - * Hardware supports Unscheduled Automatic Power Save Delivery - * (U-APSD) in managed mode. The mode is configured with - * conf_tx() operation. - * * @IEEE80211_HW_REPORTS_TX_ACK_STATUS: * Hardware can provide ack status reports of Tx frames to * the stack. @@ -1679,8 +1679,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_MFP_CAPABLE = 1<<13, IEEE80211_HW_WANT_MONITOR_VIF = 1<<14, IEEE80211_HW_NO_AUTO_VIF = 1<<15, - /* free slot */ - IEEE80211_HW_SUPPORTS_UAPSD = 1<<17, + /* free slots */ IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18, IEEE80211_HW_CONNECTION_MONITOR = 1<<19, IEEE80211_HW_QUEUE_CONTROL = 1<<20, @@ -2032,7 +2031,7 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); * enabled whenever user has enabled powersave. * * Driver informs U-APSD client support by enabling - * %IEEE80211_HW_SUPPORTS_UAPSD flag. The mode is configured through the + * %IEEE80211_VIF_SUPPORTS_UAPSD flag. The mode is configured through the * uapsd parameter in conf_tx() operation. Hardware needs to send the QoS * Nullfunc frames and stay awake until the service period has ended. To * utilize U-APSD, dynamic powersave is disabled for voip AC and all frames diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 54a189f..eeb0bbd 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -303,8 +303,6 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf, sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n"); if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE) sf += scnprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n"); - if (local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD) - sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n"); if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) sf += scnprintf(buf + sf, mxln - sf, "REPORTS_TX_ACK_STATUS\n"); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 6ab99da..d9ce336 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -916,10 +916,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) } } - WARN((local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD) - && (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK), - "U-APSD not supported with HW_PS_NULLFUNC_STACK\n"); - /* * Calculate scan IE length -- we need this to alloc * memory and to subtract from the driver limit. It diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 75a9bf5..f495b80 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -4667,8 +4667,13 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; rcu_read_unlock(); + if (WARN((sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD) && + (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK), + "U-APSD not supported with HW_PS_NULLFUNC_STACK\n")) + sdata->vif.driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD; + if (bss->wmm_used && bss->uapsd_supported && - (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) { + (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD)) { assoc_data->uapsd = true; ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED; } else { -- cgit v0.10.2 From f93d1be217d89f258f0108a8cd64b98cb8fa9e73 Mon Sep 17 00:00:00 2001 From: "Wang, Yalin" Date: Mon, 15 Dec 2014 16:05:50 +0800 Subject: regmap: Move spinlock_flags into the union This patch move struct regmap.spinlock_flags into the union of spinlock, so that we can shrink struct regmap size. Signed-off-by: Yalin Wang Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 0da5865..8e94584 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -51,9 +51,11 @@ struct regmap_async { struct regmap { union { struct mutex mutex; - spinlock_t spinlock; + struct { + spinlock_t spinlock; + unsigned long spinlock_flags; + }; }; - unsigned long spinlock_flags; regmap_lock lock; regmap_unlock unlock; void *lock_arg; /* This is passed to lock/unlock functions */ -- cgit v0.10.2 From d91525eb8ee6a622ce476955fe1a2530ade87c83 Mon Sep 17 00:00:00 2001 From: "Chen, Gong" Date: Wed, 10 Dec 2014 13:53:26 -0800 Subject: ACPI, EINJ: Enhance error injection tolerance level Some BIOSes utilize PCI MMCFG space read/write opertion to trigger specific errors. EINJ will report errors as below when hitting such cases: APEI: Can not request [mem 0x83f990a0-0x83f990a3] for APEI EINJ Trigger registers It is because on x86 platform ACPI based PCI MMCFG logic has reserved all MMCFG spaces so that EINJ can't reserve it again. We already trust the ACPI/APEI code when using the EINJ interface so it is not a big leap to also trust it to access the right MMCFG addresses. Skip address checking to allow the access. Signed-off-by: Chen, Gong Signed-off-by: Tony Luck diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index 326198a..676e5e0 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -610,6 +610,32 @@ static int __init pci_parse_mcfg(struct acpi_table_header *header) return 0; } +#ifdef CONFIG_ACPI_APEI +extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size, + void *data), void *data); + +static int pci_mmcfg_for_each_region(int (*func)(__u64 start, __u64 size, + void *data), void *data) +{ + struct pci_mmcfg_region *cfg; + int rc; + + if (list_empty(&pci_mmcfg_list)) + return 0; + + list_for_each_entry(cfg, &pci_mmcfg_list, list) { + rc = func(cfg->res.start, resource_size(&cfg->res), data); + if (rc) + return rc; + } + + return 0; +} +#define set_apei_filter() (arch_apei_filter_addr = pci_mmcfg_for_each_region) +#else +#define set_apei_filter() +#endif + static void __init __pci_mmcfg_init(int early) { pci_mmcfg_reject_broken(early); @@ -644,6 +670,8 @@ void __init pci_mmcfg_early_init(void) else acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); __pci_mmcfg_init(1); + + set_apei_filter(); } } diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index 2cd7bdd..a85ac07 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c @@ -449,7 +449,7 @@ int apei_resources_sub(struct apei_resources *resources1, } EXPORT_SYMBOL_GPL(apei_resources_sub); -static int apei_get_nvs_callback(__u64 start, __u64 size, void *data) +static int apei_get_res_callback(__u64 start, __u64 size, void *data) { struct apei_resources *resources = data; return apei_res_add(&resources->iomem, start, size); @@ -457,7 +457,15 @@ static int apei_get_nvs_callback(__u64 start, __u64 size, void *data) static int apei_get_nvs_resources(struct apei_resources *resources) { - return acpi_nvs_for_each_region(apei_get_nvs_callback, resources); + return acpi_nvs_for_each_region(apei_get_res_callback, resources); +} + +int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size, + void *data), void *data); +static int apei_get_arch_resources(struct apei_resources *resources) + +{ + return arch_apei_filter_addr(apei_get_res_callback, resources); } /* @@ -470,7 +478,7 @@ int apei_resources_request(struct apei_resources *resources, { struct apei_res *res, *res_bak = NULL; struct resource *r; - struct apei_resources nvs_resources; + struct apei_resources nvs_resources, arch_res; int rc; rc = apei_resources_sub(resources, &apei_resources_all); @@ -485,10 +493,20 @@ int apei_resources_request(struct apei_resources *resources, apei_resources_init(&nvs_resources); rc = apei_get_nvs_resources(&nvs_resources); if (rc) - goto res_fini; + goto nvs_res_fini; rc = apei_resources_sub(resources, &nvs_resources); if (rc) - goto res_fini; + goto nvs_res_fini; + + if (arch_apei_filter_addr) { + apei_resources_init(&arch_res); + rc = apei_get_arch_resources(&arch_res); + if (rc) + goto arch_res_fini; + rc = apei_resources_sub(resources, &arch_res); + if (rc) + goto arch_res_fini; + } rc = -EINVAL; list_for_each_entry(res, &resources->iomem, list) { @@ -536,7 +554,9 @@ err_unmap_iomem: break; release_mem_region(res->start, res->end - res->start); } -res_fini: +arch_res_fini: + apei_resources_fini(&arch_res); +nvs_res_fini: apei_resources_fini(&nvs_resources); return rc; } -- cgit v0.10.2 From 1010ba4c5d1cb7e884100e54c0909885dc53a755 Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Fri, 5 Dec 2014 09:16:26 -0800 Subject: ath10k: unregister and remove frag_threshold callback Setting fragmentation threshold has not been supported by any of firmware versions, hence unregister the callback and remove the function. Signed-off-by: Peter Oh Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 5475f0f..fe61201 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3991,29 +3991,6 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) return ret; } -static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) -{ - struct ath10k *ar = hw->priv; - struct ath10k_vif *arvif; - int ret = 0; - - mutex_lock(&ar->conf_mutex); - list_for_each_entry(arvif, &ar->arvifs, list) { - ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d fragmentation threshold %d\n", - arvif->vdev_id, value); - - ret = ath10k_mac_set_frag(arvif, value); - if (ret) { - ath10k_warn(ar, "failed to set fragmentation threshold for vdev %d: %d\n", - arvif->vdev_id, ret); - break; - } - } - mutex_unlock(&ar->conf_mutex); - - return ret; -} - static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop) { @@ -4657,7 +4634,6 @@ static const struct ieee80211_ops ath10k_ops = { .remain_on_channel = ath10k_remain_on_channel, .cancel_remain_on_channel = ath10k_cancel_remain_on_channel, .set_rts_threshold = ath10k_set_rts_threshold, - .set_frag_threshold = ath10k_set_frag_threshold, .flush = ath10k_flush, .tx_last_beacon = ath10k_tx_last_beacon, .set_antenna = ath10k_set_antenna, -- cgit v0.10.2 From 91b12089b18a17cf510f636e29c2cb3a901fe7a5 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 12 Dec 2014 12:41:35 +0100 Subject: ath10k: improve 11b coex Some firmware revisions need peer phymode to be specified as MODE_11B when associating as station to a 11b AP. Otherwise they can starve other stations. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index fe61201..950322d 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1416,6 +1416,12 @@ static void ath10k_peer_assoc_h_qos(struct ath10k *ar, } } +static bool ath10k_mac_sta_has_11g_rates(struct ieee80211_sta *sta) +{ + /* First 4 rates in ath10k_rates are CCK (11b) rates. */ + return sta->supp_rates[IEEE80211_BAND_2GHZ] >> 4; +} + static void ath10k_peer_assoc_h_phymode(struct ath10k *ar, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -1430,8 +1436,10 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar, phymode = MODE_11NG_HT40; else phymode = MODE_11NG_HT20; - } else { + } else if (ath10k_mac_sta_has_11g_rates(sta)) { phymode = MODE_11G; + } else { + phymode = MODE_11B; } break; @@ -4724,6 +4732,9 @@ static const struct ieee80211_channel ath10k_5ghz_channels[] = { CHAN5G(165, 5825, 0), }; +/* Note: Be careful if you re-order these. There is code which depends on this + * ordering. + */ static struct ieee80211_rate ath10k_rates[] = { /* CCK */ RATETAB_ENT(10, 0x82, 0), -- cgit v0.10.2 From 9f9b5746246014890b85a4bfd83b9bbd0f223ac2 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 12 Dec 2014 12:41:36 +0100 Subject: ath10k: fix STA u-APSD To comply with WMM-PS the device shouldn't wake up with a NullFunc frame pair when tx-ing. Instead PM bit on each tx frame should be used. To make this work correctly firmware needs to be told to use a different STA PS wake threshold when u-APSD is enabled. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 950322d..13c2bad 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1048,6 +1048,57 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif, arvif->vdev_id, ret); } +static int ath10k_mac_vif_recalc_ps_wake_threshold(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + u32 param; + u32 value; + int ret; + + lockdep_assert_held(&arvif->ar->conf_mutex); + + if (arvif->u.sta.uapsd) + value = WMI_STA_PS_TX_WAKE_THRESHOLD_NEVER; + else + value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS; + + param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD; + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, value); + if (ret) { + ath10k_warn(ar, "failed to submit ps wake threshold %u on vdev %i: %d\n", + value, arvif->vdev_id, ret); + return ret; + } + + return 0; +} + +static int ath10k_mac_vif_recalc_ps_poll_count(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + u32 param; + u32 value; + int ret; + + lockdep_assert_held(&arvif->ar->conf_mutex); + + if (arvif->u.sta.uapsd) + value = WMI_STA_PS_PSPOLL_COUNT_UAPSD; + else + value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX; + + param = WMI_STA_PS_PARAM_PSPOLL_COUNT; + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + param, value); + if (ret) { + ath10k_warn(ar, "failed to submit ps poll count %u on vdev %i: %d\n", + value, arvif->vdev_id, ret); + return ret; + } + + return 0; +} + /* * Review this when mac80211 gains per-interface powersave support. */ @@ -3036,22 +3087,16 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, goto err_peer_delete; } - param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD; - value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS; - ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, - param, value); + ret = ath10k_mac_vif_recalc_ps_wake_threshold(arvif); if (ret) { - ath10k_warn(ar, "failed to set vdev %i TX wake thresh: %d\n", + ath10k_warn(ar, "failed to recalc ps wake threshold on vdev %i: %d\n", arvif->vdev_id, ret); goto err_peer_delete; } - param = WMI_STA_PS_PARAM_PSPOLL_COUNT; - value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX; - ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, - param, value); + ret = ath10k_mac_vif_recalc_ps_poll_count(arvif); if (ret) { - ath10k_warn(ar, "failed to set vdev %i PSPOLL count: %d\n", + ath10k_warn(ar, "failed to recalc ps poll count on vdev %i: %d\n", arvif->vdev_id, ret); goto err_peer_delete; } @@ -3818,6 +3863,20 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif, if (ret) ath10k_warn(ar, "failed to set rx wake param: %d\n", ret); + ret = ath10k_mac_vif_recalc_ps_wake_threshold(arvif); + if (ret) { + ath10k_warn(ar, "failed to recalc ps wake threshold on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + ret = ath10k_mac_vif_recalc_ps_poll_count(arvif); + if (ret) { + ath10k_warn(ar, "failed to recalc ps poll count on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + exit: return ret; } diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 97f902f..ff2d076 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -4015,6 +4015,13 @@ enum wmi_sta_ps_param_pspoll_count { * Values greater than 0 indicate the maximum numer of PS-Poll frames * FW will send before waking up. */ + + /* When u-APSD is enabled the firmware will be very reluctant to exit + * STA PS. This could result in very poor Rx performance with STA doing + * PS-Poll for each and every buffered frame. This value is a bit + * arbitrary. + */ + WMI_STA_PS_PSPOLL_COUNT_UAPSD = 3, }; /* -- cgit v0.10.2 From 526549a850bf6f549b88a271c7df9dd847e02bcf Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 12 Dec 2014 12:41:37 +0100 Subject: ath10k: prevent invalid ps timeout config Setting 0 ps timeout to firmware yields very poor latency and traffic issues. This is the case when multi-vif is active. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 13c2bad..2804952 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1105,10 +1105,12 @@ static int ath10k_mac_vif_recalc_ps_poll_count(struct ath10k_vif *arvif) static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif) { struct ath10k *ar = arvif->ar; + struct ieee80211_vif *vif = arvif->vif; struct ieee80211_conf *conf = &ar->hw->conf; enum wmi_sta_powersave_param param; enum wmi_sta_ps_mode psmode; int ret; + int ps_timeout; lockdep_assert_held(&arvif->ar->conf_mutex); @@ -1119,8 +1121,15 @@ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif) psmode = WMI_STA_PS_MODE_ENABLED; param = WMI_STA_PS_PARAM_INACTIVITY_TIME; + ps_timeout = conf->dynamic_ps_timeout; + if (ps_timeout == 0) { + /* Firmware doesn't like 0 */ + ps_timeout = ieee80211_tu_to_usec( + vif->bss_conf.beacon_int) / 1000; + } + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, - conf->dynamic_ps_timeout); + ps_timeout); if (ret) { ath10k_warn(ar, "failed to set inactivity time for vdev %d: %i\n", arvif->vdev_id, ret); -- cgit v0.10.2 From bf14e65cb750be9837c8b0f61471145a03ba4a36 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 12 Dec 2014 12:41:38 +0100 Subject: ath10k: enable per-vif sta powersave Per-vif bss_conf.ps should be used to configure powersave. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 2804952..19ddbc6 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1099,9 +1099,6 @@ static int ath10k_mac_vif_recalc_ps_poll_count(struct ath10k_vif *arvif) return 0; } -/* - * Review this when mac80211 gains per-interface powersave support. - */ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif) { struct ath10k *ar = arvif->ar; @@ -1117,7 +1114,7 @@ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif) if (arvif->vif->type != NL80211_IFTYPE_STATION) return 0; - if (conf->flags & IEEE80211_CONF_PS) { + if (vif->bss_conf.ps) { psmode = WMI_STA_PS_MODE_ENABLED; param = WMI_STA_PS_PARAM_INACTIVITY_TIME; @@ -3378,6 +3375,13 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, ath10k_warn(ar, "failed to recalc tx power: %d\n", ret); } + if (changed & BSS_CHANGED_PS) { + ret = ath10k_mac_vif_setup_ps(arvif); + if (ret) + ath10k_warn(ar, "failed to setup ps on vdev %i: %d\n", + arvif->vdev_id, ret); + } + mutex_unlock(&ar->conf_mutex); } -- cgit v0.10.2 From 75d2bd488301dbe81c2eb402bfcde5f988f72044 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 12 Dec 2014 12:41:39 +0100 Subject: ath10k: advertise p2p dev support Firmware doesn't allow precise tx rate control so P2P wasn't entirely spec compliant (it was using CCK rates in some cases). The only way to make sure firmware doesn't use CCK rates is to have a vdev with P2P subtype used for scanning and tx. This can be done via a special dedicated P2P device interface support. This also removes the ancient hack from ath10k in favor of p2pdev. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 577a3d7..c83f1e7 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -32,17 +32,14 @@ unsigned int ath10k_debug_mask; static bool uart_print; -static unsigned int ath10k_p2p; static bool skip_otp; module_param_named(debug_mask, ath10k_debug_mask, uint, 0644); module_param(uart_print, bool, 0644); -module_param_named(p2p, ath10k_p2p, uint, 0644); module_param(skip_otp, bool, 0644); MODULE_PARM_DESC(debug_mask, "Debugging mask"); MODULE_PARM_DESC(uart_print, "Uart target debugging"); -MODULE_PARM_DESC(p2p, "Enable ath10k P2P support"); MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode"); static const struct ath10k_hw_params ath10k_hw_params_list[] = { @@ -1290,10 +1287,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, ar->ath_common.priv = ar; ar->ath_common.hw = ar->hw; - - ar->p2p = !!ath10k_p2p; ar->dev = dev; - ar->hif.ops = hif_ops; ar->hif.bus = bus; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 19ddbc6..42f6a4d 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2961,10 +2961,11 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, arvif->vdev_id = bit; arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE; - if (ar->p2p) - arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE; - switch (vif->type) { + case NL80211_IFTYPE_P2P_DEVICE: + arvif->vdev_type = WMI_VDEV_TYPE_STA; + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE; + break; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_STATION: arvif->vdev_type = WMI_VDEV_TYPE_STA; @@ -4860,6 +4861,10 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = { .types = BIT(NL80211_IFTYPE_P2P_GO) }, { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_DEVICE) + }, + { .max = 7, .types = BIT(NL80211_IFTYPE_AP) }, @@ -5079,6 +5084,7 @@ int ath10k_mac_register(struct ath10k *ar) if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features)) ar->hw->wiphy->interface_modes |= + BIT(NL80211_IFTYPE_P2P_DEVICE) | BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO); -- cgit v0.10.2 From e529fea919872c6568bbda7873c64ac5d807ee83 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Wed, 17 Dec 2014 00:23:51 +0100 Subject: HID: logitech-hidpp: separate HID++ from WTP processing Previously wtp_raw_event would be called through hidpp_raw_hidpp_event (for the touchpad report) and hidpp_raw_event (for the mouse report). This patch removes one calling surface, making a clearer distinction between "generic HID++ processing" (matching internal reports) and device-specific event processing. Suggested-by: Benjamin Tissoires Signed-off-by: Peter Wu Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 2f420c0..6a5fe97 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -924,7 +924,7 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, /* * If the mutex is locked then we have a pending answer from a - * previoulsly sent command + * previously sent command. */ if (unlikely(mutex_is_locked(&hidpp->send_mutex))) { /* @@ -955,9 +955,6 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, return 1; } - if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) - return wtp_raw_event(hidpp->hid_dev, data, size); - return 0; } @@ -965,7 +962,9 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { struct hidpp_device *hidpp = hid_get_drvdata(hdev); + int ret = 0; + /* Generic HID++ processing. */ switch (data[0]) { case REPORT_ID_HIDPP_LONG: if (size != HIDPP_REPORT_LONG_LENGTH) { @@ -973,16 +972,23 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report, size); return 1; } - return hidpp_raw_hidpp_event(hidpp, data, size); + ret = hidpp_raw_hidpp_event(hidpp, data, size); + break; case REPORT_ID_HIDPP_SHORT: if (size != HIDPP_REPORT_SHORT_LENGTH) { hid_err(hdev, "received hid++ report of bad size (%d)", size); return 1; } - return hidpp_raw_hidpp_event(hidpp, data, size); + ret = hidpp_raw_hidpp_event(hidpp, data, size); + break; } + /* If no report is available for further processing, skip calling + * raw_event of subclasses. */ + if (ret != 0) + return ret; + if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) return wtp_raw_event(hdev, data, size); -- cgit v0.10.2 From e3cb0acd23258cf5e1a7ea270c98805eff01dcc5 Mon Sep 17 00:00:00 2001 From: Jamie Lentin Date: Tue, 16 Dec 2014 21:26:45 +0000 Subject: HID: lenovo: Add sensitivity control to compact keyboards The trackpoint sensitivity can also be controlled, expose this via sysfs. Signed-off-by: Jamie Lentin Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index 4c55f4d..0f35a76 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -38,6 +38,7 @@ struct lenovo_drvdata_tpkbd { struct lenovo_drvdata_cptkbd { bool fn_lock; + int sensitivity; }; #define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c)) @@ -145,6 +146,7 @@ static void lenovo_features_set_cptkbd(struct hid_device *hdev) struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); ret = lenovo_send_cmd_cptkbd(hdev, 0x05, cptkbd_data->fn_lock); + ret = lenovo_send_cmd_cptkbd(hdev, 0x02, cptkbd_data->sensitivity); if (ret) hid_err(hdev, "Fn-lock setting failed: %d\n", ret); } @@ -179,13 +181,50 @@ static ssize_t attr_fn_lock_store_cptkbd(struct device *dev, return count; } +static ssize_t attr_sensitivity_show_cptkbd(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); + + return snprintf(buf, PAGE_SIZE, "%u\n", + cptkbd_data->sensitivity); +} + +static ssize_t attr_sensitivity_store_cptkbd(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); + int value; + + if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) + return -EINVAL; + + cptkbd_data->sensitivity = value; + lenovo_features_set_cptkbd(hdev); + + return count; +} + + static struct device_attribute dev_attr_fn_lock_cptkbd = __ATTR(fn_lock, S_IWUSR | S_IRUGO, attr_fn_lock_show_cptkbd, attr_fn_lock_store_cptkbd); +static struct device_attribute dev_attr_sensitivity_cptkbd = + __ATTR(sensitivity, S_IWUSR | S_IRUGO, + attr_sensitivity_show_cptkbd, + attr_sensitivity_store_cptkbd); + + static struct attribute *lenovo_attributes_cptkbd[] = { &dev_attr_fn_lock_cptkbd.attr, + &dev_attr_sensitivity_cptkbd.attr, NULL }; @@ -594,8 +633,9 @@ static int lenovo_probe_cptkbd(struct hid_device *hdev) if (ret) hid_warn(hdev, "Failed to switch F7/9/11 mode: %d\n", ret); - /* Turn Fn-Lock on by default */ + /* Set keyboard settings to known state */ cptkbd_data->fn_lock = true; + cptkbd_data->sensitivity = 0x05; lenovo_features_set_cptkbd(hdev); ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_cptkbd); -- cgit v0.10.2 From 94eefa27132358434735060c6bbdf61c669bb0ab Mon Sep 17 00:00:00 2001 From: Jamie Lentin Date: Tue, 16 Dec 2014 21:26:46 +0000 Subject: HID: lenovo: Use native middle-button mode for compact keyboards By default the middle button is in a compatibility mode, and generates standard wheel events when dragging with the middle trackpoint button. Unfortunately this is buggy: * The middle button comes up before starting wheel events, causing a middle click on whatever the mouse cursor was sitting on * The USB keyboard always generates the "native" horizontal wheel event, regardless of mode. Instead, enable the "native" mode the Windows driver uses, and add support for the custom events this generates. This fixes the USB keyboard wheel events, and the middle-click up event comes after the wheel events. Signed-off-by: Jamie Lentin Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index 0f35a76..c4c3f09 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -92,6 +92,38 @@ static int lenovo_input_mapping_cptkbd(struct hid_device *hdev, case 0x00fa: /* Fn-Esc: Fn-lock toggle */ map_key_clear(KEY_FN_ESC); return 1; + case 0x00fb: /* Middle mouse button (in native mode) */ + map_key_clear(BTN_MIDDLE); + return 1; + } + } + + /* Compatibility middle/wheel mappings should be ignored */ + if (usage->hid == HID_GD_WHEEL) + return -1; + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON && + (usage->hid & HID_USAGE) == 0x003) + return -1; + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER && + (usage->hid & HID_USAGE) == 0x238) + return -1; + + /* Map wheel emulation reports: 0xffa1 = USB, 0xff10 = BT */ + if ((usage->hid & HID_USAGE_PAGE) == 0xff100000 || + (usage->hid & HID_USAGE_PAGE) == 0xffa10000) { + field->flags |= HID_MAIN_ITEM_RELATIVE | HID_MAIN_ITEM_VARIABLE; + field->logical_minimum = -127; + field->logical_maximum = 127; + + switch (usage->hid & HID_USAGE) { + case 0x0000: + hid_map_usage(hi, usage, bit, max, EV_REL, 0x06); + return 1; + case 0x0001: + hid_map_usage(hi, usage, bit, max, EV_REL, 0x08); + return 1; + default: + return -1; } } @@ -633,6 +665,11 @@ static int lenovo_probe_cptkbd(struct hid_device *hdev) if (ret) hid_warn(hdev, "Failed to switch F7/9/11 mode: %d\n", ret); + /* Switch middle button to native mode */ + ret = lenovo_send_cmd_cptkbd(hdev, 0x09, 0x01); + if (ret) + hid_warn(hdev, "Failed to switch middle button: %d\n", ret); + /* Set keyboard settings to known state */ cptkbd_data->fn_lock = true; cptkbd_data->sensitivity = 0x05; -- cgit v0.10.2 From bf159447537ed234afb569f91f5fd8c54ffa4c36 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 16 Dec 2014 17:06:01 -0500 Subject: HID: logitech-hidpp: bail out if wtp_connect fails If wtp_connect() fails, that means most of the time that the device has been disconnected. Subsequent attempts to contact the device will fail too, so it's simpler to bail out earlier. Signed-off-by: Benjamin Tissoires Reviewed-by: Peter Wu Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 6a5fe97..e1f3aca 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -862,24 +862,24 @@ static int wtp_allocate(struct hid_device *hdev, const struct hid_device_id *id) return 0; }; -static void wtp_connect(struct hid_device *hdev, bool connected) +static int wtp_connect(struct hid_device *hdev, bool connected) { struct hidpp_device *hidpp = hid_get_drvdata(hdev); struct wtp_data *wd = hidpp->private_data; int ret; if (!connected) - return; + return 0; if (!wd->x_size) { ret = wtp_get_config(hidpp); if (ret) { hid_err(hdev, "Can not get wtp config: %d\n", ret); - return; + return ret; } } - hidpp_touchpad_set_raw_report_state(hidpp, wd->mt_feature_index, + return hidpp_touchpad_set_raw_report_state(hidpp, wd->mt_feature_index, true, true); } @@ -1063,8 +1063,11 @@ static void hidpp_connect_event(struct hidpp_device *hidpp) struct input_dev *input; char *name, *devm_name; - if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) - wtp_connect(hdev, connected); + if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) { + ret = wtp_connect(hdev, connected); + if (ret) + return; + } if (!connected || hidpp->delayed_input) return; -- cgit v0.10.2 From e39f2d5956999c05c85814787a113ffadbcd4b26 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Fri, 12 Dec 2014 10:17:26 -0800 Subject: HID: rmi: Scan the report descriptor to determine if the device is suitable for the hid-rmi driver On composite HID devices there may be multiple HID devices on separate interfaces, but hid-rmi should only bind to the touchpad. The previous version simply checked that the interface protocol was set to mouse. Unfortuately, it is not always the case that the touchpad has the mouse interface protocol set. This patch takes a different approach and scans the report descriptor looking for the Generic Desktop Pointer usage and the Vendor Specific Top Level Collection needed by the hid-rmi driver to interface with the device. Signed-off-by: Andrew Duggan Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index c3d0ac1..81665b4 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -698,6 +698,7 @@ static void hid_scan_feature_usage(struct hid_parser *parser, u32 usage) static void hid_scan_collection(struct hid_parser *parser, unsigned type) { struct hid_device *hid = parser->device; + int i; if (((parser->global.usage_page << 16) == HID_UP_SENSOR) && type == HID_COLLECTION_PHYSICAL) @@ -707,6 +708,14 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type) hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 && hid->group == HID_GROUP_MULTITOUCH) hid->group = HID_GROUP_GENERIC; + + if ((parser->global.usage_page << 16) == HID_UP_GENDESK) + for (i = 0; i < parser->local.usage_index; i++) + if (parser->local.usage[i] == HID_GD_POINTER) + parser->scan_flags |= HID_SCAN_FLAG_GD_POINTER; + + if ((parser->global.usage_page << 16) >= HID_UP_MSVENDOR) + parser->scan_flags |= HID_SCAN_FLAG_VENDOR_SPECIFIC; } static int hid_scan_main(struct hid_parser *parser, struct hid_item *item) @@ -792,11 +801,14 @@ static int hid_scan_report(struct hid_device *hid) hid->group = HID_GROUP_WACOM; break; case USB_VENDOR_ID_SYNAPTICS: - if ((hid->group == HID_GROUP_GENERIC) && - (hid->bus != BUS_USB || hid->type == HID_TYPE_USBMOUSE)) - /* hid-rmi should only bind to the mouse interface of - * composite USB devices */ - hid->group = HID_GROUP_RMI; + if (hid->group == HID_GROUP_GENERIC) + if ((parser->scan_flags & HID_SCAN_FLAG_VENDOR_SPECIFIC) + && (parser->scan_flags & HID_SCAN_FLAG_GD_POINTER)) + /* + * hid-rmi should take care of them, + * not hid-generic + */ + hid->group = HID_GROUP_RMI; break; } diff --git a/include/linux/hid.h b/include/linux/hid.h index 06c4607..efc7787 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -574,7 +574,9 @@ static inline void hid_set_drvdata(struct hid_device *hdev, void *data) #define HID_GLOBAL_STACK_SIZE 4 #define HID_COLLECTION_STACK_SIZE 4 -#define HID_SCAN_FLAG_MT_WIN_8 0x00000001 +#define HID_SCAN_FLAG_MT_WIN_8 BIT(0) +#define HID_SCAN_FLAG_VENDOR_SPECIFIC BIT(1) +#define HID_SCAN_FLAG_GD_POINTER BIT(2) struct hid_parser { struct hid_global global; -- cgit v0.10.2 From b1ac9487c824a212f3b5bb80df5ac578243b0e21 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 11 Dec 2014 17:39:59 -0500 Subject: HID: logitech-hidpp: prefix the name with "Logitech" Current names are reported as "K750", "M705", and it can be misleading for the users when they look at their input device list. Prefixing the names with "Logitech " makes things better. Signed-off-by: Benjamin Tissoires Reviewed-by: Peter Wu Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index e1f3aca..7efbd8b 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -282,6 +282,33 @@ static inline bool hidpp_report_is_connect_event(struct hidpp_report *report) (report->rap.sub_id == 0x41); } +/** + * hidpp_prefix_name() prefixes the current given name with "Logitech ". + */ +static void hidpp_prefix_name(char **name, int name_length) +{ +#define PREFIX_LENGTH 9 /* "Logitech " */ + + int new_length; + char *new_name; + + if (name_length > PREFIX_LENGTH && + strncmp(*name, "Logitech ", PREFIX_LENGTH) == 0) + /* The prefix has is already in the name */ + return; + + new_length = PREFIX_LENGTH + name_length; + new_name = kzalloc(new_length, GFP_KERNEL); + if (!new_name) + return; + + snprintf(new_name, new_length, "Logitech %s", *name); + + kfree(*name); + + *name = new_name; +} + /* -------------------------------------------------------------------------- */ /* HIDP++ 1.0 commands */ /* -------------------------------------------------------------------------- */ @@ -321,6 +348,10 @@ static char *hidpp_get_unifying_name(struct hidpp_device *hidpp_dev) return NULL; memcpy(name, &response.rap.params[2], len); + + /* include the terminating '\0' */ + hidpp_prefix_name(&name, len + 1); + return name; } @@ -498,6 +529,9 @@ static char *hidpp_get_device_name(struct hidpp_device *hidpp) index += ret; } + /* include the terminating '\0' */ + hidpp_prefix_name(&name, __name_length + 1); + return name; } -- cgit v0.10.2 From d29544571507336811b81ca631baf6159cc8146a Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 17 Dec 2014 10:00:52 +0100 Subject: mac80211: notify NSS changed when IBSS and HT When using IBSS in HT mode, we always get NSS=1 in rc_update callback. Force NSS recalculation when rates updated and notify driver that NSS changed. Signed-off-by: Janusz Dziedzic Signed-off-by: Johannes Berg diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 509bc15..b606b53 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -1069,9 +1069,16 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, } if (sta && rates_updated) { - drv_sta_rc_update(local, sdata, &sta->sta, - IEEE80211_RC_SUPP_RATES_CHANGED); + u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED; + u8 rx_nss = sta->sta.rx_nss; + + /* Force rx_nss recalculation */ + sta->sta.rx_nss = 0; rate_control_rate_init(sta); + if (sta->sta.rx_nss != rx_nss) + changed |= IEEE80211_RC_NSS_CHANGED; + + drv_sta_rc_update(local, sdata, &sta->sta, changed); } rcu_read_unlock(); -- cgit v0.10.2 From 688b1ecfb9ed0484754d2653386e3c44c58ede5c Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 16 Dec 2014 16:01:39 +0200 Subject: mac80211: notify channel switch at the end of ieee80211_chswitch_post_beacon() The call to cfg80211_ch_switch_notify() should be at the end of the ieee80211_chswitch_post_beacon() function, because it should only be sent if everything succeeded. Fixes: d04b5ac9e70b ("cfg80211/mac80211: allow any interface to send channel switch notifications") Signed-off-by: Luciano Coelho Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index f495b80..ea7c714 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1053,8 +1053,6 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata) sdata->csa_block_tx = false; } - cfg80211_ch_switch_notify(sdata->dev, &sdata->reserved_chandef); - sdata->vif.csa_active = false; ifmgd->csa_waiting_bcn = false; @@ -1066,6 +1064,8 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata) &ifmgd->csa_connection_drop_work); return; } + + cfg80211_ch_switch_notify(sdata->dev, &sdata->reserved_chandef); } void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success) -- cgit v0.10.2 From 2ae70efcea7a695a62bb47170d3fb16674b8dbea Mon Sep 17 00:00:00 2001 From: "Nishikawa, Kenzoh" Date: Tue, 16 Dec 2014 01:41:06 +0000 Subject: mac80211: keep sending peer candidate events while in listen state Instead of sending peer candidate events just once, send them as long as the peer remains in the LISTEN state in the peering state machine, when userspace is implementing the peering manager. Userspace may silence the events from a peer by progressing the state machine or by setting the link state to BLOCKED. Fixes the problem that a mesh peering process won't be fired again after the previous first peering trial fails due to like air propagation error if the peering is managed by user space such as wpa_supplicant. This patch works with another patch for wpa_supplicant described here which fires a peering process again triggered by the notice from kernel. http://lists.shmoo.com/pipermail/hostap/2014-November/031235.html Signed-off-by: Kenzoh Nishikawa Signed-off-by: Johannes Berg diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index b488e18..fa94ca1 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -523,6 +523,13 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, sdata->u.mesh.mshcfg.auto_open_plinks && rssi_threshold_check(sdata, sta)) changed = mesh_plink_open(sta); + else if (sta->plink_state == NL80211_PLINK_LISTEN && + (sdata->u.mesh.user_mpm || + sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED)) + cfg80211_notify_new_peer_candidate(sdata->dev, hw_addr, + elems->ie_start, + elems->total_len, + GFP_ATOMIC); ieee80211_mps_frame_release(sta, elems); out: -- cgit v0.10.2 From ad30ca2c03cecfb1b0749874bdceead269542de6 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 15 Dec 2014 19:25:59 +0200 Subject: cfg80211: allow usermode to query wiphy specific regdom If a wiphy-idx is specified, the kernel will return the wiphy specific regdomain, if such exists. Otherwise return the global regdom. When no wiphy-idx is specified, return the global regdomain as well as all wiphy-specific regulatory domains in the system, via a new nested list of attributes. Add a new attribute for each wiphy-specific regdomain, for usermode to identify it as such. Signed-off-by: Arik Nemtsov Signed-off-by: Johannes Berg diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index b37bd5a..2d384d0 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -252,7 +252,15 @@ * %NL80211_ATTR_IFINDEX. * * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set - * regulatory domain. + * regulatory domain. If %NL80211_ATTR_WIPHY is specified and the device + * has a private regulatory domain, it will be returned. Otherwise, the + * global regdomain will be returned. + * A device will have a private regulatory domain if it uses the + * regulatory_hint() API. Even when a private regdomain is used the channel + * information will still be mended according to further hints from + * the regulatory core to help with compliance. A dump version of this API + * is now available which will returns the global regdomain as well as + * all private regdomains of present wiphys (for those that have it). * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command * after being queried by the kernel. CRDA replies by sending a regulatory * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a17d6bc..2d5dc42 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5327,42 +5327,20 @@ static int nl80211_update_mesh_config(struct sk_buff *skb, return err; } -static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) +static int nl80211_put_regdom(const struct ieee80211_regdomain *regdom, + struct sk_buff *msg) { - const struct ieee80211_regdomain *regdom; - struct sk_buff *msg; - void *hdr = NULL; struct nlattr *nl_reg_rules; unsigned int i; - if (!cfg80211_regdomain) - return -EINVAL; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOBUFS; - - hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, - NL80211_CMD_GET_REG); - if (!hdr) - goto put_failure; - - if (reg_last_request_cell_base() && - nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, - NL80211_USER_REG_HINT_CELL_BASE)) - goto nla_put_failure; - - rcu_read_lock(); - regdom = rcu_dereference(cfg80211_regdomain); - if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) || (regdom->dfs_region && nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region))) - goto nla_put_failure_rcu; + goto nla_put_failure; nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); if (!nl_reg_rules) - goto nla_put_failure_rcu; + goto nla_put_failure; for (i = 0; i < regdom->n_reg_rules; i++) { struct nlattr *nl_reg_rule; @@ -5377,7 +5355,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) nl_reg_rule = nla_nest_start(msg, i); if (!nl_reg_rule) - goto nla_put_failure_rcu; + goto nla_put_failure; max_bandwidth_khz = freq_range->max_bandwidth_khz; if (!max_bandwidth_khz) @@ -5398,13 +5376,64 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) power_rule->max_eirp) || nla_put_u32(msg, NL80211_ATTR_DFS_CAC_TIME, reg_rule->dfs_cac_ms)) - goto nla_put_failure_rcu; + goto nla_put_failure; nla_nest_end(msg, nl_reg_rule); } - rcu_read_unlock(); nla_nest_end(msg, nl_reg_rules); + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info) +{ + const struct ieee80211_regdomain *regdom = NULL; + struct cfg80211_registered_device *rdev; + struct wiphy *wiphy = NULL; + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOBUFS; + + hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, + NL80211_CMD_GET_REG); + if (!hdr) + goto put_failure; + + if (info->attrs[NL80211_ATTR_WIPHY]) { + rdev = cfg80211_get_dev_from_info(genl_info_net(info), info); + if (IS_ERR(rdev)) { + nlmsg_free(msg); + return PTR_ERR(rdev); + } + + wiphy = &rdev->wiphy; + regdom = get_wiphy_regdom(wiphy); + + if (regdom && + nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy))) + goto nla_put_failure; + } + + if (!wiphy && reg_last_request_cell_base() && + nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, + NL80211_USER_REG_HINT_CELL_BASE)) + goto nla_put_failure; + + rcu_read_lock(); + + if (!regdom) + regdom = rcu_dereference(cfg80211_regdomain); + + if (nl80211_put_regdom(regdom, msg)) + goto nla_put_failure_rcu; + + rcu_read_unlock(); genlmsg_end(msg, hdr); return genlmsg_reply(msg, info); @@ -5418,6 +5447,79 @@ put_failure: return -EMSGSIZE; } +static int nl80211_send_regdom(struct sk_buff *msg, struct netlink_callback *cb, + u32 seq, int flags, struct wiphy *wiphy, + const struct ieee80211_regdomain *regdom) +{ + void *hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).portid, seq, flags, + NL80211_CMD_GET_REG); + + if (!hdr) + return -1; + + genl_dump_check_consistent(cb, hdr, &nl80211_fam); + + if (nl80211_put_regdom(regdom, msg)) + goto nla_put_failure; + + if (!wiphy && reg_last_request_cell_base() && + nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, + NL80211_USER_REG_HINT_CELL_BASE)) + goto nla_put_failure; + + if (wiphy && + nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy))) + goto nla_put_failure; + + return genlmsg_end(msg, hdr); + +nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int nl80211_get_reg_dump(struct sk_buff *skb, + struct netlink_callback *cb) +{ + const struct ieee80211_regdomain *regdom = NULL; + struct cfg80211_registered_device *rdev; + int err, reg_idx, start = cb->args[2]; + + rtnl_lock(); + + if (cfg80211_regdomain && start == 0) { + err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq, + NLM_F_MULTI, NULL, + rtnl_dereference(cfg80211_regdomain)); + if (err < 0) + goto out_err; + } + + /* the global regdom is idx 0 */ + reg_idx = 1; + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + regdom = get_wiphy_regdom(&rdev->wiphy); + if (!regdom) + continue; + + if (++reg_idx <= start) + continue; + + err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq, + NLM_F_MULTI, &rdev->wiphy, regdom); + if (err < 0) { + reg_idx--; + break; + } + } + + cb->args[2] = reg_idx; + err = skb->len; +out_err: + rtnl_unlock(); + return err; +} + static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) { struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; @@ -10225,7 +10327,8 @@ static const struct genl_ops nl80211_ops[] = { }, { .cmd = NL80211_CMD_GET_REG, - .doit = nl80211_get_reg, + .doit = nl80211_get_reg_do, + .dumpit = nl80211_get_reg_dump, .policy = nl80211_policy, .internal_flags = NL80211_FLAG_NEED_RTNL, /* can be retrieved by unprivileged users */ @@ -10983,9 +11086,13 @@ void nl80211_send_reg_change_event(struct regulatory_request *request) goto nla_put_failure; } - if (request->wiphy_idx != WIPHY_IDX_INVALID && - nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx)) - goto nla_put_failure; + if (request->wiphy_idx != WIPHY_IDX_INVALID) { + struct wiphy *wiphy = wiphy_idx_to_wiphy(request->wiphy_idx); + + if (wiphy && + nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx)) + goto nla_put_failure; + } genlmsg_end(msg, hdr); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index d83480b6..2d97600 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -142,7 +142,7 @@ static const struct ieee80211_regdomain *get_cfg80211_regdom(void) return rtnl_dereference(cfg80211_regdomain); } -static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) +const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) { return rtnl_dereference(wiphy->regd); } diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 5e48031..4b45d6e 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -38,6 +38,7 @@ unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd, const struct ieee80211_reg_rule *rule); bool reg_last_request_cell_base(void); +const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy); /** * regulatory_hint_found_beacon - hints a beacon was found on a channel -- cgit v0.10.2 From b0d7aa59592b4270531de5ce65dcf18338a2d98c Mon Sep 17 00:00:00 2001 From: Jonathan Doron Date: Mon, 15 Dec 2014 19:26:00 +0200 Subject: cfg80211: allow wiphy specific regdomain management Add a new regulatory flag that allows a driver to manage regdomain changes/updates for its own wiphy. A self-managed wiphys only employs regulatory information obtained from the FW and driver and does not use other cfg80211 sources like beacon-hints, country-code IEs and hints from other devices on the same system. Conversely, a self-managed wiphy does not share its regulatory hints with other devices in the system. If a system contains several devices, one or more of which are self-managed, there might be contradictory regulatory settings between them. Usage of flag is generally discouraged. Only use it if the FW/driver is incompatible with non-locally originated hints. A new API lets the driver send a complete regdomain, to be applied on its wiphy only. After a wiphy-specific regdomain change takes place, usermode will get a new type of change notification. The regulatory core also takes care enforce regulatory restrictions, in case some interfaces are on forbidden channels. Signed-off-by: Jonathan Doron Signed-off-by: Arik Nemtsov Reviewed-by: Luis R. Rodriguez Signed-off-by: Johannes Berg diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 4ebb816..4bc1fc9 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3808,6 +3808,20 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, int regulatory_hint(struct wiphy *wiphy, const char *alpha2); /** + * regulatory_set_wiphy_regd - set regdom info for self managed drivers + * @wiphy: the wireless device we want to process the regulatory domain on + * @rd: the regulatory domain informatoin to use for this wiphy + * + * Set the regulatory domain information for self-managed wiphys, only they + * may use this function. See %REGULATORY_WIPHY_SELF_MANAGED for more + * information. + * + * Return: 0 on success. -EINVAL, -EPERM + */ +int regulatory_set_wiphy_regd(struct wiphy *wiphy, + struct ieee80211_regdomain *rd); + +/** * wiphy_apply_custom_regulatory - apply a custom driver regulatory domain * @wiphy: the wireless device we want to process the regulatory domain on * @regd: the custom regulatory domain to use for this wiphy diff --git a/include/net/regulatory.h b/include/net/regulatory.h index b776d72..ebc5a2e 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -147,6 +147,24 @@ struct regulatory_request { * NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_P2P_GO, * NL80211_IFTYPE_P2P_DEVICE. The flag will be set by default if a device * includes any modes unsupported for enforcement checking. + * @REGULATORY_WIPHY_SELF_MANAGED: for devices that employ wiphy-specific + * regdom management. These devices will ignore all regdom changes not + * originating from their own wiphy. + * A self-managed wiphys only employs regulatory information obtained from + * the FW and driver and does not use other cfg80211 sources like + * beacon-hints, country-code IEs and hints from other devices on the same + * system. Conversely, a self-managed wiphy does not share its regulatory + * hints with other devices in the system. If a system contains several + * devices, one or more of which are self-managed, there might be + * contradictory regulatory settings between them. Usage of flag is + * generally discouraged. Only use it if the FW/driver is incompatible + * with non-locally originated hints. + * This flag is incompatible with the flags: %REGULATORY_CUSTOM_REG, + * %REGULATORY_STRICT_REG, %REGULATORY_COUNTRY_IE_FOLLOW_POWER, + * %REGULATORY_COUNTRY_IE_IGNORE and %REGULATORY_DISABLE_BEACON_HINTS. + * Mixing any of the above flags with this flag will result in a failure + * to register the wiphy. This flag implies + * %REGULATORY_DISABLE_BEACON_HINTS and %REGULATORY_COUNTRY_IE_IGNORE. */ enum ieee80211_regulatory_flags { REGULATORY_CUSTOM_REG = BIT(0), @@ -156,6 +174,7 @@ enum ieee80211_regulatory_flags { REGULATORY_COUNTRY_IE_IGNORE = BIT(4), REGULATORY_ENABLE_RELAX_NO_IR = BIT(5), REGULATORY_IGNORE_STALE_KICKOFF = BIT(6), + REGULATORY_WIPHY_SELF_MANAGED = BIT(7), }; struct ieee80211_freq_range { diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 2d384d0..fb58e65 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -782,6 +782,10 @@ * peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel * when this command completes. * + * @NL80211_CMD_WIPHY_REG_CHANGE: Similar to %NL80211_CMD_REG_CHANGE, but used + * as an event to indicate changes for devices with wiphy-specific regdom + * management. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -966,6 +970,8 @@ enum nl80211_commands { NL80211_CMD_TDLS_CHANNEL_SWITCH, NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH, + NL80211_CMD_WIPHY_REG_CHANGE, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ diff --git a/net/wireless/core.c b/net/wireless/core.c index 4910758..b661fcc 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -561,6 +561,14 @@ int wiphy_register(struct wiphy *wiphy) BIT(NL80211_IFTYPE_MONITOR))) wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF; + if (WARN_ON((wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) && + (wiphy->regulatory_flags & + (REGULATORY_CUSTOM_REG | + REGULATORY_STRICT_REG | + REGULATORY_COUNTRY_IE_FOLLOW_POWER | + REGULATORY_COUNTRY_IE_IGNORE)))) + return -EINVAL; + if (WARN_ON(wiphy->coalesce && (!wiphy->coalesce->n_rules || !wiphy->coalesce->n_patterns) && diff --git a/net/wireless/core.h b/net/wireless/core.h index faa5b16..e87cae5 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -36,6 +36,13 @@ struct cfg80211_registered_device { * the country on the country IE changed. */ char country_ie_alpha2[2]; + /* + * the driver requests the regulatory core to set this regulatory + * domain as the wiphy's. Only used for %REGULATORY_WIPHY_SELF_MANAGED + * devices using the regulatory_set_wiphy_regd() API + */ + const struct ieee80211_regdomain *requested_regd; + /* If a Country IE has been received this tells us the environment * which its telling us its in. This defaults to ENVIRON_ANY */ enum environment_cap env; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 2d5dc42..eebb7e4 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -11042,25 +11042,9 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, NL80211_MCGRP_SCAN, GFP_KERNEL); } -/* - * This can happen on global regulatory changes or device specific settings - * based on custom world regulatory domains. - */ -void nl80211_send_reg_change_event(struct regulatory_request *request) +static bool nl80211_reg_change_event_fill(struct sk_buff *msg, + struct regulatory_request *request) { - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE); - if (!hdr) { - nlmsg_free(msg); - return; - } - /* Userspace can always count this one always being set */ if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator)) goto nla_put_failure; @@ -11094,6 +11078,35 @@ void nl80211_send_reg_change_event(struct regulatory_request *request) goto nla_put_failure; } + return true; + +nla_put_failure: + return false; +} + +/* + * This can happen on global regulatory changes or device specific settings + * based on custom regulatory domains. + */ +void nl80211_common_reg_change_event(enum nl80211_commands cmd_id, + struct regulatory_request *request) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, cmd_id); + if (!hdr) { + nlmsg_free(msg); + return; + } + + if (nl80211_reg_change_event_fill(msg, request) == false) + goto nla_put_failure; + genlmsg_end(msg, hdr); rcu_read_lock(); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 7ad70d6..84d4edf 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -17,7 +17,21 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, struct net_device *netdev, u32 cmd); void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, struct net_device *netdev); -void nl80211_send_reg_change_event(struct regulatory_request *request); +void nl80211_common_reg_change_event(enum nl80211_commands cmd_id, + struct regulatory_request *request); + +static inline void +nl80211_send_reg_change_event(struct regulatory_request *request) +{ + nl80211_common_reg_change_event(NL80211_CMD_REG_CHANGE, request); +} + +static inline void +nl80211_send_wiphy_reg_change_event(struct regulatory_request *request) +{ + nl80211_common_reg_change_event(NL80211_CMD_WIPHY_REG_CHANGE, request); +} + void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2d97600..c040f8a 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1307,6 +1307,9 @@ static bool ignore_reg_update(struct wiphy *wiphy, { struct regulatory_request *lr = get_last_request(); + if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) + return true; + if (!lr) { REG_DBG_PRINT("Ignoring regulatory request set by %s " "since last_request is not set\n", @@ -2147,11 +2150,52 @@ static void reg_process_pending_beacon_hints(void) spin_unlock_bh(®_pending_beacons_lock); } +static void reg_process_self_managed_hints(void) +{ + struct cfg80211_registered_device *rdev; + struct wiphy *wiphy; + const struct ieee80211_regdomain *tmp; + const struct ieee80211_regdomain *regd; + enum ieee80211_band band; + struct regulatory_request request = {}; + + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + wiphy = &rdev->wiphy; + + spin_lock(®_requests_lock); + regd = rdev->requested_regd; + rdev->requested_regd = NULL; + spin_unlock(®_requests_lock); + + if (regd == NULL) + continue; + + tmp = get_wiphy_regdom(wiphy); + rcu_assign_pointer(wiphy->regd, regd); + rcu_free_regdom(tmp); + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + handle_band_custom(wiphy, wiphy->bands[band], regd); + + reg_process_ht_flags(wiphy); + + request.wiphy_idx = get_wiphy_idx(wiphy); + request.alpha2[0] = regd->alpha2[0]; + request.alpha2[1] = regd->alpha2[1]; + request.initiator = NL80211_REGDOM_SET_BY_DRIVER; + + nl80211_send_wiphy_reg_change_event(&request); + } + + reg_check_channels(); +} + static void reg_todo(struct work_struct *work) { rtnl_lock(); reg_process_pending_hints(); reg_process_pending_beacon_hints(); + reg_process_self_managed_hints(); rtnl_unlock(); } @@ -2432,6 +2476,8 @@ static void restore_regulatory_settings(bool reset_user) world_alpha2[1] = cfg80211_world_regdom->alpha2[1]; list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) + continue; if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG) restore_custom_reg_settings(&rdev->wiphy); } @@ -2835,10 +2881,52 @@ int set_regdom(const struct ieee80211_regdomain *rd) return 0; } +int regulatory_set_wiphy_regd(struct wiphy *wiphy, + struct ieee80211_regdomain *rd) +{ + const struct ieee80211_regdomain *regd; + const struct ieee80211_regdomain *prev_regd; + struct cfg80211_registered_device *rdev; + + if (WARN_ON(!wiphy || !rd)) + return -EINVAL; + + if (WARN(!(wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED), + "wiphy should have REGULATORY_WIPHY_SELF_MANAGED\n")) + return -EPERM; + + if (WARN(!is_valid_rd(rd), "Invalid regulatory domain detected\n")) { + print_regdomain_info(rd); + return -EINVAL; + } + + regd = reg_copy_regd(rd); + if (IS_ERR(regd)) + return PTR_ERR(regd); + + rdev = wiphy_to_rdev(wiphy); + + spin_lock(®_requests_lock); + prev_regd = rdev->requested_regd; + rdev->requested_regd = regd; + spin_unlock(®_requests_lock); + + kfree(prev_regd); + + schedule_work(®_work); + return 0; +} +EXPORT_SYMBOL(regulatory_set_wiphy_regd); + void wiphy_regulatory_register(struct wiphy *wiphy) { struct regulatory_request *lr; + /* self-managed devices ignore external hints */ + if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) + wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS | + REGULATORY_COUNTRY_IE_IGNORE; + if (!reg_dev_ignore_cell_hint(wiphy)) reg_num_devs_support_basehint++; -- cgit v0.10.2 From 1bdd716cbccabc8127fbbaaa663c3090302ef78b Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 15 Dec 2014 19:26:01 +0200 Subject: cfg80211: return private regdom for self-managed devices If a device has self-managed regulatory, insist on returning the wiphy specific regdomain if a wiphy-idx is specified. The global regdomain is meaningless for such devices. Also add an attribute for self-managed devices, so usermode can distinguish them as such. Signed-off-by: Arik Nemtsov Reviewed-by: Luis R. Rodriguez Signed-off-by: Johannes Berg diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index fb58e65..b3ada0b 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -261,6 +261,9 @@ * the regulatory core to help with compliance. A dump version of this API * is now available which will returns the global regdomain as well as * all private regdomains of present wiphys (for those that have it). + * If a wiphy is self-managed (%NL80211_ATTR_WIPHY_SELF_MANAGED_REG), then + * its private regdomain is the only valid one for it. The regulatory + * core is not used to help with compliance in this case. * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command * after being queried by the kernel. CRDA replies by sending a regulatory * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our @@ -1702,6 +1705,11 @@ enum nl80211_commands { * * @NL80211_ATTR_MAC_MASK: MAC address mask * + * @NL80211_ATTR_WIPHY_SELF_MANAGED_REG: flag attribute indicating this device + * is self-managing its regulatory information and any regulatory domain + * obtained from it is coming from the device's wiphy and not the global + * cfg80211 regdomain. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2059,6 +2067,8 @@ enum nl80211_attrs { NL80211_ATTR_MAC_MASK, + NL80211_ATTR_WIPHY_SELF_MANAGED_REG, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index eebb7e4..5b1907f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -396,6 +396,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 }, [NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 }, [NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN }, + [NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -1701,6 +1702,10 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, rdev->wiphy.max_num_csa_counters)) goto nla_put_failure; + if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED && + nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG)) + goto nla_put_failure; + /* done */ state->split_start = 0; break; @@ -5406,6 +5411,8 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info) goto put_failure; if (info->attrs[NL80211_ATTR_WIPHY]) { + bool self_managed; + rdev = cfg80211_get_dev_from_info(genl_info_net(info), info); if (IS_ERR(rdev)) { nlmsg_free(msg); @@ -5413,8 +5420,16 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info) } wiphy = &rdev->wiphy; + self_managed = wiphy->regulatory_flags & + REGULATORY_WIPHY_SELF_MANAGED; regdom = get_wiphy_regdom(wiphy); + /* a self-managed-reg device must have a private regdom */ + if (WARN_ON(!regdom && self_managed)) { + nlmsg_free(msg); + return -EINVAL; + } + if (regdom && nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy))) goto nla_put_failure; @@ -5471,6 +5486,10 @@ static int nl80211_send_regdom(struct sk_buff *msg, struct netlink_callback *cb, nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy))) goto nla_put_failure; + if (wiphy && wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED && + nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG)) + goto nla_put_failure; + return genlmsg_end(msg, hdr); nla_put_failure: @@ -11076,6 +11095,11 @@ static bool nl80211_reg_change_event_fill(struct sk_buff *msg, if (wiphy && nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx)) goto nla_put_failure; + + if (wiphy && + wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED && + nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG)) + goto nla_put_failure; } return true; -- cgit v0.10.2 From db8dfee57d37d2cd14a750477babbae52b69a494 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 15 Dec 2014 19:26:02 +0200 Subject: cfg80211: avoid intersection when applying self-managed reg The custom-reg handling function can currently only add flags to a given channel. This results in stale flags being left applied. In some cases a channel was disabled and even the orig_flags were changed to reflect this. Previously the API was designed for a single invocation before wiphy registration, so this didn't matter. The previous approach doesn't scale well to self-managed regulatory devices, particularly when a more permissive regdom is applied after a restrictive one. Signed-off-by: Arik Nemtsov Signed-off-by: Johannes Berg diff --git a/net/wireless/reg.c b/net/wireless/reg.c index c040f8a..9a5411c 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1680,8 +1680,12 @@ static void handle_channel_custom(struct wiphy *wiphy, if (IS_ERR(reg_rule)) { REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n", chan->center_freq); - chan->orig_flags |= IEEE80211_CHAN_DISABLED; - chan->flags = chan->orig_flags; + if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) { + chan->flags |= IEEE80211_CHAN_DISABLED; + } else { + chan->orig_flags |= IEEE80211_CHAN_DISABLED; + chan->flags = chan->orig_flags; + } return; } @@ -1706,7 +1710,13 @@ static void handle_channel_custom(struct wiphy *wiphy, chan->dfs_state = NL80211_DFS_USABLE; chan->beacon_found = false; - chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; + + if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) + chan->flags = chan->orig_flags | bw_flags | + map_regdom_flags(reg_rule->flags); + else + chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; + chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); chan->max_reg_power = chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); -- cgit v0.10.2 From 179b8fc7fb59ec69835f225e6c813040bbf52ba8 Mon Sep 17 00:00:00 2001 From: Chaya Rachel Ivgi Date: Sun, 14 Dec 2014 10:38:59 +0200 Subject: mac80211: Fix ignored HT override configurations HT override configurations was ignored when choosing the channel (until now, the override configuration affected only the capabilities shown in the IEs). The override configurations received only on association time, so in this case we should determine the channel again. Signed-off-by: Chaya Rachel Ivgi Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index ea7c714..877b647 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -157,14 +157,18 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct cfg80211_chan_def vht_chandef; + struct ieee80211_sta_ht_cap sta_ht_cap; u32 ht_cfreq, ret; + memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap)); + ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap); + chandef->chan = channel; chandef->width = NL80211_CHAN_WIDTH_20_NOHT; chandef->center_freq1 = channel->center_freq; chandef->center_freq2 = 0; - if (!ht_cap || !ht_oper || !sband->ht_cap.ht_supported) { + if (!ht_cap || !ht_oper || !sta_ht_cap.ht_supported) { ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; goto out; } @@ -197,7 +201,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, } /* check 40 MHz support, if we have it */ - if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { + if (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: chandef->width = NL80211_CHAN_WIDTH_40; @@ -4196,9 +4200,13 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; - bool have_sta = false; + struct ieee80211_supported_band *sband; + struct ieee80211_sta_ht_cap sta_ht_cap; + bool have_sta = false, is_override = false; int err; + sband = local->hw.wiphy->bands[cbss->channel->band]; + if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data)) return -EINVAL; @@ -4213,25 +4221,32 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, if (!new_sta) return -ENOMEM; } + + memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap)); + ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap); + + is_override = (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) != + (sband->ht_cap.cap & + IEEE80211_HT_CAP_SUP_WIDTH_20_40); + + if (new_sta || is_override) { + err = ieee80211_prep_channel(sdata, cbss); + if (err) { + if (new_sta) + sta_info_free(local, new_sta); + return -EINVAL; + } + } + if (new_sta) { u32 rates = 0, basic_rates = 0; bool have_higher_than_11mbit; int min_rate = INT_MAX, min_rate_index = -1; struct ieee80211_chanctx_conf *chanctx_conf; - struct ieee80211_supported_band *sband; const struct cfg80211_bss_ies *ies; - int shift; + int shift = ieee80211_vif_get_shift(&sdata->vif); u32 rate_flags; - sband = local->hw.wiphy->bands[cbss->channel->band]; - - err = ieee80211_prep_channel(sdata, cbss); - if (err) { - sta_info_free(local, new_sta); - return -EINVAL; - } - shift = ieee80211_vif_get_shift(&sdata->vif); - rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (WARN_ON(!chanctx_conf)) { -- cgit v0.10.2 From a5fee9cb6255b9bba5a977f92cb4807eafb89db0 Mon Sep 17 00:00:00 2001 From: Moshe Benji Date: Sun, 14 Dec 2014 11:05:50 +0200 Subject: mac80211: handle power constraint and country IEs in RRM In beacons, handle the Country IE even if no Power Constraint IE is present, and, capability wise, also in case that the Radio Measurements capability is enabled. In cases where the Country IE should be handled and that the Power Constraint IE is not present, the Country IE alone will set the power limit (and not both Country and Power Constraint IEs). Signed-off-by: Moshe Benji Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 877b647..38129dc 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1287,8 +1287,11 @@ ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata, country_ie_len -= 3; } - if (have_chan_pwr) + if (have_chan_pwr && pwr_constr_elem) *pwr_reduction = *pwr_constr_elem; + else + *pwr_reduction = 0; + return have_chan_pwr; } @@ -1317,10 +1320,11 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, int chan_pwr = 0, pwr_reduction_80211h = 0; int pwr_level_cisco, pwr_level_80211h; int new_ap_level; + __le16 capab = mgmt->u.probe_resp.capab_info; - if (country_ie && pwr_constr_ie && - mgmt->u.probe_resp.capab_info & - cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT)) { + if (country_ie && + (capab & cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT) || + capab & cpu_to_le16(WLAN_CAPABILITY_RADIO_MEASURE))) { has_80211h_pwr = ieee80211_find_80211h_pwr_constr( sdata, channel, country_ie, country_ie_len, pwr_constr_ie, &chan_pwr, &pwr_reduction_80211h); -- cgit v0.10.2 From 1c45c5ce324fec967dca5993f79b54769da410dc Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 14 Dec 2014 11:05:51 +0200 Subject: mac80211: update sta bw on ht chanwidth action frame Commit e1a0c6b ("mac80211: stop toggling IEEE80211_HT_CAP_SUP_WIDTH_20_40") mistakenly removed the actual update of sta->sta.bandwidth. Refactor ieee80211_sta_cur_vht_bw() into multiple functions (calculate caps-bw and chandef-bw separately, and min them with cur_max_bandwidth). On ht chanwidth action frame set only cur_max_bandwidth (according to the sta capabilities) and recalc the sta bw. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index cc6e964..4f45cab 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1704,6 +1704,7 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, const struct ieee80211_vht_cap *vht_cap_ie, struct sta_info *sta); +enum ieee80211_sta_rx_bandwidth ieee80211_sta_cap_rx_bw(struct sta_info *sta); enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta); void ieee80211_sta_set_rx_nss(struct sta_info *sta); u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 49c23bd..444ebff 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2597,7 +2597,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: { struct ieee80211_supported_band *sband; u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth; - enum ieee80211_sta_rx_bandwidth new_bw; + enum ieee80211_sta_rx_bandwidth max_bw, new_bw; /* If it doesn't support 40 MHz it can't change ... */ if (!(rx->sta->sta.ht_cap.cap & @@ -2605,13 +2605,18 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) goto handled; if (chanwidth == IEEE80211_HT_CHANWIDTH_20MHZ) - new_bw = IEEE80211_STA_RX_BW_20; + max_bw = IEEE80211_STA_RX_BW_20; else - new_bw = ieee80211_sta_cur_vht_bw(rx->sta); + max_bw = ieee80211_sta_cap_rx_bw(rx->sta); + + /* set cur_max_bandwidth and recalc sta bw */ + rx->sta->cur_max_bandwidth = max_bw; + new_bw = ieee80211_sta_cur_vht_bw(rx->sta); if (rx->sta->sta.bandwidth == new_bw) goto handled; + rx->sta->sta.bandwidth = new_bw; sband = rx->local->hw.wiphy->bands[status->band]; rate_control_rate_update(local, sband, rx->sta, diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index bc9e8fc..85f9596 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -269,51 +269,54 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta); } -enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta) +enum ieee80211_sta_rx_bandwidth ieee80211_sta_cap_rx_bw(struct sta_info *sta) { - struct ieee80211_sub_if_data *sdata = sta->sdata; - u32 cap = sta->sta.vht_cap.cap; - enum ieee80211_sta_rx_bandwidth bw; + struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; + u32 cap_width; - if (!sta->sta.vht_cap.vht_supported) { - bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? - IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; - goto check_max; - } + if (!vht_cap->vht_supported) + return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? + IEEE80211_STA_RX_BW_40 : + IEEE80211_STA_RX_BW_20; - switch (sdata->vif.bss_conf.chandef.width) { - default: - WARN_ON_ONCE(1); - /* fall through */ + cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; + + if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ || + cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) + return IEEE80211_STA_RX_BW_160; + + return IEEE80211_STA_RX_BW_80; +} + +static enum ieee80211_sta_rx_bandwidth +ieee80211_chan_width_to_rx_bw(enum nl80211_chan_width width) +{ + switch (width) { case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: - bw = IEEE80211_STA_RX_BW_20; - break; + return IEEE80211_STA_RX_BW_20; case NL80211_CHAN_WIDTH_40: - bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? - IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; - break; + return IEEE80211_STA_RX_BW_40; + case NL80211_CHAN_WIDTH_80: + return IEEE80211_STA_RX_BW_80; case NL80211_CHAN_WIDTH_160: - if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) == - IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) { - bw = IEEE80211_STA_RX_BW_160; - break; - } - /* fall through */ case NL80211_CHAN_WIDTH_80P80: - if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) == - IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) { - bw = IEEE80211_STA_RX_BW_160; - break; - } - /* fall through */ - case NL80211_CHAN_WIDTH_80: - bw = IEEE80211_STA_RX_BW_80; + return IEEE80211_STA_RX_BW_160; + default: + WARN_ON_ONCE(1); + return IEEE80211_STA_RX_BW_20; } +} + +enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta) +{ + struct ieee80211_sub_if_data *sdata = sta->sdata; + enum ieee80211_sta_rx_bandwidth bw; + + bw = ieee80211_chan_width_to_rx_bw(sdata->vif.bss_conf.chandef.width); + bw = min(bw, ieee80211_sta_cap_rx_bw(sta)); + bw = min(bw, sta->cur_max_bandwidth); - check_max: - if (bw > sta->cur_max_bandwidth) - bw = sta->cur_max_bandwidth; return bw; } -- cgit v0.10.2 From 1a952c94b544cbf01291186b8c6ca25fbb5873f2 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Sun, 14 Dec 2014 11:05:52 +0200 Subject: mac80211: remove unused variable in ieee80211_parse_ch_switch_ie() The ht_oper variable is assigned a value, but never used in ieee80211_parse_ch_switch_ie(). Remove it. Signed-off-by: Luciano Coelho Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index efeba56..06e6ac8 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -34,19 +34,15 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, struct cfg80211_chan_def new_vht_chandef = {}; const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; - const struct ieee80211_ht_operation *ht_oper; int secondary_channel_offset = -1; sec_chan_offs = elems->sec_chan_offs; wide_bw_chansw_ie = elems->wide_bw_chansw_ie; - ht_oper = elems->ht_operation; if (sta_flags & (IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_40MHZ)) { sec_chan_offs = NULL; wide_bw_chansw_ie = NULL; - /* only used for bandwidth here */ - ht_oper = NULL; } if (sta_flags & IEEE80211_STA_DISABLE_VHT) -- cgit v0.10.2 From 0f8b82456178d558f14011e06ebf9af937c4b197 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 14 Dec 2014 11:05:53 +0200 Subject: mac80211: avoid reconfig if no interfaces are up If there are no interfaces up, there is no reason to continue the reconfig flow. The current code might end up calling driver callbacks (e.g. resume(), reconfig_complete()) while the driver is already stopped. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 974ebe7..0f9bf47 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1735,6 +1735,10 @@ int ieee80211_reconfig(struct ieee80211_local *local) struct cfg80211_sched_scan_request *sched_scan_req; bool sched_scan_stopped = false; + /* nothing to do if HW shouldn't run */ + if (!local->open_count) + goto wake_up; + #ifdef CONFIG_PM if (local->suspended) local->resuming = true; @@ -1756,9 +1760,6 @@ int ieee80211_reconfig(struct ieee80211_local *local) reconfig_due_to_wowlan = true; } #endif - /* everything else happens only if HW was up & running */ - if (!local->open_count) - goto wake_up; /* * Upon resume hardware can sometimes be goofy due to @@ -2042,7 +2043,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) * If this is for hw restart things are still running. * We may want to change that later, however. */ - if (!local->suspended || reconfig_due_to_wowlan) + if (local->open_count && (!local->suspended || reconfig_due_to_wowlan)) drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART); if (!local->suspended) @@ -2054,7 +2055,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) mb(); local->resuming = false; - if (!reconfig_due_to_wowlan) + if (local->open_count && !reconfig_due_to_wowlan) drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND); list_for_each_entry(sdata, &local->interfaces, list) { -- cgit v0.10.2 From 31a60ed1e95ab8afbadb65599bef12b195080a0c Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Mon, 15 Dec 2014 13:25:38 +0200 Subject: nl80211: Convert sched_scan_req pointer to RCU pointer Because of possible races when accessing sched_scan_req pointer in rdev, the sched_scan_req is converted to RCU pointer. Signed-off-by: Jukka Rissanen Signed-off-by: Johannes Berg diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 4bc1fc9..45d4d72 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1516,6 +1516,7 @@ struct cfg80211_match_set { * @mac_addr_mask: MAC address mask used with randomisation, bits that * are 0 in the mask should be randomised, bits that are 1 should * be taken from the @mac_addr + * @rcu_head: RCU callback used to free the struct */ struct cfg80211_sched_scan_request { struct cfg80211_ssid *ssids; @@ -1537,6 +1538,7 @@ struct cfg80211_sched_scan_request { struct wiphy *wiphy; struct net_device *dev; unsigned long scan_start; + struct rcu_head rcu_head; /* keep last */ struct ieee80211_channel *channels[0]; diff --git a/net/wireless/core.c b/net/wireless/core.c index b661fcc..0743449 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -867,6 +867,7 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct net_device *dev = wdev->netdev; + struct cfg80211_sched_scan_request *sched_scan_req; ASSERT_RTNL(); ASSERT_WDEV_LOCK(wdev); @@ -877,7 +878,8 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev, break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: - if (rdev->sched_scan_req && dev == rdev->sched_scan_req->dev) + sched_scan_req = rtnl_dereference(rdev->sched_scan_req); + if (sched_scan_req && dev == sched_scan_req->dev) __cfg80211_stop_sched_scan(rdev, false); #ifdef CONFIG_CFG80211_WEXT @@ -956,6 +958,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev; + struct cfg80211_sched_scan_request *sched_scan_req; if (!wdev) return NOTIFY_DONE; @@ -1021,8 +1024,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, ___cfg80211_scan_done(rdev, false); } - if (WARN_ON(rdev->sched_scan_req && - rdev->sched_scan_req->dev == wdev->netdev)) { + sched_scan_req = rtnl_dereference(rdev->sched_scan_req); + if (WARN_ON(sched_scan_req && + sched_scan_req->dev == wdev->netdev)) { __cfg80211_stop_sched_scan(rdev, false); } diff --git a/net/wireless/core.h b/net/wireless/core.h index e87cae5..e82030c 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -70,7 +70,7 @@ struct cfg80211_registered_device { u32 bss_generation; struct cfg80211_scan_request *scan_req; /* protected by RTNL */ struct sk_buff *scan_msg; - struct cfg80211_sched_scan_request *sched_scan_req; + struct cfg80211_sched_scan_request __rcu *sched_scan_req; unsigned long suspend_at; struct work_struct scan_done_wk; struct work_struct sched_scan_results_wk; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5b1907f..bacdf22 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6190,6 +6190,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_sched_scan_request *sched_scan_req; int err; if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || @@ -6199,27 +6200,29 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, if (rdev->sched_scan_req) return -EINPROGRESS; - rdev->sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev, - info->attrs); - err = PTR_ERR_OR_ZERO(rdev->sched_scan_req); + sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev, + info->attrs); + + err = PTR_ERR_OR_ZERO(sched_scan_req); if (err) goto out_err; - err = rdev_sched_scan_start(rdev, dev, rdev->sched_scan_req); + err = rdev_sched_scan_start(rdev, dev, sched_scan_req); if (err) goto out_free; - rdev->sched_scan_req->dev = dev; - rdev->sched_scan_req->wiphy = &rdev->wiphy; + sched_scan_req->dev = dev; + sched_scan_req->wiphy = &rdev->wiphy; + + rcu_assign_pointer(rdev->sched_scan_req, sched_scan_req); nl80211_send_sched_scan(rdev, dev, NL80211_CMD_START_SCHED_SCAN); return 0; out_free: - kfree(rdev->sched_scan_req); + kfree(sched_scan_req); out_err: - rdev->sched_scan_req = NULL; return err; } diff --git a/net/wireless/scan.c b/net/wireless/scan.c index bda39f1..c705c3e 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -257,7 +257,7 @@ void __cfg80211_sched_scan_results(struct work_struct *wk) rtnl_lock(); - request = rdev->sched_scan_req; + request = rtnl_dereference(rdev->sched_scan_req); /* we don't have sched_scan_req anymore if the scan is stopping */ if (request) { @@ -279,7 +279,8 @@ void cfg80211_sched_scan_results(struct wiphy *wiphy) { trace_cfg80211_sched_scan_results(wiphy); /* ignore if we're not scanning */ - if (wiphy_to_rdev(wiphy)->sched_scan_req) + + if (rcu_access_pointer(wiphy_to_rdev(wiphy)->sched_scan_req)) queue_work(cfg80211_wq, &wiphy_to_rdev(wiphy)->sched_scan_results_wk); } @@ -308,6 +309,7 @@ EXPORT_SYMBOL(cfg80211_sched_scan_stopped); int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, bool driver_initiated) { + struct cfg80211_sched_scan_request *sched_scan_req; struct net_device *dev; ASSERT_RTNL(); @@ -315,7 +317,8 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, if (!rdev->sched_scan_req) return -ENOENT; - dev = rdev->sched_scan_req->dev; + sched_scan_req = rtnl_dereference(rdev->sched_scan_req); + dev = sched_scan_req->dev; if (!driver_initiated) { int err = rdev_sched_scan_stop(rdev, dev); @@ -325,8 +328,8 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED); - kfree(rdev->sched_scan_req); - rdev->sched_scan_req = NULL; + RCU_INIT_POINTER(rdev->sched_scan_req, NULL); + kfree_rcu(sched_scan_req, rcu_head); return 0; } -- cgit v0.10.2 From 93a1e86ce10e4898f9ca9cd09d659a8a7780ee5e Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Mon, 15 Dec 2014 13:25:39 +0200 Subject: nl80211: Stop scheduled scan if netlink client disappears An attribute NL80211_ATTR_SOCKET_OWNER can be set by the scan initiator. If present, the attribute will cause the scan to be stopped if the client dies. Signed-off-by: Jukka Rissanen Signed-off-by: Johannes Berg diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 45d4d72..bd672ea 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1517,6 +1517,8 @@ struct cfg80211_match_set { * are 0 in the mask should be randomised, bits that are 1 should * be taken from the @mac_addr * @rcu_head: RCU callback used to free the struct + * @owner_nlportid: netlink portid of owner (if this should is a request + * owned by a particular socket) */ struct cfg80211_sched_scan_request { struct cfg80211_ssid *ssids; @@ -1539,6 +1541,7 @@ struct cfg80211_sched_scan_request { struct net_device *dev; unsigned long scan_start; struct rcu_head rcu_head; + u32 owner_nlportid; /* keep last */ struct ieee80211_channel *channels[0]; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index b3ada0b..c0383e9 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1672,6 +1672,9 @@ enum nl80211_commands { * @NL80211_ATTR_SOCKET_OWNER: Flag attribute, if set during interface * creation then the new interface will be owned by the netlink socket * that created it and will be destroyed when the socket is closed. + * If set during scheduled scan start then the new scan req will be + * owned by the netlink socket that created it and the scheduled scan will + * be stopped when the socket is closed. * * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is * the TDLS link initiator. diff --git a/net/wireless/core.c b/net/wireless/core.c index 0743449..456e4c3 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -321,6 +321,20 @@ static void cfg80211_destroy_iface_wk(struct work_struct *work) rtnl_unlock(); } +static void cfg80211_sched_scan_stop_wk(struct work_struct *work) +{ + struct cfg80211_registered_device *rdev; + + rdev = container_of(work, struct cfg80211_registered_device, + sched_scan_stop_wk); + + rtnl_lock(); + + __cfg80211_stop_sched_scan(rdev, false); + + rtnl_unlock(); +} + /* exported functions */ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, @@ -407,6 +421,7 @@ use_default_name: INIT_LIST_HEAD(&rdev->destroy_list); spin_lock_init(&rdev->destroy_list_lock); INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk); + INIT_WORK(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk); #ifdef CONFIG_CFG80211_DEFAULT_PS rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; @@ -787,6 +802,7 @@ void wiphy_unregister(struct wiphy *wiphy) flush_work(&rdev->event_work); cancel_delayed_work_sync(&rdev->dfs_update_channels_wk); flush_work(&rdev->destroy_work); + flush_work(&rdev->sched_scan_stop_wk); #ifdef CONFIG_PM if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup) diff --git a/net/wireless/core.h b/net/wireless/core.h index e82030c..801cd49 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -91,6 +91,8 @@ struct cfg80211_registered_device { struct list_head destroy_list; struct work_struct destroy_work; + struct work_struct sched_scan_stop_wk; + /* must be last because of the way we do wiphy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ struct wiphy wiphy __aligned(NETDEV_ALIGN); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index bacdf22..7029201 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6214,6 +6214,9 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, sched_scan_req->dev = dev; sched_scan_req->wiphy = &rdev->wiphy; + if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) + sched_scan_req->owner_nlportid = info->snd_portid; + rcu_assign_pointer(rdev->sched_scan_req, sched_scan_req); nl80211_send_sched_scan(rdev, dev, @@ -12618,6 +12621,13 @@ static int nl80211_netlink_notify(struct notifier_block * nb, list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { bool schedule_destroy_work = false; + bool schedule_scan_stop = false; + struct cfg80211_sched_scan_request *sched_scan_req = + rcu_dereference(rdev->sched_scan_req); + + if (sched_scan_req && notify->portid && + sched_scan_req->owner_nlportid == notify->portid) + schedule_scan_stop = true; list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) { cfg80211_mlme_unregister_socket(wdev, notify->portid); @@ -12648,6 +12658,12 @@ static int nl80211_netlink_notify(struct notifier_block * nb, spin_unlock(&rdev->destroy_list_lock); schedule_work(&rdev->destroy_work); } + } else if (schedule_scan_stop) { + sched_scan_req->owner_nlportid = 0; + + if (rdev->ops->sched_scan_stop && + rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) + schedule_work(&rdev->sched_scan_stop_wk); } } -- cgit v0.10.2 From 79f241b41b5f581c6be66785ab8b9c8e3b1651c7 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 17 Dec 2014 18:00:44 +0200 Subject: nl80211: increase the max number of rules in regdomain Some network cards (Intel) produce per-channel regdomains and rely on cfg80211 to merge rules as needed. This hits the max rules limit and fails. Signed-off-by: Arik Nemtsov Signed-off-by: Johannes Berg diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index c0383e9..18cb0aa 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2112,7 +2112,7 @@ enum nl80211_attrs { #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_HT_RATES 77 -#define NL80211_MAX_SUPP_REG_RULES 32 +#define NL80211_MAX_SUPP_REG_RULES 64 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 #define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 #define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 -- cgit v0.10.2 From 5859223e0c4f69ab36dd931ea7058c3b1b2abb41 Mon Sep 17 00:00:00 2001 From: Toshi Kikuchi Date: Fri, 12 Dec 2014 10:58:05 -0800 Subject: Bluetooth: btusb: support public address configuration for ath3012 Set hdev->set_bdaddr handler for ath3012. It sends the vendor specific HCI command to change the public address. The change doesn't persist across power cycle. Signed-off-by: Toshi Kikuchi Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 19cf2cf..c926835 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1943,6 +1943,31 @@ static int btusb_set_bdaddr_bcm(struct hci_dev *hdev, const bdaddr_t *bdaddr) return 0; } +static int btusb_set_bdaddr_ath3012(struct hci_dev *hdev, + const bdaddr_t *bdaddr) +{ + struct sk_buff *skb; + u8 buf[10]; + long ret; + + buf[0] = 0x01; + buf[1] = 0x01; + buf[2] = 0x00; + buf[3] = sizeof(bdaddr_t); + memcpy(buf + 4, bdaddr, sizeof(bdaddr_t)); + + skb = __hci_cmd_sync(hdev, 0xfc0b, sizeof(buf), buf, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: Change address command failed (%ld)", + hdev->name, ret); + return ret; + } + kfree_skb(skb); + + return 0; +} + static int btusb_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -2058,6 +2083,9 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_INTEL_BOOT) set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); + if (id->driver_info & BTUSB_ATH3012) + hdev->set_bdaddr = btusb_set_bdaddr_ath3012; + /* Interface numbers are hardcoded in the specification */ data->isoc = usb_ifnum_to_if(data->udev, 1); -- cgit v0.10.2 From b40d6376ff470572e2fafb20ca06a68f2d7940cb Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 10 Dec 2014 15:33:11 +0100 Subject: nl802154: introduce cca mode enums This patch adds enums for 802.15.4 specific CCA settings. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/include/net/nl802154.h b/include/net/nl802154.h index 6dbd406..86c1b2f 100644 --- a/include/net/nl802154.h +++ b/include/net/nl802154.h @@ -119,4 +119,47 @@ enum nl802154_iftype { NL802154_IFTYPE_MAX = NUM_NL802154_IFTYPES - 1 }; +/** + * enum nl802154_cca_modes - cca modes + * + * @__NL802154_CCA_INVALID: cca mode number 0 is reserved + * @NL802154_CCA_ENERGY: Energy above threshold + * @NL802154_CCA_CARRIER: Carrier sense only + * @NL802154_CCA_ENERGY_CARRIER: Carrier sense with energy above threshold + * @NL802154_CCA_ALOHA: CCA shall always report an idle medium + * @NL802154_CCA_UWB_SHR: UWB preamble sense based on the SHR of a frame + * @NL802154_CCA_UWB_MULTIPEXED: UWB preamble sense based on the packet with + * the multiplexed preamble + * @__NL802154_CCA_ATTR_AFTER_LAST: Internal + * @NL802154_CCA_ATTR_MAX: Maximum CCA attribute number + */ +enum nl802154_cca_modes { + __NL802154_CCA_INVALID, + NL802154_CCA_ENERGY, + NL802154_CCA_CARRIER, + NL802154_CCA_ENERGY_CARRIER, + NL802154_CCA_ALOHA, + NL802154_CCA_UWB_SHR, + NL802154_CCA_UWB_MULTIPEXED, + + /* keep last */ + __NL802154_CCA_ATTR_AFTER_LAST, + NL802154_CCA_ATTR_MAX = __NL802154_CCA_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl802154_cca_opts - additional options for cca modes + * + * @NL802154_CCA_OPT_ENERGY_CARRIER_OR: NL802154_CCA_ENERGY_CARRIER with OR + * @NL802154_CCA_OPT_ENERGY_CARRIER_AND: NL802154_CCA_ENERGY_CARRIER with AND + */ +enum nl802154_cca_opts { + NL802154_CCA_OPT_ENERGY_CARRIER_AND, + NL802154_CCA_OPT_ENERGY_CARRIER_OR, + + /* keep last */ + __NL802154_CCA_OPT_ATTR_AFTER_LAST, + NL802154_CCA_OPT_ATTR_MAX = __NL802154_CCA_OPT_ATTR_AFTER_LAST - 1 +}; + #endif /* __NL802154_H */ -- cgit v0.10.2 From 7fe9a3882bb37195c41ab125a0f2852398d2646a Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 10 Dec 2014 15:33:12 +0100 Subject: ieee802154: rework cca setting The current cca setting handle is a driver specific call. We need to introduce some 802.15.4 specific layer and mapping 802.15.4 cca modes to driver specific ones inside the 802.15.4 driver. This patch will add such 802.15.4 layer and mapping the cca settings to driver specific ones. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 1c01356..1ac46ba 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -1146,11 +1146,37 @@ at86rf230_set_lbt(struct ieee802154_hw *hw, bool on) } static int -at86rf230_set_cca_mode(struct ieee802154_hw *hw, u8 mode) +at86rf230_set_cca_mode(struct ieee802154_hw *hw, + const struct wpan_phy_cca *cca) { struct at86rf230_local *lp = hw->priv; + u8 val; - return at86rf230_write_subreg(lp, SR_CCA_MODE, mode); + /* mapping 802.15.4 to driver spec */ + switch (cca->mode) { + case NL802154_CCA_ENERGY: + val = 1; + break; + case NL802154_CCA_CARRIER: + val = 2; + break; + case NL802154_CCA_ENERGY_CARRIER: + switch (cca->opt) { + case NL802154_CCA_OPT_ENERGY_CARRIER_AND: + val = 3; + break; + case NL802154_CCA_OPT_ENERGY_CARRIER_OR: + val = 0; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return at86rf230_write_subreg(lp, SR_CCA_MODE, val); } static int diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index 7f713ac..6ee2618 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -56,6 +56,11 @@ struct cfg802154_ops { struct wpan_dev *wpan_dev, bool mode); }; +struct wpan_phy_cca { + enum nl802154_cca_modes mode; + enum nl802154_cca_opts opt; +}; + struct wpan_phy { struct mutex pib_lock; @@ -76,7 +81,7 @@ struct wpan_phy { u8 current_page; u32 channels_supported[IEEE802154_MAX_PAGE + 1]; s8 transmit_power; - u8 cca_mode; + struct wpan_phy_cca cca; __le64 perm_extended_addr; diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index 83bb8a73..94a2970 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -28,6 +28,8 @@ #include #include +#include + struct ieee802154_sechdr { #if defined(__LITTLE_ENDIAN_BITFIELD) u8 level:3, @@ -337,7 +339,7 @@ struct ieee802154_mac_params { s8 frame_retries; bool lbt; - u8 cca_mode; + struct wpan_phy_cca cca; s32 cca_ed_level; }; diff --git a/include/net/mac802154.h b/include/net/mac802154.h index c823d91..8506478 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -20,6 +20,8 @@ #include #include +#include + /* General MAC frame format: * 2 bytes: Frame Control * 1 byte: Sequence Number @@ -212,7 +214,8 @@ struct ieee802154_ops { unsigned long changed); int (*set_txpower)(struct ieee802154_hw *hw, int db); int (*set_lbt)(struct ieee802154_hw *hw, bool on); - int (*set_cca_mode)(struct ieee802154_hw *hw, u8 mode); + int (*set_cca_mode)(struct ieee802154_hw *hw, + const struct wpan_phy_cca *cca); int (*set_cca_ed_level)(struct ieee802154_hw *hw, s32 level); int (*set_csma_params)(struct ieee802154_hw *hw, diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c index cd91949..3c902e9 100644 --- a/net/ieee802154/nl-mac.c +++ b/net/ieee802154/nl-mac.c @@ -121,7 +121,7 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid, params.transmit_power) || nla_put_u8(msg, IEEE802154_ATTR_LBT_ENABLED, params.lbt) || nla_put_u8(msg, IEEE802154_ATTR_CCA_MODE, - params.cca_mode) || + params.cca.mode) || nla_put_s32(msg, IEEE802154_ATTR_CCA_ED_LEVEL, params.cca_ed_level) || nla_put_u8(msg, IEEE802154_ATTR_CSMA_RETRIES, @@ -516,7 +516,7 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info) params.lbt = nla_get_u8(info->attrs[IEEE802154_ATTR_LBT_ENABLED]); if (info->attrs[IEEE802154_ATTR_CCA_MODE]) - params.cca_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_CCA_MODE]); + params.cca.mode = nla_get_u8(info->attrs[IEEE802154_ATTR_CCA_MODE]); if (info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) params.cca_ed_level = nla_get_s32(info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]); diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index 8896477..1efbe42 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -291,7 +291,7 @@ static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev, /* cca mode */ if (nla_put_u8(msg, NL802154_ATTR_CCA_MODE, - rdev->wpan_phy.cca_mode)) + rdev->wpan_phy.cca.mode)) goto nla_put_failure; if (nla_put_s8(msg, NL802154_ATTR_TX_POWER, diff --git a/net/ieee802154/sysfs.c b/net/ieee802154/sysfs.c index 1613b9c..dff55c2 100644 --- a/net/ieee802154/sysfs.c +++ b/net/ieee802154/sysfs.c @@ -68,7 +68,7 @@ static DEVICE_ATTR_RO(name) MASTER_SHOW(current_channel, "%d"); MASTER_SHOW(current_page, "%d"); MASTER_SHOW(transmit_power, "%d +- 1 dB"); -MASTER_SHOW(cca_mode, "%d"); +MASTER_SHOW_COMPLEX(cca_mode, "%d", phy->cca.mode); static ssize_t channels_supported_show(struct device *dev, struct device_attribute *attr, diff --git a/net/mac802154/driver-ops.h b/net/mac802154/driver-ops.h index f21e864..98180a9 100644 --- a/net/mac802154/driver-ops.h +++ b/net/mac802154/driver-ops.h @@ -70,7 +70,8 @@ static inline int drv_set_tx_power(struct ieee802154_local *local, s8 dbm) return local->ops->set_txpower(&local->hw, dbm); } -static inline int drv_set_cca_mode(struct ieee802154_local *local, u8 cca_mode) +static inline int drv_set_cca_mode(struct ieee802154_local *local, + const struct wpan_phy_cca *cca) { might_sleep(); @@ -79,7 +80,7 @@ static inline int drv_set_cca_mode(struct ieee802154_local *local, u8 cca_mode) return -EOPNOTSUPP; } - return local->ops->set_cca_mode(&local->hw, cca_mode); + return local->ops->set_cca_mode(&local->hw, cca); } static inline int drv_set_lbt_mode(struct ieee802154_local *local, bool mode) diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c index 6aacb18..bdccb4e 100644 --- a/net/mac802154/mac_cmd.c +++ b/net/mac802154/mac_cmd.c @@ -81,7 +81,7 @@ static int mac802154_set_mac_params(struct net_device *dev, /* PHY */ wpan_dev->wpan_phy->transmit_power = params->transmit_power; - wpan_dev->wpan_phy->cca_mode = params->cca_mode; + wpan_dev->wpan_phy->cca = params->cca; wpan_dev->wpan_phy->cca_ed_level = params->cca_ed_level; /* MAC */ @@ -98,7 +98,7 @@ static int mac802154_set_mac_params(struct net_device *dev, } if (local->hw.flags & IEEE802154_HW_CCA_MODE) { - ret = drv_set_cca_mode(local, params->cca_mode); + ret = drv_set_cca_mode(local, ¶ms->cca); if (ret < 0) return ret; } @@ -122,7 +122,7 @@ static void mac802154_get_mac_params(struct net_device *dev, /* PHY */ params->transmit_power = wpan_dev->wpan_phy->transmit_power; - params->cca_mode = wpan_dev->wpan_phy->cca_mode; + params->cca = wpan_dev->wpan_phy->cca; params->cca_ed_level = wpan_dev->wpan_phy->cca_ed_level; /* MAC */ -- cgit v0.10.2 From ba2a9506a76450568cbc0d51626d94cf8528c0c7 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 10 Dec 2014 15:33:13 +0100 Subject: nl802154: introduce support for cca settings This patch adds support for setting cca parameters via nl802154. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index 6ee2618..eeda676 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -25,6 +25,7 @@ #include struct wpan_phy; +struct wpan_phy_cca; struct cfg802154_ops { struct net_device * (*add_virtual_intf_deprecated)(struct wpan_phy *wpan_phy, @@ -39,6 +40,8 @@ struct cfg802154_ops { int (*del_virtual_intf)(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev); int (*set_channel)(struct wpan_phy *wpan_phy, u8 page, u8 channel); + int (*set_cca_mode)(struct wpan_phy *wpan_phy, + const struct wpan_phy_cca *cca); int (*set_pan_id)(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, __le16 pan_id); int (*set_short_addr)(struct wpan_phy *wpan_phy, diff --git a/include/net/nl802154.h b/include/net/nl802154.h index 86c1b2f..f8b5bc9 100644 --- a/include/net/nl802154.h +++ b/include/net/nl802154.h @@ -82,7 +82,7 @@ enum nl802154_attrs { NL802154_ATTR_TX_POWER, NL802154_ATTR_CCA_MODE, - NL802154_ATTR_CCA_MODE3_AND, + NL802154_ATTR_CCA_OPT, NL802154_ATTR_CCA_ED_LEVEL, NL802154_ATTR_MAX_FRAME_RETRIES, diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index 1efbe42..a25b9bb 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -209,7 +209,8 @@ static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = { [NL802154_ATTR_TX_POWER] = { .type = NLA_S8, }, - [NL802154_ATTR_CCA_MODE] = { .type = NLA_U8, }, + [NL802154_ATTR_CCA_MODE] = { .type = NLA_U32, }, + [NL802154_ATTR_CCA_OPT] = { .type = NLA_U32, }, [NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, }, @@ -290,10 +291,16 @@ static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev, goto nla_put_failure; /* cca mode */ - if (nla_put_u8(msg, NL802154_ATTR_CCA_MODE, - rdev->wpan_phy.cca.mode)) + if (nla_put_u32(msg, NL802154_ATTR_CCA_MODE, + rdev->wpan_phy.cca.mode)) goto nla_put_failure; + if (rdev->wpan_phy.cca.mode == NL802154_CCA_ENERGY_CARRIER) { + if (nla_put_u32(msg, NL802154_ATTR_CCA_OPT, + rdev->wpan_phy.cca.opt)) + goto nla_put_failure; + } + if (nla_put_s8(msg, NL802154_ATTR_TX_POWER, rdev->wpan_phy.transmit_power)) goto nla_put_failure; @@ -622,6 +629,31 @@ static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info) return rdev_set_channel(rdev, page, channel); } +static int nl802154_set_cca_mode(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct wpan_phy_cca cca; + + if (!info->attrs[NL802154_ATTR_CCA_MODE]) + return -EINVAL; + + cca.mode = nla_get_u32(info->attrs[NL802154_ATTR_CCA_MODE]); + /* checking 802.15.4 constraints */ + if (cca.mode < NL802154_CCA_ENERGY || cca.mode > NL802154_CCA_ATTR_MAX) + return -EINVAL; + + if (cca.mode == NL802154_CCA_ENERGY_CARRIER) { + if (!info->attrs[NL802154_ATTR_CCA_OPT]) + return -EINVAL; + + cca.opt = nla_get_u32(info->attrs[NL802154_ATTR_CCA_OPT]); + if (cca.opt > NL802154_CCA_OPT_ATTR_MAX) + return -EINVAL; + } + + return rdev_set_cca_mode(rdev, &cca); +} + static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info) { struct cfg802154_registered_device *rdev = info->user_ptr[0]; @@ -895,6 +927,14 @@ static const struct genl_ops nl802154_ops[] = { NL802154_FLAG_NEED_RTNL, }, { + .cmd = NL802154_CMD_SET_CCA_MODE, + .doit = nl802154_set_cca_mode, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | + NL802154_FLAG_NEED_RTNL, + }, + { .cmd = NL802154_CMD_SET_PAN_ID, .doit = nl802154_set_pan_id, .policy = nl802154_policy, diff --git a/net/ieee802154/rdev-ops.h b/net/ieee802154/rdev-ops.h index aff54fbd..7c46732 100644 --- a/net/ieee802154/rdev-ops.h +++ b/net/ieee802154/rdev-ops.h @@ -42,6 +42,13 @@ rdev_set_channel(struct cfg802154_registered_device *rdev, u8 page, u8 channel) } static inline int +rdev_set_cca_mode(struct cfg802154_registered_device *rdev, + const struct wpan_phy_cca *cca) +{ + return rdev->ops->set_cca_mode(&rdev->wpan_phy, cca); +} + +static inline int rdev_set_pan_id(struct cfg802154_registered_device *rdev, struct wpan_dev *wpan_dev, __le16 pan_id) { diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c index c035708..7d31da5 100644 --- a/net/mac802154/cfg.c +++ b/net/mac802154/cfg.c @@ -87,6 +87,26 @@ ieee802154_set_channel(struct wpan_phy *wpan_phy, u8 page, u8 channel) } static int +ieee802154_set_cca_mode(struct wpan_phy *wpan_phy, + const struct wpan_phy_cca *cca) +{ + struct ieee802154_local *local = wpan_phy_priv(wpan_phy); + int ret; + + ASSERT_RTNL(); + + /* check if phy support this setting */ + if (!(local->hw.flags & IEEE802154_HW_CCA_MODE)) + return -EOPNOTSUPP; + + ret = drv_set_cca_mode(local, cca); + if (!ret) + wpan_phy->cca = *cca; + + return ret; +} + +static int ieee802154_set_pan_id(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, __le16 pan_id) { @@ -201,6 +221,7 @@ const struct cfg802154_ops mac802154_config_ops = { .add_virtual_intf = ieee802154_add_iface, .del_virtual_intf = ieee802154_del_iface, .set_channel = ieee802154_set_channel, + .set_cca_mode = ieee802154_set_cca_mode, .set_pan_id = ieee802154_set_pan_id, .set_short_addr = ieee802154_set_short_addr, .set_backoff_exponent = ieee802154_set_backoff_exponent, -- cgit v0.10.2 From b48a7c1880e10be30a7b61db0108498693d41eb5 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 10 Dec 2014 15:33:14 +0100 Subject: at86rf230: add reset state cca handling This patch adds the default cca setting after device reset for at86rf230 driver. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 1ac46ba..0626e7d 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -1441,6 +1441,8 @@ at86rf230_detect_device(struct at86rf230_local *lp) IEEE802154_HW_TXPOWER | IEEE802154_HW_ARET | IEEE802154_HW_AFILT | IEEE802154_HW_PROMISCUOUS; + lp->hw->phy->cca.mode = NL802154_CCA_ENERGY; + switch (part) { case 2: chip = "at86rf230"; -- cgit v0.10.2 From ca28849d01cc72876b1c5183c7fbef4ee8f2133b Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Fri, 12 Dec 2014 12:45:28 +0100 Subject: ieee802154/at86rf230: Remove unneeded blank lines Signed-off-by: Stefan Schmidt Acked-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 0626e7d..318bc4b 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -524,7 +524,6 @@ at86rf230_async_state_assert(void *context) } } - dev_warn(&lp->spi->dev, "unexcept state change from 0x%02x to 0x%02x. Actual state: 0x%02x\n", ctx->from_state, ctx->to_state, trx_state); } @@ -762,7 +761,6 @@ at86rf230_tx_trac_check(void *context) at86rf230_tx_on(context); } - static void at86rf230_tx_trac_status(void *context) { -- cgit v0.10.2 From e80fb5eea3fc0631624c71bf604d5b6244379d40 Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Fri, 12 Dec 2014 12:45:29 +0100 Subject: ieee802154/at86rf230: Align to opening parenthesis Signed-off-by: Stefan Schmidt Acked-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 318bc4b..3523d9e 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -1080,7 +1080,7 @@ at86rf230_set_hw_addr_filt(struct ieee802154_hw *hw, u16 addr = le16_to_cpu(filt->short_addr); dev_vdbg(&lp->spi->dev, - "at86rf230_set_hw_addr_filt called for saddr\n"); + "at86rf230_set_hw_addr_filt called for saddr\n"); __at86rf230_write(lp, RG_SHORT_ADDR_0, addr); __at86rf230_write(lp, RG_SHORT_ADDR_1, addr >> 8); } @@ -1089,7 +1089,7 @@ at86rf230_set_hw_addr_filt(struct ieee802154_hw *hw, u16 pan = le16_to_cpu(filt->pan_id); dev_vdbg(&lp->spi->dev, - "at86rf230_set_hw_addr_filt called for pan id\n"); + "at86rf230_set_hw_addr_filt called for pan id\n"); __at86rf230_write(lp, RG_PAN_ID_0, pan); __at86rf230_write(lp, RG_PAN_ID_1, pan >> 8); } @@ -1099,14 +1099,14 @@ at86rf230_set_hw_addr_filt(struct ieee802154_hw *hw, memcpy(addr, &filt->ieee_addr, 8); dev_vdbg(&lp->spi->dev, - "at86rf230_set_hw_addr_filt called for IEEE addr\n"); + "at86rf230_set_hw_addr_filt called for IEEE addr\n"); for (i = 0; i < 8; i++) __at86rf230_write(lp, RG_IEEE_ADDR_0 + i, addr[i]); } if (changed & IEEE802154_AFILT_PANC_CHANGED) { dev_vdbg(&lp->spi->dev, - "at86rf230_set_hw_addr_filt called for panc change\n"); + "at86rf230_set_hw_addr_filt called for panc change\n"); if (filt->pan_coord) at86rf230_write_subreg(lp, SR_AACK_I_AM_COORD, 1); else -- cgit v0.10.2 From 2b8b7e29c4e282edadfeda70e739694594efb7b5 Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Fri, 12 Dec 2014 12:45:30 +0100 Subject: ieee802154/at86rf230: Fix typo unkown -> unknown Signed-off-by: Stefan Schmidt Acked-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 3523d9e..2f66b8a 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -1474,7 +1474,7 @@ at86rf230_detect_device(struct at86rf230_local *lp) lp->hw->phy->symbol_duration = 16; break; default: - chip = "unkown"; + chip = "unknown"; rc = -ENOTSUPP; break; } -- cgit v0.10.2 From cda8c203b37bcacb79227538f3bd3bb26ab708c2 Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Fri, 12 Dec 2014 12:45:31 +0100 Subject: ieee802154/cc2520: Remove extra blank lines CC: Varka Bhadram Signed-off-by: Stefan Schmidt Acked-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c index f9df9fa..c2b7da3 100644 --- a/drivers/net/ieee802154/cc2520.c +++ b/drivers/net/ieee802154/cc2520.c @@ -513,7 +513,6 @@ err_tx: return rc; } - static int cc2520_rx(struct cc2520_private *priv) { u8 len = 0, lqi = 0, bytes = 1; @@ -947,7 +946,6 @@ static int cc2520_probe(struct spi_device *spi) if (ret) goto err_hw_init; - gpio_set_value(pdata->vreg, HIGH); usleep_range(100, 150); -- cgit v0.10.2 From 5c1be06a3dec9ae957e759dec7a6c012960e53bf Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Fri, 12 Dec 2014 12:45:32 +0100 Subject: ieee802154/mrf24j40: Fix typo begining -> beginning CC: Alan Ott Signed-off-by: Stefan Schmidt Acked-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index a200fa1..ec605c9 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -289,7 +289,7 @@ static int mrf24j40_read_rx_buf(struct mrf24j40 *devrec, goto out; /* Range check the RX FIFO length, accounting for the one-byte - * length field at the begining. */ + * length field at the beginning. */ if (rx_len > RX_FIFO_SIZE-1) { dev_err(printdev(devrec), "Invalid length read from device. Performing short read.\n"); rx_len = RX_FIFO_SIZE-1; -- cgit v0.10.2 From ce261bc3ff376ef5e85b682c760326893ab22ba5 Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Fri, 12 Dec 2014 12:45:33 +0100 Subject: ieee802154/mrf24j40: Fix alignment of parenthesis CC: Alan Ott Signed-off-by: Stefan Schmidt Acked-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index ec605c9..fba2dfd 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -323,7 +323,7 @@ static int mrf24j40_read_rx_buf(struct mrf24j40 *devrec, #ifdef DEBUG print_hex_dump(KERN_DEBUG, "mrf24j40 rx: ", - DUMP_PREFIX_OFFSET, 16, 1, data, *len, 0); + DUMP_PREFIX_OFFSET, 16, 1, data, *len, 0); pr_debug("mrf24j40 rx: lqi: %02hhx rssi: %02hhx\n", lqi_rssi[0], lqi_rssi[1]); #endif @@ -521,7 +521,7 @@ static int mrf24j40_filter(struct ieee802154_hw *hw, */ dev_dbg(printdev(devrec), "Set Pan Coord to %s\n", - filt->pan_coord ? "on" : "off"); + filt->pan_coord ? "on" : "off"); } return 0; -- cgit v0.10.2 From 5f5c5c23e3b71110c0215f9ea20e5434b68871f1 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 15 Dec 2014 10:25:53 +0100 Subject: at86rf230: remove if branch This patch removes an unnecessary if branch inside the tx complete handler. Signed-off-by: Alexander Aring Reviewed-by: Stefan Schmidt Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 2f66b8a..59209f3 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -714,10 +714,7 @@ at86rf230_tx_complete(void *context) enable_irq(lp->spi->irq); - if (lp->max_frame_retries <= 0) - ieee802154_xmit_complete(lp->hw, skb, true); - else - ieee802154_xmit_complete(lp->hw, skb, false); + ieee802154_xmit_complete(lp->hw, skb, !lp->tx_aret); } static void -- cgit v0.10.2 From fc50c6e36de742229492117d8e005102a1d21900 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 15 Dec 2014 10:25:54 +0100 Subject: at86rf230: make at86rf230_async_error inline This patch makes the at86rf230_async_error inline. This function is small enough to handle inline. Signed-off-by: Alexander Aring Reviewed-by: Stefan Schmidt Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 59209f3..8fee326 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -450,7 +450,7 @@ at86rf230_async_error_recover(void *context) ieee802154_wake_queue(lp->hw); } -static void +static inline void at86rf230_async_error(struct at86rf230_local *lp, struct at86rf230_state_change *ctx, int rc) { -- cgit v0.10.2 From 4fef7d3b0151576019a7a055ffd18fced6e55944 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 15 Dec 2014 10:25:55 +0100 Subject: at86rf230: fix context pointer handling This patch changes the context pointer to the parameter given one inside function at86rf230_async_state_change_start. This could occur problem if context isn't pointed to lp->state. Signed-off-by: Alexander Aring Reviewed-by: Stefan Schmidt Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 8fee326..6d1ef83 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -654,7 +654,7 @@ at86rf230_async_state_change_start(void *context) if (ctx->irq_enable) enable_irq(lp->spi->irq); - at86rf230_async_error(lp, &lp->state, rc); + at86rf230_async_error(lp, ctx, rc); } } -- cgit v0.10.2 From 2a100501605461dab8e59ec6c642075dcac672aa Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 15 Dec 2014 10:25:57 +0100 Subject: at86rf230: remove unnecessary assign The attribute extra_tx_headroom should already be zero. Signed-off-by: Alexander Aring Reviewed-by: Stefan Schmidt Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 6d1ef83..2ebaba3 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -1431,7 +1431,6 @@ at86rf230_detect_device(struct at86rf230_local *lp) return -EINVAL; } - lp->hw->extra_tx_headroom = 0; lp->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AACK | IEEE802154_HW_TXPOWER | IEEE802154_HW_ARET | IEEE802154_HW_AFILT | IEEE802154_HW_PROMISCUOUS; -- cgit v0.10.2 From 7598968d6fdbe8960b941246cc6ebef725899c59 Mon Sep 17 00:00:00 2001 From: Andrey Yurovsky Date: Wed, 17 Dec 2014 13:14:42 -0800 Subject: at86rf230: fix register read for part version The driver was reading the PART_NUM register for both the part number (type of device) and the part version, the version is actually in register 0x1D, VERSION_NUM. I believe that this was a copy-paste error. Tested on AT86RF212B where the part is detected to be the expected part number (0x07) and version (0x03 on mine). Signed-off-by: Andrey Yurovsky Acked-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 2ebaba3..b37c0249 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -1421,7 +1421,7 @@ at86rf230_detect_device(struct at86rf230_local *lp) if (rc) return rc; - rc = __at86rf230_read(lp, RG_PART_NUM, &version); + rc = __at86rf230_read(lp, RG_VERSION_NUM, &version); if (rc) return rc; -- cgit v0.10.2 From 4ecc8a559bc5173708a33bbd719b27af05bdce34 Mon Sep 17 00:00:00 2001 From: Andrey Yurovsky Date: Thu, 18 Dec 2014 15:36:18 -0800 Subject: at86rf230: remove version check for AT86RF212 This version check allows the driver to only work with v=1 hardware however there is no driver-facing difference with newer versions (confirmed by Atmel FAEs) so this check needlessly prevents the driver from being used with radios now in production. Tested on AT86RF212B radio (which came up as v=3). Signed-off-by: Andrey Yurovsky Reviewed-by: Stefan Schmidt Acked-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index b37c0249..79fbf38 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -1451,16 +1451,12 @@ at86rf230_detect_device(struct at86rf230_local *lp) break; case 7: chip = "at86rf212"; - if (version == 1) { - lp->data = &at86rf212_data; - lp->hw->flags |= IEEE802154_HW_LBT; - lp->hw->phy->channels_supported[0] = 0x00007FF; - lp->hw->phy->channels_supported[2] = 0x00007FF; - lp->hw->phy->current_channel = 5; - lp->hw->phy->symbol_duration = 25; - } else { - rc = -ENOTSUPP; - } + lp->data = &at86rf212_data; + lp->hw->flags |= IEEE802154_HW_LBT; + lp->hw->phy->channels_supported[0] = 0x00007FF; + lp->hw->phy->channels_supported[2] = 0x00007FF; + lp->hw->phy->current_channel = 5; + lp->hw->phy->symbol_duration = 25; break; case 11: chip = "at86rf233"; -- cgit v0.10.2 From cab9e3a0559c039b4e13b569fcf393618c661902 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Fri, 19 Dec 2014 10:39:08 +0200 Subject: Bluetooth: 6lowpan: Add IPSP PSM value The Internet Protocol Support Profile a.k.a BT 6LoWPAN specification is ready so PSM value for it is now known. Signed-off-by: Jukka Rissanen Signed-off-by: Marcel Holtmann diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index d1bb342..2239a37 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -248,6 +248,7 @@ struct l2cap_conn_rsp { #define L2CAP_PSM_SDP 0x0001 #define L2CAP_PSM_RFCOMM 0x0003 #define L2CAP_PSM_3DSP 0x0021 +#define L2CAP_PSM_IPSP 0x0023 /* 6LoWPAN */ /* channel identifier */ #define L2CAP_CID_SIGNALING 0x0001 -- cgit v0.10.2 From f677bb150c2f7b96ebcd377cd722e9d2649e9400 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Tue, 16 Dec 2014 01:50:14 +0100 Subject: HID: logitech-hidpp: detect HID++ 2.0 errors too Devices speaking HID++ 2.0 report a different error code (0xff). Detect these errors too to avoid 5 second delays when the device reports an error. Caught by... well, a bug in the QEMU emulation of this receiver. Renamed fap to rap for HID++ 1.0 errors because it is more logical, it has no functional difference. Signed-off-by: Peter Wu Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 7efbd8b..cf57955 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -105,6 +105,7 @@ struct hidpp_device { }; +/* HID++ 1.0 error codes */ #define HIDPP_ERROR 0x8f #define HIDPP_ERROR_SUCCESS 0x00 #define HIDPP_ERROR_INVALID_SUBID 0x01 @@ -119,6 +120,8 @@ struct hidpp_device { #define HIDPP_ERROR_REQUEST_UNAVAILABLE 0x0a #define HIDPP_ERROR_INVALID_PARAM_VALUE 0x0b #define HIDPP_ERROR_WRONG_PIN_CODE 0x0c +/* HID++ 2.0 error codes */ +#define HIDPP20_ERROR 0xff static void hidpp_connect_event(struct hidpp_device *hidpp_dev); @@ -192,9 +195,16 @@ static int hidpp_send_message_sync(struct hidpp_device *hidpp, } if (response->report_id == REPORT_ID_HIDPP_SHORT && - response->fap.feature_index == HIDPP_ERROR) { + response->rap.sub_id == HIDPP_ERROR) { + ret = response->rap.params[1]; + dbg_hid("%s:got hidpp error %02X\n", __func__, ret); + goto exit; + } + + if (response->report_id == REPORT_ID_HIDPP_LONG && + response->fap.feature_index == HIDPP20_ERROR) { ret = response->fap.params[1]; - dbg_hid("__hidpp_send_report got hidpp error %02X\n", ret); + dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret); goto exit; } @@ -271,7 +281,8 @@ static inline bool hidpp_match_answer(struct hidpp_report *question, static inline bool hidpp_match_error(struct hidpp_report *question, struct hidpp_report *answer) { - return (answer->fap.feature_index == HIDPP_ERROR) && + return ((answer->rap.sub_id == HIDPP_ERROR) || + (answer->fap.feature_index == HIDPP20_ERROR)) && (answer->fap.funcindex_clientid == question->fap.feature_index) && (answer->fap.params[0] == question->fap.funcindex_clientid); } -- cgit v0.10.2 From c8c7e3db819f4fc483793d55655cbbae045203f0 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 19 Dec 2014 10:36:50 +0100 Subject: at86rf230: cleanup check on trac status Signed-off-by: Alexander Aring Reviewed-by: Stefan Schmidt Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 79fbf38..80632fc 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -749,13 +749,11 @@ at86rf230_tx_trac_check(void *context) * to STATE_FORCE_TRX_OFF then STATE_TX_ON to recover the transceiver * state to TX_ON. */ - if (trac) { + if (trac) at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF, at86rf230_tx_trac_error, true); - return; - } - - at86rf230_tx_on(context); + else + at86rf230_tx_on(context); } static void -- cgit v0.10.2 From 1d2dc5b7b32393bb2d818e0de82a66b1a654d329 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 19 Dec 2014 13:40:19 +0200 Subject: Bluetooth: Split hci_update_page_scan into two functions To keep the parameter list and its semantics clear it makes sense to split the hci_update_page_scan function into two separate functions: one taking a hci_dev and another taking a hci_request. The one taking a hci_dev constructs its own hci_request and then calls the other function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3c78270..664451a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1306,7 +1306,8 @@ bool hci_req_pending(struct hci_dev *hdev); void hci_req_add_le_scan_disable(struct hci_request *req); void hci_req_add_le_passive_scan(struct hci_request *req); -void hci_update_page_scan(struct hci_dev *hdev, struct hci_request *req); +void hci_update_page_scan(struct hci_dev *hdev); +void __hci_update_page_scan(struct hci_request *req); struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param, u32 timeout); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 5dcacf9..3aa9015 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -5787,8 +5787,9 @@ static bool disconnected_whitelist_entries(struct hci_dev *hdev) return false; } -void hci_update_page_scan(struct hci_dev *hdev, struct hci_request *req) +void __hci_update_page_scan(struct hci_request *req) { + struct hci_dev *hdev = req->hdev; u8 scan; if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) @@ -5812,8 +5813,14 @@ void hci_update_page_scan(struct hci_dev *hdev, struct hci_request *req) if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) scan |= SCAN_INQUIRY; - if (req) - hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); - else - hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); + hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); +} + +void hci_update_page_scan(struct hci_dev *hdev) +{ + struct hci_request req; + + hci_req_init(&req, hdev); + __hci_update_page_scan(&req); + hci_req_run(&req, NULL); } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 39a5c8a..65f4ec8 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2127,7 +2127,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES, sizeof(cp), &cp); - hci_update_page_scan(hdev, NULL); + hci_update_page_scan(hdev); } /* Set packet type for incoming connection */ @@ -2308,7 +2308,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) if (test_bit(HCI_CONN_FLUSH_KEY, &conn->flags)) hci_remove_link_key(hdev, &conn->dst); - hci_update_page_scan(hdev, NULL); + hci_update_page_scan(hdev); } params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 693ce8b..f86f3ec 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1566,7 +1566,7 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status) * entries. */ hci_req_init(&req, hdev); - hci_update_page_scan(hdev, &req); + __hci_update_page_scan(&req); update_class(&req); hci_req_run(&req, NULL); @@ -1813,7 +1813,7 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status) if (conn_changed || discov_changed) { new_settings(hdev, cmd->sk); - hci_update_page_scan(hdev, NULL); + hci_update_page_scan(hdev); if (discov_changed) mgmt_update_adv_data(hdev); hci_update_background_scan(hdev); @@ -1847,7 +1847,7 @@ static int set_connectable_update_settings(struct hci_dev *hdev, return err; if (changed) { - hci_update_page_scan(hdev, NULL); + hci_update_page_scan(hdev); hci_update_background_scan(hdev); return new_settings(hdev, sk); } @@ -4697,7 +4697,7 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) hci_req_init(&req, hdev); write_fast_connectable(&req, false); - hci_update_page_scan(hdev, &req); + __hci_update_page_scan(&req); /* Since only the advertising data flags will change, there * is no need to update the scan response data. @@ -5473,7 +5473,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, if (err) goto unlock; - hci_update_page_scan(hdev, NULL); + hci_update_page_scan(hdev); goto added; } @@ -5556,7 +5556,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } - hci_update_page_scan(hdev, NULL); + hci_update_page_scan(hdev); device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type); @@ -5607,7 +5607,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, kfree(b); } - hci_update_page_scan(hdev, NULL); + hci_update_page_scan(hdev); list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) { if (p->auto_connect == HCI_AUTO_CONN_DISABLED) @@ -6139,7 +6139,7 @@ static int powered_update_hci(struct hci_dev *hdev) if (lmp_bredr_capable(hdev)) { write_fast_connectable(&req, false); - hci_update_page_scan(hdev, &req); + __hci_update_page_scan(&req); update_class(&req); update_name(&req); update_eir(&req); -- cgit v0.10.2 From 0857dd3bed62d8f905f61a399d1ed76464b5270f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 19 Dec 2014 13:40:20 +0200 Subject: Bluetooth: Split hci_request helpers to hci_request.[ch] None of the hci_request related things in net/bluetooth/hci_core.h are needed anywhere outside of the core bluetooth module. This patch creates a new net/bluetooth/hci_request.c file with its corresponding h-file and moves the functionality there from hci_core.c and hci_core.h. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 664451a..93066f7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1284,31 +1284,8 @@ static inline int hci_check_conn_params(u16 min, u16 max, u16 latency, int hci_register_cb(struct hci_cb *hcb); int hci_unregister_cb(struct hci_cb *hcb); -struct hci_request { - struct hci_dev *hdev; - struct sk_buff_head cmd_q; - - /* If something goes wrong when building the HCI request, the error - * value is stored in this field. - */ - int err; -}; - -void hci_req_init(struct hci_request *req, struct hci_dev *hdev); -int hci_req_run(struct hci_request *req, hci_req_complete_t complete); -void hci_req_add(struct hci_request *req, u16 opcode, u32 plen, - const void *param); -void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, - const void *param, u8 event); -void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status); bool hci_req_pending(struct hci_dev *hdev); -void hci_req_add_le_scan_disable(struct hci_request *req); -void hci_req_add_le_passive_scan(struct hci_request *req); - -void hci_update_page_scan(struct hci_dev *hdev); -void __hci_update_page_scan(struct hci_request *req); - struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param, u32 timeout); struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen, @@ -1418,8 +1395,6 @@ u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, __u8 ltk[16]); -int hci_update_random_address(struct hci_request *req, bool require_privacy, - u8 *own_addr_type); void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *bdaddr_type); diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index a5432a6..0a176fc 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -13,6 +13,6 @@ bluetooth_6lowpan-y := 6lowpan.o bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \ - a2mp.o amp.o ecc.o + a2mp.o amp.o ecc.o hci_request.o subdir-ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index fe18825..4405fb3 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -30,6 +30,7 @@ #include #include +#include "hci_request.h" #include "smp.h" #include "a2mp.h" diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 3aa9015..2cfaaa6 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -37,6 +37,7 @@ #include #include +#include "hci_request.h" #include "smp.h" static void hci_rx_work(struct work_struct *work); @@ -3901,112 +3902,6 @@ static void le_scan_disable_work(struct work_struct *work) BT_ERR("Disable LE scanning request failed: err %d", err); } -static void set_random_addr(struct hci_request *req, bdaddr_t *rpa) -{ - struct hci_dev *hdev = req->hdev; - - /* If we're advertising or initiating an LE connection we can't - * go ahead and change the random address at this time. This is - * because the eventual initiator address used for the - * subsequently created connection will be undefined (some - * controllers use the new address and others the one we had - * when the operation started). - * - * In this kind of scenario skip the update and let the random - * address be updated at the next cycle. - */ - if (test_bit(HCI_LE_ADV, &hdev->dev_flags) || - hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT)) { - BT_DBG("Deferring random address update"); - set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags); - return; - } - - hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, rpa); -} - -int hci_update_random_address(struct hci_request *req, bool require_privacy, - u8 *own_addr_type) -{ - struct hci_dev *hdev = req->hdev; - int err; - - /* If privacy is enabled use a resolvable private address. If - * current RPA has expired or there is something else than - * the current RPA in use, then generate a new one. - */ - if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) { - int to; - - *own_addr_type = ADDR_LE_DEV_RANDOM; - - if (!test_and_clear_bit(HCI_RPA_EXPIRED, &hdev->dev_flags) && - !bacmp(&hdev->random_addr, &hdev->rpa)) - return 0; - - err = smp_generate_rpa(hdev, hdev->irk, &hdev->rpa); - if (err < 0) { - BT_ERR("%s failed to generate new RPA", hdev->name); - return err; - } - - set_random_addr(req, &hdev->rpa); - - to = msecs_to_jiffies(hdev->rpa_timeout * 1000); - queue_delayed_work(hdev->workqueue, &hdev->rpa_expired, to); - - return 0; - } - - /* In case of required privacy without resolvable private address, - * use an non-resolvable private address. This is useful for active - * scanning and non-connectable advertising. - */ - if (require_privacy) { - bdaddr_t nrpa; - - while (true) { - /* The non-resolvable private address is generated - * from random six bytes with the two most significant - * bits cleared. - */ - get_random_bytes(&nrpa, 6); - nrpa.b[5] &= 0x3f; - - /* The non-resolvable private address shall not be - * equal to the public address. - */ - if (bacmp(&hdev->bdaddr, &nrpa)) - break; - } - - *own_addr_type = ADDR_LE_DEV_RANDOM; - set_random_addr(req, &nrpa); - return 0; - } - - /* If forcing static address is in use or there is no public - * address use the static address as random address (but skip - * the HCI command if the current random address is already the - * static one. - */ - if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) || - !bacmp(&hdev->bdaddr, BDADDR_ANY)) { - *own_addr_type = ADDR_LE_DEV_RANDOM; - if (bacmp(&hdev->static_addr, &hdev->random_addr)) - hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, - &hdev->static_addr); - return 0; - } - - /* Neither privacy nor static address is being used so use a - * public address. - */ - *own_addr_type = ADDR_LE_DEV_PUBLIC; - - return 0; -} - /* Copy the Identity Address of the controller. * * If the controller has a public BD_ADDR, then by default use that one. @@ -4539,76 +4434,11 @@ static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) } } -void hci_req_init(struct hci_request *req, struct hci_dev *hdev) -{ - skb_queue_head_init(&req->cmd_q); - req->hdev = hdev; - req->err = 0; -} - -int hci_req_run(struct hci_request *req, hci_req_complete_t complete) -{ - struct hci_dev *hdev = req->hdev; - struct sk_buff *skb; - unsigned long flags; - - BT_DBG("length %u", skb_queue_len(&req->cmd_q)); - - /* If an error occurred during request building, remove all HCI - * commands queued on the HCI request queue. - */ - if (req->err) { - skb_queue_purge(&req->cmd_q); - return req->err; - } - - /* Do not allow empty requests */ - if (skb_queue_empty(&req->cmd_q)) - return -ENODATA; - - skb = skb_peek_tail(&req->cmd_q); - bt_cb(skb)->req.complete = complete; - - spin_lock_irqsave(&hdev->cmd_q.lock, flags); - skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q); - spin_unlock_irqrestore(&hdev->cmd_q.lock, flags); - - queue_work(hdev->workqueue, &hdev->cmd_work); - - return 0; -} - bool hci_req_pending(struct hci_dev *hdev) { return (hdev->req_status == HCI_REQ_PEND); } -static struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, - u32 plen, const void *param) -{ - int len = HCI_COMMAND_HDR_SIZE + plen; - struct hci_command_hdr *hdr; - struct sk_buff *skb; - - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (!skb) - return NULL; - - hdr = (struct hci_command_hdr *) skb_put(skb, HCI_COMMAND_HDR_SIZE); - hdr->opcode = cpu_to_le16(opcode); - hdr->plen = plen; - - if (plen) - memcpy(skb_put(skb, plen), param, plen); - - BT_DBG("skb len %d", skb->len); - - bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; - bt_cb(skb)->opcode = opcode; - - return skb; -} - /* Send HCI command */ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, const void *param) @@ -4634,43 +4464,6 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, return 0; } -/* Queue a command to an asynchronous HCI request */ -void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, - const void *param, u8 event) -{ - struct hci_dev *hdev = req->hdev; - struct sk_buff *skb; - - BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen); - - /* If an error occurred during request building, there is no point in - * queueing the HCI command. We can simply return. - */ - if (req->err) - return; - - skb = hci_prepare_cmd(hdev, opcode, plen, param); - if (!skb) { - BT_ERR("%s no memory for command (opcode 0x%4.4x)", - hdev->name, opcode); - req->err = -ENOMEM; - return; - } - - if (skb_queue_empty(&req->cmd_q)) - bt_cb(skb)->req.start = true; - - bt_cb(skb)->req.event = event; - - skb_queue_tail(&req->cmd_q, skb); -} - -void hci_req_add(struct hci_request *req, u16 opcode, u32 plen, - const void *param) -{ - hci_req_add_ev(req, opcode, plen, param, 0); -} - /* Get data from the previously sent command */ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode) { @@ -5519,167 +5312,6 @@ static void hci_cmd_work(struct work_struct *work) } } -void hci_req_add_le_scan_disable(struct hci_request *req) -{ - struct hci_cp_le_set_scan_enable cp; - - memset(&cp, 0, sizeof(cp)); - cp.enable = LE_SCAN_DISABLE; - hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); -} - -static void add_to_white_list(struct hci_request *req, - struct hci_conn_params *params) -{ - struct hci_cp_le_add_to_white_list cp; - - cp.bdaddr_type = params->addr_type; - bacpy(&cp.bdaddr, ¶ms->addr); - - hci_req_add(req, HCI_OP_LE_ADD_TO_WHITE_LIST, sizeof(cp), &cp); -} - -static u8 update_white_list(struct hci_request *req) -{ - struct hci_dev *hdev = req->hdev; - struct hci_conn_params *params; - struct bdaddr_list *b; - uint8_t white_list_entries = 0; - - /* Go through the current white list programmed into the - * controller one by one and check if that address is still - * in the list of pending connections or list of devices to - * report. If not present in either list, then queue the - * command to remove it from the controller. - */ - list_for_each_entry(b, &hdev->le_white_list, list) { - struct hci_cp_le_del_from_white_list cp; - - if (hci_pend_le_action_lookup(&hdev->pend_le_conns, - &b->bdaddr, b->bdaddr_type) || - hci_pend_le_action_lookup(&hdev->pend_le_reports, - &b->bdaddr, b->bdaddr_type)) { - white_list_entries++; - continue; - } - - cp.bdaddr_type = b->bdaddr_type; - bacpy(&cp.bdaddr, &b->bdaddr); - - hci_req_add(req, HCI_OP_LE_DEL_FROM_WHITE_LIST, - sizeof(cp), &cp); - } - - /* Since all no longer valid white list entries have been - * removed, walk through the list of pending connections - * and ensure that any new device gets programmed into - * the controller. - * - * If the list of the devices is larger than the list of - * available white list entries in the controller, then - * just abort and return filer policy value to not use the - * white list. - */ - list_for_each_entry(params, &hdev->pend_le_conns, action) { - if (hci_bdaddr_list_lookup(&hdev->le_white_list, - ¶ms->addr, params->addr_type)) - continue; - - if (white_list_entries >= hdev->le_white_list_size) { - /* Select filter policy to accept all advertising */ - return 0x00; - } - - if (hci_find_irk_by_addr(hdev, ¶ms->addr, - params->addr_type)) { - /* White list can not be used with RPAs */ - return 0x00; - } - - white_list_entries++; - add_to_white_list(req, params); - } - - /* After adding all new pending connections, walk through - * the list of pending reports and also add these to the - * white list if there is still space. - */ - list_for_each_entry(params, &hdev->pend_le_reports, action) { - if (hci_bdaddr_list_lookup(&hdev->le_white_list, - ¶ms->addr, params->addr_type)) - continue; - - if (white_list_entries >= hdev->le_white_list_size) { - /* Select filter policy to accept all advertising */ - return 0x00; - } - - if (hci_find_irk_by_addr(hdev, ¶ms->addr, - params->addr_type)) { - /* White list can not be used with RPAs */ - return 0x00; - } - - white_list_entries++; - add_to_white_list(req, params); - } - - /* Select filter policy to use white list */ - return 0x01; -} - -void hci_req_add_le_passive_scan(struct hci_request *req) -{ - struct hci_cp_le_set_scan_param param_cp; - struct hci_cp_le_set_scan_enable enable_cp; - struct hci_dev *hdev = req->hdev; - u8 own_addr_type; - u8 filter_policy; - - /* Set require_privacy to false since no SCAN_REQ are send - * during passive scanning. Not using an non-resolvable address - * here is important so that peer devices using direct - * advertising with our address will be correctly reported - * by the controller. - */ - if (hci_update_random_address(req, false, &own_addr_type)) - return; - - /* Adding or removing entries from the white list must - * happen before enabling scanning. The controller does - * not allow white list modification while scanning. - */ - filter_policy = update_white_list(req); - - /* When the controller is using random resolvable addresses and - * with that having LE privacy enabled, then controllers with - * Extended Scanner Filter Policies support can now enable support - * for handling directed advertising. - * - * So instead of using filter polices 0x00 (no whitelist) - * and 0x01 (whitelist enabled) use the new filter policies - * 0x02 (no whitelist) and 0x03 (whitelist enabled). - */ - if (test_bit(HCI_PRIVACY, &hdev->dev_flags) && - (hdev->le_features[0] & HCI_LE_EXT_SCAN_POLICY)) - filter_policy |= 0x02; - - memset(¶m_cp, 0, sizeof(param_cp)); - param_cp.type = LE_SCAN_PASSIVE; - param_cp.interval = cpu_to_le16(hdev->le_scan_interval); - param_cp.window = cpu_to_le16(hdev->le_scan_window); - param_cp.own_address_type = own_addr_type; - param_cp.filter_policy = filter_policy; - hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp), - ¶m_cp); - - memset(&enable_cp, 0, sizeof(enable_cp)); - enable_cp.enable = LE_SCAN_ENABLE; - enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE; - hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp), - &enable_cp); -} - static void update_background_scan_complete(struct hci_dev *hdev, u8 status) { if (status) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 65f4ec8..a412eb1 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -30,6 +30,7 @@ #include #include +#include "hci_request.h" #include "a2mp.h" #include "amp.h" #include "smp.h" diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c new file mode 100644 index 0000000..eba83a2 --- /dev/null +++ b/net/bluetooth/hci_request.c @@ -0,0 +1,397 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + + Copyright (C) 2014 Intel 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; + + 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 OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +#include +#include + +#include "smp.h" +#include "hci_request.h" + +void hci_req_init(struct hci_request *req, struct hci_dev *hdev) +{ + skb_queue_head_init(&req->cmd_q); + req->hdev = hdev; + req->err = 0; +} + +int hci_req_run(struct hci_request *req, hci_req_complete_t complete) +{ + struct hci_dev *hdev = req->hdev; + struct sk_buff *skb; + unsigned long flags; + + BT_DBG("length %u", skb_queue_len(&req->cmd_q)); + + /* If an error occurred during request building, remove all HCI + * commands queued on the HCI request queue. + */ + if (req->err) { + skb_queue_purge(&req->cmd_q); + return req->err; + } + + /* Do not allow empty requests */ + if (skb_queue_empty(&req->cmd_q)) + return -ENODATA; + + skb = skb_peek_tail(&req->cmd_q); + bt_cb(skb)->req.complete = complete; + + spin_lock_irqsave(&hdev->cmd_q.lock, flags); + skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q); + spin_unlock_irqrestore(&hdev->cmd_q.lock, flags); + + queue_work(hdev->workqueue, &hdev->cmd_work); + + return 0; +} + +struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen, + const void *param) +{ + int len = HCI_COMMAND_HDR_SIZE + plen; + struct hci_command_hdr *hdr; + struct sk_buff *skb; + + skb = bt_skb_alloc(len, GFP_ATOMIC); + if (!skb) + return NULL; + + hdr = (struct hci_command_hdr *) skb_put(skb, HCI_COMMAND_HDR_SIZE); + hdr->opcode = cpu_to_le16(opcode); + hdr->plen = plen; + + if (plen) + memcpy(skb_put(skb, plen), param, plen); + + BT_DBG("skb len %d", skb->len); + + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + bt_cb(skb)->opcode = opcode; + + return skb; +} + +/* Queue a command to an asynchronous HCI request */ +void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, + const void *param, u8 event) +{ + struct hci_dev *hdev = req->hdev; + struct sk_buff *skb; + + BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen); + + /* If an error occurred during request building, there is no point in + * queueing the HCI command. We can simply return. + */ + if (req->err) + return; + + skb = hci_prepare_cmd(hdev, opcode, plen, param); + if (!skb) { + BT_ERR("%s no memory for command (opcode 0x%4.4x)", + hdev->name, opcode); + req->err = -ENOMEM; + return; + } + + if (skb_queue_empty(&req->cmd_q)) + bt_cb(skb)->req.start = true; + + bt_cb(skb)->req.event = event; + + skb_queue_tail(&req->cmd_q, skb); +} + +void hci_req_add(struct hci_request *req, u16 opcode, u32 plen, + const void *param) +{ + hci_req_add_ev(req, opcode, plen, param, 0); +} + +void hci_req_add_le_scan_disable(struct hci_request *req) +{ + struct hci_cp_le_set_scan_enable cp; + + memset(&cp, 0, sizeof(cp)); + cp.enable = LE_SCAN_DISABLE; + hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); +} + +static void add_to_white_list(struct hci_request *req, + struct hci_conn_params *params) +{ + struct hci_cp_le_add_to_white_list cp; + + cp.bdaddr_type = params->addr_type; + bacpy(&cp.bdaddr, ¶ms->addr); + + hci_req_add(req, HCI_OP_LE_ADD_TO_WHITE_LIST, sizeof(cp), &cp); +} + +static u8 update_white_list(struct hci_request *req) +{ + struct hci_dev *hdev = req->hdev; + struct hci_conn_params *params; + struct bdaddr_list *b; + uint8_t white_list_entries = 0; + + /* Go through the current white list programmed into the + * controller one by one and check if that address is still + * in the list of pending connections or list of devices to + * report. If not present in either list, then queue the + * command to remove it from the controller. + */ + list_for_each_entry(b, &hdev->le_white_list, list) { + struct hci_cp_le_del_from_white_list cp; + + if (hci_pend_le_action_lookup(&hdev->pend_le_conns, + &b->bdaddr, b->bdaddr_type) || + hci_pend_le_action_lookup(&hdev->pend_le_reports, + &b->bdaddr, b->bdaddr_type)) { + white_list_entries++; + continue; + } + + cp.bdaddr_type = b->bdaddr_type; + bacpy(&cp.bdaddr, &b->bdaddr); + + hci_req_add(req, HCI_OP_LE_DEL_FROM_WHITE_LIST, + sizeof(cp), &cp); + } + + /* Since all no longer valid white list entries have been + * removed, walk through the list of pending connections + * and ensure that any new device gets programmed into + * the controller. + * + * If the list of the devices is larger than the list of + * available white list entries in the controller, then + * just abort and return filer policy value to not use the + * white list. + */ + list_for_each_entry(params, &hdev->pend_le_conns, action) { + if (hci_bdaddr_list_lookup(&hdev->le_white_list, + ¶ms->addr, params->addr_type)) + continue; + + if (white_list_entries >= hdev->le_white_list_size) { + /* Select filter policy to accept all advertising */ + return 0x00; + } + + if (hci_find_irk_by_addr(hdev, ¶ms->addr, + params->addr_type)) { + /* White list can not be used with RPAs */ + return 0x00; + } + + white_list_entries++; + add_to_white_list(req, params); + } + + /* After adding all new pending connections, walk through + * the list of pending reports and also add these to the + * white list if there is still space. + */ + list_for_each_entry(params, &hdev->pend_le_reports, action) { + if (hci_bdaddr_list_lookup(&hdev->le_white_list, + ¶ms->addr, params->addr_type)) + continue; + + if (white_list_entries >= hdev->le_white_list_size) { + /* Select filter policy to accept all advertising */ + return 0x00; + } + + if (hci_find_irk_by_addr(hdev, ¶ms->addr, + params->addr_type)) { + /* White list can not be used with RPAs */ + return 0x00; + } + + white_list_entries++; + add_to_white_list(req, params); + } + + /* Select filter policy to use white list */ + return 0x01; +} + +void hci_req_add_le_passive_scan(struct hci_request *req) +{ + struct hci_cp_le_set_scan_param param_cp; + struct hci_cp_le_set_scan_enable enable_cp; + struct hci_dev *hdev = req->hdev; + u8 own_addr_type; + u8 filter_policy; + + /* Set require_privacy to false since no SCAN_REQ are send + * during passive scanning. Not using an non-resolvable address + * here is important so that peer devices using direct + * advertising with our address will be correctly reported + * by the controller. + */ + if (hci_update_random_address(req, false, &own_addr_type)) + return; + + /* Adding or removing entries from the white list must + * happen before enabling scanning. The controller does + * not allow white list modification while scanning. + */ + filter_policy = update_white_list(req); + + /* When the controller is using random resolvable addresses and + * with that having LE privacy enabled, then controllers with + * Extended Scanner Filter Policies support can now enable support + * for handling directed advertising. + * + * So instead of using filter polices 0x00 (no whitelist) + * and 0x01 (whitelist enabled) use the new filter policies + * 0x02 (no whitelist) and 0x03 (whitelist enabled). + */ + if (test_bit(HCI_PRIVACY, &hdev->dev_flags) && + (hdev->le_features[0] & HCI_LE_EXT_SCAN_POLICY)) + filter_policy |= 0x02; + + memset(¶m_cp, 0, sizeof(param_cp)); + param_cp.type = LE_SCAN_PASSIVE; + param_cp.interval = cpu_to_le16(hdev->le_scan_interval); + param_cp.window = cpu_to_le16(hdev->le_scan_window); + param_cp.own_address_type = own_addr_type; + param_cp.filter_policy = filter_policy; + hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp), + ¶m_cp); + + memset(&enable_cp, 0, sizeof(enable_cp)); + enable_cp.enable = LE_SCAN_ENABLE; + enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE; + hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp), + &enable_cp); +} + +static void set_random_addr(struct hci_request *req, bdaddr_t *rpa) +{ + struct hci_dev *hdev = req->hdev; + + /* If we're advertising or initiating an LE connection we can't + * go ahead and change the random address at this time. This is + * because the eventual initiator address used for the + * subsequently created connection will be undefined (some + * controllers use the new address and others the one we had + * when the operation started). + * + * In this kind of scenario skip the update and let the random + * address be updated at the next cycle. + */ + if (test_bit(HCI_LE_ADV, &hdev->dev_flags) || + hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT)) { + BT_DBG("Deferring random address update"); + set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags); + return; + } + + hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, rpa); +} + +int hci_update_random_address(struct hci_request *req, bool require_privacy, + u8 *own_addr_type) +{ + struct hci_dev *hdev = req->hdev; + int err; + + /* If privacy is enabled use a resolvable private address. If + * current RPA has expired or there is something else than + * the current RPA in use, then generate a new one. + */ + if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) { + int to; + + *own_addr_type = ADDR_LE_DEV_RANDOM; + + if (!test_and_clear_bit(HCI_RPA_EXPIRED, &hdev->dev_flags) && + !bacmp(&hdev->random_addr, &hdev->rpa)) + return 0; + + err = smp_generate_rpa(hdev, hdev->irk, &hdev->rpa); + if (err < 0) { + BT_ERR("%s failed to generate new RPA", hdev->name); + return err; + } + + set_random_addr(req, &hdev->rpa); + + to = msecs_to_jiffies(hdev->rpa_timeout * 1000); + queue_delayed_work(hdev->workqueue, &hdev->rpa_expired, to); + + return 0; + } + + /* In case of required privacy without resolvable private address, + * use an non-resolvable private address. This is useful for active + * scanning and non-connectable advertising. + */ + if (require_privacy) { + bdaddr_t nrpa; + + while (true) { + /* The non-resolvable private address is generated + * from random six bytes with the two most significant + * bits cleared. + */ + get_random_bytes(&nrpa, 6); + nrpa.b[5] &= 0x3f; + + /* The non-resolvable private address shall not be + * equal to the public address. + */ + if (bacmp(&hdev->bdaddr, &nrpa)) + break; + } + + *own_addr_type = ADDR_LE_DEV_RANDOM; + set_random_addr(req, &nrpa); + return 0; + } + + /* If forcing static address is in use or there is no public + * address use the static address as random address (but skip + * the HCI command if the current random address is already the + * static one. + */ + if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) || + !bacmp(&hdev->bdaddr, BDADDR_ANY)) { + *own_addr_type = ADDR_LE_DEV_RANDOM; + if (bacmp(&hdev->static_addr, &hdev->random_addr)) + hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, + &hdev->static_addr); + return 0; + } + + /* Neither privacy nor static address is being used so use a + * public address. + */ + *own_addr_type = ADDR_LE_DEV_PUBLIC; + + return 0; +} diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h new file mode 100644 index 0000000..1793a46 --- /dev/null +++ b/net/bluetooth/hci_request.h @@ -0,0 +1,51 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + Copyright (C) 2014 Intel 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; + + 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 OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +struct hci_request { + struct hci_dev *hdev; + struct sk_buff_head cmd_q; + + /* If something goes wrong when building the HCI request, the error + * value is stored in this field. + */ + int err; +}; + +void hci_req_init(struct hci_request *req, struct hci_dev *hdev); +int hci_req_run(struct hci_request *req, hci_req_complete_t complete); +void hci_req_add(struct hci_request *req, u16 opcode, u32 plen, + const void *param); +void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, + const void *param, u8 event); +void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status); + +struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen, + const void *param); + +void hci_req_add_le_scan_disable(struct hci_request *req); +void hci_req_add_le_passive_scan(struct hci_request *req); + +void hci_update_page_scan(struct hci_dev *hdev); +void __hci_update_page_scan(struct hci_request *req); + +int hci_update_random_address(struct hci_request *req, bool require_privacy, + u8 *own_addr_type); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f86f3ec..95473e9 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -32,6 +32,7 @@ #include #include +#include "hci_request.h" #include "smp.h" #define MGMT_VERSION 1 -- cgit v0.10.2 From 8d819a92cc7fef4294dd11faa60050fd3c5460e0 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 17 Dec 2014 13:38:34 +0100 Subject: mac80211: minstrel: reduce size of struct minstrel_rate_stats On minstrel_ht, the size of the per-sta struct is almost 18k, making it an order-3 allocation. A few fields inside the per-rate statistics are bigger than they need to be. This patch reduces the size enough to cut down the per-sta struct to about 13k (order-2 allocation). Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index d51f6b1..7c86a00 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -263,12 +263,12 @@ static inline unsigned int minstrel_get_retry_count(struct minstrel_rate *mr, struct ieee80211_tx_info *info) { - unsigned int retry = mr->adjusted_retry_count; + u8 retry = mr->adjusted_retry_count; if (info->control.use_rts) - retry = max(2U, min(mr->stats.retry_count_rtscts, retry)); + retry = max_t(u8, 2, min(mr->stats.retry_count_rtscts, retry)); else if (info->control.use_cts_prot) - retry = max(2U, min(mr->retry_count_cts, retry)); + retry = max_t(u8, 2, min(mr->retry_count_cts, retry)); return retry; } diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index 97eca86..410efe6 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h @@ -33,8 +33,8 @@ minstrel_ewma(int old, int new, int weight) struct minstrel_rate_stats { /* current / last sampling period attempts/success counters */ - unsigned int attempts, last_attempts; - unsigned int success, last_success; + u16 attempts, last_attempts; + u16 success, last_success; /* total attempts/success counters */ u64 att_hist, succ_hist; @@ -46,8 +46,8 @@ struct minstrel_rate_stats { unsigned int cur_prob, probability; /* maximum retry counts */ - unsigned int retry_count; - unsigned int retry_count_rtscts; + u8 retry_count; + u8 retry_count_rtscts; u8 sample_skipped; bool retry_updated; @@ -55,14 +55,15 @@ struct minstrel_rate_stats { struct minstrel_rate { int bitrate; - int rix; + + s8 rix; + u8 retry_count_cts; + u8 adjusted_retry_count; unsigned int perfect_tx_time; unsigned int ack_time; int sample_limit; - unsigned int retry_count_cts; - unsigned int adjusted_retry_count; struct minstrel_rate_stats stats; }; -- cgit v0.10.2 From 2cf22218b00f46f93b39a9355b830e9e8e4fd077 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 19 Dec 2014 22:26:00 +0200 Subject: Bluetooth: Add hci_request support for hci_update_background_scan Many places using hci_update_background_scan() try to synchronize whatever they're doing with the help of hci_request callbacks. However, since the hci_update_background_scan() function hasn't so far accepted a hci_request pointer any commands triggered by it have been left out by the synchronization. This patch modifies the API in a similar way as was done for hci_update_page_scan, i.e. there's a variant that takes a hci_request and another one that takes a hci_dev. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 93066f7..8eccdf0 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -930,8 +930,6 @@ struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list, bdaddr_t *addr, u8 addr_type); -void hci_update_background_scan(struct hci_dev *hdev); - void hci_uuids_clear(struct hci_dev *hdev); void hci_link_keys_clear(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 2cfaaa6..def6fba 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -5312,95 +5312,6 @@ static void hci_cmd_work(struct work_struct *work) } } -static void update_background_scan_complete(struct hci_dev *hdev, u8 status) -{ - if (status) - BT_DBG("HCI request failed to update background scanning: " - "status 0x%2.2x", status); -} - -/* This function controls the background scanning based on hdev->pend_le_conns - * list. If there are pending LE connection we start the background scanning, - * otherwise we stop it. - * - * This function requires the caller holds hdev->lock. - */ -void hci_update_background_scan(struct hci_dev *hdev) -{ - struct hci_request req; - struct hci_conn *conn; - int err; - - if (!test_bit(HCI_UP, &hdev->flags) || - test_bit(HCI_INIT, &hdev->flags) || - test_bit(HCI_SETUP, &hdev->dev_flags) || - test_bit(HCI_CONFIG, &hdev->dev_flags) || - test_bit(HCI_AUTO_OFF, &hdev->dev_flags) || - test_bit(HCI_UNREGISTER, &hdev->dev_flags)) - return; - - /* No point in doing scanning if LE support hasn't been enabled */ - if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) - return; - - /* If discovery is active don't interfere with it */ - if (hdev->discovery.state != DISCOVERY_STOPPED) - return; - - /* Reset RSSI and UUID filters when starting background scanning - * since these filters are meant for service discovery only. - * - * The Start Discovery and Start Service Discovery operations - * ensure to set proper values for RSSI threshold and UUID - * filter list. So it is safe to just reset them here. - */ - hci_discovery_filter_clear(hdev); - - hci_req_init(&req, hdev); - - if (list_empty(&hdev->pend_le_conns) && - list_empty(&hdev->pend_le_reports)) { - /* If there is no pending LE connections or devices - * to be scanned for, we should stop the background - * scanning. - */ - - /* If controller is not scanning we are done. */ - if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags)) - return; - - hci_req_add_le_scan_disable(&req); - - BT_DBG("%s stopping background scanning", hdev->name); - } else { - /* If there is at least one pending LE connection, we should - * keep the background scan running. - */ - - /* If controller is connecting, we should not start scanning - * since some controllers are not able to scan and connect at - * the same time. - */ - conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); - if (conn) - return; - - /* If controller is currently scanning, we stop it to ensure we - * don't miss any advertising (due to duplicates filter). - */ - if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) - hci_req_add_le_scan_disable(&req); - - hci_req_add_le_passive_scan(&req); - - BT_DBG("%s starting background scanning", hdev->name); - } - - err = hci_req_run(&req, update_background_scan_complete); - if (err) - BT_ERR("Failed to run HCI request: err %d", err); -} - static bool disconnected_whitelist_entries(struct hci_dev *hdev) { struct bdaddr_list *b; diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index eba83a2..e49f682 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -395,3 +395,99 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy, return 0; } + +/* This function controls the background scanning based on hdev->pend_le_conns + * list. If there are pending LE connection we start the background scanning, + * otherwise we stop it. + * + * This function requires the caller holds hdev->lock. + */ +void __hci_update_background_scan(struct hci_request *req) +{ + struct hci_dev *hdev = req->hdev; + struct hci_conn *conn; + + if (!test_bit(HCI_UP, &hdev->flags) || + test_bit(HCI_INIT, &hdev->flags) || + test_bit(HCI_SETUP, &hdev->dev_flags) || + test_bit(HCI_CONFIG, &hdev->dev_flags) || + test_bit(HCI_AUTO_OFF, &hdev->dev_flags) || + test_bit(HCI_UNREGISTER, &hdev->dev_flags)) + return; + + /* No point in doing scanning if LE support hasn't been enabled */ + if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + return; + + /* If discovery is active don't interfere with it */ + if (hdev->discovery.state != DISCOVERY_STOPPED) + return; + + /* Reset RSSI and UUID filters when starting background scanning + * since these filters are meant for service discovery only. + * + * The Start Discovery and Start Service Discovery operations + * ensure to set proper values for RSSI threshold and UUID + * filter list. So it is safe to just reset them here. + */ + hci_discovery_filter_clear(hdev); + + if (list_empty(&hdev->pend_le_conns) && + list_empty(&hdev->pend_le_reports)) { + /* If there is no pending LE connections or devices + * to be scanned for, we should stop the background + * scanning. + */ + + /* If controller is not scanning we are done. */ + if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags)) + return; + + hci_req_add_le_scan_disable(req); + + BT_DBG("%s stopping background scanning", hdev->name); + } else { + /* If there is at least one pending LE connection, we should + * keep the background scan running. + */ + + /* If controller is connecting, we should not start scanning + * since some controllers are not able to scan and connect at + * the same time. + */ + conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); + if (conn) + return; + + /* If controller is currently scanning, we stop it to ensure we + * don't miss any advertising (due to duplicates filter). + */ + if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) + hci_req_add_le_scan_disable(req); + + hci_req_add_le_passive_scan(req); + + BT_DBG("%s starting background scanning", hdev->name); + } +} + +static void update_background_scan_complete(struct hci_dev *hdev, u8 status) +{ + if (status) + BT_DBG("HCI request failed to update background scanning: " + "status 0x%2.2x", status); +} + +void hci_update_background_scan(struct hci_dev *hdev) +{ + int err; + struct hci_request req; + + hci_req_init(&req, hdev); + + __hci_update_background_scan(&req); + + err = hci_req_run(&req, update_background_scan_complete); + if (err && err != -ENODATA) + BT_ERR("Failed to run HCI request: err %d", err); +} diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h index 1793a46..adf074d 100644 --- a/net/bluetooth/hci_request.h +++ b/net/bluetooth/hci_request.h @@ -49,3 +49,6 @@ void __hci_update_page_scan(struct hci_request *req); int hci_update_random_address(struct hci_request *req, bool require_privacy, u8 *own_addr_type); + +void hci_update_background_scan(struct hci_dev *hdev); +void __hci_update_background_scan(struct hci_request *req); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 95473e9..3afe1e1 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2228,9 +2228,8 @@ static void le_enable_complete(struct hci_dev *hdev, u8 status) hci_req_init(&req, hdev); update_adv_data(&req); update_scan_rsp_data(&req); + __hci_update_background_scan(&req); hci_req_run(&req, NULL); - - hci_update_background_scan(hdev); } unlock: @@ -6038,8 +6037,9 @@ void mgmt_index_removed(struct hci_dev *hdev) } /* This function requires the caller holds hdev->lock */ -static void restart_le_actions(struct hci_dev *hdev) +static void restart_le_actions(struct hci_request *req) { + struct hci_dev *hdev = req->hdev; struct hci_conn_params *p; list_for_each_entry(p, &hdev->le_conn_params, list) { @@ -6061,7 +6061,7 @@ static void restart_le_actions(struct hci_dev *hdev) } } - hci_update_background_scan(hdev); + __hci_update_background_scan(req); } static void powered_complete(struct hci_dev *hdev, u8 status) @@ -6072,8 +6072,6 @@ static void powered_complete(struct hci_dev *hdev, u8 status) hci_dev_lock(hdev); - restart_le_actions(hdev); - mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); new_settings(hdev, match.sk); @@ -6131,6 +6129,8 @@ static int powered_update_hci(struct hci_dev *hdev) if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) enable_advertising(&req); + + restart_le_actions(&req); } link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags); -- cgit v0.10.2 From 51ef3ebe7bcc212f4cbbeac48bda26ee90a6edbe Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 19 Dec 2014 22:26:01 +0200 Subject: Bluetooth: Fix Remove Device to wait for HCI before sending cmd_complete This patch updates the Remove Device mgmt command handler to use a hci_request to wait for HCI command completion before notifying user space of the mgmt command completion. This way we ensure that once the mgmt command returns all HCI commands triggered by it have also completed. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 3afe1e1..6b92573 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5523,24 +5523,55 @@ static void device_removed(struct sock *sk, struct hci_dev *hdev, mgmt_event(MGMT_EV_DEVICE_REMOVED, hdev, &ev, sizeof(ev), sk); } +static void remove_device_complete(struct hci_dev *hdev, u8 status) +{ + struct pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = mgmt_pending_find(MGMT_OP_REMOVE_DEVICE, hdev); + if (!cmd) + goto unlock; + + cmd->cmd_complete(cmd, mgmt_status(status)); + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + static int remove_device(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_remove_device *cp = data; + struct pending_cmd *cmd; + struct hci_request req; int err; BT_DBG("%s", hdev->name); + hci_req_init(&req, hdev); + hci_dev_lock(hdev); + cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_DEVICE, hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto unlock; + } + + cmd->cmd_complete = addr_cmd_complete; + if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) { struct hci_conn_params *params; u8 addr_type; if (!bdaddr_type_is_valid(cp->addr.type)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, - MGMT_STATUS_INVALID_PARAMS, - &cp->addr, sizeof(cp->addr)); + err = 0; + cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS); + mgmt_pending_remove(cmd); goto unlock; } @@ -5549,14 +5580,14 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, &cp->addr.bdaddr, cp->addr.type); if (err) { - err = cmd_complete(sk, hdev->id, - MGMT_OP_REMOVE_DEVICE, - MGMT_STATUS_INVALID_PARAMS, - &cp->addr, sizeof(cp->addr)); + err = 0; + cmd->cmd_complete(cmd, + MGMT_STATUS_INVALID_PARAMS); + mgmt_pending_remove(cmd); goto unlock; } - hci_update_page_scan(hdev); + __hci_update_page_scan(&req); device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type); @@ -5571,23 +5602,23 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, addr_type); if (!params) { - err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, - MGMT_STATUS_INVALID_PARAMS, - &cp->addr, sizeof(cp->addr)); + err = 0; + cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS); + mgmt_pending_remove(cmd); goto unlock; } if (params->auto_connect == HCI_AUTO_CONN_DISABLED) { - err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, - MGMT_STATUS_INVALID_PARAMS, - &cp->addr, sizeof(cp->addr)); + err = 0; + cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS); + mgmt_pending_remove(cmd); goto unlock; } list_del(¶ms->action); list_del(¶ms->list); kfree(params); - hci_update_background_scan(hdev); + __hci_update_background_scan(&req); device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type); } else { @@ -5595,9 +5626,9 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, struct bdaddr_list *b, *btmp; if (cp->addr.type) { - err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, - MGMT_STATUS_INVALID_PARAMS, - &cp->addr, sizeof(cp->addr)); + err = 0; + cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS); + mgmt_pending_remove(cmd); goto unlock; } @@ -5607,7 +5638,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, kfree(b); } - hci_update_page_scan(hdev); + __hci_update_page_scan(&req); list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) { if (p->auto_connect == HCI_AUTO_CONN_DISABLED) @@ -5620,12 +5651,21 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, BT_DBG("All LE connection parameters were removed"); - hci_update_background_scan(hdev); + __hci_update_background_scan(&req); } complete: - err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, - MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr)); + err = hci_req_run(&req, remove_device_complete); + if (err < 0) { + /* ENODATA means no HCI commands were needed (e.g. if + * the adapter is powered off). + */ + if (err == -ENODATA) { + cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS); + err = 0; + } + mgmt_pending_remove(cmd); + } unlock: hci_dev_unlock(hdev); -- cgit v0.10.2 From 5a154e6f71dfd41c7b5cf96a13c83fca91e7df7f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 19 Dec 2014 22:26:02 +0200 Subject: Bluetooth: Fix Add Device to wait for HCI before sending cmd_complete This patch updates the Add Device mgmt command handler to use a hci_request to wait for HCI command completion before notifying user space of the mgmt command completion. To do this we need to add an extra hci_request parameter to the hci_conn_params_set function. Since this function has no other users besides mgmt.c it's moved there as a static function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 8eccdf0..79724c8 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -920,8 +920,6 @@ struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); -int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, - u8 auto_connect); void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_conn_params_clear_all(struct hci_dev *hdev); void hci_conn_params_clear_disabled(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index def6fba..ee2096c 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3660,23 +3660,6 @@ struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, return NULL; } -static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type) -{ - struct hci_conn *conn; - - conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr); - if (!conn) - return false; - - if (conn->dst_type != type) - return false; - - if (conn->state != BT_CONNECTED) - return false; - - return true; -} - /* This function requires the caller holds hdev->lock */ struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list, bdaddr_t *addr, u8 addr_type) @@ -3732,47 +3715,6 @@ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, return params; } -/* This function requires the caller holds hdev->lock */ -int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, - u8 auto_connect) -{ - struct hci_conn_params *params; - - params = hci_conn_params_add(hdev, addr, addr_type); - if (!params) - return -EIO; - - if (params->auto_connect == auto_connect) - return 0; - - list_del_init(¶ms->action); - - switch (auto_connect) { - case HCI_AUTO_CONN_DISABLED: - case HCI_AUTO_CONN_LINK_LOSS: - hci_update_background_scan(hdev); - break; - case HCI_AUTO_CONN_REPORT: - list_add(¶ms->action, &hdev->pend_le_reports); - hci_update_background_scan(hdev); - break; - case HCI_AUTO_CONN_DIRECT: - case HCI_AUTO_CONN_ALWAYS: - if (!is_connected(hdev, addr, addr_type)) { - list_add(¶ms->action, &hdev->pend_le_conns); - hci_update_background_scan(hdev); - } - break; - } - - params->auto_connect = auto_connect; - - BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type, - auto_connect); - - return 0; -} - static void hci_conn_params_free(struct hci_conn_params *params) { if (params->conn) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 6b92573..ec7c0ec 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5425,6 +5425,65 @@ unlock: return err; } +static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type) +{ + struct hci_conn *conn; + + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr); + if (!conn) + return false; + + if (conn->dst_type != type) + return false; + + if (conn->state != BT_CONNECTED) + return false; + + return true; +} + +/* This function requires the caller holds hdev->lock */ +static int hci_conn_params_set(struct hci_request *req, bdaddr_t *addr, + u8 addr_type, u8 auto_connect) +{ + struct hci_dev *hdev = req->hdev; + struct hci_conn_params *params; + + params = hci_conn_params_add(hdev, addr, addr_type); + if (!params) + return -EIO; + + if (params->auto_connect == auto_connect) + return 0; + + list_del_init(¶ms->action); + + switch (auto_connect) { + case HCI_AUTO_CONN_DISABLED: + case HCI_AUTO_CONN_LINK_LOSS: + __hci_update_background_scan(req); + break; + case HCI_AUTO_CONN_REPORT: + list_add(¶ms->action, &hdev->pend_le_reports); + __hci_update_background_scan(req); + break; + case HCI_AUTO_CONN_DIRECT: + case HCI_AUTO_CONN_ALWAYS: + if (!is_connected(hdev, addr, addr_type)) { + list_add(¶ms->action, &hdev->pend_le_conns); + __hci_update_background_scan(req); + } + break; + } + + params->auto_connect = auto_connect; + + BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type, + auto_connect); + + return 0; +} + static void device_added(struct sock *sk, struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, u8 action) { @@ -5437,10 +5496,31 @@ static void device_added(struct sock *sk, struct hci_dev *hdev, mgmt_event(MGMT_EV_DEVICE_ADDED, hdev, &ev, sizeof(ev), sk); } +static void add_device_complete(struct hci_dev *hdev, u8 status) +{ + struct pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = mgmt_pending_find(MGMT_OP_ADD_DEVICE, hdev); + if (!cmd) + goto unlock; + + cmd->cmd_complete(cmd, mgmt_status(status)); + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + static int add_device(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_add_device *cp = data; + struct pending_cmd *cmd; + struct hci_request req; u8 auto_conn, addr_type; int err; @@ -5457,14 +5537,24 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, MGMT_STATUS_INVALID_PARAMS, &cp->addr, sizeof(cp->addr)); + hci_req_init(&req, hdev); + hci_dev_lock(hdev); + cmd = mgmt_pending_add(sk, MGMT_OP_ADD_DEVICE, hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto unlock; + } + + cmd->cmd_complete = addr_cmd_complete; + if (cp->addr.type == BDADDR_BREDR) { /* Only incoming connections action is supported for now */ if (cp->action != 0x01) { - err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, - MGMT_STATUS_INVALID_PARAMS, - &cp->addr, sizeof(cp->addr)); + err = 0; + cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS); + mgmt_pending_remove(cmd); goto unlock; } @@ -5473,7 +5563,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, if (err) goto unlock; - hci_update_page_scan(hdev); + __hci_update_page_scan(&req); goto added; } @@ -5493,19 +5583,28 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, /* If the connection parameters don't exist for this device, * they will be created and configured with defaults. */ - if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type, + if (hci_conn_params_set(&req, &cp->addr.bdaddr, addr_type, auto_conn) < 0) { - err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, - MGMT_STATUS_FAILED, - &cp->addr, sizeof(cp->addr)); + err = 0; + cmd->cmd_complete(cmd, MGMT_STATUS_FAILED); + mgmt_pending_remove(cmd); goto unlock; } added: device_added(sk, hdev, &cp->addr.bdaddr, cp->addr.type, cp->action); - err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, - MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr)); + err = hci_req_run(&req, add_device_complete); + if (err < 0) { + /* ENODATA means no HCI commands were needed (e.g. if + * the adapter is powered off). + */ + if (err == -ENODATA) { + cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS); + err = 0; + } + mgmt_pending_remove(cmd); + } unlock: hci_dev_unlock(hdev); -- cgit v0.10.2 From 9df7465351c00707d7d63a52e01187a6e169fe4a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 19 Dec 2014 22:26:03 +0200 Subject: Bluetooth: Add return parameter to cmd_complete callbacks The cmd_complete callbacks for pending mgmt commands may fail e.g. in the case of memory allocation. Previously this error would be caught and returned to user space in the form of a failed write on the mgmt socket (when the error happened in the mgmt command handler) but with the introduction of the generic cmd_complete callback this information was lost. This patch returns the feature by making cmd_complete callbacks return int instead of void. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ec7c0ec..3d2f7ad 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -139,7 +139,7 @@ struct pending_cmd { size_t param_len; struct sock *sk; void *user_data; - void (*cmd_complete)(struct pending_cmd *cmd, u8 status); + int (*cmd_complete)(struct pending_cmd *cmd, u8 status); }; /* HCI to MGMT error code conversion table */ @@ -1487,16 +1487,16 @@ static void cmd_complete_rsp(struct pending_cmd *cmd, void *data) cmd_status_rsp(cmd, data); } -static void generic_cmd_complete(struct pending_cmd *cmd, u8 status) +static int generic_cmd_complete(struct pending_cmd *cmd, u8 status) { - cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param, - cmd->param_len); + return cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, + cmd->param, cmd->param_len); } -static void addr_cmd_complete(struct pending_cmd *cmd, u8 status) +static int addr_cmd_complete(struct pending_cmd *cmd, u8 status) { - cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param, - sizeof(struct mgmt_addr_info)); + return cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param, + sizeof(struct mgmt_addr_info)); } static u8 mgmt_bredr_support(struct hci_dev *hdev) @@ -3098,16 +3098,17 @@ static struct pending_cmd *find_pairing(struct hci_conn *conn) return NULL; } -static void pairing_complete(struct pending_cmd *cmd, u8 status) +static int pairing_complete(struct pending_cmd *cmd, u8 status) { struct mgmt_rp_pair_device rp; struct hci_conn *conn = cmd->user_data; + int err; bacpy(&rp.addr.bdaddr, &conn->dst); rp.addr.type = link_to_bdaddr(conn->type, conn->dst_type); - cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, status, - &rp, sizeof(rp)); + err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, status, + &rp, sizeof(rp)); /* So we don't get further callbacks for this connection */ conn->connect_cfm_cb = NULL; @@ -3122,6 +3123,8 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status) clear_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags); hci_conn_put(conn); + + return err; } void mgmt_smp_complete(struct hci_conn *conn, bool complete) @@ -3947,9 +3950,10 @@ failed: return err; } -static void service_discovery_cmd_complete(struct pending_cmd *cmd, u8 status) +static int service_discovery_cmd_complete(struct pending_cmd *cmd, u8 status) { - cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param, 1); + return cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, + cmd->param, 1); } static int start_service_discovery(struct sock *sk, struct hci_dev *hdev, @@ -5091,10 +5095,11 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, return err; } -static void conn_info_cmd_complete(struct pending_cmd *cmd, u8 status) +static int conn_info_cmd_complete(struct pending_cmd *cmd, u8 status) { struct hci_conn *conn = cmd->user_data; struct mgmt_rp_get_conn_info rp; + int err; memcpy(&rp.addr, cmd->param, sizeof(rp.addr)); @@ -5108,11 +5113,13 @@ static void conn_info_cmd_complete(struct pending_cmd *cmd, u8 status) rp.max_tx_power = HCI_TX_POWER_INVALID; } - cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, status, - &rp, sizeof(rp)); + err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, status, + &rp, sizeof(rp)); hci_conn_drop(conn); hci_conn_put(conn); + + return err; } static void conn_info_refresh_complete(struct hci_dev *hdev, u8 hci_status) @@ -5286,11 +5293,12 @@ unlock: return err; } -static void clock_info_cmd_complete(struct pending_cmd *cmd, u8 status) +static int clock_info_cmd_complete(struct pending_cmd *cmd, u8 status) { struct hci_conn *conn = cmd->user_data; struct mgmt_rp_get_clock_info rp; struct hci_dev *hdev; + int err; memset(&rp, 0, sizeof(rp)); memcpy(&rp.addr, &cmd->param, sizeof(rp.addr)); @@ -5310,12 +5318,15 @@ static void clock_info_cmd_complete(struct pending_cmd *cmd, u8 status) } complete: - cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, &rp, sizeof(rp)); + err = cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, &rp, + sizeof(rp)); if (conn) { hci_conn_drop(conn); hci_conn_put(conn); } + + return err; } static void get_clock_info_complete(struct hci_dev *hdev, u8 status) @@ -5552,8 +5563,8 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, if (cp->addr.type == BDADDR_BREDR) { /* Only incoming connections action is supported for now */ if (cp->action != 0x01) { - err = 0; - cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS); + err = cmd->cmd_complete(cmd, + MGMT_STATUS_INVALID_PARAMS); mgmt_pending_remove(cmd); goto unlock; } @@ -5585,8 +5596,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, */ if (hci_conn_params_set(&req, &cp->addr.bdaddr, addr_type, auto_conn) < 0) { - err = 0; - cmd->cmd_complete(cmd, MGMT_STATUS_FAILED); + err = cmd->cmd_complete(cmd, MGMT_STATUS_FAILED); mgmt_pending_remove(cmd); goto unlock; } @@ -5599,10 +5609,8 @@ added: /* ENODATA means no HCI commands were needed (e.g. if * the adapter is powered off). */ - if (err == -ENODATA) { - cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS); - err = 0; - } + if (err == -ENODATA) + err = cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS); mgmt_pending_remove(cmd); } @@ -5668,8 +5676,8 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, u8 addr_type; if (!bdaddr_type_is_valid(cp->addr.type)) { - err = 0; - cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS); + err = cmd->cmd_complete(cmd, + MGMT_STATUS_INVALID_PARAMS); mgmt_pending_remove(cmd); goto unlock; } @@ -5679,9 +5687,8 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, &cp->addr.bdaddr, cp->addr.type); if (err) { - err = 0; - cmd->cmd_complete(cmd, - MGMT_STATUS_INVALID_PARAMS); + err = cmd->cmd_complete(cmd, + MGMT_STATUS_INVALID_PARAMS); mgmt_pending_remove(cmd); goto unlock; } @@ -5701,15 +5708,15 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, addr_type); if (!params) { - err = 0; - cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS); + err = cmd->cmd_complete(cmd, + MGMT_STATUS_INVALID_PARAMS); mgmt_pending_remove(cmd); goto unlock; } if (params->auto_connect == HCI_AUTO_CONN_DISABLED) { - err = 0; - cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS); + err = cmd->cmd_complete(cmd, + MGMT_STATUS_INVALID_PARAMS); mgmt_pending_remove(cmd); goto unlock; } @@ -5725,8 +5732,8 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, struct bdaddr_list *b, *btmp; if (cp->addr.type) { - err = 0; - cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS); + err = cmd->cmd_complete(cmd, + MGMT_STATUS_INVALID_PARAMS); mgmt_pending_remove(cmd); goto unlock; } @@ -5759,10 +5766,8 @@ complete: /* ENODATA means no HCI commands were needed (e.g. if * the adapter is powered off). */ - if (err == -ENODATA) { - cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS); - err = 0; - } + if (err == -ENODATA) + err = cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS); mgmt_pending_remove(cmd); } -- cgit v0.10.2 From 405a26110a735b88963875459efe63f4d274f49d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 19 Dec 2014 23:18:22 +0200 Subject: Bluetooth: Move hci_update_page_scan to hci_request.c This is a left-over from the patch that created hci_request.c. The hci_update_page_scan functions should have been moved from hci_core.c there. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ee2096c..9790a01 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -5253,59 +5253,3 @@ static void hci_cmd_work(struct work_struct *work) } } } - -static bool disconnected_whitelist_entries(struct hci_dev *hdev) -{ - struct bdaddr_list *b; - - list_for_each_entry(b, &hdev->whitelist, list) { - struct hci_conn *conn; - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &b->bdaddr); - if (!conn) - return true; - - if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG) - return true; - } - - return false; -} - -void __hci_update_page_scan(struct hci_request *req) -{ - struct hci_dev *hdev = req->hdev; - u8 scan; - - if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) - return; - - if (!hdev_is_powered(hdev)) - return; - - if (mgmt_powering_down(hdev)) - return; - - if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) || - disconnected_whitelist_entries(hdev)) - scan = SCAN_PAGE; - else - scan = SCAN_DISABLED; - - if (test_bit(HCI_PSCAN, &hdev->flags) == !!(scan & SCAN_PAGE)) - return; - - if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) - scan |= SCAN_INQUIRY; - - hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); -} - -void hci_update_page_scan(struct hci_dev *hdev) -{ - struct hci_request req; - - hci_req_init(&req, hdev); - __hci_update_page_scan(&req); - hci_req_run(&req, NULL); -} diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index e49f682..5e64ea7 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -396,6 +396,62 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy, return 0; } +static bool disconnected_whitelist_entries(struct hci_dev *hdev) +{ + struct bdaddr_list *b; + + list_for_each_entry(b, &hdev->whitelist, list) { + struct hci_conn *conn; + + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &b->bdaddr); + if (!conn) + return true; + + if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG) + return true; + } + + return false; +} + +void __hci_update_page_scan(struct hci_request *req) +{ + struct hci_dev *hdev = req->hdev; + u8 scan; + + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + return; + + if (!hdev_is_powered(hdev)) + return; + + if (mgmt_powering_down(hdev)) + return; + + if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) || + disconnected_whitelist_entries(hdev)) + scan = SCAN_PAGE; + else + scan = SCAN_DISABLED; + + if (test_bit(HCI_PSCAN, &hdev->flags) == !!(scan & SCAN_PAGE)) + return; + + if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) + scan |= SCAN_INQUIRY; + + hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); +} + +void hci_update_page_scan(struct hci_dev *hdev) +{ + struct hci_request req; + + hci_req_init(&req, hdev); + __hci_update_page_scan(&req); + hci_req_run(&req, NULL); +} + /* This function controls the background scanning based on hdev->pend_le_conns * list. If there are pending LE connection we start the background scanning, * otherwise we stop it. -- cgit v0.10.2 From bd37a78de91e1b67b540b43f10bbb2d552348cbd Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 19 Dec 2014 23:45:58 +0100 Subject: mac802154: iface: check concurrent ifaces This patch adds a check for concurrent interfaces while calling interface up. This avoids to have different mac parameters on one phy. Otherwise it could be that a interface can overwrite current phy mac settings which is set by an another interface. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index 9ae8930..61f3ff0 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -175,6 +175,79 @@ err: return res; } +static int +ieee802154_check_mac_settings(struct ieee802154_local *local, + struct wpan_dev *wpan_dev, + struct wpan_dev *nwpan_dev) +{ + ASSERT_RTNL(); + + if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) { + if (wpan_dev->promiscuous_mode != nwpan_dev->promiscuous_mode) + return -EBUSY; + } + + if (local->hw.flags & IEEE802154_HW_AFILT) { + if (wpan_dev->pan_id != nwpan_dev->pan_id) + return -EBUSY; + + if (wpan_dev->short_addr != nwpan_dev->short_addr) + return -EBUSY; + + if (wpan_dev->extended_addr != nwpan_dev->extended_addr) + return -EBUSY; + } + + if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) { + if (wpan_dev->min_be != nwpan_dev->min_be) + return -EBUSY; + + if (wpan_dev->max_be != nwpan_dev->max_be) + return -EBUSY; + + if (wpan_dev->csma_retries != nwpan_dev->csma_retries) + return -EBUSY; + } + + if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) { + if (wpan_dev->frame_retries != nwpan_dev->frame_retries) + return -EBUSY; + } + + if (local->hw.flags & IEEE802154_HW_LBT) { + if (wpan_dev->lbt != nwpan_dev->lbt) + return -EBUSY; + } + + return 0; +} + +static int +ieee802154_check_concurrent_iface(struct ieee802154_sub_if_data *sdata, + enum nl802154_iftype iftype) +{ + struct ieee802154_local *local = sdata->local; + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + struct ieee802154_sub_if_data *nsdata; + + /* we hold the RTNL here so can safely walk the list */ + list_for_each_entry(nsdata, &local->interfaces, list) { + if (nsdata != sdata && ieee802154_sdata_running(nsdata)) { + int ret; + + /* check all phy mac sublayer settings are the same. + * We have only one phy, different values makes trouble. + */ + ret = ieee802154_check_mac_settings(local, wpan_dev, + &nsdata->wpan_dev); + if (ret < 0) + return ret; + } + } + + return 0; +} + static int mac802154_wpan_open(struct net_device *dev) { int rc; @@ -183,6 +256,10 @@ static int mac802154_wpan_open(struct net_device *dev) struct wpan_dev *wpan_dev = &sdata->wpan_dev; struct wpan_phy *phy = sdata->local->phy; + rc = ieee802154_check_concurrent_iface(sdata, sdata->vif.type); + if (rc < 0) + return rc; + rc = mac802154_slave_open(dev); if (rc < 0) return rc; -- cgit v0.10.2 From cd1c56653a65e5559cf48effe8864ff6df4b4430 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 19 Dec 2014 23:45:59 +0100 Subject: ieee802154: iface: move multiple node type check This patch moves the handling for checking on multiple node type interface to the corresponding concurrent iface check function. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index 61f3ff0..6fb6bdf 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -137,25 +137,11 @@ static int mac802154_wpan_mac_addr(struct net_device *dev, void *p) static int mac802154_slave_open(struct net_device *dev) { struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); - struct ieee802154_sub_if_data *subif; struct ieee802154_local *local = sdata->local; int res = 0; ASSERT_RTNL(); - if (sdata->vif.type == NL802154_IFTYPE_NODE) { - mutex_lock(&sdata->local->iflist_mtx); - list_for_each_entry(subif, &sdata->local->interfaces, list) { - if (subif != sdata && - subif->vif.type == sdata->vif.type && - ieee802154_sdata_running(subif)) { - mutex_unlock(&sdata->local->iflist_mtx); - return -EBUSY; - } - } - mutex_unlock(&sdata->local->iflist_mtx); - } - set_bit(SDATA_STATE_RUNNING, &sdata->state); if (!local->open_count) { @@ -235,6 +221,15 @@ ieee802154_check_concurrent_iface(struct ieee802154_sub_if_data *sdata, if (nsdata != sdata && ieee802154_sdata_running(nsdata)) { int ret; + /* TODO currently we don't support multiple node types + * we need to run skb_clone at rx path. Check if there + * exist really an use case if we need to support + * multiple node types at the same time. + */ + if (sdata->vif.type == NL802154_IFTYPE_NODE && + nsdata->vif.type == NL802154_IFTYPE_NODE) + return -EBUSY; + /* check all phy mac sublayer settings are the same. * We have only one phy, different values makes trouble. */ -- cgit v0.10.2 From 50b5b952b7c2bf2c75c257a62a6c456a0bbfdfa3 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 19 Dec 2014 23:05:35 +0100 Subject: Bluetooth: Support static address when BR/EDR has been disabled Every BR/EDR/LE dual-mode controller requires to have a public address and so far that has become the identity address and own address. The only way to change that behavior was with a force_static_address debugfs option. However the host can actually disable the BR/EDR part of a dual-mode controller and turn into a single mode LE controller. In that case it makes perfect sense for a host to use a chosen static address instead of the public address. So if the host disables BR/EDR and configures a static address, then that static address is used as identity address and own address. If the host does not configure a static address, then the public address is used as before. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9790a01..91dca12 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3852,12 +3852,18 @@ static void le_scan_disable_work(struct work_struct *work) * * For debugging purposes it is possible to force controllers with a * public address to use the static random address instead. + * + * In case BR/EDR has been disabled on a dual-mode controller and + * userspace has configured a static address, then that address + * becomes the identity address instead of the public BR/EDR address. */ void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *bdaddr_type) { if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) || - !bacmp(&hdev->bdaddr, BDADDR_ANY)) { + !bacmp(&hdev->bdaddr, BDADDR_ANY) || + (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) && + bacmp(&hdev->static_addr, BDADDR_ANY))) { bacpy(bdaddr, &hdev->static_addr); *bdaddr_type = ADDR_LE_DEV_RANDOM; } else { diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index 5e64ea7..324c641 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -378,9 +378,15 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy, * address use the static address as random address (but skip * the HCI command if the current random address is already the * static one. + * + * In case BR/EDR has been disabled on a dual-mode controller + * and a static address has been configured, then use that + * address instead of the public BR/EDR address. */ if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) || - !bacmp(&hdev->bdaddr, BDADDR_ANY)) { + !bacmp(&hdev->bdaddr, BDADDR_ANY) || + (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) && + bacmp(&hdev->static_addr, BDADDR_ANY))) { *own_addr_type = ADDR_LE_DEV_RANDOM; if (bacmp(&hdev->static_addr, &hdev->random_addr)) hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, -- cgit v0.10.2 From 60c5f5fb1f8640cb050822512246b79a68914145 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 20 Dec 2014 16:05:13 +0100 Subject: Bluetooth: Add skeleton functions for debugfs creation The debugfs file creation has been part of the core initialization handling of controllers. With the introduction of Bluetooth 4.2 core specification, the number of debugfs files is increasing even further. To avoid cluttering the core controller handling, create a separate file hci_debugfs.c to centralize all debugfs file creation. For now leave the current files in the core, but in the future all debugfs file creation will be moved. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 0a176fc..364cff1 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -13,6 +13,6 @@ bluetooth_6lowpan-y := 6lowpan.o bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \ - a2mp.o amp.o ecc.o hci_request.o + a2mp.o amp.o ecc.o hci_request.o hci_debugfs.o subdir-ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 91dca12..b3ab2bf 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -38,6 +38,7 @@ #include #include "hci_request.h" +#include "hci_debugfs.h" #include "smp.h" static void hci_rx_work(struct work_struct *work); @@ -1865,6 +1866,8 @@ static int __hci_init(struct hci_dev *hdev) debugfs_create_file("conn_info_max_age", 0644, hdev->debugfs, hdev, &conn_info_max_age_fops); + hci_debugfs_create_common(hdev); + if (lmp_bredr_capable(hdev)) { debugfs_create_file("inquiry_cache", 0444, hdev->debugfs, hdev, &inquiry_cache_fops); @@ -1874,6 +1877,8 @@ static int __hci_init(struct hci_dev *hdev) hdev, &dev_class_fops); debugfs_create_file("voice_setting", 0444, hdev->debugfs, hdev, &voice_setting_fops); + + hci_debugfs_create_bredr(hdev); } if (lmp_ssp_capable(hdev)) { @@ -1944,6 +1949,8 @@ static int __hci_init(struct hci_dev *hdev) hdev->debugfs, &hdev->discov_interleaved_timeout); + hci_debugfs_create_le(hdev); + smp_register(hdev); } diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c new file mode 100644 index 0000000..b8cf073 --- /dev/null +++ b/net/bluetooth/hci_debugfs.c @@ -0,0 +1,41 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + + Copyright (C) 2014 Intel 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; + + 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 OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +#include + +#include +#include + +#include "hci_debugfs.h" + +void hci_debugfs_create_common(struct hci_dev *hdev) +{ +} + +void hci_debugfs_create_bredr(struct hci_dev *hdev) +{ +} + +void hci_debugfs_create_le(struct hci_dev *hdev) +{ +} diff --git a/net/bluetooth/hci_debugfs.h b/net/bluetooth/hci_debugfs.h new file mode 100644 index 0000000..f191100 --- /dev/null +++ b/net/bluetooth/hci_debugfs.h @@ -0,0 +1,25 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + Copyright (C) 2014 Intel 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; + + 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 OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +void hci_debugfs_create_common(struct hci_dev *hdev); +void hci_debugfs_create_bredr(struct hci_dev *hdev); +void hci_debugfs_create_le(struct hci_dev *hdev); -- cgit v0.10.2 From 40ce72b1951c594c7f9f5f5df4aeebed6a07af92 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 20 Dec 2014 16:05:14 +0100 Subject: Bluetooth: Move common debugfs file creation into hci_debugfs.c This patch moves the creation of the debugs files common for all controllers into hci_debugfs.c file. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b3ab2bf..dde29f4 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -139,104 +139,6 @@ static const struct file_operations dut_mode_fops = { .llseek = default_llseek, }; -static int features_show(struct seq_file *f, void *ptr) -{ - struct hci_dev *hdev = f->private; - u8 p; - - hci_dev_lock(hdev); - for (p = 0; p < HCI_MAX_PAGES && p <= hdev->max_page; p++) { - seq_printf(f, "%2u: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x " - "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", p, - hdev->features[p][0], hdev->features[p][1], - hdev->features[p][2], hdev->features[p][3], - hdev->features[p][4], hdev->features[p][5], - hdev->features[p][6], hdev->features[p][7]); - } - if (lmp_le_capable(hdev)) - seq_printf(f, "LE: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x " - "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", - hdev->le_features[0], hdev->le_features[1], - hdev->le_features[2], hdev->le_features[3], - hdev->le_features[4], hdev->le_features[5], - hdev->le_features[6], hdev->le_features[7]); - hci_dev_unlock(hdev); - - return 0; -} - -static int features_open(struct inode *inode, struct file *file) -{ - return single_open(file, features_show, inode->i_private); -} - -static const struct file_operations features_fops = { - .open = features_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int blacklist_show(struct seq_file *f, void *p) -{ - struct hci_dev *hdev = f->private; - struct bdaddr_list *b; - - hci_dev_lock(hdev); - list_for_each_entry(b, &hdev->blacklist, list) - seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type); - hci_dev_unlock(hdev); - - return 0; -} - -static int blacklist_open(struct inode *inode, struct file *file) -{ - return single_open(file, blacklist_show, inode->i_private); -} - -static const struct file_operations blacklist_fops = { - .open = blacklist_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int uuids_show(struct seq_file *f, void *p) -{ - struct hci_dev *hdev = f->private; - struct bt_uuid *uuid; - - hci_dev_lock(hdev); - list_for_each_entry(uuid, &hdev->uuids, list) { - u8 i, val[16]; - - /* The Bluetooth UUID values are stored in big endian, - * but with reversed byte order. So convert them into - * the right order for the %pUb modifier. - */ - for (i = 0; i < 16; i++) - val[i] = uuid->uuid[15 - i]; - - seq_printf(f, "%pUb\n", val); - } - hci_dev_unlock(hdev); - - return 0; -} - -static int uuids_open(struct inode *inode, struct file *file) -{ - return single_open(file, uuids_show, inode->i_private); -} - -static const struct file_operations uuids_fops = { - .open = uuids_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - static int inquiry_cache_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; @@ -584,62 +486,6 @@ static int sniff_max_interval_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get, sniff_max_interval_set, "%llu\n"); -static int conn_info_min_age_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - - if (val == 0 || val > hdev->conn_info_max_age) - return -EINVAL; - - hci_dev_lock(hdev); - hdev->conn_info_min_age = val; - hci_dev_unlock(hdev); - - return 0; -} - -static int conn_info_min_age_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - *val = hdev->conn_info_min_age; - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(conn_info_min_age_fops, conn_info_min_age_get, - conn_info_min_age_set, "%llu\n"); - -static int conn_info_max_age_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - - if (val == 0 || val < hdev->conn_info_min_age) - return -EINVAL; - - hci_dev_lock(hdev); - hdev->conn_info_max_age = val; - hci_dev_unlock(hdev); - - return 0; -} - -static int conn_info_max_age_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - *val = hdev->conn_info_max_age; - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(conn_info_max_age_fops, conn_info_max_age_get, - conn_info_max_age_set, "%llu\n"); - static int identity_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; @@ -1041,36 +887,6 @@ static int adv_max_interval_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(adv_max_interval_fops, adv_max_interval_get, adv_max_interval_set, "%llu\n"); -static int device_list_show(struct seq_file *f, void *ptr) -{ - struct hci_dev *hdev = f->private; - struct hci_conn_params *p; - struct bdaddr_list *b; - - hci_dev_lock(hdev); - list_for_each_entry(b, &hdev->whitelist, list) - seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type); - list_for_each_entry(p, &hdev->le_conn_params, list) { - seq_printf(f, "%pMR (type %u) %u\n", &p->addr, p->addr_type, - p->auto_connect); - } - hci_dev_unlock(hdev); - - return 0; -} - -static int device_list_open(struct inode *inode, struct file *file) -{ - return single_open(file, device_list_show, inode->i_private); -} - -static const struct file_operations device_list_fops = { - .open = device_list_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - /* ---- HCI requests ---- */ static void hci_req_sync_complete(struct hci_dev *hdev, u8 result) @@ -1849,23 +1665,6 @@ static int __hci_init(struct hci_dev *hdev) if (!test_bit(HCI_SETUP, &hdev->dev_flags)) return 0; - debugfs_create_file("features", 0444, hdev->debugfs, hdev, - &features_fops); - debugfs_create_u16("manufacturer", 0444, hdev->debugfs, - &hdev->manufacturer); - debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver); - debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev); - debugfs_create_file("device_list", 0444, hdev->debugfs, hdev, - &device_list_fops); - debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev, - &blacklist_fops); - debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops); - - debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev, - &conn_info_min_age_fops); - debugfs_create_file("conn_info_max_age", 0644, hdev->debugfs, hdev, - &conn_info_max_age_fops); - hci_debugfs_create_common(hdev); if (lmp_bredr_capable(hdev)) { diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index b8cf073..4d06bee 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -28,8 +28,208 @@ #include "hci_debugfs.h" +static int features_show(struct seq_file *f, void *ptr) +{ + struct hci_dev *hdev = f->private; + u8 p; + + hci_dev_lock(hdev); + for (p = 0; p < HCI_MAX_PAGES && p <= hdev->max_page; p++) { + seq_printf(f, "%2u: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x " + "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", p, + hdev->features[p][0], hdev->features[p][1], + hdev->features[p][2], hdev->features[p][3], + hdev->features[p][4], hdev->features[p][5], + hdev->features[p][6], hdev->features[p][7]); + } + if (lmp_le_capable(hdev)) + seq_printf(f, "LE: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x " + "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", + hdev->le_features[0], hdev->le_features[1], + hdev->le_features[2], hdev->le_features[3], + hdev->le_features[4], hdev->le_features[5], + hdev->le_features[6], hdev->le_features[7]); + hci_dev_unlock(hdev); + + return 0; +} + +static int features_open(struct inode *inode, struct file *file) +{ + return single_open(file, features_show, inode->i_private); +} + +static const struct file_operations features_fops = { + .open = features_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int device_list_show(struct seq_file *f, void *ptr) +{ + struct hci_dev *hdev = f->private; + struct hci_conn_params *p; + struct bdaddr_list *b; + + hci_dev_lock(hdev); + list_for_each_entry(b, &hdev->whitelist, list) + seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type); + list_for_each_entry(p, &hdev->le_conn_params, list) { + seq_printf(f, "%pMR (type %u) %u\n", &p->addr, p->addr_type, + p->auto_connect); + } + hci_dev_unlock(hdev); + + return 0; +} + +static int device_list_open(struct inode *inode, struct file *file) +{ + return single_open(file, device_list_show, inode->i_private); +} + +static const struct file_operations device_list_fops = { + .open = device_list_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int blacklist_show(struct seq_file *f, void *p) +{ + struct hci_dev *hdev = f->private; + struct bdaddr_list *b; + + hci_dev_lock(hdev); + list_for_each_entry(b, &hdev->blacklist, list) + seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type); + hci_dev_unlock(hdev); + + return 0; +} + +static int blacklist_open(struct inode *inode, struct file *file) +{ + return single_open(file, blacklist_show, inode->i_private); +} + +static const struct file_operations blacklist_fops = { + .open = blacklist_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int uuids_show(struct seq_file *f, void *p) +{ + struct hci_dev *hdev = f->private; + struct bt_uuid *uuid; + + hci_dev_lock(hdev); + list_for_each_entry(uuid, &hdev->uuids, list) { + u8 i, val[16]; + + /* The Bluetooth UUID values are stored in big endian, + * but with reversed byte order. So convert them into + * the right order for the %pUb modifier. + */ + for (i = 0; i < 16; i++) + val[i] = uuid->uuid[15 - i]; + + seq_printf(f, "%pUb\n", val); + } + hci_dev_unlock(hdev); + + return 0; +} + +static int uuids_open(struct inode *inode, struct file *file) +{ + return single_open(file, uuids_show, inode->i_private); +} + +static const struct file_operations uuids_fops = { + .open = uuids_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int conn_info_min_age_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val == 0 || val > hdev->conn_info_max_age) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->conn_info_min_age = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int conn_info_min_age_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->conn_info_min_age; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(conn_info_min_age_fops, conn_info_min_age_get, + conn_info_min_age_set, "%llu\n"); + +static int conn_info_max_age_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val == 0 || val < hdev->conn_info_min_age) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->conn_info_max_age = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int conn_info_max_age_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->conn_info_max_age; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(conn_info_max_age_fops, conn_info_max_age_get, + conn_info_max_age_set, "%llu\n"); + void hci_debugfs_create_common(struct hci_dev *hdev) { + debugfs_create_file("features", 0444, hdev->debugfs, hdev, + &features_fops); + debugfs_create_u16("manufacturer", 0444, hdev->debugfs, + &hdev->manufacturer); + debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver); + debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev); + debugfs_create_file("device_list", 0444, hdev->debugfs, hdev, + &device_list_fops); + debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev, + &blacklist_fops); + debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops); + + debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev, + &conn_info_min_age_fops); + debugfs_create_file("conn_info_max_age", 0644, hdev->debugfs, hdev, + &conn_info_max_age_fops); } void hci_debugfs_create_bredr(struct hci_dev *hdev) -- cgit v0.10.2 From 71c3b60ec6d288f2551b517186b025da4cbb18b5 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 20 Dec 2014 16:05:15 +0100 Subject: Bluetooth: Move BR/EDR debugfs file creation into hci_debugfs.c This patch moves the creation of the debugs files for BR/EDR controllers into hci_debugfs.c file. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index dde29f4..d8976cb 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -139,266 +139,6 @@ static const struct file_operations dut_mode_fops = { .llseek = default_llseek, }; -static int inquiry_cache_show(struct seq_file *f, void *p) -{ - struct hci_dev *hdev = f->private; - struct discovery_state *cache = &hdev->discovery; - struct inquiry_entry *e; - - hci_dev_lock(hdev); - - list_for_each_entry(e, &cache->all, all) { - struct inquiry_data *data = &e->data; - seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n", - &data->bdaddr, - data->pscan_rep_mode, data->pscan_period_mode, - data->pscan_mode, data->dev_class[2], - data->dev_class[1], data->dev_class[0], - __le16_to_cpu(data->clock_offset), - data->rssi, data->ssp_mode, e->timestamp); - } - - hci_dev_unlock(hdev); - - return 0; -} - -static int inquiry_cache_open(struct inode *inode, struct file *file) -{ - return single_open(file, inquiry_cache_show, inode->i_private); -} - -static const struct file_operations inquiry_cache_fops = { - .open = inquiry_cache_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int link_keys_show(struct seq_file *f, void *ptr) -{ - struct hci_dev *hdev = f->private; - struct link_key *key; - - rcu_read_lock(); - list_for_each_entry_rcu(key, &hdev->link_keys, list) - seq_printf(f, "%pMR %u %*phN %u\n", &key->bdaddr, key->type, - HCI_LINK_KEY_SIZE, key->val, key->pin_len); - rcu_read_unlock(); - - return 0; -} - -static int link_keys_open(struct inode *inode, struct file *file) -{ - return single_open(file, link_keys_show, inode->i_private); -} - -static const struct file_operations link_keys_fops = { - .open = link_keys_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int dev_class_show(struct seq_file *f, void *ptr) -{ - struct hci_dev *hdev = f->private; - - hci_dev_lock(hdev); - seq_printf(f, "0x%.2x%.2x%.2x\n", hdev->dev_class[2], - hdev->dev_class[1], hdev->dev_class[0]); - hci_dev_unlock(hdev); - - return 0; -} - -static int dev_class_open(struct inode *inode, struct file *file) -{ - return single_open(file, dev_class_show, inode->i_private); -} - -static const struct file_operations dev_class_fops = { - .open = dev_class_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int voice_setting_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - *val = hdev->voice_setting; - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(voice_setting_fops, voice_setting_get, - NULL, "0x%4.4llx\n"); - -static int auto_accept_delay_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - hdev->auto_accept_delay = val; - hci_dev_unlock(hdev); - - return 0; -} - -static int auto_accept_delay_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - *val = hdev->auto_accept_delay; - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get, - auto_accept_delay_set, "%llu\n"); - -static ssize_t force_sc_support_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct hci_dev *hdev = file->private_data; - char buf[3]; - - buf[0] = test_bit(HCI_FORCE_SC, &hdev->dbg_flags) ? 'Y': 'N'; - buf[1] = '\n'; - buf[2] = '\0'; - return simple_read_from_buffer(user_buf, count, ppos, buf, 2); -} - -static ssize_t force_sc_support_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct hci_dev *hdev = file->private_data; - char buf[32]; - size_t buf_size = min(count, (sizeof(buf)-1)); - bool enable; - - if (test_bit(HCI_UP, &hdev->flags)) - return -EBUSY; - - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; - if (strtobool(buf, &enable)) - return -EINVAL; - - if (enable == test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) - return -EALREADY; - - change_bit(HCI_FORCE_SC, &hdev->dbg_flags); - - return count; -} - -static const struct file_operations force_sc_support_fops = { - .open = simple_open, - .read = force_sc_support_read, - .write = force_sc_support_write, - .llseek = default_llseek, -}; - -static ssize_t force_lesc_support_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct hci_dev *hdev = file->private_data; - char buf[3]; - - buf[0] = test_bit(HCI_FORCE_LESC, &hdev->dbg_flags) ? 'Y': 'N'; - buf[1] = '\n'; - buf[2] = '\0'; - return simple_read_from_buffer(user_buf, count, ppos, buf, 2); -} - -static ssize_t force_lesc_support_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct hci_dev *hdev = file->private_data; - char buf[32]; - size_t buf_size = min(count, (sizeof(buf)-1)); - bool enable; - - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; - if (strtobool(buf, &enable)) - return -EINVAL; - - if (enable == test_bit(HCI_FORCE_LESC, &hdev->dbg_flags)) - return -EALREADY; - - change_bit(HCI_FORCE_LESC, &hdev->dbg_flags); - - return count; -} - -static const struct file_operations force_lesc_support_fops = { - .open = simple_open, - .read = force_lesc_support_read, - .write = force_lesc_support_write, - .llseek = default_llseek, -}; - -static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct hci_dev *hdev = file->private_data; - char buf[3]; - - buf[0] = test_bit(HCI_SC_ONLY, &hdev->dev_flags) ? 'Y': 'N'; - buf[1] = '\n'; - buf[2] = '\0'; - return simple_read_from_buffer(user_buf, count, ppos, buf, 2); -} - -static const struct file_operations sc_only_mode_fops = { - .open = simple_open, - .read = sc_only_mode_read, - .llseek = default_llseek, -}; - -static int idle_timeout_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - - if (val != 0 && (val < 500 || val > 3600000)) - return -EINVAL; - - hci_dev_lock(hdev); - hdev->idle_timeout = val; - hci_dev_unlock(hdev); - - return 0; -} - -static int idle_timeout_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - *val = hdev->idle_timeout; - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(idle_timeout_fops, idle_timeout_get, - idle_timeout_set, "%llu\n"); - static int rpa_timeout_set(void *data, u64 val) { struct hci_dev *hdev = data; @@ -430,62 +170,6 @@ static int rpa_timeout_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(rpa_timeout_fops, rpa_timeout_get, rpa_timeout_set, "%llu\n"); -static int sniff_min_interval_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - - if (val == 0 || val % 2 || val > hdev->sniff_max_interval) - return -EINVAL; - - hci_dev_lock(hdev); - hdev->sniff_min_interval = val; - hci_dev_unlock(hdev); - - return 0; -} - -static int sniff_min_interval_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - *val = hdev->sniff_min_interval; - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(sniff_min_interval_fops, sniff_min_interval_get, - sniff_min_interval_set, "%llu\n"); - -static int sniff_max_interval_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - - if (val == 0 || val % 2 || val < hdev->sniff_min_interval) - return -EINVAL; - - hci_dev_lock(hdev); - hdev->sniff_max_interval = val; - hci_dev_unlock(hdev); - - return 0; -} - -static int sniff_max_interval_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - *val = hdev->sniff_max_interval; - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get, - sniff_max_interval_set, "%llu\n"); - static int identity_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; @@ -1667,40 +1351,8 @@ static int __hci_init(struct hci_dev *hdev) hci_debugfs_create_common(hdev); - if (lmp_bredr_capable(hdev)) { - debugfs_create_file("inquiry_cache", 0444, hdev->debugfs, - hdev, &inquiry_cache_fops); - debugfs_create_file("link_keys", 0400, hdev->debugfs, - hdev, &link_keys_fops); - debugfs_create_file("dev_class", 0444, hdev->debugfs, - hdev, &dev_class_fops); - debugfs_create_file("voice_setting", 0444, hdev->debugfs, - hdev, &voice_setting_fops); - + if (lmp_bredr_capable(hdev)) hci_debugfs_create_bredr(hdev); - } - - if (lmp_ssp_capable(hdev)) { - debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs, - hdev, &auto_accept_delay_fops); - debugfs_create_file("force_sc_support", 0644, hdev->debugfs, - hdev, &force_sc_support_fops); - debugfs_create_file("sc_only_mode", 0444, hdev->debugfs, - hdev, &sc_only_mode_fops); - if (lmp_le_capable(hdev)) - debugfs_create_file("force_lesc_support", 0644, - hdev->debugfs, hdev, - &force_lesc_support_fops); - } - - if (lmp_sniff_capable(hdev)) { - debugfs_create_file("idle_timeout", 0644, hdev->debugfs, - hdev, &idle_timeout_fops); - debugfs_create_file("sniff_min_interval", 0644, hdev->debugfs, - hdev, &sniff_min_interval_fops); - debugfs_create_file("sniff_max_interval", 0644, hdev->debugfs, - hdev, &sniff_max_interval_fops); - } if (lmp_le_capable(hdev)) { debugfs_create_file("identity", 0400, hdev->debugfs, diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index 4d06bee..435f091 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -232,8 +232,357 @@ void hci_debugfs_create_common(struct hci_dev *hdev) &conn_info_max_age_fops); } +static int inquiry_cache_show(struct seq_file *f, void *p) +{ + struct hci_dev *hdev = f->private; + struct discovery_state *cache = &hdev->discovery; + struct inquiry_entry *e; + + hci_dev_lock(hdev); + + list_for_each_entry(e, &cache->all, all) { + struct inquiry_data *data = &e->data; + seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n", + &data->bdaddr, + data->pscan_rep_mode, data->pscan_period_mode, + data->pscan_mode, data->dev_class[2], + data->dev_class[1], data->dev_class[0], + __le16_to_cpu(data->clock_offset), + data->rssi, data->ssp_mode, e->timestamp); + } + + hci_dev_unlock(hdev); + + return 0; +} + +static int inquiry_cache_open(struct inode *inode, struct file *file) +{ + return single_open(file, inquiry_cache_show, inode->i_private); +} + +static const struct file_operations inquiry_cache_fops = { + .open = inquiry_cache_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int link_keys_show(struct seq_file *f, void *ptr) +{ + struct hci_dev *hdev = f->private; + struct link_key *key; + + rcu_read_lock(); + list_for_each_entry_rcu(key, &hdev->link_keys, list) + seq_printf(f, "%pMR %u %*phN %u\n", &key->bdaddr, key->type, + HCI_LINK_KEY_SIZE, key->val, key->pin_len); + rcu_read_unlock(); + + return 0; +} + +static int link_keys_open(struct inode *inode, struct file *file) +{ + return single_open(file, link_keys_show, inode->i_private); +} + +static const struct file_operations link_keys_fops = { + .open = link_keys_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int dev_class_show(struct seq_file *f, void *ptr) +{ + struct hci_dev *hdev = f->private; + + hci_dev_lock(hdev); + seq_printf(f, "0x%.2x%.2x%.2x\n", hdev->dev_class[2], + hdev->dev_class[1], hdev->dev_class[0]); + hci_dev_unlock(hdev); + + return 0; +} + +static int dev_class_open(struct inode *inode, struct file *file) +{ + return single_open(file, dev_class_show, inode->i_private); +} + +static const struct file_operations dev_class_fops = { + .open = dev_class_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int voice_setting_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->voice_setting; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(voice_setting_fops, voice_setting_get, + NULL, "0x%4.4llx\n"); + +static int auto_accept_delay_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + hdev->auto_accept_delay = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int auto_accept_delay_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->auto_accept_delay; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get, + auto_accept_delay_set, "%llu\n"); + +static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[3]; + + buf[0] = test_bit(HCI_SC_ONLY, &hdev->dev_flags) ? 'Y': 'N'; + buf[1] = '\n'; + buf[2] = '\0'; + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static const struct file_operations sc_only_mode_fops = { + .open = simple_open, + .read = sc_only_mode_read, + .llseek = default_llseek, +}; + +static ssize_t force_sc_support_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[3]; + + buf[0] = test_bit(HCI_FORCE_SC, &hdev->dbg_flags) ? 'Y': 'N'; + buf[1] = '\n'; + buf[2] = '\0'; + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static ssize_t force_sc_support_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[32]; + size_t buf_size = min(count, (sizeof(buf)-1)); + bool enable; + + if (test_bit(HCI_UP, &hdev->flags)) + return -EBUSY; + + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + buf[buf_size] = '\0'; + if (strtobool(buf, &enable)) + return -EINVAL; + + if (enable == test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) + return -EALREADY; + + change_bit(HCI_FORCE_SC, &hdev->dbg_flags); + + return count; +} + +static const struct file_operations force_sc_support_fops = { + .open = simple_open, + .read = force_sc_support_read, + .write = force_sc_support_write, + .llseek = default_llseek, +}; + +static ssize_t force_lesc_support_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[3]; + + buf[0] = test_bit(HCI_FORCE_LESC, &hdev->dbg_flags) ? 'Y': 'N'; + buf[1] = '\n'; + buf[2] = '\0'; + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static ssize_t force_lesc_support_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[32]; + size_t buf_size = min(count, (sizeof(buf)-1)); + bool enable; + + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + buf[buf_size] = '\0'; + if (strtobool(buf, &enable)) + return -EINVAL; + + if (enable == test_bit(HCI_FORCE_LESC, &hdev->dbg_flags)) + return -EALREADY; + + change_bit(HCI_FORCE_LESC, &hdev->dbg_flags); + + return count; +} + +static const struct file_operations force_lesc_support_fops = { + .open = simple_open, + .read = force_lesc_support_read, + .write = force_lesc_support_write, + .llseek = default_llseek, +}; + +static int idle_timeout_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val != 0 && (val < 500 || val > 3600000)) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->idle_timeout = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int idle_timeout_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->idle_timeout; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(idle_timeout_fops, idle_timeout_get, + idle_timeout_set, "%llu\n"); + +static int sniff_min_interval_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val == 0 || val % 2 || val > hdev->sniff_max_interval) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->sniff_min_interval = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int sniff_min_interval_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->sniff_min_interval; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(sniff_min_interval_fops, sniff_min_interval_get, + sniff_min_interval_set, "%llu\n"); + +static int sniff_max_interval_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val == 0 || val % 2 || val < hdev->sniff_min_interval) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->sniff_max_interval = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int sniff_max_interval_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->sniff_max_interval; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get, + sniff_max_interval_set, "%llu\n"); + void hci_debugfs_create_bredr(struct hci_dev *hdev) { + debugfs_create_file("inquiry_cache", 0444, hdev->debugfs, hdev, + &inquiry_cache_fops); + debugfs_create_file("link_keys", 0400, hdev->debugfs, hdev, + &link_keys_fops); + debugfs_create_file("dev_class", 0444, hdev->debugfs, hdev, + &dev_class_fops); + debugfs_create_file("voice_setting", 0444, hdev->debugfs, hdev, + &voice_setting_fops); + + if (lmp_ssp_capable(hdev)) { + debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs, + hdev, &auto_accept_delay_fops); + debugfs_create_file("sc_only_mode", 0444, hdev->debugfs, + hdev, &sc_only_mode_fops); + + debugfs_create_file("force_sc_support", 0644, hdev->debugfs, + hdev, &force_sc_support_fops); + + if (lmp_le_capable(hdev)) + debugfs_create_file("force_lesc_support", 0644, + hdev->debugfs, hdev, + &force_lesc_support_fops); + } + + if (lmp_sniff_capable(hdev)) { + debugfs_create_file("idle_timeout", 0644, hdev->debugfs, + hdev, &idle_timeout_fops); + debugfs_create_file("sniff_min_interval", 0644, hdev->debugfs, + hdev, &sniff_min_interval_fops); + debugfs_create_file("sniff_max_interval", 0644, hdev->debugfs, + hdev, &sniff_max_interval_fops); + } } void hci_debugfs_create_le(struct hci_dev *hdev) -- cgit v0.10.2 From 3a5c82b78fd28ef39c5d07e72c78a569a6ea658a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 20 Dec 2014 16:05:16 +0100 Subject: Bluetooth: Move LE debugfs file creation into hci_debugfs.c This patch moves the creation of the debugs files for LE controllers into hci_debugfs.c file. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index d8976cb..2db071b 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -139,438 +139,6 @@ static const struct file_operations dut_mode_fops = { .llseek = default_llseek, }; -static int rpa_timeout_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - - /* Require the RPA timeout to be at least 30 seconds and at most - * 24 hours. - */ - if (val < 30 || val > (60 * 60 * 24)) - return -EINVAL; - - hci_dev_lock(hdev); - hdev->rpa_timeout = val; - hci_dev_unlock(hdev); - - return 0; -} - -static int rpa_timeout_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - *val = hdev->rpa_timeout; - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(rpa_timeout_fops, rpa_timeout_get, - rpa_timeout_set, "%llu\n"); - -static int identity_show(struct seq_file *f, void *p) -{ - struct hci_dev *hdev = f->private; - bdaddr_t addr; - u8 addr_type; - - hci_dev_lock(hdev); - - hci_copy_identity_address(hdev, &addr, &addr_type); - - seq_printf(f, "%pMR (type %u) %*phN %pMR\n", &addr, addr_type, - 16, hdev->irk, &hdev->rpa); - - hci_dev_unlock(hdev); - - return 0; -} - -static int identity_open(struct inode *inode, struct file *file) -{ - return single_open(file, identity_show, inode->i_private); -} - -static const struct file_operations identity_fops = { - .open = identity_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int random_address_show(struct seq_file *f, void *p) -{ - struct hci_dev *hdev = f->private; - - hci_dev_lock(hdev); - seq_printf(f, "%pMR\n", &hdev->random_addr); - hci_dev_unlock(hdev); - - return 0; -} - -static int random_address_open(struct inode *inode, struct file *file) -{ - return single_open(file, random_address_show, inode->i_private); -} - -static const struct file_operations random_address_fops = { - .open = random_address_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int static_address_show(struct seq_file *f, void *p) -{ - struct hci_dev *hdev = f->private; - - hci_dev_lock(hdev); - seq_printf(f, "%pMR\n", &hdev->static_addr); - hci_dev_unlock(hdev); - - return 0; -} - -static int static_address_open(struct inode *inode, struct file *file) -{ - return single_open(file, static_address_show, inode->i_private); -} - -static const struct file_operations static_address_fops = { - .open = static_address_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static ssize_t force_static_address_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct hci_dev *hdev = file->private_data; - char buf[3]; - - buf[0] = test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ? 'Y': 'N'; - buf[1] = '\n'; - buf[2] = '\0'; - return simple_read_from_buffer(user_buf, count, ppos, buf, 2); -} - -static ssize_t force_static_address_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct hci_dev *hdev = file->private_data; - char buf[32]; - size_t buf_size = min(count, (sizeof(buf)-1)); - bool enable; - - if (test_bit(HCI_UP, &hdev->flags)) - return -EBUSY; - - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; - if (strtobool(buf, &enable)) - return -EINVAL; - - if (enable == test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags)) - return -EALREADY; - - change_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags); - - return count; -} - -static const struct file_operations force_static_address_fops = { - .open = simple_open, - .read = force_static_address_read, - .write = force_static_address_write, - .llseek = default_llseek, -}; - -static int white_list_show(struct seq_file *f, void *ptr) -{ - struct hci_dev *hdev = f->private; - struct bdaddr_list *b; - - hci_dev_lock(hdev); - list_for_each_entry(b, &hdev->le_white_list, list) - seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type); - hci_dev_unlock(hdev); - - return 0; -} - -static int white_list_open(struct inode *inode, struct file *file) -{ - return single_open(file, white_list_show, inode->i_private); -} - -static const struct file_operations white_list_fops = { - .open = white_list_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int identity_resolving_keys_show(struct seq_file *f, void *ptr) -{ - struct hci_dev *hdev = f->private; - struct smp_irk *irk; - - rcu_read_lock(); - list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) { - seq_printf(f, "%pMR (type %u) %*phN %pMR\n", - &irk->bdaddr, irk->addr_type, - 16, irk->val, &irk->rpa); - } - rcu_read_unlock(); - - return 0; -} - -static int identity_resolving_keys_open(struct inode *inode, struct file *file) -{ - return single_open(file, identity_resolving_keys_show, - inode->i_private); -} - -static const struct file_operations identity_resolving_keys_fops = { - .open = identity_resolving_keys_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int long_term_keys_show(struct seq_file *f, void *ptr) -{ - struct hci_dev *hdev = f->private; - struct smp_ltk *ltk; - - rcu_read_lock(); - list_for_each_entry_rcu(ltk, &hdev->long_term_keys, list) - seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %.16llx %*phN\n", - <k->bdaddr, ltk->bdaddr_type, ltk->authenticated, - ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv), - __le64_to_cpu(ltk->rand), 16, ltk->val); - rcu_read_unlock(); - - return 0; -} - -static int long_term_keys_open(struct inode *inode, struct file *file) -{ - return single_open(file, long_term_keys_show, inode->i_private); -} - -static const struct file_operations long_term_keys_fops = { - .open = long_term_keys_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int conn_min_interval_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - - if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval) - return -EINVAL; - - hci_dev_lock(hdev); - hdev->le_conn_min_interval = val; - hci_dev_unlock(hdev); - - return 0; -} - -static int conn_min_interval_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - *val = hdev->le_conn_min_interval; - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(conn_min_interval_fops, conn_min_interval_get, - conn_min_interval_set, "%llu\n"); - -static int conn_max_interval_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - - if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval) - return -EINVAL; - - hci_dev_lock(hdev); - hdev->le_conn_max_interval = val; - hci_dev_unlock(hdev); - - return 0; -} - -static int conn_max_interval_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - *val = hdev->le_conn_max_interval; - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get, - conn_max_interval_set, "%llu\n"); - -static int conn_latency_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - - if (val > 0x01f3) - return -EINVAL; - - hci_dev_lock(hdev); - hdev->le_conn_latency = val; - hci_dev_unlock(hdev); - - return 0; -} - -static int conn_latency_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - *val = hdev->le_conn_latency; - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(conn_latency_fops, conn_latency_get, - conn_latency_set, "%llu\n"); - -static int supervision_timeout_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - - if (val < 0x000a || val > 0x0c80) - return -EINVAL; - - hci_dev_lock(hdev); - hdev->le_supv_timeout = val; - hci_dev_unlock(hdev); - - return 0; -} - -static int supervision_timeout_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - *val = hdev->le_supv_timeout; - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(supervision_timeout_fops, supervision_timeout_get, - supervision_timeout_set, "%llu\n"); - -static int adv_channel_map_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - - if (val < 0x01 || val > 0x07) - return -EINVAL; - - hci_dev_lock(hdev); - hdev->le_adv_channel_map = val; - hci_dev_unlock(hdev); - - return 0; -} - -static int adv_channel_map_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - *val = hdev->le_adv_channel_map; - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(adv_channel_map_fops, adv_channel_map_get, - adv_channel_map_set, "%llu\n"); - -static int adv_min_interval_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - - if (val < 0x0020 || val > 0x4000 || val > hdev->le_adv_max_interval) - return -EINVAL; - - hci_dev_lock(hdev); - hdev->le_adv_min_interval = val; - hci_dev_unlock(hdev); - - return 0; -} - -static int adv_min_interval_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - *val = hdev->le_adv_min_interval; - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(adv_min_interval_fops, adv_min_interval_get, - adv_min_interval_set, "%llu\n"); - -static int adv_max_interval_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - - if (val < 0x0020 || val > 0x4000 || val < hdev->le_adv_min_interval) - return -EINVAL; - - hci_dev_lock(hdev); - hdev->le_adv_max_interval = val; - hci_dev_unlock(hdev); - - return 0; -} - -static int adv_max_interval_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - *val = hdev->le_adv_max_interval; - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(adv_max_interval_fops, adv_max_interval_get, - adv_max_interval_set, "%llu\n"); - /* ---- HCI requests ---- */ static void hci_req_sync_complete(struct hci_dev *hdev, u8 result) @@ -1355,53 +923,7 @@ static int __hci_init(struct hci_dev *hdev) hci_debugfs_create_bredr(hdev); if (lmp_le_capable(hdev)) { - debugfs_create_file("identity", 0400, hdev->debugfs, - hdev, &identity_fops); - debugfs_create_file("rpa_timeout", 0644, hdev->debugfs, - hdev, &rpa_timeout_fops); - debugfs_create_file("random_address", 0444, hdev->debugfs, - hdev, &random_address_fops); - debugfs_create_file("static_address", 0444, hdev->debugfs, - hdev, &static_address_fops); - - /* For controllers with a public address, provide a debug - * option to force the usage of the configured static - * address. By default the public address is used. - */ - if (bacmp(&hdev->bdaddr, BDADDR_ANY)) - debugfs_create_file("force_static_address", 0644, - hdev->debugfs, hdev, - &force_static_address_fops); - - debugfs_create_u8("white_list_size", 0444, hdev->debugfs, - &hdev->le_white_list_size); - debugfs_create_file("white_list", 0444, hdev->debugfs, hdev, - &white_list_fops); - debugfs_create_file("identity_resolving_keys", 0400, - hdev->debugfs, hdev, - &identity_resolving_keys_fops); - debugfs_create_file("long_term_keys", 0400, hdev->debugfs, - hdev, &long_term_keys_fops); - debugfs_create_file("conn_min_interval", 0644, hdev->debugfs, - hdev, &conn_min_interval_fops); - debugfs_create_file("conn_max_interval", 0644, hdev->debugfs, - hdev, &conn_max_interval_fops); - debugfs_create_file("conn_latency", 0644, hdev->debugfs, - hdev, &conn_latency_fops); - debugfs_create_file("supervision_timeout", 0644, hdev->debugfs, - hdev, &supervision_timeout_fops); - debugfs_create_file("adv_channel_map", 0644, hdev->debugfs, - hdev, &adv_channel_map_fops); - debugfs_create_file("adv_min_interval", 0644, hdev->debugfs, - hdev, &adv_min_interval_fops); - debugfs_create_file("adv_max_interval", 0644, hdev->debugfs, - hdev, &adv_max_interval_fops); - debugfs_create_u16("discov_interleaved_timeout", 0644, - hdev->debugfs, - &hdev->discov_interleaved_timeout); - hci_debugfs_create_le(hdev); - smp_register(hdev); } diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index 435f091..a7a0db0 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -585,6 +585,480 @@ void hci_debugfs_create_bredr(struct hci_dev *hdev) } } +static int identity_show(struct seq_file *f, void *p) +{ + struct hci_dev *hdev = f->private; + bdaddr_t addr; + u8 addr_type; + + hci_dev_lock(hdev); + + hci_copy_identity_address(hdev, &addr, &addr_type); + + seq_printf(f, "%pMR (type %u) %*phN %pMR\n", &addr, addr_type, + 16, hdev->irk, &hdev->rpa); + + hci_dev_unlock(hdev); + + return 0; +} + +static int identity_open(struct inode *inode, struct file *file) +{ + return single_open(file, identity_show, inode->i_private); +} + +static const struct file_operations identity_fops = { + .open = identity_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int rpa_timeout_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + /* Require the RPA timeout to be at least 30 seconds and at most + * 24 hours. + */ + if (val < 30 || val > (60 * 60 * 24)) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->rpa_timeout = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int rpa_timeout_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->rpa_timeout; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(rpa_timeout_fops, rpa_timeout_get, + rpa_timeout_set, "%llu\n"); + +static int random_address_show(struct seq_file *f, void *p) +{ + struct hci_dev *hdev = f->private; + + hci_dev_lock(hdev); + seq_printf(f, "%pMR\n", &hdev->random_addr); + hci_dev_unlock(hdev); + + return 0; +} + +static int random_address_open(struct inode *inode, struct file *file) +{ + return single_open(file, random_address_show, inode->i_private); +} + +static const struct file_operations random_address_fops = { + .open = random_address_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int static_address_show(struct seq_file *f, void *p) +{ + struct hci_dev *hdev = f->private; + + hci_dev_lock(hdev); + seq_printf(f, "%pMR\n", &hdev->static_addr); + hci_dev_unlock(hdev); + + return 0; +} + +static int static_address_open(struct inode *inode, struct file *file) +{ + return single_open(file, static_address_show, inode->i_private); +} + +static const struct file_operations static_address_fops = { + .open = static_address_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static ssize_t force_static_address_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[3]; + + buf[0] = test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ? 'Y': 'N'; + buf[1] = '\n'; + buf[2] = '\0'; + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static ssize_t force_static_address_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[32]; + size_t buf_size = min(count, (sizeof(buf)-1)); + bool enable; + + if (test_bit(HCI_UP, &hdev->flags)) + return -EBUSY; + + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + buf[buf_size] = '\0'; + if (strtobool(buf, &enable)) + return -EINVAL; + + if (enable == test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags)) + return -EALREADY; + + change_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags); + + return count; +} + +static const struct file_operations force_static_address_fops = { + .open = simple_open, + .read = force_static_address_read, + .write = force_static_address_write, + .llseek = default_llseek, +}; + +static int white_list_show(struct seq_file *f, void *ptr) +{ + struct hci_dev *hdev = f->private; + struct bdaddr_list *b; + + hci_dev_lock(hdev); + list_for_each_entry(b, &hdev->le_white_list, list) + seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type); + hci_dev_unlock(hdev); + + return 0; +} + +static int white_list_open(struct inode *inode, struct file *file) +{ + return single_open(file, white_list_show, inode->i_private); +} + +static const struct file_operations white_list_fops = { + .open = white_list_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int identity_resolving_keys_show(struct seq_file *f, void *ptr) +{ + struct hci_dev *hdev = f->private; + struct smp_irk *irk; + + rcu_read_lock(); + list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) { + seq_printf(f, "%pMR (type %u) %*phN %pMR\n", + &irk->bdaddr, irk->addr_type, + 16, irk->val, &irk->rpa); + } + rcu_read_unlock(); + + return 0; +} + +static int identity_resolving_keys_open(struct inode *inode, struct file *file) +{ + return single_open(file, identity_resolving_keys_show, + inode->i_private); +} + +static const struct file_operations identity_resolving_keys_fops = { + .open = identity_resolving_keys_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int long_term_keys_show(struct seq_file *f, void *ptr) +{ + struct hci_dev *hdev = f->private; + struct smp_ltk *ltk; + + rcu_read_lock(); + list_for_each_entry_rcu(ltk, &hdev->long_term_keys, list) + seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %.16llx %*phN\n", + <k->bdaddr, ltk->bdaddr_type, ltk->authenticated, + ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv), + __le64_to_cpu(ltk->rand), 16, ltk->val); + rcu_read_unlock(); + + return 0; +} + +static int long_term_keys_open(struct inode *inode, struct file *file) +{ + return single_open(file, long_term_keys_show, inode->i_private); +} + +static const struct file_operations long_term_keys_fops = { + .open = long_term_keys_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int conn_min_interval_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->le_conn_min_interval = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int conn_min_interval_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->le_conn_min_interval; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(conn_min_interval_fops, conn_min_interval_get, + conn_min_interval_set, "%llu\n"); + +static int conn_max_interval_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->le_conn_max_interval = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int conn_max_interval_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->le_conn_max_interval; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get, + conn_max_interval_set, "%llu\n"); + +static int conn_latency_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val > 0x01f3) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->le_conn_latency = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int conn_latency_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->le_conn_latency; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(conn_latency_fops, conn_latency_get, + conn_latency_set, "%llu\n"); + +static int supervision_timeout_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val < 0x000a || val > 0x0c80) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->le_supv_timeout = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int supervision_timeout_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->le_supv_timeout; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(supervision_timeout_fops, supervision_timeout_get, + supervision_timeout_set, "%llu\n"); + +static int adv_channel_map_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val < 0x01 || val > 0x07) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->le_adv_channel_map = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int adv_channel_map_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->le_adv_channel_map; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(adv_channel_map_fops, adv_channel_map_get, + adv_channel_map_set, "%llu\n"); + +static int adv_min_interval_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val < 0x0020 || val > 0x4000 || val > hdev->le_adv_max_interval) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->le_adv_min_interval = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int adv_min_interval_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->le_adv_min_interval; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(adv_min_interval_fops, adv_min_interval_get, + adv_min_interval_set, "%llu\n"); + +static int adv_max_interval_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val < 0x0020 || val > 0x4000 || val < hdev->le_adv_min_interval) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->le_adv_max_interval = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int adv_max_interval_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->le_adv_max_interval; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(adv_max_interval_fops, adv_max_interval_get, + adv_max_interval_set, "%llu\n"); + void hci_debugfs_create_le(struct hci_dev *hdev) { + debugfs_create_file("identity", 0400, hdev->debugfs, hdev, + &identity_fops); + debugfs_create_file("rpa_timeout", 0644, hdev->debugfs, hdev, + &rpa_timeout_fops); + debugfs_create_file("random_address", 0444, hdev->debugfs, hdev, + &random_address_fops); + debugfs_create_file("static_address", 0444, hdev->debugfs, hdev, + &static_address_fops); + + /* For controllers with a public address, provide a debug + * option to force the usage of the configured static + * address. By default the public address is used. + */ + if (bacmp(&hdev->bdaddr, BDADDR_ANY)) + debugfs_create_file("force_static_address", 0644, + hdev->debugfs, hdev, + &force_static_address_fops); + + debugfs_create_u8("white_list_size", 0444, hdev->debugfs, + &hdev->le_white_list_size); + debugfs_create_file("white_list", 0444, hdev->debugfs, hdev, + &white_list_fops); + debugfs_create_file("identity_resolving_keys", 0400, hdev->debugfs, + hdev, &identity_resolving_keys_fops); + debugfs_create_file("long_term_keys", 0400, hdev->debugfs, hdev, + &long_term_keys_fops); + debugfs_create_file("conn_min_interval", 0644, hdev->debugfs, hdev, + &conn_min_interval_fops); + debugfs_create_file("conn_max_interval", 0644, hdev->debugfs, hdev, + &conn_max_interval_fops); + debugfs_create_file("conn_latency", 0644, hdev->debugfs, hdev, + &conn_latency_fops); + debugfs_create_file("supervision_timeout", 0644, hdev->debugfs, hdev, + &supervision_timeout_fops); + debugfs_create_file("adv_channel_map", 0644, hdev->debugfs, hdev, + &adv_channel_map_fops); + debugfs_create_file("adv_min_interval", 0644, hdev->debugfs, hdev, + &adv_min_interval_fops); + debugfs_create_file("adv_max_interval", 0644, hdev->debugfs, hdev, + &adv_max_interval_fops); + debugfs_create_u16("discov_interleaved_timeout", 0644, hdev->debugfs, + &hdev->discov_interleaved_timeout); } -- cgit v0.10.2 From 94a3bd02a6b79b98236d9805a1f95a2857e7696a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 20 Dec 2014 16:28:38 +0100 Subject: Bluetooth: Add structures for LE Data Length Extension feature This patch adds the structures for HCI commands and events of the LE Data Length Extension feature from Bluetooth 4.2 specification. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 40129b3..f39926e 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -343,6 +343,7 @@ enum { #define HCI_LE_ENCRYPTION 0x01 #define HCI_LE_CONN_PARAM_REQ_PROC 0x02 #define HCI_LE_PING 0x10 +#define HCI_LE_DATA_LEN_EXT 0x20 #define HCI_LE_EXT_SCAN_POLICY 0x80 /* Connection modes */ @@ -1371,6 +1372,39 @@ struct hci_cp_le_conn_param_req_neg_reply { __u8 reason; } __packed; +#define HCI_OP_LE_SET_DATA_LEN 0x2022 +struct hci_cp_le_set_data_len { + __le16 handle; + __le16 tx_len; + __le16 tx_time; +} __packed; +struct hci_rp_le_set_data_len { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_OP_LE_READ_DEF_DATA_LEN 0x2023 +struct hci_rp_le_read_def_data_len { + __u8 status; + __le16 tx_len; + __le16 tx_time; +} __packed; + +#define HCI_OP_LE_WRITE_DEF_DATA_LEN 0x2024 +struct hci_cp_le_write_def_data_len { + __le16 tx_len; + __le16 tx_time; +} __packed; + +#define HCI_OP_LE_READ_MAX_DATA_LEN 0x202f +struct hci_rp_le_read_max_data_len { + __u8 status; + __le16 tx_len; + __le16 tx_time; + __le16 rx_len; + __le16 rx_time; +} __packed; + /* ---- HCI Events ---- */ #define HCI_EV_INQUIRY_COMPLETE 0x01 @@ -1796,6 +1830,15 @@ struct hci_ev_le_remote_conn_param_req { __le16 timeout; } __packed; +#define HCI_EV_LE_DATA_LEN_CHANGE 0x07 +struct hci_ev_le_data_len_change { + __le16 handle; + __le16 tx_len; + __le16 tx_time; + __le16 rx_len; + __le16 rx_time; +} __packed; + #define HCI_EV_LE_DIRECT_ADV_REPORT 0x0B struct hci_ev_le_direct_adv_info { __u8 evt_type; -- cgit v0.10.2 From a9f6068e0072839594d246089204644bffd2c988 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 20 Dec 2014 16:28:39 +0100 Subject: Bluetooth: Enable basics for LE Data Length Extension feature When the controller supports the new LE Data Length Extension feature from Bluetooth 4.2 specification, enable the new events and read the values for default and maxmimum data length supported by the controller. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 2db071b..01e35ef 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -805,6 +805,12 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) * Parameter Request */ + /* If the controller supports the Data Length Extension + * feature, enable the corresponding event. + */ + if (hdev->le_features[0] & HCI_LE_DATA_LEN_EXT) + events[0] |= 0x40; /* LE Data Length Change */ + /* If the controller supports Extended Scanner Filter * Policies, enable the correspondig event. */ @@ -835,6 +841,14 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) hci_req_add(req, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL); } + if (hdev->le_features[0] & HCI_LE_DATA_LEN_EXT) { + /* Read LE Maximum Data Length */ + hci_req_add(req, HCI_OP_LE_READ_MAX_DATA_LEN, 0, NULL); + + /* Read LE Suggested Default Data Length */ + hci_req_add(req, HCI_OP_LE_READ_DEF_DATA_LEN, 0, NULL); + } + hci_set_le_support(req); } -- cgit v0.10.2 From a8e1bfaa55cf8ac4e419a09bdda5bb45bcd8f985 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 20 Dec 2014 16:28:40 +0100 Subject: Bluetooth: Store default and maximum LE data length settings When the controller supports the LE Data Length Extension feature, the default and maximum data length are read and now stored. For backwards compatibility all values are initialized to the data length values from Bluetooth 4.1 and earlier specifications. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 79724c8..f20f6bd 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -220,6 +220,12 @@ struct hci_dev { __u16 le_conn_max_interval; __u16 le_conn_latency; __u16 le_supv_timeout; + __u16 le_def_tx_len; + __u16 le_def_tx_time; + __u16 le_max_tx_len; + __u16 le_max_tx_time; + __u16 le_max_rx_len; + __u16 le_max_rx_time; __u16 discov_interleaved_timeout; __u16 conn_info_min_age; __u16 conn_info_max_age; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 01e35ef..47f0311 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2896,6 +2896,12 @@ struct hci_dev *hci_alloc_dev(void) hdev->le_conn_max_interval = 0x0038; hdev->le_conn_latency = 0x0000; hdev->le_supv_timeout = 0x002a; + hdev->le_def_tx_len = 0x001b; + hdev->le_def_tx_time = 0x0148; + hdev->le_max_tx_len = 0x001b; + hdev->le_max_tx_time = 0x0148; + hdev->le_max_rx_len = 0x001b; + hdev->le_max_rx_time = 0x0148; hdev->rpa_timeout = HCI_DEFAULT_RPA_TIMEOUT; hdev->discov_interleaved_timeout = DISCOV_INTERLEAVED_TIMEOUT; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a412eb1..a3055e9 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1280,6 +1280,55 @@ static void hci_cc_le_read_supported_states(struct hci_dev *hdev, memcpy(hdev->le_states, rp->le_states, 8); } +static void hci_cc_le_read_def_data_len(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_le_read_def_data_len *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (rp->status) + return; + + hdev->le_def_tx_len = le16_to_cpu(rp->tx_len); + hdev->le_def_tx_time = le16_to_cpu(rp->tx_time); +} + +static void hci_cc_le_write_def_data_len(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_cp_le_write_def_data_len *sent; + __u8 status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + if (status) + return; + + sent = hci_sent_cmd_data(hdev, HCI_OP_LE_WRITE_DEF_DATA_LEN); + if (!sent) + return; + + hdev->le_def_tx_len = le16_to_cpu(sent->tx_len); + hdev->le_def_tx_time = le16_to_cpu(sent->tx_time); +} + +static void hci_cc_le_read_max_data_len(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_le_read_max_data_len *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (rp->status) + return; + + hdev->le_max_tx_len = le16_to_cpu(rp->tx_len); + hdev->le_max_tx_time = le16_to_cpu(rp->tx_time); + hdev->le_max_rx_len = le16_to_cpu(rp->rx_len); + hdev->le_max_rx_time = le16_to_cpu(rp->rx_time); +} + static void hci_cc_write_le_host_supported(struct hci_dev *hdev, struct sk_buff *skb) { @@ -2847,6 +2896,18 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_le_read_supported_states(hdev, skb); break; + case HCI_OP_LE_READ_DEF_DATA_LEN: + hci_cc_le_read_def_data_len(hdev, skb); + break; + + case HCI_OP_LE_WRITE_DEF_DATA_LEN: + hci_cc_le_write_def_data_len(hdev, skb); + break; + + case HCI_OP_LE_READ_MAX_DATA_LEN: + hci_cc_le_read_max_data_len(hdev, skb); + break; + case HCI_OP_WRITE_LE_HOST_SUPPORTED: hci_cc_write_le_host_supported(hdev, skb); break; -- cgit v0.10.2 From 23b9ceb74f8e46bddd61a1e2afd9317221be74b7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 20 Dec 2014 17:13:41 +0100 Subject: Bluetooth: Create debugfs directory for each connection handle For every internal representation of a Bluetooth connection which is identified by hci_conn, create a debugfs directory with the handle number as directory name. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f20f6bd..3e7e511 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -440,6 +440,7 @@ struct hci_conn { struct delayed_work le_conn_timeout; struct device dev; + struct dentry *debugfs; struct hci_dev *hdev; void *l2cap_data; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 4405fb3..75240aa 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -25,6 +25,7 @@ /* Bluetooth HCI connection handling. */ #include +#include #include #include @@ -547,6 +548,8 @@ int hci_conn_del(struct hci_conn *conn) hci_conn_del_sysfs(conn); + debugfs_remove_recursive(conn->debugfs); + if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags)) hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type); diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index a7a0db0..ee33ce8 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -1062,3 +1062,15 @@ void hci_debugfs_create_le(struct hci_dev *hdev) debugfs_create_u16("discov_interleaved_timeout", 0644, hdev->debugfs, &hdev->discov_interleaved_timeout); } + +void hci_debugfs_create_conn(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + char name[6]; + + if (IS_ERR_OR_NULL(hdev->debugfs)) + return; + + snprintf(name, sizeof(name), "%u", conn->handle); + conn->debugfs = debugfs_create_dir(name, hdev->debugfs); +} diff --git a/net/bluetooth/hci_debugfs.h b/net/bluetooth/hci_debugfs.h index f191100..fb68efe 100644 --- a/net/bluetooth/hci_debugfs.h +++ b/net/bluetooth/hci_debugfs.h @@ -23,3 +23,4 @@ void hci_debugfs_create_common(struct hci_dev *hdev); void hci_debugfs_create_bredr(struct hci_dev *hdev); void hci_debugfs_create_le(struct hci_dev *hdev); +void hci_debugfs_create_conn(struct hci_conn *conn); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a3055e9..eed44c6 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -31,6 +31,7 @@ #include #include "hci_request.h" +#include "hci_debugfs.h" #include "a2mp.h" #include "amp.h" #include "smp.h" @@ -2162,6 +2163,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) } else conn->state = BT_CONNECTED; + hci_debugfs_create_conn(conn); hci_conn_add_sysfs(conn); if (test_bit(HCI_AUTH, &hdev->flags)) @@ -3638,6 +3640,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, conn->handle = __le16_to_cpu(ev->handle); conn->state = BT_CONNECTED; + hci_debugfs_create_conn(conn); hci_conn_add_sysfs(conn); break; @@ -4178,6 +4181,7 @@ static void hci_phy_link_complete_evt(struct hci_dev *hdev, hcon->disc_timeout = HCI_DISCONN_TIMEOUT; hci_conn_drop(hcon); + hci_debugfs_create_conn(hcon); hci_conn_add_sysfs(hcon); amp_physical_cfm(bredr_hcon, hcon); @@ -4384,6 +4388,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->le_conn_latency = le16_to_cpu(ev->latency); conn->le_supv_timeout = le16_to_cpu(ev->supervision_timeout); + hci_debugfs_create_conn(conn); hci_conn_add_sysfs(conn); hci_proto_connect_cfm(conn, ev->status); -- cgit v0.10.2 From 72e4a6bd02204eed0464d6139439d7e89b94266e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 20 Dec 2014 18:00:41 +0100 Subject: Bluetooth: Remove duplicate constant for RFCOMM PSM The RFCOMM_PSM constant is actually a duplicate. So remove it and use the L2CAP_PSM_RFCOMM constant instead. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index 578b831..4190af5 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -24,8 +24,6 @@ #ifndef __RFCOMM_H #define __RFCOMM_H -#define RFCOMM_PSM 3 - #define RFCOMM_CONN_TIMEOUT (HZ * 30) #define RFCOMM_DISC_TIMEOUT (HZ * 20) #define RFCOMM_AUTH_TIMEOUT (HZ * 25) diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 73f8c75..4fea242 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -771,7 +771,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bacpy(&addr.l2_bdaddr, dst); addr.l2_family = AF_BLUETOOTH; - addr.l2_psm = cpu_to_le16(RFCOMM_PSM); + addr.l2_psm = cpu_to_le16(L2CAP_PSM_RFCOMM); addr.l2_cid = 0; addr.l2_bdaddr_type = BDADDR_BREDR; *err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK); @@ -2038,7 +2038,7 @@ static int rfcomm_add_listener(bdaddr_t *ba) /* Bind socket */ bacpy(&addr.l2_bdaddr, ba); addr.l2_family = AF_BLUETOOTH; - addr.l2_psm = cpu_to_le16(RFCOMM_PSM); + addr.l2_psm = cpu_to_le16(L2CAP_PSM_RFCOMM); addr.l2_cid = 0; addr.l2_bdaddr_type = BDADDR_BREDR; err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr)); -- cgit v0.10.2 From e7ddf4b7b3c5fe64902c4fb9edac92532c87cd75 Mon Sep 17 00:00:00 2001 From: Srinidhi Kasagar Date: Fri, 19 Dec 2014 23:13:51 +0530 Subject: cpufreq: Add SFI based cpufreq driver support This adds the SFI based cpu freq driver for some of the Intel's Silvermont based Atom architectures like Z34xx and Z35xx. Signed-off-by: Rudramuni, Vishwesh M Signed-off-by: Srinidhi Kasagar Acked-by: Viresh Kumar Signed-off-by: Len Brown diff --git a/arch/x86/include/uapi/asm/msr-index.h b/arch/x86/include/uapi/asm/msr-index.h index e21331ce..4f6dae6 100644 --- a/arch/x86/include/uapi/asm/msr-index.h +++ b/arch/x86/include/uapi/asm/msr-index.h @@ -318,6 +318,7 @@ #define MSR_IA32_PERF_STATUS 0x00000198 #define MSR_IA32_PERF_CTL 0x00000199 +#define INTEL_PERF_CTL_MASK 0xffff #define MSR_AMD_PSTATE_DEF_BASE 0xc0010064 #define MSR_AMD_PERF_STATUS 0xc0010063 #define MSR_AMD_PERF_CTL 0xc0010062 diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86 index 89ae88f..c59bdcb 100644 --- a/drivers/cpufreq/Kconfig.x86 +++ b/drivers/cpufreq/Kconfig.x86 @@ -57,6 +57,16 @@ config X86_ACPI_CPUFREQ_CPB By enabling this option the acpi_cpufreq driver provides the old entry in addition to the new boost ones, for compatibility reasons. +config X86_SFI_CPUFREQ + tristate "SFI Performance-States driver" + depends on X86_INTEL_MID && SFI + help + This adds a CPUFreq driver for some Silvermont based Intel Atom + architectures like Z34xx and Z35xx which enumerate processor + performance states through SFI. + + If in doubt, say N. + config ELAN_CPUFREQ tristate "AMD Elan SC400 and SC410" depends on MELAN diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 40c53dc..2e50f55 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_X86_P4_CLOCKMOD) += p4-clockmod.o obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o obj-$(CONFIG_X86_INTEL_PSTATE) += intel_pstate.o obj-$(CONFIG_X86_AMD_FREQ_SENSITIVITY) += amd_freq_sensitivity.o +obj-$(CONFIG_X86_SFI_CPUFREQ) += sfi-cpufreq.o ################################################################################## # ARM SoC drivers diff --git a/drivers/cpufreq/sfi-cpufreq.c b/drivers/cpufreq/sfi-cpufreq.c new file mode 100644 index 0000000..ffa3389 --- /dev/null +++ b/drivers/cpufreq/sfi-cpufreq.c @@ -0,0 +1,136 @@ +/* + * SFI Performance States Driver + * + * 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. + * + * 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. + * + * Author: Vishwesh M Rudramuni + * Author: Srinidhi Kasagar + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +struct cpufreq_frequency_table *freq_table; +static struct sfi_freq_table_entry *sfi_cpufreq_array; +static int num_freq_table_entries; + +static int sfi_parse_freq(struct sfi_table_header *table) +{ + struct sfi_table_simple *sb; + struct sfi_freq_table_entry *pentry; + int totallen; + + sb = (struct sfi_table_simple *)table; + num_freq_table_entries = SFI_GET_NUM_ENTRIES(sb, + struct sfi_freq_table_entry); + if (num_freq_table_entries <= 1) { + pr_err("No p-states discovered\n"); + return -ENODEV; + } + + pentry = (struct sfi_freq_table_entry *)sb->pentry; + totallen = num_freq_table_entries * sizeof(*pentry); + + sfi_cpufreq_array = kzalloc(totallen, GFP_KERNEL); + if (!sfi_cpufreq_array) + return -ENOMEM; + + memcpy(sfi_cpufreq_array, pentry, totallen); + + return 0; +} + +static int sfi_cpufreq_target(struct cpufreq_policy *policy, unsigned int index) +{ + unsigned int next_perf_state = 0; /* Index into perf table */ + u32 lo, hi; + + next_perf_state = policy->freq_table[index].driver_data; + + rdmsr_on_cpu(policy->cpu, MSR_IA32_PERF_CTL, &lo, &hi); + lo = (lo & ~INTEL_PERF_CTL_MASK) | + ((u32) sfi_cpufreq_array[next_perf_state].ctrl_val & + INTEL_PERF_CTL_MASK); + wrmsr_on_cpu(policy->cpu, MSR_IA32_PERF_CTL, lo, hi); + + return 0; +} + +static int sfi_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + policy->shared_type = CPUFREQ_SHARED_TYPE_HW; + policy->cpuinfo.transition_latency = 100000; /* 100us */ + + return cpufreq_table_validate_and_show(policy, freq_table); +} + +static struct cpufreq_driver sfi_cpufreq_driver = { + .flags = CPUFREQ_CONST_LOOPS, + .verify = cpufreq_generic_frequency_table_verify, + .target_index = sfi_cpufreq_target, + .init = sfi_cpufreq_cpu_init, + .name = "sfi-cpufreq", + .attr = cpufreq_generic_attr, +}; + +static int __init sfi_cpufreq_init(void) +{ + int ret, i; + + /* parse the freq table from SFI */ + ret = sfi_table_parse(SFI_SIG_FREQ, NULL, NULL, sfi_parse_freq); + if (ret) + return ret; + + freq_table = kzalloc(sizeof(*freq_table) * + (num_freq_table_entries + 1), GFP_KERNEL); + if (!freq_table) { + ret = -ENOMEM; + goto err_free_array; + } + + for (i = 0; i < num_freq_table_entries; i++) { + freq_table[i].driver_data = i; + freq_table[i].frequency = sfi_cpufreq_array[i].freq_mhz * 1000; + } + freq_table[i].frequency = CPUFREQ_TABLE_END; + + ret = cpufreq_register_driver(&sfi_cpufreq_driver); + if (ret) + goto err_free_tbl; + + return ret; + +err_free_tbl: + kfree(freq_table); +err_free_array: + kfree(sfi_cpufreq_array); + return ret; +} +late_initcall(sfi_cpufreq_init); + +static void __exit sfi_cpufreq_exit(void) +{ + cpufreq_unregister_driver(&sfi_cpufreq_driver); + kfree(freq_table); + kfree(sfi_cpufreq_array); +} +module_exit(sfi_cpufreq_exit); + +MODULE_AUTHOR("Vishwesh M Rudramuni "); +MODULE_DESCRIPTION("SFI Performance-States Driver"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 8c2b117fdf004ebcb81129307996218a7d471cda Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 9 Dec 2014 09:04:55 +0800 Subject: EDAC, mce_amd_inj: Fix sparse non static symbol warning Fixes the following sparse warnings: drivers/edac/mce_amd_inj.c:204:3: warning: symbol 'dfs_fls' was not declared. Should it be static? Signed-off-by: Wei Yongjun Link: http://lkml.kernel.org/r/1418087095-14174-1-git-send-email-weiyj_lk@163.com Signed-off-by: Borislav Petkov diff --git a/drivers/edac/mce_amd_inj.c b/drivers/edac/mce_amd_inj.c index 0bd91a8..f7681b5 100644 --- a/drivers/edac/mce_amd_inj.c +++ b/drivers/edac/mce_amd_inj.c @@ -197,7 +197,7 @@ static int inj_bank_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n"); -struct dfs_node { +static struct dfs_node { char *name; struct dentry *d; const struct file_operations *fops; -- cgit v0.10.2 From 4dba4185e3d984a7a856dfaeb7e8aec26633e845 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 18 Dec 2014 19:10:35 +0800 Subject: ARM: dts: sunxi: Fix usb-phy support for sun4i/sun5i usbphy0 support in the sunxi usb-phy driver has been merged, but the dtsi's for sun4i/sun5i haven't been updated. This results in the phy driver failing to load, breaking usb support. Fixes: 6827a46f5994 ('phy: sun4i: add support for USB phy0') Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi index 7b4099f..e342230 100644 --- a/arch/arm/boot/dts/sun4i-a10.dtsi +++ b/arch/arm/boot/dts/sun4i-a10.dtsi @@ -438,8 +438,8 @@ reg-names = "phy_ctrl", "pmu1", "pmu2"; clocks = <&usb_clk 8>; clock-names = "usb_phy"; - resets = <&usb_clk 1>, <&usb_clk 2>; - reset-names = "usb1_reset", "usb2_reset"; + resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>; + reset-names = "usb0_reset", "usb1_reset", "usb2_reset"; status = "disabled"; }; diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi index 1b76667..3c3c192 100644 --- a/arch/arm/boot/dts/sun5i-a10s.dtsi +++ b/arch/arm/boot/dts/sun5i-a10s.dtsi @@ -390,8 +390,8 @@ reg-names = "phy_ctrl", "pmu1"; clocks = <&usb_clk 8>; clock-names = "usb_phy"; - resets = <&usb_clk 1>; - reset-names = "usb1_reset"; + resets = <&usb_clk 0>, <&usb_clk 1>; + reset-names = "usb0_reset", "usb1_reset"; status = "disabled"; }; diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi index c35217e..2448d7f 100644 --- a/arch/arm/boot/dts/sun5i-a13.dtsi +++ b/arch/arm/boot/dts/sun5i-a13.dtsi @@ -349,8 +349,8 @@ reg-names = "phy_ctrl", "pmu1"; clocks = <&usb_clk 8>; clock-names = "usb_phy"; - resets = <&usb_clk 1>; - reset-names = "usb1_reset"; + resets = <&usb_clk 0>, <&usb_clk 1>; + reset-names = "usb0_reset", "usb1_reset"; status = "disabled"; }; -- cgit v0.10.2 From d64cb71bede87dbca60d586a7bb4cef87fbe2731 Mon Sep 17 00:00:00 2001 From: Jaewon Kim Date: Wed, 17 Dec 2014 10:31:08 -0800 Subject: Input: add regulator haptic driver This change adds support for haptic driver controlled by voltage of a regulator. Userspace can control the device via Force Feedback interface from input framework. Signed-off-by: Jaewon Kim Signed-off-by: Hyunhee Kim Acked-by: Kyungmin Park Tested-by: Chanwoo Choi Reviewed-by: Chanwoo Choi Reviewed-by: Pankaj Dubey Signed-off-by: Dmitry Torokhov diff --git a/Documentation/devicetree/bindings/input/regulator-haptic.txt b/Documentation/devicetree/bindings/input/regulator-haptic.txt new file mode 100644 index 0000000..3ed1c7e --- /dev/null +++ b/Documentation/devicetree/bindings/input/regulator-haptic.txt @@ -0,0 +1,21 @@ +* Regulator Haptic Device Tree Bindings + +Required Properties: + - compatible : Should be "regulator-haptic" + - haptic-supply : Power supply to the haptic motor. + [*] refer Documentation/devicetree/bindings/regulator/regulator.txt + + - max-microvolt : The maximum voltage value supplied to the haptic motor. + [The unit of the voltage is a micro] + + - min-microvolt : The minimum voltage value supplied to the haptic motor. + [The unit of the voltage is a micro] + +Example: + + haptics { + compatible = "regulator-haptic"; + haptic-supply = <&motor_regulator>; + max-microvolt = <2700000>; + min-microvolt = <1100000>; + }; diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 23297ab..1da0a20 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -394,6 +394,18 @@ config INPUT_CM109 To compile this driver as a module, choose M here: the module will be called cm109. +config INPUT_REGULATOR_HAPTIC + tristate "Regulator haptics support" + depends on REGULATOR + select INPUT_FF_MEMLESS + help + This option enables device driver support for the haptic controlled + by a regulator. This driver supports ff-memless interface + from input framework. + + To compile this driver as a module, choose M here: the + module will be called regulator-haptic. + config INPUT_RETU_PWRBUTTON tristate "Retu Power button Driver" depends on MFD_RETU diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 19c7603..1f135af 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o +obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o diff --git a/drivers/input/misc/regulator-haptic.c b/drivers/input/misc/regulator-haptic.c new file mode 100644 index 0000000..9426221 --- /dev/null +++ b/drivers/input/misc/regulator-haptic.c @@ -0,0 +1,279 @@ +/* + * Regulator haptic driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Author: Jaewon Kim + * Author: Hyunhee Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_MAGNITUDE_SHIFT 16 + +struct regulator_haptic { + struct device *dev; + struct input_dev *input_dev; + struct regulator *regulator; + + struct work_struct work; + struct mutex mutex; + + bool active; + bool suspended; + + unsigned int max_volt; + unsigned int min_volt; + unsigned int magnitude; +}; + +static int regulator_haptic_toggle(struct regulator_haptic *haptic, bool on) +{ + int error; + + if (haptic->active != on) { + + error = on ? regulator_enable(haptic->regulator) : + regulator_disable(haptic->regulator); + if (error) { + dev_err(haptic->dev, + "failed to switch regulator %s: %d\n", + on ? "on" : "off", error); + return error; + } + + haptic->active = on; + } + + return 0; +} + +static int regulator_haptic_set_voltage(struct regulator_haptic *haptic, + unsigned int magnitude) +{ + u64 volt_mag_multi; + unsigned int intensity; + int error; + + volt_mag_multi = (u64)(haptic->max_volt - haptic->min_volt) * magnitude; + intensity = (unsigned int)(volt_mag_multi >> MAX_MAGNITUDE_SHIFT); + + error = regulator_set_voltage(haptic->regulator, + intensity + haptic->min_volt, + haptic->max_volt); + if (error) { + dev_err(haptic->dev, "cannot set regulator voltage to %d: %d\n", + intensity + haptic->min_volt, error); + return error; + } + + return 0; +} + +static void regulator_haptic_work(struct work_struct *work) +{ + struct regulator_haptic *haptic = container_of(work, + struct regulator_haptic, work); + unsigned int magnitude; + int error; + + mutex_lock(&haptic->mutex); + + if (haptic->suspended) + goto out; + + magnitude = ACCESS_ONCE(haptic->magnitude); + + error = regulator_haptic_set_voltage(haptic, magnitude); + if (error) + goto out; + + regulator_haptic_toggle(haptic, magnitude != 0); + +out: + mutex_unlock(&haptic->mutex); +} + +static int regulator_haptic_play_effect(struct input_dev *input, void *data, + struct ff_effect *effect) +{ + struct regulator_haptic *haptic = input_get_drvdata(input); + + haptic->magnitude = effect->u.rumble.strong_magnitude; + if (!haptic->magnitude) + haptic->magnitude = effect->u.rumble.weak_magnitude; + + schedule_work(&haptic->work); + + return 0; +} + +static void regulator_haptic_close(struct input_dev *input) +{ + struct regulator_haptic *haptic = input_get_drvdata(input); + + cancel_work_sync(&haptic->work); + regulator_haptic_set_voltage(haptic, 0); + regulator_haptic_toggle(haptic, false); +} + +static int __maybe_unused +regulator_haptic_parse_dt(struct device *dev, struct regulator_haptic *haptic) +{ + struct device_node *node; + int error; + + node = dev->of_node; + if(!node) { + dev_err(dev, "Missing dveice tree data\n"); + return -EINVAL; + } + + error = of_property_read_u32(node, "max-microvolt", &haptic->max_volt); + if (error) { + dev_err(dev, "cannot parse max-microvolt\n"); + return error; + } + + error = of_property_read_u32(node, "min-microvolt", &haptic->min_volt); + if (error) { + dev_err(dev, "cannot parse min-microvolt\n"); + return error; + } + + return 0; +} + +static int regulator_haptic_probe(struct platform_device *pdev) +{ + const struct regulator_haptic_data *pdata = dev_get_platdata(&pdev->dev); + struct regulator_haptic *haptic; + struct input_dev *input_dev; + int error; + + haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL); + if (!haptic) + return -ENOMEM; + + platform_set_drvdata(pdev, haptic); + haptic->dev = &pdev->dev; + mutex_init(&haptic->mutex); + INIT_WORK(&haptic->work, regulator_haptic_work); + + if (pdata) { + haptic->max_volt = pdata->max_volt; + haptic->min_volt = pdata->min_volt; + } else if (IS_ENABLED(CONFIG_OF)) { + error = regulator_haptic_parse_dt(&pdev->dev, haptic); + if (error) + return error; + } else { + dev_err(&pdev->dev, "Missing platform data\n"); + return -EINVAL; + } + + haptic->regulator = devm_regulator_get_exclusive(&pdev->dev, "haptic"); + if (IS_ERR(haptic->regulator)) { + dev_err(&pdev->dev, "failed to get regulator\n"); + return PTR_ERR(haptic->regulator); + } + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) + return -ENOMEM; + + haptic->input_dev = input_dev; + haptic->input_dev->name = "regulator-haptic"; + haptic->input_dev->dev.parent = &pdev->dev; + haptic->input_dev->close = regulator_haptic_close; + input_set_drvdata(haptic->input_dev, haptic); + input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(input_dev, NULL, + regulator_haptic_play_effect); + if (error) { + dev_err(&pdev->dev, "failed to create force-feedback\n"); + return error; + } + + error = input_register_device(haptic->input_dev); + if (error) { + dev_err(&pdev->dev, "failed to register input device\n"); + return error; + } + + return 0; +} + +static int __maybe_unused regulator_haptic_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct regulator_haptic *haptic = platform_get_drvdata(pdev); + int error; + + error = mutex_lock_interruptible(&haptic->mutex); + if (error) + return error; + + regulator_haptic_set_voltage(haptic, 0); + regulator_haptic_toggle(haptic, false); + + haptic->suspended = true; + + mutex_unlock(&haptic->mutex); + + return 0; +} + +static int __maybe_unused regulator_haptic_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct regulator_haptic *haptic = platform_get_drvdata(pdev); + unsigned int magnitude; + + mutex_lock(&haptic->mutex); + + haptic->suspended = false; + + magnitude = ACCESS_ONCE(haptic->magnitude); + if (magnitude) { + regulator_haptic_set_voltage(haptic, magnitude); + regulator_haptic_toggle(haptic, true); + } + + mutex_unlock(&haptic->mutex); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops, + regulator_haptic_suspend, regulator_haptic_resume); + +static struct of_device_id regulator_haptic_dt_match[] = { + { .compatible = "regulator-haptic" }, + { /* sentinel */ }, +}; + +static struct platform_driver regulator_haptic_driver = { + .probe = regulator_haptic_probe, + .driver = { + .name = "regulator-haptic", + .of_match_table = regulator_haptic_dt_match, + .pm = ®ulator_haptic_pm_ops, + }, +}; +module_platform_driver(regulator_haptic_driver); + +MODULE_AUTHOR("Jaewon Kim "); +MODULE_AUTHOR("Hyunhee Kim "); +MODULE_DESCRIPTION("Regulator haptic driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/regulator-haptic.h b/include/linux/platform_data/regulator-haptic.h new file mode 100644 index 0000000..5658e58 --- /dev/null +++ b/include/linux/platform_data/regulator-haptic.h @@ -0,0 +1,29 @@ +/* + * Regulator Haptic Platform Data + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Author: Jaewon Kim + * Author: Hyunhee Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _REGULATOR_HAPTIC_H +#define _REGULATOR_HAPTIC_H + +/* + * struct regulator_haptic_data - Platform device data + * + * @max_volt: maximum voltage value supplied to the haptic motor. + * + * @min_volt: minimum voltage value supplied to the haptic motor. + * + */ +struct regulator_haptic_data { + unsigned int max_volt; + unsigned int min_volt; +}; + +#endif /* _REGULATOR_HAPTIC_H */ -- cgit v0.10.2 From af6a5af8e8cc1566fc06636de02347825808650e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 18 Dec 2014 09:24:50 -0800 Subject: Input: add new sun4i-lradc-keys driver Allwinnner sunxi SoCs have a low resolution adc (called lradc) which is specifically designed to have various (tablet) keys (ie home, back, search, etc). attached to it using a resistor network. This adds a driver for this. There are 2 channels, currently this driver only supports chan0 since there are no boards known to use chan1. This has been tested on an olimex a10s-olinuxino-micro, a13-olinuxino, and a20-olinuxino-micro. Signed-off-by: Hans de Goede Signed-off-by: Dmitry Torokhov diff --git a/Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt b/Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt new file mode 100644 index 0000000..b9c32f6 --- /dev/null +++ b/Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt @@ -0,0 +1,62 @@ +Allwinner sun4i low res adc attached tablet keys +------------------------------------------------ + +Required properties: + - compatible: "allwinner,sun4i-a10-lradc-keys" + - reg: mmio address range of the chip + - interrupts: interrupt to which the chip is connected + - vref-supply: powersupply for the lradc reference voltage + +Each key is represented as a sub-node of "allwinner,sun4i-a10-lradc-keys": + +Required subnode-properties: + - label: Descriptive name of the key. + - linux,code: Keycode to emit. + - channel: Channel this key is attached to, mut be 0 or 1. + - voltage: Voltage in µV at lradc input when this key is pressed. + +Example: + +#include + + lradc: lradc@01c22800 { + compatible = "allwinner,sun4i-a10-lradc-keys"; + reg = <0x01c22800 0x100>; + interrupts = <31>; + vref-supply = <®_vcc3v0>; + + button@191 { + label = "Volume Up"; + linux,code = ; + channel = <0>; + voltage = <191274>; + }; + + button@392 { + label = "Volume Down"; + linux,code = ; + channel = <0>; + voltage = <392644>; + }; + + button@601 { + label = "Menu"; + linux,code = ; + channel = <0>; + voltage = <601151>; + }; + + button@795 { + label = "Enter"; + linux,code = ; + channel = <0>; + voltage = <795090>; + }; + + button@987 { + label = "Home"; + linux,code = ; + channel = <0>; + voltage = <987387>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index f73bb41..21b834b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8806,6 +8806,13 @@ F: arch/m68k/sun3*/ F: arch/m68k/include/asm/sun3* F: drivers/net/ethernet/i825xx/sun3* +SUN4I LOW RES ADC ATTACHED TABLET KEYS DRIVER +M: Hans de Goede +L: linux-input@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt +F: drivers/input/keyboard/sun4i-lradc-keys.c + SUNDANCE NETWORK DRIVER M: Denis Kirjanov L: netdev@vger.kernel.org diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index a5d9b3f..a89ba7c 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -568,6 +568,16 @@ config KEYBOARD_STMPE To compile this driver as a module, choose M here: the module will be called stmpe-keypad. +config KEYBOARD_SUN4I_LRADC + tristate "Allwinner sun4i low res adc attached tablet keys support" + depends on ARCH_SUNXI + help + This selects support for the Allwinner low res adc attached tablet + keys found on Allwinner sunxi SoCs. + + To compile this driver as a module, choose M here: the + module will be called sun4i-lradc-keys. + config KEYBOARD_DAVINCI tristate "TI DaVinci Key Scan" depends on ARCH_DAVINCI_DM365 diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index febafa5..4707678 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o obj-$(CONFIG_KEYBOARD_ST_KEYSCAN) += st-keyscan.o +obj-$(CONFIG_KEYBOARD_SUN4I_LRADC) += sun4i-lradc-keys.o obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o diff --git a/drivers/input/keyboard/sun4i-lradc-keys.c b/drivers/input/keyboard/sun4i-lradc-keys.c new file mode 100644 index 0000000..cc8f7dd --- /dev/null +++ b/drivers/input/keyboard/sun4i-lradc-keys.c @@ -0,0 +1,286 @@ +/* + * Allwinner sun4i low res adc attached tablet keys driver + * + * Copyright (C) 2014 Hans de Goede + * + * 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. + */ + +/* + * Allwinnner sunxi SoCs have a lradc which is specifically designed to have + * various (tablet) keys (ie home, back, search, etc). attached to it using + * a resistor network. This driver is for the keys on such boards. + * + * There are 2 channels, currently this driver only supports channel 0 since + * there are no boards known to use channel 1. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LRADC_CTRL 0x00 +#define LRADC_INTC 0x04 +#define LRADC_INTS 0x08 +#define LRADC_DATA0 0x0c +#define LRADC_DATA1 0x10 + +/* LRADC_CTRL bits */ +#define FIRST_CONVERT_DLY(x) ((x) << 24) /* 8 bits */ +#define CHAN_SELECT(x) ((x) << 22) /* 2 bits */ +#define CONTINUE_TIME_SEL(x) ((x) << 16) /* 4 bits */ +#define KEY_MODE_SEL(x) ((x) << 12) /* 2 bits */ +#define LEVELA_B_CNT(x) ((x) << 8) /* 4 bits */ +#define HOLD_EN(x) ((x) << 6) +#define LEVELB_VOL(x) ((x) << 4) /* 2 bits */ +#define SAMPLE_RATE(x) ((x) << 2) /* 2 bits */ +#define ENABLE(x) ((x) << 0) + +/* LRADC_INTC and LRADC_INTS bits */ +#define CHAN1_KEYUP_IRQ BIT(12) +#define CHAN1_ALRDY_HOLD_IRQ BIT(11) +#define CHAN1_HOLD_IRQ BIT(10) +#define CHAN1_KEYDOWN_IRQ BIT(9) +#define CHAN1_DATA_IRQ BIT(8) +#define CHAN0_KEYUP_IRQ BIT(4) +#define CHAN0_ALRDY_HOLD_IRQ BIT(3) +#define CHAN0_HOLD_IRQ BIT(2) +#define CHAN0_KEYDOWN_IRQ BIT(1) +#define CHAN0_DATA_IRQ BIT(0) + +struct sun4i_lradc_keymap { + u32 voltage; + u32 keycode; +}; + +struct sun4i_lradc_data { + struct device *dev; + struct input_dev *input; + void __iomem *base; + struct regulator *vref_supply; + struct sun4i_lradc_keymap *chan0_map; + u32 chan0_map_count; + u32 chan0_keycode; + u32 vref; +}; + +static irqreturn_t sun4i_lradc_irq(int irq, void *dev_id) +{ + struct sun4i_lradc_data *lradc = dev_id; + u32 i, ints, val, voltage, diff, keycode = 0, closest = 0xffffffff; + + ints = readl(lradc->base + LRADC_INTS); + + /* + * lradc supports only one keypress at a time, release does not give + * any info as to which key was released, so we cache the keycode. + */ + + if (ints & CHAN0_KEYUP_IRQ) { + input_report_key(lradc->input, lradc->chan0_keycode, 0); + lradc->chan0_keycode = 0; + } + + if ((ints & CHAN0_KEYDOWN_IRQ) && lradc->chan0_keycode == 0) { + val = readl(lradc->base + LRADC_DATA0) & 0x3f; + voltage = val * lradc->vref / 63; + + for (i = 0; i < lradc->chan0_map_count; i++) { + diff = abs(lradc->chan0_map[i].voltage - voltage); + if (diff < closest) { + closest = diff; + keycode = lradc->chan0_map[i].keycode; + } + } + + lradc->chan0_keycode = keycode; + input_report_key(lradc->input, lradc->chan0_keycode, 1); + } + + input_sync(lradc->input); + + writel(ints, lradc->base + LRADC_INTS); + + return IRQ_HANDLED; +} + +static int sun4i_lradc_open(struct input_dev *dev) +{ + struct sun4i_lradc_data *lradc = input_get_drvdata(dev); + int error; + + error = regulator_enable(lradc->vref_supply); + if (error) + return error; + + /* lradc Vref internally is divided by 2/3 */ + lradc->vref = regulator_get_voltage(lradc->vref_supply) * 2 / 3; + + /* + * Set sample time to 4 ms / 250 Hz. Wait 2 * 4 ms for key to + * stabilize on press, wait (1 + 1) * 4 ms for key release + */ + writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) | + SAMPLE_RATE(0) | ENABLE(1), lradc->base + LRADC_CTRL); + + writel(CHAN0_KEYUP_IRQ | CHAN0_KEYDOWN_IRQ, lradc->base + LRADC_INTC); + + return 0; +} + +static void sun4i_lradc_close(struct input_dev *dev) +{ + struct sun4i_lradc_data *lradc = input_get_drvdata(dev); + + /* Disable lradc, leave other settings unchanged */ + writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) | + SAMPLE_RATE(2), lradc->base + LRADC_CTRL); + writel(0, lradc->base + LRADC_INTC); + + regulator_disable(lradc->vref_supply); +} + +static int sun4i_lradc_load_dt_keymap(struct device *dev, + struct sun4i_lradc_data *lradc) +{ + struct device_node *np, *pp; + int i; + int error; + + np = dev->of_node; + if (!np) + return -EINVAL; + + lradc->chan0_map_count = of_get_child_count(np); + if (lradc->chan0_map_count == 0) { + dev_err(dev, "keymap is missing in device tree\n"); + return -EINVAL; + } + + lradc->chan0_map = devm_kmalloc_array(dev, lradc->chan0_map_count, + sizeof(struct sun4i_lradc_keymap), + GFP_KERNEL); + if (!lradc->chan0_map) + return -ENOMEM; + + i = 0; + for_each_child_of_node(np, pp) { + struct sun4i_lradc_keymap *map = &lradc->chan0_map[i]; + u32 channel; + + error = of_property_read_u32(pp, "channel", &channel); + if (error || channel != 0) { + dev_err(dev, "%s: Inval channel prop\n", pp->name); + return -EINVAL; + } + + error = of_property_read_u32(pp, "voltage", &map->voltage); + if (error) { + dev_err(dev, "%s: Inval voltage prop\n", pp->name); + return -EINVAL; + } + + error = of_property_read_u32(pp, "linux,code", &map->keycode); + if (error) { + dev_err(dev, "%s: Inval linux,code prop\n", pp->name); + return -EINVAL; + } + + i++; + } + + return 0; +} + +static int sun4i_lradc_probe(struct platform_device *pdev) +{ + struct sun4i_lradc_data *lradc; + struct device *dev = &pdev->dev; + int i; + int error; + + lradc = devm_kzalloc(dev, sizeof(struct sun4i_lradc_data), GFP_KERNEL); + if (!lradc) + return -ENOMEM; + + error = sun4i_lradc_load_dt_keymap(dev, lradc); + if (error) + return error; + + lradc->vref_supply = devm_regulator_get(dev, "vref"); + if (IS_ERR(lradc->vref_supply)) + return PTR_ERR(lradc->vref_supply); + + lradc->dev = dev; + lradc->input = devm_input_allocate_device(dev); + if (!lradc->input) + return -ENOMEM; + + lradc->input->name = pdev->name; + lradc->input->phys = "sun4i_lradc/input0"; + lradc->input->open = sun4i_lradc_open; + lradc->input->close = sun4i_lradc_close; + lradc->input->id.bustype = BUS_HOST; + lradc->input->id.vendor = 0x0001; + lradc->input->id.product = 0x0001; + lradc->input->id.version = 0x0100; + + __set_bit(EV_KEY, lradc->input->evbit); + for (i = 0; i < lradc->chan0_map_count; i++) + __set_bit(lradc->chan0_map[i].keycode, lradc->input->keybit); + + input_set_drvdata(lradc->input, lradc); + + lradc->base = devm_ioremap_resource(dev, + platform_get_resource(pdev, IORESOURCE_MEM, 0)); + if (IS_ERR(lradc->base)) + return PTR_ERR(lradc->base); + + error = devm_request_irq(dev, platform_get_irq(pdev, 0), + sun4i_lradc_irq, 0, + "sun4i-a10-lradc-keys", lradc); + if (error) + return error; + + error = input_register_device(lradc->input); + if (error) + return error; + + platform_set_drvdata(pdev, lradc); + return 0; +} + +static const struct of_device_id sun4i_lradc_of_match[] = { + { .compatible = "allwinner,sun4i-a10-lradc-keys", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match); + +static struct platform_driver sun4i_lradc_driver = { + .driver = { + .name = "sun4i-a10-lradc-keys", + .of_match_table = of_match_ptr(sun4i_lradc_of_match), + }, + .probe = sun4i_lradc_probe, +}; + +module_platform_driver(sun4i_lradc_driver); + +MODULE_DESCRIPTION("Allwinner sun4i low res adc attached tablet keys driver"); +MODULE_AUTHOR("Hans de Goede "); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From a380acb88611e1ee90dcb3da39e05991cadc6682 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 23 Nov 2014 13:37:26 +0100 Subject: ASoC: wm8750: Cleanup manual bias level transitions Set the CODEC driver's suspend_bias_off flag rather than manually going to SND_SOC_BIAS_OFF in suspend and SND_SOC_BIAS_STANDBY in resume. This makes the code a bit shorter and cleaner. Since the ASoC core now takes care of setting the bias level to SND_SOC_BIAS_OFF when removing the CODEC there is no need to do it manually anymore either. The manual transition to SND_SOC_BIAS_STANDBY at the end of CODEC probe() can also be removed as the core will automatically do this after the CODEC has been probed. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index d9e2626..eb0a164 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -686,18 +686,6 @@ static struct snd_soc_dai_driver wm8750_dai = { .ops = &wm8750_dai_ops, }; -static int wm8750_suspend(struct snd_soc_codec *codec) -{ - wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8750_resume(struct snd_soc_codec *codec) -{ - wm8750_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - static int wm8750_probe(struct snd_soc_codec *codec) { int ret; @@ -708,9 +696,6 @@ static int wm8750_probe(struct snd_soc_codec *codec) return ret; } - /* charge output caps */ - wm8750_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - /* set the update bits */ snd_soc_update_bits(codec, WM8750_LDAC, 0x0100, 0x0100); snd_soc_update_bits(codec, WM8750_RDAC, 0x0100, 0x0100); @@ -724,18 +709,10 @@ static int wm8750_probe(struct snd_soc_codec *codec) return ret; } -static int wm8750_remove(struct snd_soc_codec *codec) -{ - wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm8750 = { .probe = wm8750_probe, - .remove = wm8750_remove, - .suspend = wm8750_suspend, - .resume = wm8750_resume, .set_bias_level = wm8750_set_bias_level, + .suspend_bias_off = true, .controls = wm8750_snd_controls, .num_controls = ARRAY_SIZE(wm8750_snd_controls), -- cgit v0.10.2 From 5875755c577b00b06aba77ba471175c3e3a33c25 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 22 Dec 2014 12:51:25 +0100 Subject: mac80211_hwsim: fix check for custom world regdom array size David Binderman reports that the conditions in the first loop are the wrong way around - checking the array contents before the size. Instead of leaving the empty loop there and reordering the two checks unify it into a single loop that skips over non-matches and exits after the first match. Reported-by: David Binderman Signed-off-by: Johannes Berg diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index a71b9d5..057a99e 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2150,14 +2150,14 @@ static int append_radio_msg(struct sk_buff *skb, int id, if (param->regd) { int i; - for (i = 0; hwsim_world_regdom_custom[i] != param->regd && - i < ARRAY_SIZE(hwsim_world_regdom_custom); i++) - ; + for (i = 0; i < ARRAY_SIZE(hwsim_world_regdom_custom); i++) { + if (hwsim_world_regdom_custom[i] != param->regd) + continue; - if (i < ARRAY_SIZE(hwsim_world_regdom_custom)) { ret = nla_put_u32(skb, HWSIM_ATTR_REG_CUSTOM_REG, i); if (ret < 0) return ret; + break; } } -- cgit v0.10.2 From eca80394abc50abae9847b264b0a3120336f590a Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Sun, 21 Dec 2014 22:40:43 +0800 Subject: ASoC: Intel: fix platform_no_drv_owner.cocci warnings sound/soc/intel/cht_bsw_rt5672.c:273:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: Fengguang Wu Signed-off-by: Mark Brown diff --git a/sound/soc/intel/cht_bsw_rt5672.c b/sound/soc/intel/cht_bsw_rt5672.c index 9b8b561..a406c610 100644 --- a/sound/soc/intel/cht_bsw_rt5672.c +++ b/sound/soc/intel/cht_bsw_rt5672.c @@ -270,7 +270,6 @@ static int snd_cht_mc_probe(struct platform_device *pdev) static struct platform_driver snd_cht_mc_driver = { .driver = { - .owner = THIS_MODULE, .name = "cht-bsw-rt5672", .pm = &snd_soc_pm_ops, }, -- cgit v0.10.2 From 602129c8d1da0b2d43f2afae2ff7f1f2919f6eaa Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Sun, 21 Dec 2014 22:34:50 +0800 Subject: ASoC: Intel: fix platform_no_drv_owner.cocci warnings sound/soc/intel/bytcr_dpcm_rt5640.c:218:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Subhransu S. Prusty Signed-off-by: Fengguang Wu Signed-off-by: Mark Brown diff --git a/sound/soc/intel/bytcr_dpcm_rt5640.c b/sound/soc/intel/bytcr_dpcm_rt5640.c index f5d0fc1..9277f2b 100644 --- a/sound/soc/intel/bytcr_dpcm_rt5640.c +++ b/sound/soc/intel/bytcr_dpcm_rt5640.c @@ -215,7 +215,6 @@ static int snd_byt_mc_probe(struct platform_device *pdev) static struct platform_driver snd_byt_mc_driver = { .driver = { - .owner = THIS_MODULE, .name = "bytt100_rt5640", .pm = &snd_soc_pm_ops, }, -- cgit v0.10.2 From 676ebf68873f97c0e2a386ce979529573e0a8931 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 Dec 2014 22:14:45 +0100 Subject: ASoC: intel: sst: drop owner assignment from platform_drivers This platform_driver does not need to set an owner, it will be populated by the driver core. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c index 3abc29e..f1180ff 100644 --- a/sound/soc/intel/sst/sst_acpi.c +++ b/sound/soc/intel/sst/sst_acpi.c @@ -366,7 +366,6 @@ MODULE_DEVICE_TABLE(acpi, sst_acpi_ids); static struct platform_driver sst_acpi_driver = { .driver = { .name = "intel_sst_acpi", - .owner = THIS_MODULE, .acpi_match_table = ACPI_PTR(sst_acpi_ids), .pm = &intel_sst_pm, }, -- cgit v0.10.2 From 97d62a3096355168db71bdcbc618cbf2c3b7c2a0 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 Dec 2014 22:14:46 +0100 Subject: ASoC: omap: drop owner assignment from platform_drivers This platform_driver does not need to set an owner, it will be populated by the driver core. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c index 3f9ac7d..ccfb41c 100644 --- a/sound/soc/omap/omap-hdmi-audio.c +++ b/sound/soc/omap/omap-hdmi-audio.c @@ -393,7 +393,6 @@ static int omap_hdmi_audio_remove(struct platform_device *pdev) static struct platform_driver hdmi_audio_driver = { .driver = { .name = DRV_NAME, - .owner = THIS_MODULE, }, .probe = omap_hdmi_audio_probe, .remove = omap_hdmi_audio_remove, -- cgit v0.10.2 From 7e0fc116c41f0ca93c4d3de6d26c223c92182857 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 Dec 2014 22:14:47 +0100 Subject: ASoC: pxa: drop owner assignment from platform_drivers This platform_driver does not need to set an owner, it will be populated by the driver core. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index d7d5fb2..a6d680a 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -352,7 +352,6 @@ static int spitz_remove(struct platform_device *pdev) static struct platform_driver spitz_driver = { .driver = { .name = "spitz-audio", - .owner = THIS_MODULE, .pm = &snd_soc_pm_ops, }, .probe = spitz_probe, -- cgit v0.10.2 From 03da09a02de2ffd15df89b9ddef17dd7af195f32 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 Dec 2014 22:14:48 +0100 Subject: ASoC: samsung: drop owner assignment from platform_drivers This platform_driver does not need to set an owner, it will be populated by the driver core. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/arndale_rt5631.c b/sound/soc/samsung/arndale_rt5631.c index 1e2b61c..8bf2e2c 100644 --- a/sound/soc/samsung/arndale_rt5631.c +++ b/sound/soc/samsung/arndale_rt5631.c @@ -135,7 +135,6 @@ MODULE_DEVICE_TABLE(of, samsung_arndale_rt5631_of_match); static struct platform_driver arndale_audio_driver = { .driver = { .name = "arndale-audio", - .owner = THIS_MODULE, .pm = &snd_soc_pm_ops, .of_match_table = of_match_ptr(samsung_arndale_rt5631_of_match), }, -- cgit v0.10.2 From e951ceef65f76d6c611a4afa65dfc9420a3123f4 Mon Sep 17 00:00:00 2001 From: Beomho Seo Date: Fri, 19 Dec 2014 18:04:46 +0900 Subject: regulator: max14577: Use regulator_nodes/of_match in the descriptor This patch is add regulator_nodes/ofmatch in the regulator descriptor for using information from DT instead of specific codes. That will be used regulation_of_get_init_data function for get regulator property on device tree. Using that make driver simpler. Signed-off-by: Beomho Seo Signed-off-by: Mark Brown diff --git a/drivers/regulator/max14577.c b/drivers/regulator/max14577.c index bf9a44c..b3678d2 100644 --- a/drivers/regulator/max14577.c +++ b/drivers/regulator/max14577.c @@ -103,6 +103,8 @@ static struct regulator_ops max14577_charger_ops = { static const struct regulator_desc max14577_supported_regulators[] = { [MAX14577_SAFEOUT] = { .name = "SAFEOUT", + .of_match = of_match_ptr("SAFEOUT"), + .regulators_node = of_match_ptr("regulators"), .id = MAX14577_SAFEOUT, .ops = &max14577_safeout_ops, .type = REGULATOR_VOLTAGE, @@ -114,6 +116,8 @@ static const struct regulator_desc max14577_supported_regulators[] = { }, [MAX14577_CHARGER] = { .name = "CHARGER", + .of_match = of_match_ptr("CHARGER"), + .regulators_node = of_match_ptr("regulators"), .id = MAX14577_CHARGER, .ops = &max14577_charger_ops, .type = REGULATOR_CURRENT, @@ -137,6 +141,8 @@ static struct regulator_ops max77836_ldo_ops = { static const struct regulator_desc max77836_supported_regulators[] = { [MAX14577_SAFEOUT] = { .name = "SAFEOUT", + .of_match = of_match_ptr("SAFEOUT"), + .regulators_node = of_match_ptr("regulators"), .id = MAX14577_SAFEOUT, .ops = &max14577_safeout_ops, .type = REGULATOR_VOLTAGE, @@ -148,6 +154,8 @@ static const struct regulator_desc max77836_supported_regulators[] = { }, [MAX14577_CHARGER] = { .name = "CHARGER", + .of_match = of_match_ptr("CHARGER"), + .regulators_node = of_match_ptr("regulators"), .id = MAX14577_CHARGER, .ops = &max14577_charger_ops, .type = REGULATOR_CURRENT, @@ -157,6 +165,8 @@ static const struct regulator_desc max77836_supported_regulators[] = { }, [MAX77836_LDO1] = { .name = "LDO1", + .of_match = of_match_ptr("LDO1"), + .regulators_node = of_match_ptr("regulators"), .id = MAX77836_LDO1, .ops = &max77836_ldo_ops, .type = REGULATOR_VOLTAGE, @@ -171,6 +181,8 @@ static const struct regulator_desc max77836_supported_regulators[] = { }, [MAX77836_LDO2] = { .name = "LDO2", + .of_match = of_match_ptr("LDO2"), + .regulators_node = of_match_ptr("regulators"), .id = MAX77836_LDO2, .ops = &max77836_ldo_ops, .type = REGULATOR_VOLTAGE, @@ -198,43 +210,6 @@ static struct of_regulator_match max77836_regulator_matches[] = { { .name = "LDO2", }, }; -static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev, - enum maxim_device_type dev_type) -{ - int ret; - struct device_node *np; - struct of_regulator_match *regulator_matches; - unsigned int regulator_matches_size; - - np = of_get_child_by_name(pdev->dev.parent->of_node, "regulators"); - if (!np) { - dev_err(&pdev->dev, "Failed to get child OF node for regulators\n"); - return -EINVAL; - } - - switch (dev_type) { - case MAXIM_DEVICE_TYPE_MAX77836: - regulator_matches = max77836_regulator_matches; - regulator_matches_size = ARRAY_SIZE(max77836_regulator_matches); - break; - case MAXIM_DEVICE_TYPE_MAX14577: - default: - regulator_matches = max14577_regulator_matches; - regulator_matches_size = ARRAY_SIZE(max14577_regulator_matches); - } - - ret = of_regulator_match(&pdev->dev, np, regulator_matches, - regulator_matches_size); - if (ret < 0) - dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret); - else - ret = 0; - - of_node_put(np); - - return ret; -} - static inline struct regulator_init_data *match_init_data(int index, enum maxim_device_type dev_type) { @@ -261,11 +236,6 @@ static inline struct device_node *match_of_node(int index, } } #else /* CONFIG_OF */ -static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev, - enum maxim_device_type dev_type) -{ - return 0; -} static inline struct regulator_init_data *match_init_data(int index, enum maxim_device_type dev_type) { @@ -308,16 +278,12 @@ static int max14577_regulator_probe(struct platform_device *pdev) { struct max14577 *max14577 = dev_get_drvdata(pdev->dev.parent); struct max14577_platform_data *pdata = dev_get_platdata(max14577->dev); - int i, ret; + int i, ret = 0; struct regulator_config config = {}; const struct regulator_desc *supported_regulators; unsigned int supported_regulators_size; enum maxim_device_type dev_type = max14577->dev_type; - ret = max14577_regulator_dt_parse_pdata(pdev, dev_type); - if (ret) - return ret; - switch (dev_type) { case MAXIM_DEVICE_TYPE_MAX77836: supported_regulators = max77836_supported_regulators; @@ -329,7 +295,7 @@ static int max14577_regulator_probe(struct platform_device *pdev) supported_regulators_size = ARRAY_SIZE(max14577_supported_regulators); } - config.dev = &pdev->dev; + config.dev = max14577->dev; config.driver_data = max14577; for (i = 0; i < supported_regulators_size; i++) { -- cgit v0.10.2 From 34084a4367037b5bb3de71dfa7cdcd8e998d153a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 10 Dec 2014 11:53:47 +0100 Subject: ASoC: intel: Remove superfluous backslash in Kconfig The line continuation in Kconfig should be a '\' instead of '\\'. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index e989ecf..f06fcf1 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -46,7 +46,7 @@ config SND_SOC_INTEL_BAYTRAIL config SND_SOC_INTEL_HASWELL_MACH tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint" - depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C && \\ + depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C && \ I2C_DESIGNWARE_PLATFORM select SND_SOC_INTEL_HASWELL select SND_SOC_RT5640 @@ -76,7 +76,7 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH config SND_SOC_INTEL_BROADWELL_MACH tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint" - depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC && \\ + depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC && \ I2C_DESIGNWARE_PLATFORM select SND_SOC_INTEL_HASWELL select SND_COMPRESS_OFFLOAD -- cgit v0.10.2 From 768c0564439bce179e3f0f2ffafe8dcab6a08f40 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 21 Dec 2014 11:05:43 +0100 Subject: ASoC: dapm: Don't mark MICBIAS widgets as auto non-connected The connected flag of a widget only affects widgets that are either a source or a sink. The MICBIAS widget is a simple pass-through widget though and hence its behavior is the same regardless of whether the connected flag is set or not. Hence there is not much point in trying to automatically mark MICBIAS widgets as non-connected, so just remove it. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index c5136bb..b8eba93 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3880,7 +3880,6 @@ void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card) switch (w->id) { case snd_soc_dapm_input: case snd_soc_dapm_output: - case snd_soc_dapm_micbias: dev_dbg(card->dev, "ASoC: Auto NC: Checking widget %s\n", w->name); if (!snd_soc_dapm_widget_in_card_paths(card, w)) { -- cgit v0.10.2 From 86d7500326ea71ea72aeaf0da78671eef28be2af Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 21 Dec 2014 11:05:44 +0100 Subject: ASoC: dapm: Simplify fully route card handling For legacy reasons the ASoC framework assumes that a CODEC INPUT or OUTPUT widget that is not explicitly connected to a external source or sink is potentially connected to a source or a sink and hence the framework treats the widget itself as source (for INPUT) or sink (for OUTPUT). For this reason a INPUT or OUTPUT widget that is really not connected needs to be explicitly marked as so. Setting the card's fully_routed flag will cause the ASoC core, once that all widgets and routes have been registered, to go through the list of all widgets and mark all INPUT and OUTPUT that are not externally connected as non-connected. This essentially negates the default behaviour of treating INPUT or OUTPUT widgets without external routes as sources or sinks. This patch takes a different approach while getting the same result. Instead of first marking INPUT and OUTPUT widgets as sinks/sources and then later marking them as non-connected, just never mark them as a sink or a source if the fully_routed flag is set on a card. This requires a lot less code and also results in a slightly faster card initialization since there is no need to iterate over all widgets and check whether the INPUT and OUTPUT widgets are connected or not. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 89823cf..ecffecc 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -431,7 +431,6 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin); int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin); -void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card); unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol); /* Mostly internal - should not normally be used */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 985052b..89aa671 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1626,9 +1626,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) } } - if (card->fully_routed) - snd_soc_dapm_auto_nc_pins(card); - snd_soc_dapm_new_widgets(card); ret = snd_card_register(card->snd_card); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b8eba93..ea49684 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2279,6 +2279,9 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) switch (w->id) { case snd_soc_dapm_input: + /* On a fully routed card a input is never a source */ + if (w->dapm->card->fully_routed) + break; w->is_source = 1; list_for_each_entry(p, &w->sources, list_sink) { if (p->source->id == snd_soc_dapm_micbias || @@ -2291,6 +2294,9 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) } break; case snd_soc_dapm_output: + /* On a fully routed card a output is never a sink */ + if (w->dapm->card->fully_routed) + break; w->is_sink = 1; list_for_each_entry(p, &w->sinks, list_source) { if (p->sink->id == snd_soc_dapm_spk || @@ -3085,16 +3091,24 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, switch (w->id) { case snd_soc_dapm_mic: - case snd_soc_dapm_input: w->is_source = 1; w->power_check = dapm_generic_check_power; break; + case snd_soc_dapm_input: + if (!dapm->card->fully_routed) + w->is_source = 1; + w->power_check = dapm_generic_check_power; + break; case snd_soc_dapm_spk: case snd_soc_dapm_hp: - case snd_soc_dapm_output: w->is_sink = 1; w->power_check = dapm_generic_check_power; break; + case snd_soc_dapm_output: + if (!dapm->card->fully_routed) + w->is_sink = 1; + w->power_check = dapm_generic_check_power; + break; case snd_soc_dapm_vmid: case snd_soc_dapm_siggen: w->is_source = 1; @@ -3809,92 +3823,6 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); /** - * dapm_is_external_path() - Checks if a path is a external path - * @card: The card the path belongs to - * @path: The path to check - * - * Returns true if the path is either between two different DAPM contexts or - * between two external pins of the same DAPM context. Otherwise returns - * false. - */ -static bool dapm_is_external_path(struct snd_soc_card *card, - struct snd_soc_dapm_path *path) -{ - dev_dbg(card->dev, - "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n", - path->source->name, path->source->id, path->source->dapm, - path->sink->name, path->sink->id, path->sink->dapm); - - /* Connection between two different DAPM contexts */ - if (path->source->dapm != path->sink->dapm) - return true; - - /* Loopback connection from external pin to external pin */ - if (path->sink->id == snd_soc_dapm_input) { - switch (path->source->id) { - case snd_soc_dapm_output: - case snd_soc_dapm_micbias: - return true; - default: - break; - } - } - - return false; -} - -static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card, - struct snd_soc_dapm_widget *w) -{ - struct snd_soc_dapm_path *p; - - list_for_each_entry(p, &w->sources, list_sink) { - if (dapm_is_external_path(card, p)) - return true; - } - - list_for_each_entry(p, &w->sinks, list_source) { - if (dapm_is_external_path(card, p)) - return true; - } - - return false; -} - -/** - * snd_soc_dapm_auto_nc_pins - call snd_soc_dapm_nc_pin for unused pins - * @card: The card whose pins should be processed - * - * Automatically call snd_soc_dapm_nc_pin() for any external pins in the card - * which are unused. Pins are used if they are connected externally to a - * component, whether that be to some other device, or a loop-back connection to - * the component itself. - */ -void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card) -{ - struct snd_soc_dapm_widget *w; - - dev_dbg(card->dev, "ASoC: Auto NC: DAPMs: card:%p\n", &card->dapm); - - list_for_each_entry(w, &card->widgets, list) { - switch (w->id) { - case snd_soc_dapm_input: - case snd_soc_dapm_output: - dev_dbg(card->dev, "ASoC: Auto NC: Checking widget %s\n", - w->name); - if (!snd_soc_dapm_widget_in_card_paths(card, w)) { - dev_dbg(card->dev, - "... Not in map; disabling\n"); - snd_soc_dapm_nc_pin(w->dapm, w->name); - } - break; - default: - break; - } - } -} - -/** * snd_soc_dapm_free - free dapm resources * @dapm: DAPM context * -- cgit v0.10.2 From 2f43de605e700f5aa5cad15e19f8ffe54b1d4c86 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Fri, 19 Dec 2014 14:45:41 -0800 Subject: HID: rmi: Support non rmi devices by passing events to hid-input Allowing hid-rmi to bind to non rmi devices allows us to support composite USB devices which contain several HID devices one of which is a HID touchpad. Since all of the devices have the same VID and PID we can add the device to the hid_have_special_driver list and have hid-rmi handle all of the devices. Then hid-rmi's probe can look for the rmi specific HID report IDs and decide if it should handle the device as a rmi device or simply report that the events needs additional processing. Signed-off-by: Andrew Duggan Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index b51200f..018f80f 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -33,6 +33,9 @@ #define RMI_READ_DATA_PENDING BIT(1) #define RMI_STARTED BIT(2) +/* device flags */ +#define RMI_DEVICE BIT(0) + enum rmi_mode_type { RMI_MODE_OFF = 0, RMI_MODE_ATTN_REPORTS = 1, @@ -118,6 +121,8 @@ struct rmi_data { struct work_struct reset_work; struct hid_device *hdev; + + unsigned long device_flags; }; #define RMI_PAGE(addr) (((addr) >> 8) & 0xff) @@ -452,9 +457,23 @@ static int rmi_raw_event(struct hid_device *hdev, return rmi_read_data_event(hdev, data, size); case RMI_ATTN_REPORT_ID: return rmi_input_event(hdev, data, size); - case RMI_MOUSE_REPORT_ID: + default: + return 1; + } + + return 0; +} + +static int rmi_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct rmi_data *data = hid_get_drvdata(hdev); + + if ((data->device_flags & RMI_DEVICE) && + (field->application == HID_GD_POINTER || + field->application == HID_GD_MOUSE)) { rmi_schedule_reset(hdev); - break; + return 1; } return 0; @@ -856,6 +875,9 @@ static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi) if (ret) return; + if (!(data->device_flags & RMI_DEVICE)) + return; + /* Allow incoming hid reports */ hid_device_io_start(hdev); @@ -914,8 +936,33 @@ static int rmi_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { - /* we want to make HID ignore the advertised HID collection */ - return -1; + struct rmi_data *data = hid_get_drvdata(hdev); + + /* + * we want to make HID ignore the advertised HID collection + * for RMI deivces + */ + if (data->device_flags & RMI_DEVICE) + return -1; + + return 0; +} + +static int rmi_check_valid_report_id(struct hid_device *hdev, unsigned type, + unsigned id, struct hid_report **report) +{ + int i; + + *report = hdev->report_enum[type].report_id_hash[id]; + if (*report) { + for (i = 0; i < (*report)->maxfield; i++) { + unsigned app = (*report)->field[i]->application; + if ((app & HID_USAGE_PAGE) >= HID_UP_MSVENDOR) + return 1; + } + } + + return 0; } static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) @@ -925,6 +972,7 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) size_t alloc_size; struct hid_report *input_report; struct hid_report *output_report; + struct hid_report *feature_report; data = devm_kzalloc(&hdev->dev, sizeof(struct rmi_data), GFP_KERNEL); if (!data) @@ -943,27 +991,35 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) return ret; } - input_report = hdev->report_enum[HID_INPUT_REPORT] - .report_id_hash[RMI_ATTN_REPORT_ID]; - if (!input_report) { - hid_err(hdev, "device does not have expected input report\n"); - ret = -ENODEV; - return ret; + /* + * Check for the RMI specific report ids. If they are misisng + * simply return and let the events be processed by hid-input + */ + if (!rmi_check_valid_report_id(hdev, HID_FEATURE_REPORT, + RMI_SET_RMI_MODE_REPORT_ID, &feature_report)) { + hid_dbg(hdev, "device does not have set mode feature report\n"); + goto start; + } + + if (!rmi_check_valid_report_id(hdev, HID_INPUT_REPORT, + RMI_ATTN_REPORT_ID, &input_report)) { + hid_dbg(hdev, "device does not have attention input report\n"); + goto start; } data->input_report_size = (input_report->size >> 3) + 1 /* report id */; - output_report = hdev->report_enum[HID_OUTPUT_REPORT] - .report_id_hash[RMI_WRITE_REPORT_ID]; - if (!output_report) { - hid_err(hdev, "device does not have expected output report\n"); - ret = -ENODEV; - return ret; + if (!rmi_check_valid_report_id(hdev, HID_OUTPUT_REPORT, + RMI_WRITE_REPORT_ID, &output_report)) { + hid_dbg(hdev, + "device does not have rmi write output report\n"); + goto start; } data->output_report_size = (output_report->size >> 3) + 1 /* report id */; + data->device_flags |= RMI_DEVICE; alloc_size = data->output_report_size + data->input_report_size; data->writeReport = devm_kzalloc(&hdev->dev, alloc_size, GFP_KERNEL); @@ -978,13 +1034,15 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) mutex_init(&data->page_mutex); +start: ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "hw start failed\n"); return ret; } - if (!test_bit(RMI_STARTED, &data->flags)) + if ((data->device_flags & RMI_DEVICE) && + !test_bit(RMI_STARTED, &data->flags)) /* * The device maybe in the bootloader if rmi_input_configured * failed to find F11 in the PDT. Print an error, but don't @@ -1017,6 +1075,7 @@ static struct hid_driver rmi_driver = { .id_table = rmi_id, .probe = rmi_probe, .remove = rmi_remove, + .event = rmi_event, .raw_event = rmi_raw_event, .input_mapping = rmi_input_mapping, .input_configured = rmi_input_configured, -- cgit v0.10.2 From a5a41645d0096f4c9ffd7154270b43daebcb4386 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 18 Dec 2014 11:32:52 +0800 Subject: ASoC: rt5670: set platform data by dmi This patch set specific data according to dmi data. Signed-off-by: Jin, Yao Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 8a0833d..cd47ef1 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -2549,6 +2550,17 @@ static struct acpi_device_id rt5670_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match); #endif +static const struct dmi_system_id dmi_platform_intel_braswell[] = { + { + .ident = "Intel Braswell", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "Braswell CRB"), + }, + }, + {} +}; + static int rt5670_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -2568,6 +2580,12 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, if (pdata) rt5670->pdata = *pdata; + if (dmi_check_system(dmi_platform_intel_braswell)) { + rt5670->pdata.dmic_en = true; + rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P; + rt5670->pdata.jd_mode = 1; + } + rt5670->regmap = devm_regmap_init_i2c(i2c, &rt5670_regmap); if (IS_ERR(rt5670->regmap)) { ret = PTR_ERR(rt5670->regmap); -- cgit v0.10.2 From fc38a8a66e1bda87da13e8eb549dd87ddd86f3b8 Mon Sep 17 00:00:00 2001 From: Huang Bo Date: Wed, 26 Nov 2014 18:21:03 +0800 Subject: HID: add BETOP game controller force feedback support Adds force feedback support for BETOP USB game controllers. These devices are mass produced in China. Signed-off-by: Huang Bo Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 230b6f8..c33d98e 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -146,6 +146,16 @@ config HID_BELKIN ---help--- Support for Belkin Flip KVM and Wireless keyboard. +config HID_BETOP_FF + tristate "Betop Production Inc. force feedback support" + depends on USB_HID + select INPUT_FF_MEMLESS + ---help--- + Say Y here if you want to enable force feedback support for devices by + BETOP Production Ltd. + Currently the following devices are known to be supported: + - BETOP 2185 PC & BFM MODE + config HID_CHERRY tristate "Cherry Cymotion keyboard" if EXPERT depends on HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index debd15b..3a27d14 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o obj-$(CONFIG_HID_AUREAL) += hid-aureal.o obj-$(CONFIG_HID_BELKIN) += hid-belkin.o +obj-$(CONFIG_HID_BETOP_FF) += hid-betopff.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o obj-$(CONFIG_HID_CP2112) += hid-cp2112.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index c3d0ac1..9b1442a 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1757,6 +1757,10 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) }, { HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185BFM, 0x2208) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185PC, 0x5506) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185V2PC, 0x1850) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185V2BFM, 0x5500) }, { HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 7460f34..ac20ba0 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -189,6 +189,11 @@ #define USB_VENDOR_ID_BERKSHIRE 0x0c98 #define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140 +#define USB_VENDOR_ID_BETOP_2185BFM 0x11c2 +#define USB_VENDOR_ID_BETOP_2185PC 0x11c0 +#define USB_VENDOR_ID_BETOP_2185V2PC 0x8380 +#define USB_VENDOR_ID_BETOP_2185V2BFM 0x20bc + #define USB_VENDOR_ID_BTC 0x046e #define USB_DEVICE_ID_BTC_EMPREX_REMOTE 0x5578 #define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2 0x5577 -- cgit v0.10.2 From be33465e58ab3a3300c5182844d45c67e73b15e0 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Fri, 12 Dec 2014 09:25:00 +0000 Subject: ASoC: dwc: Remove unnecessary debug messages and tests The devm_XXX allocation functions print a message on failure, so additional messages are not required. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index b93168d..f81e747 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -345,26 +345,17 @@ static int dw_i2s_probe(struct platform_device *pdev) } dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL); - if (!dw_i2s_dai) { - dev_err(&pdev->dev, "mem allocation failed for dai driver\n"); + if (!dw_i2s_dai) return -ENOMEM; - } dw_i2s_dai->ops = &dw_i2s_dai_ops; dw_i2s_dai->suspend = dw_i2s_suspend; dw_i2s_dai->resume = dw_i2s_resume; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no i2s resource defined\n"); - return -ENODEV; - } - dev->i2s_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(dev->i2s_base)) { - dev_err(&pdev->dev, "ioremap fail for i2s_region\n"); + if (IS_ERR(dev->i2s_base)) return PTR_ERR(dev->i2s_base); - } cap = pdata->cap; dev->capability = cap; -- cgit v0.10.2 From afa8603c6253204bf96c88739f711e89c2b00cd5 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Fri, 19 Dec 2014 16:18:07 +0000 Subject: ASoC: dwc: Reorder code in preparation for DT support Move code that configures the DAI and DMA into a separate function. This reduces the size of the dw_i2s_probe function and will make it easier to add support for device tree to the driver. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index f81e747..23a7c13 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -324,13 +324,47 @@ static int dw_i2s_resume(struct snd_soc_dai *dai) #define dw_i2s_resume NULL #endif +static void dw_configure_dai_by_pd(struct dw_i2s_dev *dev, + struct snd_soc_dai_driver *dw_i2s_dai, + struct resource *res, + const struct i2s_platform_data *pdata) +{ + /* Set DMA slaves info */ + + dev->play_dma_data.data = pdata->play_dma_data; + dev->capture_dma_data.data = pdata->capture_dma_data; + dev->play_dma_data.addr = res->start + I2S_TXDMA; + dev->capture_dma_data.addr = res->start + I2S_RXDMA; + dev->play_dma_data.max_burst = 16; + dev->capture_dma_data.max_burst = 16; + dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + dev->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + dev->play_dma_data.filter = pdata->filter; + dev->capture_dma_data.filter = pdata->filter; + + if (pdata->cap & DWC_I2S_PLAY) { + dev_dbg(dev->dev, " designware: play supported\n"); + dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; + dw_i2s_dai->playback.channels_max = pdata->channel; + dw_i2s_dai->playback.formats = pdata->snd_fmts; + dw_i2s_dai->playback.rates = pdata->snd_rates; + } + + if (pdata->cap & DWC_I2S_RECORD) { + dev_dbg(dev->dev, "designware: record supported\n"); + dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; + dw_i2s_dai->capture.channels_max = pdata->channel; + dw_i2s_dai->capture.formats = pdata->snd_fmts; + dw_i2s_dai->capture.rates = pdata->snd_rates; + } +} + static int dw_i2s_probe(struct platform_device *pdev) { const struct i2s_platform_data *pdata = pdev->dev.platform_data; struct dw_i2s_dev *dev; struct resource *res; int ret; - unsigned int cap; struct snd_soc_dai_driver *dw_i2s_dai; if (!pdata) { @@ -357,23 +391,11 @@ static int dw_i2s_probe(struct platform_device *pdev) if (IS_ERR(dev->i2s_base)) return PTR_ERR(dev->i2s_base); - cap = pdata->cap; - dev->capability = cap; - dev->i2s_clk_cfg = pdata->i2s_clk_cfg; - - /* Set DMA slaves info */ - - dev->play_dma_data.data = pdata->play_dma_data; - dev->capture_dma_data.data = pdata->capture_dma_data; - dev->play_dma_data.addr = res->start + I2S_TXDMA; - dev->capture_dma_data.addr = res->start + I2S_RXDMA; - dev->play_dma_data.max_burst = 16; - dev->capture_dma_data.max_burst = 16; - dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - dev->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - dev->play_dma_data.filter = pdata->filter; - dev->capture_dma_data.filter = pdata->filter; + dev->dev = &pdev->dev; + dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); + dev->capability = pdata->cap; + dev->i2s_clk_cfg = pdata->i2s_clk_cfg; dev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) return PTR_ERR(dev->clk); @@ -382,23 +404,6 @@ static int dw_i2s_probe(struct platform_device *pdev) if (ret < 0) goto err_clk_put; - if (cap & DWC_I2S_PLAY) { - dev_dbg(&pdev->dev, " designware: play supported\n"); - dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; - dw_i2s_dai->playback.channels_max = pdata->channel; - dw_i2s_dai->playback.formats = pdata->snd_fmts; - dw_i2s_dai->playback.rates = pdata->snd_rates; - } - - if (cap & DWC_I2S_RECORD) { - dev_dbg(&pdev->dev, "designware: record supported\n"); - dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; - dw_i2s_dai->capture.channels_max = pdata->channel; - dw_i2s_dai->capture.formats = pdata->snd_fmts; - dw_i2s_dai->capture.rates = pdata->snd_rates; - } - - dev->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, dev); ret = snd_soc_register_component(&pdev->dev, &dw_i2s_component, dw_i2s_dai, 1); -- cgit v0.10.2 From 52cd7785f3cdd2724f4efb5b21dbc75d6f9ccef4 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 22 Dec 2014 14:58:13 +0100 Subject: HID: betop: add drivers/hid/hid-betopff.c Commit fc38a8a66e ("HID: add BETOP game controller force feedback support") is missing the actual addition of drivers/hid/hid-betopff.c due to my mistake (I forgot to add the file after fixing conflicts). Fixes: fc38a8a66e1b ("HID: add BETOP game controller force feedback support") Reported-by: kbuild test robot Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-betopff.c b/drivers/hid/hid-betopff.c new file mode 100644 index 0000000..69cfc8d --- /dev/null +++ b/drivers/hid/hid-betopff.c @@ -0,0 +1,160 @@ +/* + * Force feedback support for Betop based devices + * + * The devices are distributed under various names and the same USB device ID + * can be used in both adapters and actual game controllers. + * + * 0x11c2:0x2208 "BTP2185 BFM mode Joystick" + * - tested with BTP2185 BFM Mode. + * + * 0x11C0:0x5506 "BTP2185 PC mode Joystick" + * - tested with BTP2185 PC Mode. + * + * 0x8380:0x1850 "BTP2185 V2 PC mode USB Gamepad" + * - tested with BTP2185 PC Mode with another version. + * + * 0x20bc:0x5500 "BTP2185 V2 BFM mode Joystick" + * - tested with BTP2171s. + * Copyright (c) 2014 Huang Bo + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + + +#include +#include +#include +#include + +#include "hid-ids.h" + +struct betopff_device { + struct hid_report *report; +}; + +static int hid_betopff_play(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct betopff_device *betopff = data; + __u16 left, right; + + left = effect->u.rumble.strong_magnitude; + right = effect->u.rumble.weak_magnitude; + + betopff->report->field[2]->value[0] = left / 256; + betopff->report->field[3]->value[0] = right / 256; + + hid_hw_request(hid, betopff->report, HID_REQ_SET_REPORT); + + return 0; +} + +static int betopff_init(struct hid_device *hid) +{ + struct betopff_device *betopff; + struct hid_report *report; + struct hid_input *hidinput = + list_first_entry(&hid->inputs, struct hid_input, list); + struct list_head *report_list = + &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct input_dev *dev = hidinput->input; + int field_count = 0; + int error; + int i, j; + + if (list_empty(report_list)) { + hid_err(hid, "no output reports found\n"); + return -ENODEV; + } + + report = list_first_entry(report_list, struct hid_report, list); + /* + * Actually there are 4 fields for 4 Bytes as below: + * ----------------------------------------- + * Byte0 Byte1 Byte2 Byte3 + * 0x00 0x00 left_motor right_motor + * ----------------------------------------- + * Do init them with default value. + */ + for (i = 0; i < report->maxfield; i++) { + for (j = 0; j < report->field[i]->report_count; j++) { + report->field[i]->value[j] = 0x00; + field_count++; + } + } + + if (field_count < 4) { + hid_err(hid, "not enough fields in the report: %d\n", + field_count); + return -ENODEV; + } + + betopff = kzalloc(sizeof(*betopff), GFP_KERNEL); + if (!betopff) + return -ENOMEM; + + set_bit(FF_RUMBLE, dev->ffbit); + + error = input_ff_create_memless(dev, betopff, hid_betopff_play); + if (error) { + kfree(betopff); + return error; + } + + betopff->report = report; + hid_hw_request(hid, betopff->report, HID_REQ_SET_REPORT); + + hid_info(hid, "Force feedback for betop devices by huangbo \n"); + + return 0; +} + +static int betop_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + if (id->driver_data) + hdev->quirks |= HID_QUIRK_MULTI_INPUT; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + goto err; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); + if (ret) { + hid_err(hdev, "hw start failed\n"); + goto err; + } + + betopff_init(hdev); + + return 0; +err: + return ret; +} + +static const struct hid_device_id betop_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185BFM, 0x2208) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185PC, 0x5506) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185V2PC, 0x1850) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185V2BFM, 0x5500) }, + { } +}; +MODULE_DEVICE_TABLE(hid, betop_devices); + +static struct hid_driver betop_driver = { + .name = "betop", + .id_table = betop_devices, + .probe = betop_probe, +}; +module_hid_driver(betop_driver); + +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From c5f4546593e9911800f0926c1090959b58bc5c93 Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Tue, 16 Dec 2014 11:58:18 -0600 Subject: livepatch: kernel: add TAINT_LIVEPATCH This adds a new taint flag to indicate when the kernel or a kernel module has been live patched. This will provide a clean indication in bug reports that live patching was used. Additionally, if the crash occurs in a live patched function, the live patch module will appear beside the patched function in the backtrace. Signed-off-by: Seth Jennings Acked-by: Josh Poimboeuf Reviewed-by: Miroslav Benes Reviewed-by: Petr Mladek Reviewed-by: Masami Hiramatsu Signed-off-by: Jiri Kosina diff --git a/Documentation/oops-tracing.txt b/Documentation/oops-tracing.txt index beefb9f..f3ac05c 100644 --- a/Documentation/oops-tracing.txt +++ b/Documentation/oops-tracing.txt @@ -270,6 +270,8 @@ characters, each representing a particular tainted value. 15: 'L' if a soft lockup has previously occurred on the system. + 16: 'K' if the kernel has been live patched. + The primary reason for the 'Tainted: ' string is to tell kernel debuggers if this is a clean kernel or if anything unusual has occurred. Tainting is permanent: even if an offending module is diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 75511ef..83ab256 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -843,6 +843,7 @@ can be ORed together: 8192 - An unsigned module has been loaded in a kernel supporting module signature. 16384 - A soft lockup has previously occurred on the system. +32768 - The kernel has been live patched. ============================================================== diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 5449d2f..d03e3de 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -471,6 +471,7 @@ extern enum system_states { #define TAINT_OOT_MODULE 12 #define TAINT_UNSIGNED_MODULE 13 #define TAINT_SOFTLOCKUP 14 +#define TAINT_LIVEPATCH 15 extern const char hex_asc[]; #define hex_asc_lo(x) hex_asc[((x) & 0x0f)] diff --git a/kernel/panic.c b/kernel/panic.c index 4d8d6f9..8136ad7 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -226,6 +226,7 @@ static const struct tnt tnts[] = { { TAINT_OOT_MODULE, 'O', ' ' }, { TAINT_UNSIGNED_MODULE, 'E', ' ' }, { TAINT_SOFTLOCKUP, 'L', ' ' }, + { TAINT_LIVEPATCH, 'K', ' ' }, }; /** @@ -246,6 +247,7 @@ static const struct tnt tnts[] = { * 'O' - Out-of-tree module has been loaded. * 'E' - Unsigned module has been loaded. * 'L' - A soft lockup has previously occurred. + * 'K' - Kernel has been live patched. * * The string is overwritten by the next call to print_tainted(). */ -- cgit v0.10.2 From b700e7f03df5d92f85fa5247fe1f557528d3363d Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Tue, 16 Dec 2014 11:58:19 -0600 Subject: livepatch: kernel: add support for live patching This commit introduces code for the live patching core. It implements an ftrace-based mechanism and kernel interface for doing live patching of kernel and kernel module functions. It represents the greatest common functionality set between kpatch and kgraft and can accept patches built using either method. This first version does not implement any consistency mechanism that ensures that old and new code do not run together. In practice, ~90% of CVEs are safe to apply in this way, since they simply add a conditional check. However, any function change that can not execute safely with the old version of the function can _not_ be safely applied in this version. [ jkosina@suse.cz: due to the number of contributions that got folded into this original patch from Seth Jennings, add SUSE's copyright as well, as discussed via e-mail ] Signed-off-by: Seth Jennings Signed-off-by: Josh Poimboeuf Reviewed-by: Miroslav Benes Reviewed-by: Petr Mladek Reviewed-by: Masami Hiramatsu Signed-off-by: Miroslav Benes Signed-off-by: Petr Mladek Signed-off-by: Jiri Kosina diff --git a/Documentation/ABI/testing/sysfs-kernel-livepatch b/Documentation/ABI/testing/sysfs-kernel-livepatch new file mode 100644 index 0000000..5bf42a8 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-kernel-livepatch @@ -0,0 +1,44 @@ +What: /sys/kernel/livepatch +Date: Nov 2014 +KernelVersion: 3.19.0 +Contact: live-patching@vger.kernel.org +Description: + Interface for kernel live patching + + The /sys/kernel/livepatch directory contains subdirectories for + each loaded live patch module. + +What: /sys/kernel/livepatch/ +Date: Nov 2014 +KernelVersion: 3.19.0 +Contact: live-patching@vger.kernel.org +Description: + The patch directory contains subdirectories for each kernel + object (vmlinux or a module) in which it patched functions. + +What: /sys/kernel/livepatch//enabled +Date: Nov 2014 +KernelVersion: 3.19.0 +Contact: live-patching@vger.kernel.org +Description: + A writable attribute that indicates whether the patched + code is currently applied. Writing 0 will disable the patch + while writing 1 will re-enable the patch. + +What: /sys/kernel/livepatch// +Date: Nov 2014 +KernelVersion: 3.19.0 +Contact: live-patching@vger.kernel.org +Description: + The object directory contains subdirectories for each function + that is patched within the object. + +What: /sys/kernel/livepatch/// +Date: Nov 2014 +KernelVersion: 3.19.0 +Contact: live-patching@vger.kernel.org +Description: + The function directory contains attributes regarding the + properties and state of the patched function. + + There are currently no such attributes. diff --git a/MAINTAINERS b/MAINTAINERS index ddb9ac8..df6a078 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5784,6 +5784,19 @@ F: Documentation/misc-devices/lis3lv02d F: drivers/misc/lis3lv02d/ F: drivers/platform/x86/hp_accel.c +LIVE PATCHING +M: Josh Poimboeuf +M: Seth Jennings +M: Jiri Kosina +M: Vojtech Pavlik +S: Maintained +F: kernel/livepatch/ +F: include/linux/livepatch.h +F: arch/x86/include/asm/livepatch.h +F: arch/x86/kernel/livepatch.c +F: Documentation/ABI/testing/sysfs-kernel-livepatch +L: live-patching@vger.kernel.org + LLC (802.2) M: Arnaldo Carvalho de Melo S: Maintained diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index ba397bd..460b31b 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -17,6 +17,7 @@ config X86_64 depends on 64BIT select X86_DEV_DMA_OPS select ARCH_USE_CMPXCHG_LOCKREF + select ARCH_HAVE_LIVE_PATCHING ### Arch settings config X86 @@ -2008,6 +2009,8 @@ config CMDLINE_OVERRIDE This is used to work around broken boot loaders. This should be set to 'N' under normal conditions. +source "kernel/livepatch/Kconfig" + endmenu config ARCH_ENABLE_MEMORY_HOTPLUG diff --git a/arch/x86/include/asm/livepatch.h b/arch/x86/include/asm/livepatch.h new file mode 100644 index 0000000..d529db1 --- /dev/null +++ b/arch/x86/include/asm/livepatch.h @@ -0,0 +1,37 @@ +/* + * livepatch.h - x86-specific Kernel Live Patching Core + * + * Copyright (C) 2014 Seth Jennings + * Copyright (C) 2014 SUSE + * + * 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, see . + */ + +#ifndef _ASM_X86_LIVEPATCH_H +#define _ASM_X86_LIVEPATCH_H + +#include + +#ifdef CONFIG_LIVE_PATCHING +#ifndef CC_USING_FENTRY +#error Your compiler must support -mfentry for live patching to work +#endif +extern int klp_write_module_reloc(struct module *mod, unsigned long type, + unsigned long loc, unsigned long value); + +#else +#error Live patching support is disabled; check CONFIG_LIVE_PATCHING +#endif + +#endif /* _ASM_X86_LIVEPATCH_H */ diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 5d4502c..316b34e 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_X86_MPPARSE) += mpparse.o obj-y += apic/ obj-$(CONFIG_X86_REBOOTFIXUPS) += reboot_fixups_32.o obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o +obj-$(CONFIG_LIVE_PATCHING) += livepatch.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o obj-$(CONFIG_X86_TSC) += trace_clock.o diff --git a/arch/x86/kernel/livepatch.c b/arch/x86/kernel/livepatch.c new file mode 100644 index 0000000..ff3c3101d --- /dev/null +++ b/arch/x86/kernel/livepatch.c @@ -0,0 +1,90 @@ +/* + * livepatch.c - x86-specific Kernel Live Patching Core + * + * Copyright (C) 2014 Seth Jennings + * Copyright (C) 2014 SUSE + * + * 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, see . + */ + +#include +#include +#include +#include +#include +#include + +/** + * klp_write_module_reloc() - write a relocation in a module + * @mod: module in which the section to be modified is found + * @type: ELF relocation type (see asm/elf.h) + * @loc: address that the relocation should be written to + * @value: relocation value (sym address + addend) + * + * This function writes a relocation to the specified location for + * a particular module. + */ +int klp_write_module_reloc(struct module *mod, unsigned long type, + unsigned long loc, unsigned long value) +{ + int ret, numpages, size = 4; + bool readonly; + unsigned long val; + unsigned long core = (unsigned long)mod->module_core; + unsigned long core_ro_size = mod->core_ro_size; + unsigned long core_size = mod->core_size; + + switch (type) { + case R_X86_64_NONE: + return 0; + case R_X86_64_64: + val = value; + size = 8; + break; + case R_X86_64_32: + val = (u32)value; + break; + case R_X86_64_32S: + val = (s32)value; + break; + case R_X86_64_PC32: + val = (u32)(value - loc); + break; + default: + /* unsupported relocation type */ + return -EINVAL; + } + + if (loc < core || loc >= core + core_size) + /* loc does not point to any symbol inside the module */ + return -EINVAL; + + if (loc < core + core_ro_size) + readonly = true; + else + readonly = false; + + /* determine if the relocation spans a page boundary */ + numpages = ((loc & PAGE_MASK) == ((loc + size) & PAGE_MASK)) ? 1 : 2; + + if (readonly) + set_memory_rw(loc & PAGE_MASK, numpages); + + ret = probe_kernel_write((void *)loc, &val, size); + + if (readonly) + set_memory_ro(loc & PAGE_MASK, numpages); + + return ret; +} diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h new file mode 100644 index 0000000..950bc61 --- /dev/null +++ b/include/linux/livepatch.h @@ -0,0 +1,133 @@ +/* + * livepatch.h - Kernel Live Patching Core + * + * Copyright (C) 2014 Seth Jennings + * Copyright (C) 2014 SUSE + * + * 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, see . + */ + +#ifndef _LINUX_LIVEPATCH_H_ +#define _LINUX_LIVEPATCH_H_ + +#include +#include + +#if IS_ENABLED(CONFIG_LIVE_PATCHING) + +#include + +enum klp_state { + KLP_DISABLED, + KLP_ENABLED +}; + +/** + * struct klp_func - function structure for live patching + * @old_name: name of the function to be patched + * @new_func: pointer to the patched function code + * @old_addr: a hint conveying at what address the old function + * can be found (optional, vmlinux patches only) + * @kobj: kobject for sysfs resources + * @fops: ftrace operations structure + * @state: tracks function-level patch application state + */ +struct klp_func { + /* external */ + const char *old_name; + void *new_func; + /* + * The old_addr field is optional and can be used to resolve + * duplicate symbol names in the vmlinux object. If this + * information is not present, the symbol is located by name + * with kallsyms. If the name is not unique and old_addr is + * not provided, the patch application fails as there is no + * way to resolve the ambiguity. + */ + unsigned long old_addr; + + /* internal */ + struct kobject kobj; + struct ftrace_ops *fops; + enum klp_state state; +}; + +/** + * struct klp_reloc - relocation structure for live patching + * @loc: address where the relocation will be written + * @val: address of the referenced symbol (optional, + * vmlinux patches only) + * @type: ELF relocation type + * @name: name of the referenced symbol (for lookup/verification) + * @addend: offset from the referenced symbol + * @external: symbol is either exported or within the live patch module itself + */ +struct klp_reloc { + unsigned long loc; + unsigned long val; + unsigned long type; + const char *name; + int addend; + int external; +}; + +/** + * struct klp_object - kernel object structure for live patching + * @name: module name (or NULL for vmlinux) + * @relocs: relocation entries to be applied at load time + * @funcs: function entries for functions to be patched in the object + * @kobj: kobject for sysfs resources + * @mod: kernel module associated with the patched object + * (NULL for vmlinux) + * @state: tracks object-level patch application state + */ +struct klp_object { + /* external */ + const char *name; + struct klp_reloc *relocs; + struct klp_func *funcs; + + /* internal */ + struct kobject *kobj; + struct module *mod; + enum klp_state state; +}; + +/** + * struct klp_patch - patch structure for live patching + * @mod: reference to the live patch module + * @objs: object entries for kernel objects to be patched + * @list: list node for global list of registered patches + * @kobj: kobject for sysfs resources + * @state: tracks patch-level application state + */ +struct klp_patch { + /* external */ + struct module *mod; + struct klp_object *objs; + + /* internal */ + struct list_head list; + struct kobject kobj; + enum klp_state state; +}; + +extern int klp_register_patch(struct klp_patch *); +extern int klp_unregister_patch(struct klp_patch *); +extern int klp_enable_patch(struct klp_patch *); +extern int klp_disable_patch(struct klp_patch *); + +#endif /* CONFIG_LIVE_PATCHING */ + +#endif /* _LINUX_LIVEPATCH_H_ */ diff --git a/kernel/Makefile b/kernel/Makefile index a59481a..616994f 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -26,6 +26,7 @@ obj-y += power/ obj-y += printk/ obj-y += irq/ obj-y += rcu/ +obj-y += livepatch/ obj-$(CONFIG_CHECKPOINT_RESTORE) += kcmp.o obj-$(CONFIG_FREEZER) += freezer.o diff --git a/kernel/livepatch/Kconfig b/kernel/livepatch/Kconfig new file mode 100644 index 0000000..96da00f --- /dev/null +++ b/kernel/livepatch/Kconfig @@ -0,0 +1,18 @@ +config ARCH_HAVE_LIVE_PATCHING + boolean + help + Arch supports kernel live patching + +config LIVE_PATCHING + boolean "Kernel Live Patching" + depends on DYNAMIC_FTRACE_WITH_REGS + depends on MODULES + depends on SYSFS + depends on KALLSYMS_ALL + depends on ARCH_HAVE_LIVE_PATCHING + help + Say Y here if you want to support kernel live patching. + This option has no runtime impact until a kernel "patch" + module uses the interface provided by this option to register + a patch, causing calls to patched functions to be redirected + to new function code contained in the patch module. diff --git a/kernel/livepatch/Makefile b/kernel/livepatch/Makefile new file mode 100644 index 0000000..7c1f008 --- /dev/null +++ b/kernel/livepatch/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_LIVE_PATCHING) += livepatch.o + +livepatch-objs := core.o diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c new file mode 100644 index 0000000..f99fe18 --- /dev/null +++ b/kernel/livepatch/core.c @@ -0,0 +1,930 @@ +/* + * core.c - Kernel Live Patching Core + * + * Copyright (C) 2014 Seth Jennings + * Copyright (C) 2014 SUSE + * + * 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, see . + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The klp_mutex protects the klp_patches list and state transitions of any + * structure reachable from the patches list. References to any structure must + * be obtained under mutex protection. + */ + +static DEFINE_MUTEX(klp_mutex); +static LIST_HEAD(klp_patches); + +static struct kobject *klp_root_kobj; + +static bool klp_is_module(struct klp_object *obj) +{ + return obj->name; +} + +static bool klp_is_object_loaded(struct klp_object *obj) +{ + return !obj->name || obj->mod; +} + +/* sets obj->mod if object is not vmlinux and module is found */ +static void klp_find_object_module(struct klp_object *obj) +{ + if (!klp_is_module(obj)) + return; + + mutex_lock(&module_mutex); + /* + * We don't need to take a reference on the module here because we have + * the klp_mutex, which is also taken by the module notifier. This + * prevents any module from unloading until we release the klp_mutex. + */ + obj->mod = find_module(obj->name); + mutex_unlock(&module_mutex); +} + +/* klp_mutex must be held by caller */ +static bool klp_is_patch_registered(struct klp_patch *patch) +{ + struct klp_patch *mypatch; + + list_for_each_entry(mypatch, &klp_patches, list) + if (mypatch == patch) + return true; + + return false; +} + +static bool klp_initialized(void) +{ + return klp_root_kobj; +} + +struct klp_find_arg { + const char *objname; + const char *name; + unsigned long addr; + /* + * If count == 0, the symbol was not found. If count == 1, a unique + * match was found and addr is set. If count > 1, there is + * unresolvable ambiguity among "count" number of symbols with the same + * name in the same object. + */ + unsigned long count; +}; + +static int klp_find_callback(void *data, const char *name, + struct module *mod, unsigned long addr) +{ + struct klp_find_arg *args = data; + + if ((mod && !args->objname) || (!mod && args->objname)) + return 0; + + if (strcmp(args->name, name)) + return 0; + + if (args->objname && strcmp(args->objname, mod->name)) + return 0; + + /* + * args->addr might be overwritten if another match is found + * but klp_find_object_symbol() handles this and only returns the + * addr if count == 1. + */ + args->addr = addr; + args->count++; + + return 0; +} + +static int klp_find_object_symbol(const char *objname, const char *name, + unsigned long *addr) +{ + struct klp_find_arg args = { + .objname = objname, + .name = name, + .addr = 0, + .count = 0 + }; + + kallsyms_on_each_symbol(klp_find_callback, &args); + + if (args.count == 0) + pr_err("symbol '%s' not found in symbol table\n", name); + else if (args.count > 1) + pr_err("unresolvable ambiguity (%lu matches) on symbol '%s' in object '%s'\n", + args.count, name, objname); + else { + *addr = args.addr; + return 0; + } + + *addr = 0; + return -EINVAL; +} + +struct klp_verify_args { + const char *name; + const unsigned long addr; +}; + +static int klp_verify_callback(void *data, const char *name, + struct module *mod, unsigned long addr) +{ + struct klp_verify_args *args = data; + + if (!mod && + !strcmp(args->name, name) && + args->addr == addr) + return 1; + + return 0; +} + +static int klp_verify_vmlinux_symbol(const char *name, unsigned long addr) +{ + struct klp_verify_args args = { + .name = name, + .addr = addr, + }; + + if (kallsyms_on_each_symbol(klp_verify_callback, &args)) + return 0; + + pr_err("symbol '%s' not found at specified address 0x%016lx, kernel mismatch?", + name, addr); + return -EINVAL; +} + +static int klp_find_verify_func_addr(struct klp_object *obj, + struct klp_func *func) +{ + int ret; + +#if defined(CONFIG_RANDOMIZE_BASE) + /* KASLR is enabled, disregard old_addr from user */ + func->old_addr = 0; +#endif + + if (!func->old_addr || klp_is_module(obj)) + ret = klp_find_object_symbol(obj->name, func->old_name, + &func->old_addr); + else + ret = klp_verify_vmlinux_symbol(func->old_name, + func->old_addr); + + return ret; +} + +/* + * external symbols are located outside the parent object (where the parent + * object is either vmlinux or the kmod being patched). + */ +static int klp_find_external_symbol(struct module *pmod, const char *name, + unsigned long *addr) +{ + const struct kernel_symbol *sym; + + /* first, check if it's an exported symbol */ + preempt_disable(); + sym = find_symbol(name, NULL, NULL, true, true); + preempt_enable(); + if (sym) { + *addr = sym->value; + return 0; + } + + /* otherwise check if it's in another .o within the patch module */ + return klp_find_object_symbol(pmod->name, name, addr); +} + +static int klp_write_object_relocations(struct module *pmod, + struct klp_object *obj) +{ + int ret; + struct klp_reloc *reloc; + + if (WARN_ON(!klp_is_object_loaded(obj))) + return -EINVAL; + + if (WARN_ON(!obj->relocs)) + return -EINVAL; + + for (reloc = obj->relocs; reloc->name; reloc++) { + if (!klp_is_module(obj)) { + ret = klp_verify_vmlinux_symbol(reloc->name, + reloc->val); + if (ret) + return ret; + } else { + /* module, reloc->val needs to be discovered */ + if (reloc->external) + ret = klp_find_external_symbol(pmod, + reloc->name, + &reloc->val); + else + ret = klp_find_object_symbol(obj->mod->name, + reloc->name, + &reloc->val); + if (ret) + return ret; + } + ret = klp_write_module_reloc(pmod, reloc->type, reloc->loc, + reloc->val + reloc->addend); + if (ret) { + pr_err("relocation failed for symbol '%s' at 0x%016lx (%d)\n", + reloc->name, reloc->val, ret); + return ret; + } + } + + return 0; +} + +static void notrace klp_ftrace_handler(unsigned long ip, + unsigned long parent_ip, + struct ftrace_ops *ops, + struct pt_regs *regs) +{ + struct klp_func *func = ops->private; + + regs->ip = (unsigned long)func->new_func; +} + +static int klp_disable_func(struct klp_func *func) +{ + int ret; + + if (WARN_ON(func->state != KLP_ENABLED)) + return -EINVAL; + + if (WARN_ON(!func->old_addr)) + return -EINVAL; + + ret = unregister_ftrace_function(func->fops); + if (ret) { + pr_err("failed to unregister ftrace handler for function '%s' (%d)\n", + func->old_name, ret); + return ret; + } + + ret = ftrace_set_filter_ip(func->fops, func->old_addr, 1, 0); + if (ret) + pr_warn("function unregister succeeded but failed to clear the filter\n"); + + func->state = KLP_DISABLED; + + return 0; +} + +static int klp_enable_func(struct klp_func *func) +{ + int ret; + + if (WARN_ON(!func->old_addr)) + return -EINVAL; + + if (WARN_ON(func->state != KLP_DISABLED)) + return -EINVAL; + + ret = ftrace_set_filter_ip(func->fops, func->old_addr, 0, 0); + if (ret) { + pr_err("failed to set ftrace filter for function '%s' (%d)\n", + func->old_name, ret); + return ret; + } + + ret = register_ftrace_function(func->fops); + if (ret) { + pr_err("failed to register ftrace handler for function '%s' (%d)\n", + func->old_name, ret); + ftrace_set_filter_ip(func->fops, func->old_addr, 1, 0); + } else { + func->state = KLP_ENABLED; + } + + return ret; +} + +static int klp_disable_object(struct klp_object *obj) +{ + struct klp_func *func; + int ret; + + for (func = obj->funcs; func->old_name; func++) { + if (func->state != KLP_ENABLED) + continue; + + ret = klp_disable_func(func); + if (ret) + return ret; + } + + obj->state = KLP_DISABLED; + + return 0; +} + +static int klp_enable_object(struct klp_object *obj) +{ + struct klp_func *func; + int ret; + + if (WARN_ON(obj->state != KLP_DISABLED)) + return -EINVAL; + + if (WARN_ON(!klp_is_object_loaded(obj))) + return -EINVAL; + + for (func = obj->funcs; func->old_name; func++) { + ret = klp_enable_func(func); + if (ret) + goto unregister; + } + obj->state = KLP_ENABLED; + + return 0; + +unregister: + WARN_ON(klp_disable_object(obj)); + return ret; +} + +static int __klp_disable_patch(struct klp_patch *patch) +{ + struct klp_object *obj; + int ret; + + pr_notice("disabling patch '%s'\n", patch->mod->name); + + for (obj = patch->objs; obj->funcs; obj++) { + if (obj->state != KLP_ENABLED) + continue; + + ret = klp_disable_object(obj); + if (ret) + return ret; + } + + patch->state = KLP_DISABLED; + + return 0; +} + +/** + * klp_disable_patch() - disables a registered patch + * @patch: The registered, enabled patch to be disabled + * + * Unregisters the patched functions from ftrace. + * + * Return: 0 on success, otherwise error + */ +int klp_disable_patch(struct klp_patch *patch) +{ + int ret; + + mutex_lock(&klp_mutex); + + if (!klp_is_patch_registered(patch)) { + ret = -EINVAL; + goto err; + } + + if (patch->state == KLP_DISABLED) { + ret = -EINVAL; + goto err; + } + + ret = __klp_disable_patch(patch); + +err: + mutex_unlock(&klp_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(klp_disable_patch); + +static int __klp_enable_patch(struct klp_patch *patch) +{ + struct klp_object *obj; + int ret; + + if (WARN_ON(patch->state != KLP_DISABLED)) + return -EINVAL; + + pr_notice_once("tainting kernel with TAINT_LIVEPATCH\n"); + add_taint(TAINT_LIVEPATCH, LOCKDEP_STILL_OK); + + pr_notice("enabling patch '%s'\n", patch->mod->name); + + for (obj = patch->objs; obj->funcs; obj++) { + klp_find_object_module(obj); + + if (!klp_is_object_loaded(obj)) + continue; + + ret = klp_enable_object(obj); + if (ret) + goto unregister; + } + + patch->state = KLP_ENABLED; + + return 0; + +unregister: + WARN_ON(__klp_disable_patch(patch)); + return ret; +} + +/** + * klp_enable_patch() - enables a registered patch + * @patch: The registered, disabled patch to be enabled + * + * Performs the needed symbol lookups and code relocations, + * then registers the patched functions with ftrace. + * + * Return: 0 on success, otherwise error + */ +int klp_enable_patch(struct klp_patch *patch) +{ + int ret; + + mutex_lock(&klp_mutex); + + if (!klp_is_patch_registered(patch)) { + ret = -EINVAL; + goto err; + } + + ret = __klp_enable_patch(patch); + +err: + mutex_unlock(&klp_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(klp_enable_patch); + +/* + * Sysfs Interface + * + * /sys/kernel/livepatch + * /sys/kernel/livepatch/ + * /sys/kernel/livepatch//enabled + * /sys/kernel/livepatch// + * /sys/kernel/livepatch/// + */ + +static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct klp_patch *patch; + int ret; + unsigned long val; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return -EINVAL; + + if (val != KLP_DISABLED && val != KLP_ENABLED) + return -EINVAL; + + patch = container_of(kobj, struct klp_patch, kobj); + + mutex_lock(&klp_mutex); + + if (val == patch->state) { + /* already in requested state */ + ret = -EINVAL; + goto err; + } + + if (val == KLP_ENABLED) { + ret = __klp_enable_patch(patch); + if (ret) + goto err; + } else { + ret = __klp_disable_patch(patch); + if (ret) + goto err; + } + + mutex_unlock(&klp_mutex); + + return count; + +err: + mutex_unlock(&klp_mutex); + return ret; +} + +static ssize_t enabled_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct klp_patch *patch; + + patch = container_of(kobj, struct klp_patch, kobj); + return snprintf(buf, PAGE_SIZE-1, "%d\n", patch->state); +} + +static struct kobj_attribute enabled_kobj_attr = __ATTR_RW(enabled); +static struct attribute *klp_patch_attrs[] = { + &enabled_kobj_attr.attr, + NULL +}; + +static void klp_kobj_release_patch(struct kobject *kobj) +{ + /* + * Once we have a consistency model we'll need to module_put() the + * patch module here. See klp_register_patch() for more details. + */ +} + +static struct kobj_type klp_ktype_patch = { + .release = klp_kobj_release_patch, + .sysfs_ops = &kobj_sysfs_ops, + .default_attrs = klp_patch_attrs, +}; + +static void klp_kobj_release_func(struct kobject *kobj) +{ + struct klp_func *func; + + func = container_of(kobj, struct klp_func, kobj); + kfree(func->fops); +} + +static struct kobj_type klp_ktype_func = { + .release = klp_kobj_release_func, + .sysfs_ops = &kobj_sysfs_ops, +}; + +/* + * Free all functions' kobjects in the array up to some limit. When limit is + * NULL, all kobjects are freed. + */ +static void klp_free_funcs_limited(struct klp_object *obj, + struct klp_func *limit) +{ + struct klp_func *func; + + for (func = obj->funcs; func->old_name && func != limit; func++) + kobject_put(&func->kobj); +} + +/* Clean up when a patched object is unloaded */ +static void klp_free_object_loaded(struct klp_object *obj) +{ + struct klp_func *func; + + obj->mod = NULL; + + for (func = obj->funcs; func->old_name; func++) + func->old_addr = 0; +} + +/* + * Free all objects' kobjects in the array up to some limit. When limit is + * NULL, all kobjects are freed. + */ +static void klp_free_objects_limited(struct klp_patch *patch, + struct klp_object *limit) +{ + struct klp_object *obj; + + for (obj = patch->objs; obj->funcs && obj != limit; obj++) { + klp_free_funcs_limited(obj, NULL); + kobject_put(obj->kobj); + } +} + +static void klp_free_patch(struct klp_patch *patch) +{ + klp_free_objects_limited(patch, NULL); + if (!list_empty(&patch->list)) + list_del(&patch->list); + kobject_put(&patch->kobj); +} + +static int klp_init_func(struct klp_object *obj, struct klp_func *func) +{ + struct ftrace_ops *ops; + int ret; + + ops = kzalloc(sizeof(*ops), GFP_KERNEL); + if (!ops) + return -ENOMEM; + + ops->private = func; + ops->func = klp_ftrace_handler; + ops->flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_DYNAMIC; + func->fops = ops; + func->state = KLP_DISABLED; + + ret = kobject_init_and_add(&func->kobj, &klp_ktype_func, + obj->kobj, func->old_name); + if (ret) { + kfree(func->fops); + return ret; + } + + return 0; +} + +/* parts of the initialization that is done only when the object is loaded */ +static int klp_init_object_loaded(struct klp_patch *patch, + struct klp_object *obj) +{ + struct klp_func *func; + int ret; + + if (obj->relocs) { + ret = klp_write_object_relocations(patch->mod, obj); + if (ret) + return ret; + } + + for (func = obj->funcs; func->old_name; func++) { + ret = klp_find_verify_func_addr(obj, func); + if (ret) + return ret; + } + + return 0; +} + +static int klp_init_object(struct klp_patch *patch, struct klp_object *obj) +{ + struct klp_func *func; + int ret; + const char *name; + + if (!obj->funcs) + return -EINVAL; + + obj->state = KLP_DISABLED; + + klp_find_object_module(obj); + + name = klp_is_module(obj) ? obj->name : "vmlinux"; + obj->kobj = kobject_create_and_add(name, &patch->kobj); + if (!obj->kobj) + return -ENOMEM; + + for (func = obj->funcs; func->old_name; func++) { + ret = klp_init_func(obj, func); + if (ret) + goto free; + } + + if (klp_is_object_loaded(obj)) { + ret = klp_init_object_loaded(patch, obj); + if (ret) + goto free; + } + + return 0; + +free: + klp_free_funcs_limited(obj, func); + kobject_put(obj->kobj); + return ret; +} + +static int klp_init_patch(struct klp_patch *patch) +{ + struct klp_object *obj; + int ret; + + if (!patch->objs) + return -EINVAL; + + mutex_lock(&klp_mutex); + + patch->state = KLP_DISABLED; + + ret = kobject_init_and_add(&patch->kobj, &klp_ktype_patch, + klp_root_kobj, patch->mod->name); + if (ret) + goto unlock; + + for (obj = patch->objs; obj->funcs; obj++) { + ret = klp_init_object(patch, obj); + if (ret) + goto free; + } + + list_add(&patch->list, &klp_patches); + + mutex_unlock(&klp_mutex); + + return 0; + +free: + klp_free_objects_limited(patch, obj); + kobject_put(&patch->kobj); +unlock: + mutex_unlock(&klp_mutex); + return ret; +} + +/** + * klp_unregister_patch() - unregisters a patch + * @patch: Disabled patch to be unregistered + * + * Frees the data structures and removes the sysfs interface. + * + * Return: 0 on success, otherwise error + */ +int klp_unregister_patch(struct klp_patch *patch) +{ + int ret = 0; + + mutex_lock(&klp_mutex); + + if (!klp_is_patch_registered(patch)) { + ret = -EINVAL; + goto out; + } + + if (patch->state == KLP_ENABLED) { + ret = -EBUSY; + goto out; + } + + klp_free_patch(patch); + +out: + mutex_unlock(&klp_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(klp_unregister_patch); + +/** + * klp_register_patch() - registers a patch + * @patch: Patch to be registered + * + * Initializes the data structure associated with the patch and + * creates the sysfs interface. + * + * Return: 0 on success, otherwise error + */ +int klp_register_patch(struct klp_patch *patch) +{ + int ret; + + if (!klp_initialized()) + return -ENODEV; + + if (!patch || !patch->mod) + return -EINVAL; + + /* + * A reference is taken on the patch module to prevent it from being + * unloaded. Right now, we don't allow patch modules to unload since + * there is currently no method to determine if a thread is still + * running in the patched code contained in the patch module once + * the ftrace registration is successful. + */ + if (!try_module_get(patch->mod)) + return -ENODEV; + + ret = klp_init_patch(patch); + if (ret) + module_put(patch->mod); + + return ret; +} +EXPORT_SYMBOL_GPL(klp_register_patch); + +static void klp_module_notify_coming(struct klp_patch *patch, + struct klp_object *obj) +{ + struct module *pmod = patch->mod; + struct module *mod = obj->mod; + int ret; + + ret = klp_init_object_loaded(patch, obj); + if (ret) + goto err; + + if (patch->state == KLP_DISABLED) + return; + + pr_notice("applying patch '%s' to loading module '%s'\n", + pmod->name, mod->name); + + ret = klp_enable_object(obj); + if (!ret) + return; + +err: + pr_warn("failed to apply patch '%s' to module '%s' (%d)\n", + pmod->name, mod->name, ret); +} + +static void klp_module_notify_going(struct klp_patch *patch, + struct klp_object *obj) +{ + struct module *pmod = patch->mod; + struct module *mod = obj->mod; + int ret; + + if (patch->state == KLP_DISABLED) + goto disabled; + + pr_notice("reverting patch '%s' on unloading module '%s'\n", + pmod->name, mod->name); + + ret = klp_disable_object(obj); + if (ret) + pr_warn("failed to revert patch '%s' on module '%s' (%d)\n", + pmod->name, mod->name, ret); + +disabled: + klp_free_object_loaded(obj); +} + +static int klp_module_notify(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct module *mod = data; + struct klp_patch *patch; + struct klp_object *obj; + + if (action != MODULE_STATE_COMING && action != MODULE_STATE_GOING) + return 0; + + mutex_lock(&klp_mutex); + + list_for_each_entry(patch, &klp_patches, list) { + for (obj = patch->objs; obj->funcs; obj++) { + if (!klp_is_module(obj) || strcmp(obj->name, mod->name)) + continue; + + if (action == MODULE_STATE_COMING) { + obj->mod = mod; + klp_module_notify_coming(patch, obj); + } else /* MODULE_STATE_GOING */ + klp_module_notify_going(patch, obj); + + break; + } + } + + mutex_unlock(&klp_mutex); + + return 0; +} + +static struct notifier_block klp_module_nb = { + .notifier_call = klp_module_notify, + .priority = INT_MIN+1, /* called late but before ftrace notifier */ +}; + +static int klp_init(void) +{ + int ret; + + ret = register_module_notifier(&klp_module_nb); + if (ret) + return ret; + + klp_root_kobj = kobject_create_and_add("livepatch", kernel_kobj); + if (!klp_root_kobj) { + ret = -ENOMEM; + goto unregister; + } + + return 0; + +unregister: + unregister_module_notifier(&klp_module_nb); + return ret; +} + +module_init(klp_init); -- cgit v0.10.2 From 13d1cf7e702596e0cd8ec62afa6bd49c431f2d0c Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Tue, 16 Dec 2014 11:58:20 -0600 Subject: livepatch: samples: add sample live patching module Add a sample live patching module. Signed-off-by: Seth Jennings Reviewed-by: Miroslav Benes Reviewed-by: Petr Mladek Reviewed-by: Masami Hiramatsu Signed-off-by: Jiri Kosina diff --git a/MAINTAINERS b/MAINTAINERS index df6a078..afe93ea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5795,6 +5795,7 @@ F: include/linux/livepatch.h F: arch/x86/include/asm/livepatch.h F: arch/x86/kernel/livepatch.c F: Documentation/ABI/testing/sysfs-kernel-livepatch +F: samples/livepatch/ L: live-patching@vger.kernel.org LLC (802.2) diff --git a/samples/Kconfig b/samples/Kconfig index 6181c2c..0aed20d 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -63,4 +63,11 @@ config SAMPLE_RPMSG_CLIENT to communicate with an AMP-configured remote processor over the rpmsg bus. +config SAMPLE_LIVE_PATCHING + tristate "Build live patching sample -- loadable modules only" + depends on LIVE_PATCHING && m + help + Builds a sample live patch that replaces the procfs handler + for /proc/cmdline to print "this has been live patched". + endif # SAMPLES diff --git a/samples/Makefile b/samples/Makefile index 1a60c62..f00257b 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -1,4 +1,4 @@ # Makefile for Linux samples code -obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ trace_events/ \ +obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ trace_events/ livepatch/ \ hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ diff --git a/samples/livepatch/Makefile b/samples/livepatch/Makefile new file mode 100644 index 0000000..7f1cdc1 --- /dev/null +++ b/samples/livepatch/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SAMPLE_LIVE_PATCHING) += livepatch-sample.o diff --git a/samples/livepatch/livepatch-sample.c b/samples/livepatch/livepatch-sample.c new file mode 100644 index 0000000..21f159d --- /dev/null +++ b/samples/livepatch/livepatch-sample.c @@ -0,0 +1,87 @@ +/* + * livepatch-sample.c - Kernel Live Patching Sample Module + * + * Copyright (C) 2014 Seth Jennings + * + * 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, see . + */ + +#include +#include +#include + +/* + * This (dumb) live patch overrides the function that prints the + * kernel boot cmdline when /proc/cmdline is read. + * + * Example: + * $ cat /proc/cmdline + * + * $ insmod livepatch-sample.ko + * $ cat /proc/cmdline + * this has been live patched + * $ echo 0 > /sys/kernel/livepatch/klp_sample/enabled + * + */ + +#include +static int livepatch_cmdline_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%s\n", "this has been live patched"); + return 0; +} + +static struct klp_func funcs[] = { + { + .old_name = "cmdline_proc_show", + .new_func = livepatch_cmdline_proc_show, + }, { } +}; + +static struct klp_object objs[] = { + { + /* name being NULL means vmlinux */ + .funcs = funcs, + }, { } +}; + +static struct klp_patch patch = { + .mod = THIS_MODULE, + .objs = objs, +}; + +static int livepatch_init(void) +{ + int ret; + + ret = klp_register_patch(&patch); + if (ret) + return ret; + ret = klp_enable_patch(&patch); + if (ret) { + WARN_ON(klp_unregister_patch(&patch)); + return ret; + } + return 0; +} + +static void livepatch_exit(void) +{ + WARN_ON(klp_disable_patch(&patch)); + WARN_ON(klp_unregister_patch(&patch)); +} + +module_init(livepatch_init); +module_exit(livepatch_exit); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From b5bfc51707f1b56b0b733980bb4fcc0562bf02d8 Mon Sep 17 00:00:00 2001 From: Li Bin Date: Fri, 19 Dec 2014 14:11:17 +0800 Subject: livepatch: move x86 specific ftrace handler code to arch/x86 The execution flow redirection related implemention in the livepatch ftrace handler is depended on the specific architecture. This patch introduces klp_arch_set_pc(like kgdb_arch_set_pc) interface to change the pt_regs. Signed-off-by: Li Bin Acked-by: Josh Poimboeuf Signed-off-by: Jiri Kosina diff --git a/arch/x86/include/asm/livepatch.h b/arch/x86/include/asm/livepatch.h index d529db1..b5608d7 100644 --- a/arch/x86/include/asm/livepatch.h +++ b/arch/x86/include/asm/livepatch.h @@ -22,6 +22,7 @@ #define _ASM_X86_LIVEPATCH_H #include +#include #ifdef CONFIG_LIVE_PATCHING #ifndef CC_USING_FENTRY @@ -30,6 +31,10 @@ extern int klp_write_module_reloc(struct module *mod, unsigned long type, unsigned long loc, unsigned long value); +static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip) +{ + regs->ip = ip; +} #else #error Live patching support is disabled; check CONFIG_LIVE_PATCHING #endif diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index f99fe18..07a2db9 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c @@ -272,7 +272,7 @@ static void notrace klp_ftrace_handler(unsigned long ip, { struct klp_func *func = ops->private; - regs->ip = (unsigned long)func->new_func; + klp_arch_set_pc(regs, (unsigned long)func->new_func); } static int klp_disable_func(struct klp_func *func) -- cgit v0.10.2 From 2c658e212ce7e40ace56d9441c8c5634d4d420e3 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 18 Dec 2014 16:12:08 +0200 Subject: spi: Remove FSF mailing addresses Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-au1550.c b/drivers/spi/spi-au1550.c index 326f479..f45e085 100644 --- a/drivers/spi/spi-au1550.c +++ b/drivers/spi/spi-au1550.c @@ -15,10 +15,6 @@ * 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 diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 98aab45..419a782 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -17,10 +17,6 @@ * 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 diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index c205309..e73e2b05 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -13,10 +13,6 @@ * 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, */ #include diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index dc7d2c2..5ef6638 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -10,10 +10,6 @@ * 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 diff --git a/drivers/spi/spi-butterfly.c b/drivers/spi/spi-butterfly.c index ee4f91c..9a95862 100644 --- a/drivers/spi/spi-butterfly.c +++ b/drivers/spi/spi-butterfly.c @@ -12,10 +12,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c index 41b5dc4..688956ff 100644 --- a/drivers/spi/spi-coldfire-qspi.c +++ b/drivers/spi/spi-coldfire-qspi.c @@ -12,11 +12,6 @@ * 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 - * */ #include diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index b3707ba..5e99106 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -11,10 +11,6 @@ * 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 diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index aee4e75..2b76492 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -12,10 +12,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include diff --git a/drivers/spi/spi-lm70llp.c b/drivers/spi/spi-lm70llp.c index 41c5765..ba72347 100644 --- a/drivers/spi/spi-lm70llp.c +++ b/drivers/spi/spi-lm70llp.c @@ -12,10 +12,6 @@ * 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 diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index 79399ae..d890d30 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -16,11 +16,6 @@ * 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 #include diff --git a/drivers/spi/spi-omap-uwire.c b/drivers/spi/spi-omap-uwire.c index daf1ada..3c08444 100644 --- a/drivers/spi/spi-omap-uwire.c +++ b/drivers/spi/spi-omap-uwire.c @@ -28,10 +28,6 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 3bc3cba..4df8942 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -14,11 +14,6 @@ * 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 diff --git a/drivers/spi/spi-pxa2xx-pxadma.c b/drivers/spi/spi-pxa2xx-pxadma.c index e8a26f2..e51fcf9 100644 --- a/drivers/spi/spi-pxa2xx-pxadma.c +++ b/drivers/spi/spi-pxa2xx-pxadma.c @@ -12,10 +12,6 @@ * 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 diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 05c623c..7a9a605 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -11,10 +11,6 @@ * 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 diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 2071f78..46ce470 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -15,11 +15,6 @@ * 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 - * */ #include diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 37b1983..9231c34 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -11,10 +11,6 @@ * 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 diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c index 237f2e7..5a56acf 100644 --- a/drivers/spi/spi-sc18is602.c +++ b/drivers/spi/spi-sc18is602.c @@ -12,10 +12,6 @@ * 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 diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c index fc29233..20e800e 100644 --- a/drivers/spi/spi-sh-hspi.c +++ b/drivers/spi/spi-sh-hspi.c @@ -16,11 +16,6 @@ * 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 - * */ #include diff --git a/drivers/spi/spi-sh.c b/drivers/spi/spi-sh.c index 1cfc906..5025011 100644 --- a/drivers/spi/spi-sh.c +++ b/drivers/spi/spi-sh.c @@ -14,11 +14,6 @@ * 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 - * */ #include diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index be692ad..93dfcee 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -11,10 +11,6 @@ * 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 diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 66a70e9..c76cc75 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -13,10 +13,6 @@ * 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 diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 6941e04..6f97e5a 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -14,10 +14,6 @@ * 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 diff --git a/include/linux/spi/at86rf230.h b/include/linux/spi/at86rf230.h index b2b1afb..cd519a1 100644 --- a/include/linux/spi/at86rf230.h +++ b/include/linux/spi/at86rf230.h @@ -12,10 +12,6 @@ * 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. - * * Written by: * Dmitry Eremin-Solenikov */ diff --git a/include/linux/spi/l4f00242t03.h b/include/linux/spi/l4f00242t03.h index bc8677c..e69e9b5 100644 --- a/include/linux/spi/l4f00242t03.h +++ b/include/linux/spi/l4f00242t03.h @@ -12,10 +12,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _INCLUDE_LINUX_SPI_L4F00242T03_H_ diff --git a/include/linux/spi/lms283gf05.h b/include/linux/spi/lms283gf05.h index 555d254..fdd1d1d 100644 --- a/include/linux/spi/lms283gf05.h +++ b/include/linux/spi/lms283gf05.h @@ -11,10 +11,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _INCLUDE_LINUX_SPI_LMS283GF05_H_ diff --git a/include/linux/spi/mxs-spi.h b/include/linux/spi/mxs-spi.h index 4835486..381d368 100644 --- a/include/linux/spi/mxs-spi.h +++ b/include/linux/spi/mxs-spi.h @@ -15,10 +15,6 @@ * 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 __LINUX_SPI_MXS_SPI_H__ diff --git a/include/linux/spi/pxa2xx_spi.h b/include/linux/spi/pxa2xx_spi.h index d5a3165..5eb56e3 100644 --- a/include/linux/spi/pxa2xx_spi.h +++ b/include/linux/spi/pxa2xx_spi.h @@ -10,10 +10,6 @@ * 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 __linux_pxa2xx_spi_h #define __linux_pxa2xx_spi_h diff --git a/include/linux/spi/rspi.h b/include/linux/spi/rspi.h index e546b2c..a693188 100644 --- a/include/linux/spi/rspi.h +++ b/include/linux/spi/rspi.h @@ -11,11 +11,6 @@ * 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 - * */ #ifndef __LINUX_SPI_RENESAS_SPI_H__ diff --git a/include/linux/spi/sh_hspi.h b/include/linux/spi/sh_hspi.h index a1121f8..aa0d440 100644 --- a/include/linux/spi/sh_hspi.h +++ b/include/linux/spi/sh_hspi.h @@ -9,10 +9,6 @@ * 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 */ #ifndef SH_HSPI_H #define SH_HSPI_H diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index a6ef2a8..6e2664a 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -10,10 +10,6 @@ * 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 __LINUX_SPI_H diff --git a/include/linux/spi/tle62x0.h b/include/linux/spi/tle62x0.h index 60b5918..414c6fd 100644 --- a/include/linux/spi/tle62x0.h +++ b/include/linux/spi/tle62x0.h @@ -12,10 +12,6 @@ * 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 */ struct tle62x0_pdata { diff --git a/include/linux/spi/tsc2005.h b/include/linux/spi/tsc2005.h index 8f721e4..563b3b1 100644 --- a/include/linux/spi/tsc2005.h +++ b/include/linux/spi/tsc2005.h @@ -12,11 +12,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #ifndef _LINUX_SPI_TSC2005_H -- cgit v0.10.2 From 2914e72681cdb3f310b0a0d570e9ac17b8c37d0c Mon Sep 17 00:00:00 2001 From: Prabhakar Lad Date: Wed, 26 Nov 2014 19:42:24 -0300 Subject: [media] media: s3c-camif: use vb2_ops_wait_prepare/finish helper This patch drops driver specific wait_prepare() and wait_finish() callbacks from vb2_ops and instead uses the the helpers vb2_ops_wait_prepare/finish() provided by the vb2 core, the lock member of the queue needs to be initalized to a mutex so that vb2 helpers vb2_ops_wait_prepare/finish() can make use of it. Signed-off-by: Lad, Prabhakar Cc: Sylwester Nawrocki Acked-by: Sylwester Nawrocki Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index aa40c82..54479d6 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -536,24 +536,12 @@ static void buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&camif->slock, flags); } -static void camif_lock(struct vb2_queue *vq) -{ - struct camif_vp *vp = vb2_get_drv_priv(vq); - mutex_lock(&vp->camif->lock); -} - -static void camif_unlock(struct vb2_queue *vq) -{ - struct camif_vp *vp = vb2_get_drv_priv(vq); - mutex_unlock(&vp->camif->lock); -} - static const struct vb2_ops s3c_camif_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, - .wait_prepare = camif_unlock, - .wait_finish = camif_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .start_streaming = start_streaming, .stop_streaming = stop_streaming, }; @@ -1161,6 +1149,7 @@ int s3c_camif_register_video_node(struct camif_dev *camif, int idx) q->buf_struct_size = sizeof(struct camif_buffer); q->drv_priv = vp; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &vp->camif->lock; ret = vb2_queue_init(q); if (ret) -- cgit v0.10.2 From 3d0aed38342b42bc532cc16cf4bf57a863f32df3 Mon Sep 17 00:00:00 2001 From: Prabhakar Lad Date: Wed, 26 Nov 2014 19:42:25 -0300 Subject: [media] media: ti-vpe: use vb2_ops_wait_prepare/finish helper This patch drops driver specific wait_prepare() and wait_finish() callbacks from vb2_ops and instead uses the the helpers vb2_ops_wait_prepare/finish() provided by the vb2 core, the lock member of the queue needs to be initalized to a mutex so that vb2 helpers vb2_ops_wait_prepare/finish() can make use of it. Signed-off-by: Lad, Prabhakar Cc: Kukjin Kim Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index d628d1a..968fb63 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1913,30 +1913,19 @@ static void vpe_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); } -static void vpe_wait_prepare(struct vb2_queue *q) -{ - struct vpe_ctx *ctx = vb2_get_drv_priv(q); - vpe_unlock(ctx); -} - -static void vpe_wait_finish(struct vb2_queue *q) -{ - struct vpe_ctx *ctx = vb2_get_drv_priv(q); - vpe_lock(ctx); -} - static struct vb2_ops vpe_qops = { .queue_setup = vpe_queue_setup, .buf_prepare = vpe_buf_prepare, .buf_queue = vpe_buf_queue, - .wait_prepare = vpe_wait_prepare, - .wait_finish = vpe_wait_finish, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) { struct vpe_ctx *ctx = priv; + struct vpe_dev *dev = ctx->dev; int ret; memset(src_vq, 0, sizeof(*src_vq)); @@ -1947,6 +1936,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &vpe_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &dev->dev_mutex; ret = vb2_queue_init(src_vq); if (ret) @@ -1960,6 +1950,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &vpe_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &dev->dev_mutex; return vb2_queue_init(dst_vq); } -- cgit v0.10.2 From 0637f054cef5b734b0b1dc5de7ee21aad6e0f441 Mon Sep 17 00:00:00 2001 From: Prabhakar Lad Date: Wed, 26 Nov 2014 19:42:26 -0300 Subject: [media] media: exynos-gsc: use vb2_ops_wait_prepare/finish helper This patch drops driver specific wait_prepare() and wait_finish() callbacks from vb2_ops and instead uses the the helpers vb2_ops_wait_prepare/finish() provided by the vb2 core, the lock member of the queue needs to be initalized to a mutex so that vb2 helpers vb2_ops_wait_prepare/finish() can make use of it. Signed-off-by: Lad, Prabhakar Cc: Kukjin Kim Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h index 0abdb17..fa572aa 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/exynos-gsc/gsc-core.h @@ -466,18 +466,6 @@ static inline void gsc_hw_clear_irq(struct gsc_dev *dev, int irq) writel(cfg, dev->regs + GSC_IRQ); } -static inline void gsc_lock(struct vb2_queue *vq) -{ - struct gsc_ctx *ctx = vb2_get_drv_priv(vq); - mutex_lock(&ctx->gsc_dev->lock); -} - -static inline void gsc_unlock(struct vb2_queue *vq) -{ - struct gsc_ctx *ctx = vb2_get_drv_priv(vq); - mutex_unlock(&ctx->gsc_dev->lock); -} - static inline bool gsc_ctx_state_is_set(u32 mask, struct gsc_ctx *ctx) { unsigned long flags; diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index 74e1de6..d5cffef 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -267,8 +267,8 @@ static struct vb2_ops gsc_m2m_qops = { .queue_setup = gsc_m2m_queue_setup, .buf_prepare = gsc_m2m_buf_prepare, .buf_queue = gsc_m2m_buf_queue, - .wait_prepare = gsc_unlock, - .wait_finish = gsc_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .stop_streaming = gsc_m2m_stop_streaming, .start_streaming = gsc_m2m_start_streaming, }; @@ -590,6 +590,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->gsc_dev->lock; ret = vb2_queue_init(src_vq); if (ret) @@ -603,6 +604,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->gsc_dev->lock; return vb2_queue_init(dst_vq); } -- cgit v0.10.2 From 8776ff659d8f9e86850fbe432610461ab09a94a0 Mon Sep 17 00:00:00 2001 From: Prabhakar Lad Date: Wed, 26 Nov 2014 19:42:28 -0300 Subject: [media] media: sh_veu: use vb2_ops_wait_prepare/finish helper This patch drops driver specific wait_prepare() and wait_finish() callbacks from vb2_ops and instead uses the the helpers vb2_ops_wait_prepare/finish() provided by the vb2 core, the lock member of the queue needs to be initalized to a mutex so that vb2 helpers vb2_ops_wait_prepare/finish() can make use of it. Signed-off-by: Lad, Prabhakar Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index aaa1f6f..a901b62 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -242,20 +242,6 @@ static void sh_veu_job_abort(void *priv) veu->aborting = true; } -static void sh_veu_lock(void *priv) -{ - struct sh_veu_dev *veu = priv; - - mutex_lock(&veu->fop_lock); -} - -static void sh_veu_unlock(void *priv) -{ - struct sh_veu_dev *veu = priv; - - mutex_unlock(&veu->fop_lock); -} - static void sh_veu_process(struct sh_veu_dev *veu, struct vb2_buffer *src_buf, struct vb2_buffer *dst_buf) @@ -950,36 +936,28 @@ static void sh_veu_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(veu->m2m_ctx, vb); } -static void sh_veu_wait_prepare(struct vb2_queue *q) -{ - sh_veu_unlock(vb2_get_drv_priv(q)); -} - -static void sh_veu_wait_finish(struct vb2_queue *q) -{ - sh_veu_lock(vb2_get_drv_priv(q)); -} - static const struct vb2_ops sh_veu_qops = { .queue_setup = sh_veu_queue_setup, .buf_prepare = sh_veu_buf_prepare, .buf_queue = sh_veu_buf_queue, - .wait_prepare = sh_veu_wait_prepare, - .wait_finish = sh_veu_wait_finish, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) { + struct sh_veu_dev *veu = priv; int ret; memset(src_vq, 0, sizeof(*src_vq)); src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; src_vq->io_modes = VB2_MMAP | VB2_USERPTR; - src_vq->drv_priv = priv; + src_vq->drv_priv = veu; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &sh_veu_qops; src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->lock = &veu->fop_lock; ret = vb2_queue_init(src_vq); if (ret < 0) @@ -988,10 +966,11 @@ static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, memset(dst_vq, 0, sizeof(*dst_vq)); dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; - dst_vq->drv_priv = priv; + dst_vq->drv_priv = veu; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &sh_veu_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->lock = &veu->fop_lock; return vb2_queue_init(dst_vq); } -- cgit v0.10.2 From c747404dbf2dcc0d8cb5d2e8aee5810b6ebba496 Mon Sep 17 00:00:00 2001 From: Prabhakar Lad Date: Wed, 26 Nov 2014 19:42:30 -0300 Subject: [media] media: s5p-tv: use vb2_ops_wait_prepare/finish helper This patch drops driver specific wait_prepare() and wait_finish() callbacks from vb2_ops and instead uses the the helpers vb2_ops_wait_prepare/finish() provided by the vb2 core, the lock member of the queue needs to be initalized to a mutex so that vb2 helpers vb2_ops_wait_prepare/finish() can make use of it. Signed-off-by: Lad, Prabhakar Cc: Kyungmin Park Cc: Tomasz Stanislawski Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index b4d2696..72d4f2e 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -926,22 +926,6 @@ static void buf_queue(struct vb2_buffer *vb) mxr_dbg(mdev, "queuing buffer\n"); } -static void wait_lock(struct vb2_queue *vq) -{ - struct mxr_layer *layer = vb2_get_drv_priv(vq); - - mxr_dbg(layer->mdev, "%s\n", __func__); - mutex_lock(&layer->mutex); -} - -static void wait_unlock(struct vb2_queue *vq) -{ - struct mxr_layer *layer = vb2_get_drv_priv(vq); - - mxr_dbg(layer->mdev, "%s\n", __func__); - mutex_unlock(&layer->mutex); -} - static int start_streaming(struct vb2_queue *vq, unsigned int count) { struct mxr_layer *layer = vb2_get_drv_priv(vq); @@ -1040,8 +1024,8 @@ static void stop_streaming(struct vb2_queue *vq) static struct vb2_ops mxr_video_qops = { .queue_setup = queue_setup, .buf_queue = buf_queue, - .wait_prepare = wait_unlock, - .wait_finish = wait_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .start_streaming = start_streaming, .stop_streaming = stop_streaming, }; @@ -1122,6 +1106,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, .ops = &mxr_video_qops, .min_buffers_needed = 1, .mem_ops = &vb2_dma_contig_memops, + .lock = &layer->mutex, }; return layer; -- cgit v0.10.2 From 654a731be1a0b6f606f3f3d12b50db08f2ae3c34 Mon Sep 17 00:00:00 2001 From: Prabhakar Lad Date: Wed, 26 Nov 2014 19:42:32 -0300 Subject: [media] media: s5p-mfc: use vb2_ops_wait_prepare/finish helper This patch drops driver specific wait_prepare() and wait_finish() callbacks from vb2_ops and instead uses the the helpers vb2_ops_wait_prepare/finish() provided by the vb2 core, the lock member of the queue needs to be initalized to a mutex so that vb2 helpers vb2_ops_wait_prepare/finish() can make use of it. Signed-off-by: Lad, Prabhakar Cc: Kyungmin Park Cc: Kamil Debski Cc: Jeongtae Park Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index fbfdf03..8e44a59 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -810,6 +810,7 @@ static int s5p_mfc_open(struct file *file) q = &ctx->vq_dst; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; q->drv_priv = &ctx->fh; + q->lock = &dev->mfc_mutex; if (vdev == dev->vfd_dec) { q->io_modes = VB2_MMAP; q->ops = get_dec_queue_ops(); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index c6c3452..9b14827 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -944,22 +944,6 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, return 0; } -static void s5p_mfc_unlock(struct vb2_queue *q) -{ - struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv); - struct s5p_mfc_dev *dev = ctx->dev; - - mutex_unlock(&dev->mfc_mutex); -} - -static void s5p_mfc_lock(struct vb2_queue *q) -{ - struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv); - struct s5p_mfc_dev *dev = ctx->dev; - - mutex_lock(&dev->mfc_mutex); -} - static int s5p_mfc_buf_init(struct vb2_buffer *vb) { struct vb2_queue *vq = vb->vb2_queue; @@ -1107,8 +1091,8 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb) static struct vb2_ops s5p_mfc_dec_qops = { .queue_setup = s5p_mfc_queue_setup, - .wait_prepare = s5p_mfc_unlock, - .wait_finish = s5p_mfc_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .buf_init = s5p_mfc_buf_init, .start_streaming = s5p_mfc_start_streaming, .stop_streaming = s5p_mfc_stop_streaming, diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index bd64f1d..ac12f65 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -1867,22 +1867,6 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, return 0; } -static void s5p_mfc_unlock(struct vb2_queue *q) -{ - struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv); - struct s5p_mfc_dev *dev = ctx->dev; - - mutex_unlock(&dev->mfc_mutex); -} - -static void s5p_mfc_lock(struct vb2_queue *q) -{ - struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv); - struct s5p_mfc_dev *dev = ctx->dev; - - mutex_lock(&dev->mfc_mutex); -} - static int s5p_mfc_buf_init(struct vb2_buffer *vb) { struct vb2_queue *vq = vb->vb2_queue; @@ -2052,8 +2036,8 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb) static struct vb2_ops s5p_mfc_enc_qops = { .queue_setup = s5p_mfc_queue_setup, - .wait_prepare = s5p_mfc_unlock, - .wait_finish = s5p_mfc_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .buf_init = s5p_mfc_buf_init, .buf_prepare = s5p_mfc_buf_prepare, .start_streaming = s5p_mfc_start_streaming, -- cgit v0.10.2 From 556d2f055bf6d79ce81587dfe774d4dd10da473f Mon Sep 17 00:00:00 2001 From: Yalin Wang Date: Mon, 3 Nov 2014 03:01:03 +0100 Subject: ARM: 8187/1: add CONFIG_HAVE_ARCH_BITREVERSE to support rbit instruction this change add CONFIG_HAVE_ARCH_BITREVERSE config option, so that we can use some architecture's bitrev hardware instruction to do bitrev operation. Introduce __constant_bitrev* macro for constant bitrev operation. Change __bitrev16() __bitrev32() to be inline function, don't need export symbol for these tiny functions. Signed-off-by: Yalin Wang Acked-by: Will Deacon Signed-off-by: Russell King diff --git a/include/linux/bitrev.h b/include/linux/bitrev.h index 7ffe03f..fb790b8 100644 --- a/include/linux/bitrev.h +++ b/include/linux/bitrev.h @@ -3,14 +3,83 @@ #include -extern u8 const byte_rev_table[256]; +#ifdef CONFIG_HAVE_ARCH_BITREVERSE +#include + +#define __bitrev32 __arch_bitrev32 +#define __bitrev16 __arch_bitrev16 +#define __bitrev8 __arch_bitrev8 -static inline u8 bitrev8(u8 byte) +#else +extern u8 const byte_rev_table[256]; +static inline u8 __bitrev8(u8 byte) { return byte_rev_table[byte]; } -extern u16 bitrev16(u16 in); -extern u32 bitrev32(u32 in); +static inline u16 __bitrev16(u16 x) +{ + return (__bitrev8(x & 0xff) << 8) | __bitrev8(x >> 8); +} + +static inline u32 __bitrev32(u32 x) +{ + return (__bitrev16(x & 0xffff) << 16) | __bitrev16(x >> 16); +} + +#endif /* CONFIG_HAVE_ARCH_BITREVERSE */ + +#define __constant_bitrev32(x) \ +({ \ + u32 __x = x; \ + __x = (__x >> 16) | (__x << 16); \ + __x = ((__x & (u32)0xFF00FF00UL) >> 8) | ((__x & (u32)0x00FF00FFUL) << 8); \ + __x = ((__x & (u32)0xF0F0F0F0UL) >> 4) | ((__x & (u32)0x0F0F0F0FUL) << 4); \ + __x = ((__x & (u32)0xCCCCCCCCUL) >> 2) | ((__x & (u32)0x33333333UL) << 2); \ + __x = ((__x & (u32)0xAAAAAAAAUL) >> 1) | ((__x & (u32)0x55555555UL) << 1); \ + __x; \ +}) + +#define __constant_bitrev16(x) \ +({ \ + u16 __x = x; \ + __x = (__x >> 8) | (__x << 8); \ + __x = ((__x & (u16)0xF0F0U) >> 4) | ((__x & (u16)0x0F0FU) << 4); \ + __x = ((__x & (u16)0xCCCCU) >> 2) | ((__x & (u16)0x3333U) << 2); \ + __x = ((__x & (u16)0xAAAAU) >> 1) | ((__x & (u16)0x5555U) << 1); \ + __x; \ +}) + +#define __constant_bitrev8(x) \ +({ \ + u8 __x = x; \ + __x = (__x >> 4) | (__x << 4); \ + __x = ((__x & (u8)0xCCU) >> 2) | ((__x & (u8)0x33U) << 2); \ + __x = ((__x & (u8)0xAAU) >> 1) | ((__x & (u8)0x55U) << 1); \ + __x; \ +}) + +#define bitrev32(x) \ +({ \ + u32 __x = x; \ + __builtin_constant_p(__x) ? \ + __constant_bitrev32(__x) : \ + __bitrev32(__x); \ +}) + +#define bitrev16(x) \ +({ \ + u16 __x = x; \ + __builtin_constant_p(__x) ? \ + __constant_bitrev16(__x) : \ + __bitrev16(__x); \ + }) +#define bitrev8(x) \ +({ \ + u8 __x = x; \ + __builtin_constant_p(__x) ? \ + __constant_bitrev8(__x) : \ + __bitrev8(__x) ; \ + }) #endif /* _LINUX_BITREV_H */ diff --git a/lib/Kconfig b/lib/Kconfig index 54cf309..cd177ca 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -13,6 +13,15 @@ config RAID6_PQ config BITREVERSE tristate +config HAVE_ARCH_BITREVERSE + boolean + default n + depends on BITREVERSE + help + This option provides an config for the architecture which have instruction + can do bitreverse operation, we use the hardware instruction if the architecture + have this capability. + config RATIONAL boolean diff --git a/lib/bitrev.c b/lib/bitrev.c index 3956203..40ffda9 100644 --- a/lib/bitrev.c +++ b/lib/bitrev.c @@ -1,3 +1,4 @@ +#ifndef CONFIG_HAVE_ARCH_BITREVERSE #include #include #include @@ -42,18 +43,4 @@ const u8 byte_rev_table[256] = { }; EXPORT_SYMBOL_GPL(byte_rev_table); -u16 bitrev16(u16 x) -{ - return (bitrev8(x & 0xff) << 8) | bitrev8(x >> 8); -} -EXPORT_SYMBOL(bitrev16); - -/** - * bitrev32 - reverse the order of bits in a u32 value - * @x: value to be bit-reversed - */ -u32 bitrev32(u32 x) -{ - return (bitrev16(x & 0xffff) << 16) | bitrev16(x >> 16); -} -EXPORT_SYMBOL(bitrev32); +#endif /* CONFIG_HAVE_ARCH_BITREVERSE */ -- cgit v0.10.2 From 8e7a4cef71790d9406a7bd85aea6cfb12bc07d3c Mon Sep 17 00:00:00 2001 From: Yalin Wang Date: Mon, 3 Nov 2014 03:02:23 +0100 Subject: ARM: 8189/1: arm64:add bitrev.h file to support rbit instruction This patch add bitrev.h file to support rbit instruction, so that we can do bitrev operation by hardware. Signed-off-by: Yalin Wang Acked-by: Will Deacon Signed-off-by: Russell King diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index b1f9a20..0ed36d1 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -39,6 +39,7 @@ config ARM64 select HARDIRQS_SW_RESEND select HAVE_ALIGNED_STRUCT_PAGE if SLUB select HAVE_ARCH_AUDITSYSCALL + select HAVE_ARCH_BITREVERSE select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_KGDB select HAVE_ARCH_SECCOMP_FILTER diff --git a/arch/arm64/include/asm/bitrev.h b/arch/arm64/include/asm/bitrev.h new file mode 100644 index 0000000..a5a0c36 --- /dev/null +++ b/arch/arm64/include/asm/bitrev.h @@ -0,0 +1,19 @@ +#ifndef __ASM_BITREV_H +#define __ASM_BITREV_H +static __always_inline __attribute_const__ u32 __arch_bitrev32(u32 x) +{ + __asm__ ("rbit %w0, %w1" : "=r" (x) : "r" (x)); + return x; +} + +static __always_inline __attribute_const__ u16 __arch_bitrev16(u16 x) +{ + return __arch_bitrev32((u32)x) >> 16; +} + +static __always_inline __attribute_const__ u8 __arch_bitrev8(u8 x) +{ + return __arch_bitrev32((u32)x) >> 24; +} + +#endif -- cgit v0.10.2 From 6c80f87ed4483bff0ecbf4455a1b09e7f950e9ab Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Sun, 21 Dec 2014 08:18:25 -0800 Subject: x86, mce: Improve timeout error messages There are four different possible types of timeouts. Distinguish them in the logs to help debug them. Signed-off-by: Andy Lutomirski Link: http://lkml.kernel.org/r/0fa6d2653a54a01c48b43a3583caf950ea99606e.1419178397.git.luto@amacapital.net Signed-off-by: Borislav Petkov diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index d2c6116..323e02c 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -311,7 +311,7 @@ static void wait_for_panic(void) panic("Panicing machine check CPU died"); } -static void mce_panic(char *msg, struct mce *final, char *exp) +static void mce_panic(const char *msg, struct mce *final, char *exp) { int i, apei_err = 0; @@ -735,7 +735,7 @@ static atomic_t mce_callin; /* * Check if a timeout waiting for other CPUs happened. */ -static int mce_timed_out(u64 *t) +static int mce_timed_out(u64 *t, const char *msg) { /* * The others already did panic for some reason. @@ -750,8 +750,7 @@ static int mce_timed_out(u64 *t) goto out; if ((s64)*t < SPINUNIT) { if (mca_cfg.tolerant <= 1) - mce_panic("Timeout synchronizing machine check over CPUs", - NULL, NULL); + mce_panic(msg, NULL, NULL); cpu_missing = 1; return 1; } @@ -867,7 +866,8 @@ static int mce_start(int *no_way_out) * Wait for everyone. */ while (atomic_read(&mce_callin) != cpus) { - if (mce_timed_out(&timeout)) { + if (mce_timed_out(&timeout, + "Timeout: Not all CPUs entered broadcast exception handler")) { atomic_set(&global_nwo, 0); return -1; } @@ -892,7 +892,8 @@ static int mce_start(int *no_way_out) * only seen by one CPU before cleared, avoiding duplicates. */ while (atomic_read(&mce_executing) < order) { - if (mce_timed_out(&timeout)) { + if (mce_timed_out(&timeout, + "Timeout: Subject CPUs unable to finish machine check processing")) { atomic_set(&global_nwo, 0); return -1; } @@ -936,7 +937,8 @@ static int mce_end(int order) * loops. */ while (atomic_read(&mce_executing) <= cpus) { - if (mce_timed_out(&timeout)) + if (mce_timed_out(&timeout, + "Timeout: Monarch CPU unable to finish machine check processing")) goto reset; ndelay(SPINUNIT); } @@ -949,7 +951,8 @@ static int mce_end(int order) * Subject: Wait for Monarch to finish. */ while (atomic_read(&mce_executing) != 0) { - if (mce_timed_out(&timeout)) + if (mce_timed_out(&timeout, + "Timeout: Monarch CPU did not finish machine check processing")) goto reset; ndelay(SPINUNIT); } -- cgit v0.10.2 From 6fd753572c34a2469b41813aa6f376569cf2681f Mon Sep 17 00:00:00 2001 From: Arnaud Ebalard Date: Tue, 16 Dec 2014 22:20:50 +0100 Subject: regulator: isl9305: deprecate use of isl in compatible string for isil "isil" and "isl" prefixes are used at various locations inside the kernel to reference Intersil corporation. This patch is part of a series fixing those locations were "isl" is used in compatible strings to use the now expected "isil" prefix instead (NASDAQ symbol for Intersil and most used version). The old compatible string is kept for backward compatibility. Signed-off-by: Arnaud Ebalard Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/regulator/isl9305.txt b/Documentation/devicetree/bindings/regulator/isl9305.txt index a626fc1..d6e7c9e 100644 --- a/Documentation/devicetree/bindings/regulator/isl9305.txt +++ b/Documentation/devicetree/bindings/regulator/isl9305.txt @@ -2,7 +2,7 @@ Intersil ISL9305/ISL9305H voltage regulator Required properties: -- compatible: "isl,isl9305" or "isl,isl9305h" +- compatible: "isil,isl9305" or "isil,isl9305h" - reg: I2C slave address, usually 0x68. - regulators: A node that houses a sub-node for each regulator within the device. Each sub-node is identified using the node's name, with valid @@ -19,7 +19,7 @@ Optional properties: Example pmic: isl9305@68 { - compatible = "isl,isl9305"; + compatible = "isil,isl9305"; reg = <0x68>; VINDCD1-supply = <&system_power>; diff --git a/drivers/regulator/isl9305.c b/drivers/regulator/isl9305.c index 92fefd9..6e3a15f 100644 --- a/drivers/regulator/isl9305.c +++ b/drivers/regulator/isl9305.c @@ -177,8 +177,10 @@ static int isl9305_i2c_probe(struct i2c_client *i2c, #ifdef CONFIG_OF static const struct of_device_id isl9305_dt_ids[] = { - { .compatible = "isl,isl9305" }, - { .compatible = "isl,isl9305h" }, + { .compatible = "isl,isl9305" }, /* for backward compat., don't use */ + { .compatible = "isil,isl9305" }, + { .compatible = "isl,isl9305h" }, /* for backward compat., don't use */ + { .compatible = "isil,isl9305h" }, {}, }; #endif -- cgit v0.10.2 From ba5295e55dd941425b10924f4f5c7af6eac4a1cb Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Tue, 9 Dec 2014 09:28:09 +0100 Subject: ASoC: pcm512x: Also support PCM514x devices Signed-off-by: Peter Rosin Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/pcm512x.txt b/Documentation/devicetree/bindings/sound/pcm512x.txt index faff75e..98e0d34 100644 --- a/Documentation/devicetree/bindings/sound/pcm512x.txt +++ b/Documentation/devicetree/bindings/sound/pcm512x.txt @@ -5,7 +5,8 @@ on the board). Required properties: - - compatible : One of "ti,pcm5121" or "ti,pcm5122" + - compatible : One of "ti,pcm5121", "ti,pcm5122", "ti,pcm5141" or + "ti,pcm5142" - reg : the I2C address of the device for I2C, the chip select number for SPI. diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index d0547fa..dcdfac0 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -46,6 +46,8 @@ static int pcm512x_i2c_remove(struct i2c_client *i2c) static const struct i2c_device_id pcm512x_i2c_id[] = { { "pcm5121", }, { "pcm5122", }, + { "pcm5141", }, + { "pcm5142", }, { } }; MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id); @@ -53,6 +55,8 @@ MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id); static const struct of_device_id pcm512x_of_match[] = { { .compatible = "ti,pcm5121", }, { .compatible = "ti,pcm5122", }, + { .compatible = "ti,pcm5141", }, + { .compatible = "ti,pcm5142", }, { } }; MODULE_DEVICE_TABLE(of, pcm512x_of_match); diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c index f297058..7b64a9c 100644 --- a/sound/soc/codecs/pcm512x-spi.c +++ b/sound/soc/codecs/pcm512x-spi.c @@ -43,6 +43,8 @@ static int pcm512x_spi_remove(struct spi_device *spi) static const struct spi_device_id pcm512x_spi_id[] = { { "pcm5121", }, { "pcm5122", }, + { "pcm5141", }, + { "pcm5142", }, { }, }; MODULE_DEVICE_TABLE(spi, pcm512x_spi_id); @@ -50,6 +52,8 @@ MODULE_DEVICE_TABLE(spi, pcm512x_spi_id); static const struct of_device_id pcm512x_of_match[] = { { .compatible = "ti,pcm5121", }, { .compatible = "ti,pcm5122", }, + { .compatible = "ti,pcm5141", }, + { .compatible = "ti,pcm5142", }, { } }; MODULE_DEVICE_TABLE(of, pcm512x_of_match); -- cgit v0.10.2 From 5129ad6e4e699924f7bc77d3d70ffc011a9024b8 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 9 Dec 2014 09:10:44 +0800 Subject: ASoC: Intel: fix sparse non static symbol warnings Fixes the following sparse warnings: sound/soc/intel/sst/sst_acpi.c:248:5: warning: symbol 'sst_acpi_probe' was not declared. Should it be static? sound/soc/intel/sst/sst_acpi.c:335:5: warning: symbol 'sst_acpi_remove' was not declared. Should it be static? Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c index f1180ff..df2b5cc 100644 --- a/sound/soc/intel/sst/sst_acpi.c +++ b/sound/soc/intel/sst/sst_acpi.c @@ -245,7 +245,7 @@ static struct sst_machines *sst_acpi_find_machine( return NULL; } -int sst_acpi_probe(struct platform_device *pdev) +static int sst_acpi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; int ret = 0; @@ -332,7 +332,7 @@ do_sst_cleanup: * This function is called by OS when a device is unloaded * This frees the interrupt etc */ -int sst_acpi_remove(struct platform_device *pdev) +static int sst_acpi_remove(struct platform_device *pdev) { struct intel_sst_drv *ctx; -- cgit v0.10.2 From 9e862375c5420a329521c458a3c808c127e9038f Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 9 Dec 2014 20:21:30 +0000 Subject: spi: Add new driver for STMicroelectronics' SPI Controller This patch adds support for the SPI portion of ST's SSC device. Signed-off-by: Lee Jones Signed-off-by: Mark Brown diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 9982998..000f774 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -503,6 +503,13 @@ config SPI_SIRF help SPI driver for CSR SiRFprimaII SoCs +config SPI_ST_SSC4 + tristate "STMicroelectronics SPI SSC-based driver" + depends on ARCH_STI + help + STMicroelectronics SoCs support for SPI. If you say yes to + this option, support will be included for the SSC driven SPI. + config SPI_SUN4I tristate "Allwinner A10 SoCs SPI controller" depends on ARCH_SUNXI || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 6b9d2ac..c33577b 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o obj-$(CONFIG_SPI_SIRF) += spi-sirf.o +obj-$(CONFIG_SPI_ST_SSC4) += spi-st-ssc4.o obj-$(CONFIG_SPI_SUN4I) += spi-sun4i.o obj-$(CONFIG_SPI_SUN6I) += spi-sun6i.o obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o diff --git a/drivers/spi/spi-st-ssc4.c b/drivers/spi/spi-st-ssc4.c new file mode 100644 index 0000000..8f8770a --- /dev/null +++ b/drivers/spi/spi-st-ssc4.c @@ -0,0 +1,510 @@ +/* + * Copyright (c) 2008-2014 STMicroelectronics Limited + * + * Author: Angus Clark + * Patrice Chotard + * Lee Jones + * + * SPI master mode controller driver, used in STMicroelectronics devices. + * + * May be copied or modified under the terms of the GNU General Public + * License Version 2.0 only. See linux/COPYING for more information. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SSC registers */ +#define SSC_BRG 0x000 +#define SSC_TBUF 0x004 +#define SSC_RBUF 0x008 +#define SSC_CTL 0x00C +#define SSC_IEN 0x010 +#define SSC_I2C 0x018 + +/* SSC Control */ +#define SSC_CTL_DATA_WIDTH_9 0x8 +#define SSC_CTL_DATA_WIDTH_MSK 0xf +#define SSC_CTL_BM 0xf +#define SSC_CTL_HB BIT(4) +#define SSC_CTL_PH BIT(5) +#define SSC_CTL_PO BIT(6) +#define SSC_CTL_SR BIT(7) +#define SSC_CTL_MS BIT(8) +#define SSC_CTL_EN BIT(9) +#define SSC_CTL_LPB BIT(10) +#define SSC_CTL_EN_TX_FIFO BIT(11) +#define SSC_CTL_EN_RX_FIFO BIT(12) +#define SSC_CTL_EN_CLST_RX BIT(13) + +/* SSC Interrupt Enable */ +#define SSC_IEN_TEEN BIT(2) + +#define FIFO_SIZE 8 + +struct spi_st { + /* SSC SPI Controller */ + void __iomem *base; + struct clk *clk; + struct device *dev; + + /* SSC SPI current transaction */ + const u8 *tx_ptr; + u8 *rx_ptr; + u16 bytes_per_word; + unsigned int words_remaining; + unsigned int baud; + struct completion done; +}; + +static int spi_st_clk_enable(struct spi_st *spi_st) +{ + /* + * Current platforms use one of the core clocks for SPI and I2C. + * If we attempt to disable the clock, the system will hang. + * + * TODO: Remove this when platform supports power domains. + */ + return 0; + + return clk_prepare_enable(spi_st->clk); +} + +static void spi_st_clk_disable(struct spi_st *spi_st) +{ + /* + * Current platforms use one of the core clocks for SPI and I2C. + * If we attempt to disable the clock, the system will hang. + * + * TODO: Remove this when platform supports power domains. + */ + return; + + clk_disable_unprepare(spi_st->clk); +} + +/* Load the TX FIFO */ +static void ssc_write_tx_fifo(struct spi_st *spi_st) +{ + unsigned int count, i; + uint32_t word = 0; + + if (spi_st->words_remaining > FIFO_SIZE) + count = FIFO_SIZE; + else + count = spi_st->words_remaining; + + for (i = 0; i < count; i++) { + if (spi_st->tx_ptr) { + if (spi_st->bytes_per_word == 1) { + word = *spi_st->tx_ptr++; + } else { + word = *spi_st->tx_ptr++; + word = *spi_st->tx_ptr++ | (word << 8); + } + } + writel_relaxed(word, spi_st->base + SSC_TBUF); + } +} + +/* Read the RX FIFO */ +static void ssc_read_rx_fifo(struct spi_st *spi_st) +{ + unsigned int count, i; + uint32_t word = 0; + + if (spi_st->words_remaining > FIFO_SIZE) + count = FIFO_SIZE; + else + count = spi_st->words_remaining; + + for (i = 0; i < count; i++) { + word = readl_relaxed(spi_st->base + SSC_RBUF); + + if (spi_st->rx_ptr) { + if (spi_st->bytes_per_word == 1) { + *spi_st->rx_ptr++ = (uint8_t)word; + } else { + *spi_st->rx_ptr++ = (word >> 8); + *spi_st->rx_ptr++ = word & 0xff; + } + } + } + spi_st->words_remaining -= count; +} + +static int spi_st_transfer_one(struct spi_master *master, + struct spi_device *spi, struct spi_transfer *t) +{ + struct spi_st *spi_st = spi_master_get_devdata(master); + uint32_t ctl = 0; + + /* Setup transfer */ + spi_st->tx_ptr = t->tx_buf; + spi_st->rx_ptr = t->rx_buf; + + if (spi->bits_per_word > 8) { + /* + * Anything greater than 8 bits-per-word requires 2 + * bytes-per-word in the RX/TX buffers + */ + spi_st->bytes_per_word = 2; + spi_st->words_remaining = t->len / 2; + + } else if (spi->bits_per_word == 8 && !(t->len & 0x1)) { + /* + * If transfer is even-length, and 8 bits-per-word, then + * implement as half-length 16 bits-per-word transfer + */ + spi_st->bytes_per_word = 2; + spi_st->words_remaining = t->len / 2; + + /* Set SSC_CTL to 16 bits-per-word */ + ctl = readl_relaxed(spi_st->base + SSC_CTL); + writel_relaxed((ctl | 0xf), spi_st->base + SSC_CTL); + + readl_relaxed(spi_st->base + SSC_RBUF); + + } else { + spi_st->bytes_per_word = 1; + spi_st->words_remaining = t->len; + } + + reinit_completion(&spi_st->done); + + /* Start transfer by writing to the TX FIFO */ + ssc_write_tx_fifo(spi_st); + writel_relaxed(SSC_IEN_TEEN, spi_st->base + SSC_IEN); + + /* Wait for transfer to complete */ + wait_for_completion(&spi_st->done); + + /* Restore SSC_CTL if necessary */ + if (ctl) + writel_relaxed(ctl, spi_st->base + SSC_CTL); + + spi_finalize_current_transfer(spi->master); + + return t->len; +} + +static void spi_st_cleanup(struct spi_device *spi) +{ + int cs = spi->cs_gpio; + + if (gpio_is_valid(cs)) + devm_gpio_free(&spi->dev, cs); +} + +/* the spi->mode bits understood by this driver: */ +#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_LOOP | SPI_CS_HIGH) +static int spi_st_setup(struct spi_device *spi) +{ + struct spi_st *spi_st = spi_master_get_devdata(spi->master); + u32 spi_st_clk, sscbrg, var; + u32 hz = spi->max_speed_hz; + int cs = spi->cs_gpio; + int ret; + + if (spi->mode & ~MODEBITS) { + dev_err(&spi->dev, "unsupported mode bits 0x%x\n", + spi->mode & ~MODEBITS); + return -EINVAL; + } + + if (!hz) { + dev_err(&spi->dev, "max_speed_hz unspecified\n"); + return -EINVAL; + } + + if (!gpio_is_valid(cs)) { + dev_err(&spi->dev, "%d is not a valid gpio\n", cs); + return -EINVAL; + } + + if (devm_gpio_request(&spi->dev, cs, dev_name(&spi->dev))) { + dev_err(&spi->dev, "could not request gpio:%d\n", cs); + return -EINVAL; + } + + ret = gpio_direction_output(cs, spi->mode & SPI_CS_HIGH); + if (ret) + return ret; + + spi_st_clk = clk_get_rate(spi_st->clk); + + /* Set SSC_BRF */ + sscbrg = spi_st_clk / (2 * hz); + if (sscbrg < 0x07 || sscbrg > BIT(16)) { + dev_err(&spi->dev, + "baudrate %d outside valid range %d\n", sscbrg, hz); + return -EINVAL; + } + + spi_st->baud = spi_st_clk / (2 * sscbrg); + if (sscbrg == BIT(16)) /* 16-bit counter wraps */ + sscbrg = 0x0; + + writel_relaxed(sscbrg, spi_st->base + SSC_BRG); + + dev_dbg(&spi->dev, + "setting baudrate:target= %u hz, actual= %u hz, sscbrg= %u\n", + hz, spi_st->baud, sscbrg); + + /* Set SSC_CTL and enable SSC */ + var = readl_relaxed(spi_st->base + SSC_CTL); + var |= SSC_CTL_MS; + + if (spi->mode & SPI_CPOL) + var |= SSC_CTL_PO; + else + var &= ~SSC_CTL_PO; + + if (spi->mode & SPI_CPHA) + var |= SSC_CTL_PH; + else + var &= ~SSC_CTL_PH; + + if ((spi->mode & SPI_LSB_FIRST) == 0) + var |= SSC_CTL_HB; + else + var &= ~SSC_CTL_HB; + + if (spi->mode & SPI_LOOP) + var |= SSC_CTL_LPB; + else + var &= ~SSC_CTL_LPB; + + var &= ~SSC_CTL_DATA_WIDTH_MSK; + var |= (spi->bits_per_word - 1); + + var |= SSC_CTL_EN_TX_FIFO | SSC_CTL_EN_RX_FIFO; + var |= SSC_CTL_EN; + + writel_relaxed(var, spi_st->base + SSC_CTL); + + /* Clear the status register */ + readl_relaxed(spi_st->base + SSC_RBUF); + + return 0; +} + +/* Interrupt fired when TX shift register becomes empty */ +static irqreturn_t spi_st_irq(int irq, void *dev_id) +{ + struct spi_st *spi_st = (struct spi_st *)dev_id; + + /* Read RX FIFO */ + ssc_read_rx_fifo(spi_st); + + /* Fill TX FIFO */ + if (spi_st->words_remaining) { + ssc_write_tx_fifo(spi_st); + } else { + /* TX/RX complete */ + writel_relaxed(0x0, spi_st->base + SSC_IEN); + /* + * read SSC_IEN to ensure that this bit is set + * before re-enabling interrupt + */ + readl(spi_st->base + SSC_IEN); + complete(&spi_st->done); + } + + return IRQ_HANDLED; +} + +static int spi_st_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct spi_master *master; + struct resource *res; + struct spi_st *spi_st; + int irq, ret = 0; + u32 var; + + master = spi_alloc_master(&pdev->dev, sizeof(*spi_st)); + if (!master) + return -ENOMEM; + + master->dev.of_node = np; + master->mode_bits = MODEBITS; + master->setup = spi_st_setup; + master->cleanup = spi_st_cleanup; + master->transfer_one = spi_st_transfer_one; + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); + master->auto_runtime_pm = true; + master->bus_num = pdev->id; + spi_st = spi_master_get_devdata(master); + + spi_st->clk = devm_clk_get(&pdev->dev, "ssc"); + if (IS_ERR(spi_st->clk)) { + dev_err(&pdev->dev, "Unable to request clock\n"); + return PTR_ERR(spi_st->clk); + } + + ret = spi_st_clk_enable(spi_st); + if (ret) + return ret; + + init_completion(&spi_st->done); + + /* Get resources */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + spi_st->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(spi_st->base)) { + ret = PTR_ERR(spi_st->base); + goto clk_disable; + } + + /* Disable I2C and Reset SSC */ + writel_relaxed(0x0, spi_st->base + SSC_I2C); + var = readw_relaxed(spi_st->base + SSC_CTL); + var |= SSC_CTL_SR; + writel_relaxed(var, spi_st->base + SSC_CTL); + + udelay(1); + var = readl_relaxed(spi_st->base + SSC_CTL); + var &= ~SSC_CTL_SR; + writel_relaxed(var, spi_st->base + SSC_CTL); + + /* Set SSC into slave mode before reconfiguring PIO pins */ + var = readl_relaxed(spi_st->base + SSC_CTL); + var &= ~SSC_CTL_MS; + writel_relaxed(var, spi_st->base + SSC_CTL); + + irq = irq_of_parse_and_map(np, 0); + if (!irq) { + dev_err(&pdev->dev, "IRQ missing or invalid\n"); + ret = -EINVAL; + goto clk_disable; + } + + ret = devm_request_irq(&pdev->dev, irq, spi_st_irq, 0, + pdev->name, spi_st); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq %d\n", irq); + goto clk_disable; + } + + /* by default the device is on */ + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + platform_set_drvdata(pdev, master); + + ret = devm_spi_register_master(&pdev->dev, master); + if (ret) { + dev_err(&pdev->dev, "Failed to register master\n"); + goto clk_disable; + } + + return 0; + +clk_disable: + spi_st_clk_disable(spi_st); + + return ret; +} + +static int spi_st_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct spi_st *spi_st = spi_master_get_devdata(master); + + spi_st_clk_disable(spi_st); + + pinctrl_pm_select_sleep_state(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM +static int spi_st_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct spi_st *spi_st = spi_master_get_devdata(master); + + writel_relaxed(0, spi_st->base + SSC_IEN); + pinctrl_pm_select_sleep_state(dev); + + spi_st_clk_disable(spi_st); + + return 0; +} + +static int spi_st_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct spi_st *spi_st = spi_master_get_devdata(master); + int ret; + + ret = spi_st_clk_enable(spi_st); + pinctrl_pm_select_default_state(dev); + + return ret; +} +#endif + +#ifdef CONFIG_PM_SLEEP +static int spi_st_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + int ret; + + ret = spi_master_suspend(master); + if (ret) + return ret; + + return pm_runtime_force_suspend(dev); +} + +static int spi_st_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + int ret; + + ret = spi_master_resume(master); + if (ret) + return ret; + + return pm_runtime_force_resume(dev); +} +#endif + +static const struct dev_pm_ops spi_st_pm = { + SET_SYSTEM_SLEEP_PM_OPS(spi_st_suspend, spi_st_resume) + SET_RUNTIME_PM_OPS(spi_st_runtime_suspend, spi_st_runtime_resume, NULL) +}; + +static struct of_device_id stm_spi_match[] = { + { .compatible = "st,comms-ssc4-spi", }, + {}, +}; +MODULE_DEVICE_TABLE(of, stm_spi_match); + +static struct platform_driver spi_st_driver = { + .driver = { + .name = "spi-st", + .pm = &spi_st_pm, + .of_match_table = of_match_ptr(stm_spi_match), + }, + .probe = spi_st_probe, + .remove = spi_st_remove, +}; +module_platform_driver(spi_st_driver); + +MODULE_AUTHOR("Patrice Chotard "); +MODULE_DESCRIPTION("STM SSC SPI driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From f5bac70f4fec1fdc7798f025edd5666c17bcf51f Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 9 Dec 2014 20:21:31 +0000 Subject: spi: st: Provide Device Tree binding documentation This patch adds DT documentation for the SPI portion of ST's SSC device. Signed-off-by: Lee Jones Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/spi-st-ssc.txt b/Documentation/devicetree/bindings/spi/spi-st-ssc.txt new file mode 100644 index 0000000..fe54959 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-st-ssc.txt @@ -0,0 +1,40 @@ +STMicroelectronics SSC (SPI) Controller +--------------------------------------- + +Required properties: +- compatible : "st,comms-ssc4-spi" +- reg : Offset and length of the device's register set +- interrupts : The interrupt specifier +- clock-names : Must contain "ssc" +- clocks : Must contain an entry for each name in clock-names + See ../clk/* +- pinctrl-names : Uses "default", can use "sleep" if provided + See ../pinctrl/pinctrl-binding.txt + +Optional properties: +- cs-gpios : List of GPIO chip selects + See ../spi/spi-bus.txt + +Child nodes represent devices on the SPI bus + See ../spi/spi-bus.txt + +Example: + spi@9840000 { + compatible = "st,comms-ssc4-spi"; + reg = <0x9840000 0x110>; + interrupts = ; + clocks = <&clk_s_c0_flexgen CLK_EXT2F_A9>; + clock-names = "ssc"; + pinctrl-0 = <&pinctrl_spi0_default>; + pinctrl-names = "default"; + cs-gpios = <&pio17 5 0>; + #address-cells = <1>; + #size-cells = <0>; + + st95hf@0{ + compatible = "st,st95hf"; + reg = <0>; + spi-max-frequency = <1000000>; + interrupts = <2 IRQ_TYPE_EDGE_FALLING>; + }; + }; -- cgit v0.10.2 From 3d8c0d749da326310a525d19e1a7aef83ae872b3 Mon Sep 17 00:00:00 2001 From: Laurentiu Palcu Date: Mon, 8 Dec 2014 15:52:29 +0200 Subject: spi: add support for DLN-2 USB-SPI adapter This adds support for Diolan DLN2 USB-SPI adapter. Information about the USB protocol interface can be found in the Programmer's Reference Manual [1], see section 5.4.6 for the SPI master module commands and responses. [1] https://www.diolan.com/downloads/dln-api-manual.pdf Signed-off-by: Laurentiu Palcu Signed-off-by: Mark Brown diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 9982998..f60f37c 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -185,6 +185,16 @@ config SPI_DAVINCI help SPI master controller for DaVinci/DA8x/OMAP-L/AM1x SPI modules. +config SPI_DLN2 + tristate "Diolan DLN-2 USB SPI adapter" + depends on MFD_DLN2 + help + If you say yes to this option, support will be included for Diolan + DLN2, a USB to SPI interface. + + This driver can also be built as a module. If so, the module + will be called spi-dln2. + config SPI_EFM32 tristate "EFM32 SPI controller" depends on OF && ARM && (ARCH_EFM32 || COMPILE_TEST) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 6b9d2ac..df5e23c 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_SPI_CADENCE) += spi-cadence.o obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o +obj-$(CONFIG_SPI_DLN2) += spi-dln2.o obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o diff --git a/drivers/spi/spi-dln2.c b/drivers/spi/spi-dln2.c new file mode 100644 index 0000000..7b35454 --- /dev/null +++ b/drivers/spi/spi-dln2.c @@ -0,0 +1,890 @@ +/* + * Driver for the Diolan DLN-2 USB-SPI adapter + * + * Copyright (c) 2014 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DLN2_SPI_MODULE_ID 0x02 +#define DLN2_SPI_CMD(cmd) DLN2_CMD(cmd, DLN2_SPI_MODULE_ID) + +/* SPI commands */ +#define DLN2_SPI_GET_PORT_COUNT DLN2_SPI_CMD(0x00) +#define DLN2_SPI_ENABLE DLN2_SPI_CMD(0x11) +#define DLN2_SPI_DISABLE DLN2_SPI_CMD(0x12) +#define DLN2_SPI_IS_ENABLED DLN2_SPI_CMD(0x13) +#define DLN2_SPI_SET_MODE DLN2_SPI_CMD(0x14) +#define DLN2_SPI_GET_MODE DLN2_SPI_CMD(0x15) +#define DLN2_SPI_SET_FRAME_SIZE DLN2_SPI_CMD(0x16) +#define DLN2_SPI_GET_FRAME_SIZE DLN2_SPI_CMD(0x17) +#define DLN2_SPI_SET_FREQUENCY DLN2_SPI_CMD(0x18) +#define DLN2_SPI_GET_FREQUENCY DLN2_SPI_CMD(0x19) +#define DLN2_SPI_READ_WRITE DLN2_SPI_CMD(0x1A) +#define DLN2_SPI_READ DLN2_SPI_CMD(0x1B) +#define DLN2_SPI_WRITE DLN2_SPI_CMD(0x1C) +#define DLN2_SPI_SET_DELAY_BETWEEN_SS DLN2_SPI_CMD(0x20) +#define DLN2_SPI_GET_DELAY_BETWEEN_SS DLN2_SPI_CMD(0x21) +#define DLN2_SPI_SET_DELAY_AFTER_SS DLN2_SPI_CMD(0x22) +#define DLN2_SPI_GET_DELAY_AFTER_SS DLN2_SPI_CMD(0x23) +#define DLN2_SPI_SET_DELAY_BETWEEN_FRAMES DLN2_SPI_CMD(0x24) +#define DLN2_SPI_GET_DELAY_BETWEEN_FRAMES DLN2_SPI_CMD(0x25) +#define DLN2_SPI_SET_SS DLN2_SPI_CMD(0x26) +#define DLN2_SPI_GET_SS DLN2_SPI_CMD(0x27) +#define DLN2_SPI_RELEASE_SS DLN2_SPI_CMD(0x28) +#define DLN2_SPI_SS_VARIABLE_ENABLE DLN2_SPI_CMD(0x2B) +#define DLN2_SPI_SS_VARIABLE_DISABLE DLN2_SPI_CMD(0x2C) +#define DLN2_SPI_SS_VARIABLE_IS_ENABLED DLN2_SPI_CMD(0x2D) +#define DLN2_SPI_SS_AAT_ENABLE DLN2_SPI_CMD(0x2E) +#define DLN2_SPI_SS_AAT_DISABLE DLN2_SPI_CMD(0x2F) +#define DLN2_SPI_SS_AAT_IS_ENABLED DLN2_SPI_CMD(0x30) +#define DLN2_SPI_SS_BETWEEN_FRAMES_ENABLE DLN2_SPI_CMD(0x31) +#define DLN2_SPI_SS_BETWEEN_FRAMES_DISABLE DLN2_SPI_CMD(0x32) +#define DLN2_SPI_SS_BETWEEN_FRAMES_IS_ENABLED DLN2_SPI_CMD(0x33) +#define DLN2_SPI_SET_CPHA DLN2_SPI_CMD(0x34) +#define DLN2_SPI_GET_CPHA DLN2_SPI_CMD(0x35) +#define DLN2_SPI_SET_CPOL DLN2_SPI_CMD(0x36) +#define DLN2_SPI_GET_CPOL DLN2_SPI_CMD(0x37) +#define DLN2_SPI_SS_MULTI_ENABLE DLN2_SPI_CMD(0x38) +#define DLN2_SPI_SS_MULTI_DISABLE DLN2_SPI_CMD(0x39) +#define DLN2_SPI_SS_MULTI_IS_ENABLED DLN2_SPI_CMD(0x3A) +#define DLN2_SPI_GET_SUPPORTED_MODES DLN2_SPI_CMD(0x40) +#define DLN2_SPI_GET_SUPPORTED_CPHA_VALUES DLN2_SPI_CMD(0x41) +#define DLN2_SPI_GET_SUPPORTED_CPOL_VALUES DLN2_SPI_CMD(0x42) +#define DLN2_SPI_GET_SUPPORTED_FRAME_SIZES DLN2_SPI_CMD(0x43) +#define DLN2_SPI_GET_SS_COUNT DLN2_SPI_CMD(0x44) +#define DLN2_SPI_GET_MIN_FREQUENCY DLN2_SPI_CMD(0x45) +#define DLN2_SPI_GET_MAX_FREQUENCY DLN2_SPI_CMD(0x46) +#define DLN2_SPI_GET_MIN_DELAY_BETWEEN_SS DLN2_SPI_CMD(0x47) +#define DLN2_SPI_GET_MAX_DELAY_BETWEEN_SS DLN2_SPI_CMD(0x48) +#define DLN2_SPI_GET_MIN_DELAY_AFTER_SS DLN2_SPI_CMD(0x49) +#define DLN2_SPI_GET_MAX_DELAY_AFTER_SS DLN2_SPI_CMD(0x4A) +#define DLN2_SPI_GET_MIN_DELAY_BETWEEN_FRAMES DLN2_SPI_CMD(0x4B) +#define DLN2_SPI_GET_MAX_DELAY_BETWEEN_FRAMES DLN2_SPI_CMD(0x4C) + +#define DLN2_SPI_MAX_XFER_SIZE 256 +#define DLN2_SPI_BUF_SIZE (DLN2_SPI_MAX_XFER_SIZE + 16) +#define DLN2_SPI_ATTR_LEAVE_SS_LOW BIT(0) +#define DLN2_TRANSFERS_WAIT_COMPLETE 1 +#define DLN2_TRANSFERS_CANCEL 0 +#define DLN2_RPM_AUTOSUSPEND_TIMEOUT 2000 + +struct dln2_spi { + struct platform_device *pdev; + struct spi_master *master; + u8 port; + + /* + * This buffer will be used mainly for read/write operations. Since + * they're quite large, we cannot use the stack. Protection is not + * needed because all SPI communication is serialized by the SPI core. + */ + void *buf; + + u8 bpw; + u32 speed; + u16 mode; + u8 cs; +}; + +/* + * Enable/Disable SPI module. The disable command will wait for transfers to + * complete first. + */ +static int dln2_spi_enable(struct dln2_spi *dln2, bool enable) +{ + int ret; + u16 cmd; + struct { + u8 port; + u8 wait_for_completion; + } tx; + unsigned len = sizeof(tx); + + tx.port = dln2->port; + + if (enable) { + cmd = DLN2_SPI_ENABLE; + len -= sizeof(tx.wait_for_completion); + } else { + tx.wait_for_completion = DLN2_TRANSFERS_WAIT_COMPLETE; + cmd = DLN2_SPI_DISABLE; + } + + ret = dln2_transfer_tx(dln2->pdev, cmd, &tx, len); + if (ret < 0) + return ret; + + return 0; +} + +/* + * Select/unselect multiple CS lines. The selected lines will be automatically + * toggled LOW/HIGH by the board firmware during transfers, provided they're + * enabled first. + * + * Ex: cs_mask = 0x03 -> CS0 & CS1 will be selected and the next WR/RD operation + * will toggle the lines LOW/HIGH automatically. + */ +static int dln2_spi_cs_set(struct dln2_spi *dln2, u8 cs_mask) +{ + struct { + u8 port; + u8 cs; + } tx; + + tx.port = dln2->port; + + /* + * According to Diolan docs, "a slave device can be selected by changing + * the corresponding bit value to 0". The rest must be set to 1. Hence + * the bitwise NOT in front. + */ + tx.cs = ~cs_mask; + + return dln2_transfer_tx(dln2->pdev, DLN2_SPI_SET_SS, &tx, sizeof(tx)); +} + +/* + * Select one CS line. The other lines will be un-selected. + */ +static int dln2_spi_cs_set_one(struct dln2_spi *dln2, u8 cs) +{ + return dln2_spi_cs_set(dln2, BIT(cs)); +} + +/* + * Enable/disable CS lines for usage. The module has to be disabled first. + */ +static int dln2_spi_cs_enable(struct dln2_spi *dln2, u8 cs_mask, bool enable) +{ + struct { + u8 port; + u8 cs; + } tx; + u16 cmd; + + tx.port = dln2->port; + tx.cs = cs_mask; + cmd = enable ? DLN2_SPI_SS_MULTI_ENABLE : DLN2_SPI_SS_MULTI_DISABLE; + + return dln2_transfer_tx(dln2->pdev, cmd, &tx, sizeof(tx)); +} + +static int dln2_spi_cs_enable_all(struct dln2_spi *dln2, bool enable) +{ + u8 cs_mask = GENMASK(dln2->master->num_chipselect - 1, 0); + + return dln2_spi_cs_enable(dln2, cs_mask, enable); +} + +static int dln2_spi_get_cs_num(struct dln2_spi *dln2, u16 *cs_num) +{ + int ret; + struct { + u8 port; + } tx; + struct { + __le16 cs_count; + } rx; + unsigned rx_len = sizeof(rx); + + tx.port = dln2->port; + ret = dln2_transfer(dln2->pdev, DLN2_SPI_GET_SS_COUNT, &tx, sizeof(tx), + &rx, &rx_len); + if (ret < 0) + return ret; + if (rx_len < sizeof(rx)) + return -EPROTO; + + *cs_num = le16_to_cpu(rx.cs_count); + + dev_dbg(&dln2->pdev->dev, "cs_num = %d\n", *cs_num); + + return 0; +} + +static int dln2_spi_get_speed(struct dln2_spi *dln2, u16 cmd, u32 *freq) +{ + int ret; + struct { + u8 port; + } tx; + struct { + __le32 speed; + } rx; + unsigned rx_len = sizeof(rx); + + tx.port = dln2->port; + + ret = dln2_transfer(dln2->pdev, cmd, &tx, sizeof(tx), &rx, &rx_len); + if (ret < 0) + return ret; + if (rx_len < sizeof(rx)) + return -EPROTO; + + *freq = le32_to_cpu(rx.speed); + + return 0; +} + +/* + * Get bus min/max frequencies. + */ +static int dln2_spi_get_speed_range(struct dln2_spi *dln2, u32 *fmin, u32 *fmax) +{ + int ret; + + ret = dln2_spi_get_speed(dln2, DLN2_SPI_GET_MIN_FREQUENCY, fmin); + if (ret < 0) + return ret; + + ret = dln2_spi_get_speed(dln2, DLN2_SPI_GET_MAX_FREQUENCY, fmax); + if (ret < 0) + return ret; + + dev_dbg(&dln2->pdev->dev, "freq_min = %d, freq_max = %d\n", + *fmin, *fmax); + + return 0; +} + +/* + * Set the bus speed. The module will automatically round down to the closest + * available frequency and returns it. The module has to be disabled first. + */ +static int dln2_spi_set_speed(struct dln2_spi *dln2, u32 speed) +{ + int ret; + struct { + u8 port; + __le32 speed; + } __packed tx; + struct { + __le32 speed; + } rx; + int rx_len = sizeof(rx); + + tx.port = dln2->port; + tx.speed = cpu_to_le32(speed); + + ret = dln2_transfer(dln2->pdev, DLN2_SPI_SET_FREQUENCY, &tx, sizeof(tx), + &rx, &rx_len); + if (ret < 0) + return ret; + if (rx_len < sizeof(rx)) + return -EPROTO; + + return 0; +} + +/* + * Change CPOL & CPHA. The module has to be disabled first. + */ +static int dln2_spi_set_mode(struct dln2_spi *dln2, u8 mode) +{ + struct { + u8 port; + u8 mode; + } tx; + + tx.port = dln2->port; + tx.mode = mode; + + return dln2_transfer_tx(dln2->pdev, DLN2_SPI_SET_MODE, &tx, sizeof(tx)); +} + +/* + * Change frame size. The module has to be disabled first. + */ +static int dln2_spi_set_bpw(struct dln2_spi *dln2, u8 bpw) +{ + struct { + u8 port; + u8 bpw; + } tx; + + tx.port = dln2->port; + tx.bpw = bpw; + + return dln2_transfer_tx(dln2->pdev, DLN2_SPI_SET_FRAME_SIZE, + &tx, sizeof(tx)); +} + +static int dln2_spi_get_supported_frame_sizes(struct dln2_spi *dln2, + u32 *bpw_mask) +{ + int ret; + struct { + u8 port; + } tx; + struct { + u8 count; + u8 frame_sizes[36]; + } *rx = dln2->buf; + unsigned rx_len = sizeof(*rx); + int i; + + tx.port = dln2->port; + + ret = dln2_transfer(dln2->pdev, DLN2_SPI_GET_SUPPORTED_FRAME_SIZES, + &tx, sizeof(tx), rx, &rx_len); + if (ret < 0) + return ret; + if (rx_len < sizeof(*rx)) + return -EPROTO; + if (rx->count > ARRAY_SIZE(rx->frame_sizes)) + return -EPROTO; + + *bpw_mask = 0; + for (i = 0; i < rx->count; i++) + *bpw_mask |= BIT(rx->frame_sizes[i] - 1); + + dev_dbg(&dln2->pdev->dev, "bpw_mask = 0x%X\n", *bpw_mask); + + return 0; +} + +/* + * Copy the data to DLN2 buffer and change the byte order to LE, requested by + * DLN2 module. SPI core makes sure that the data length is a multiple of word + * size. + */ +static int dln2_spi_copy_to_buf(u8 *dln2_buf, const u8 *src, u16 len, u8 bpw) +{ +#ifdef __LITTLE_ENDIAN + memcpy(dln2_buf, src, len); +#else + if (bpw <= 8) { + memcpy(dln2_buf, src, len); + } else if (bpw <= 16) { + __le16 *d = (__le16 *)dln2_buf; + u16 *s = (u16 *)src; + + len = len / 2; + while (len--) + *d++ = cpu_to_le16p(s++); + } else { + __le32 *d = (__le32 *)dln2_buf; + u32 *s = (u32 *)src; + + len = len / 4; + while (len--) + *d++ = cpu_to_le32p(s++); + } +#endif + + return 0; +} + +/* + * Copy the data from DLN2 buffer and convert to CPU byte order since the DLN2 + * buffer is LE ordered. SPI core makes sure that the data length is a multiple + * of word size. The RX dln2_buf is 2 byte aligned so, for BE, we have to make + * sure we avoid unaligned accesses for 32 bit case. + */ +static int dln2_spi_copy_from_buf(u8 *dest, const u8 *dln2_buf, u16 len, u8 bpw) +{ +#ifdef __LITTLE_ENDIAN + memcpy(dest, dln2_buf, len); +#else + if (bpw <= 8) { + memcpy(dest, dln2_buf, len); + } else if (bpw <= 16) { + u16 *d = (u16 *)dest; + __le16 *s = (__le16 *)dln2_buf; + + len = len / 2; + while (len--) + *d++ = le16_to_cpup(s++); + } else { + u32 *d = (u32 *)dest; + __le32 *s = (__le32 *)dln2_buf; + + len = len / 4; + while (len--) + *d++ = get_unaligned_le32(s++); + } +#endif + + return 0; +} + +/* + * Perform one write operation. + */ +static int dln2_spi_write_one(struct dln2_spi *dln2, const u8 *data, + u16 data_len, u8 attr) +{ + struct { + u8 port; + __le16 size; + u8 attr; + u8 buf[DLN2_SPI_MAX_XFER_SIZE]; + } __packed *tx = dln2->buf; + unsigned tx_len; + + BUILD_BUG_ON(sizeof(*tx) > DLN2_SPI_BUF_SIZE); + + if (data_len > DLN2_SPI_MAX_XFER_SIZE) + return -EINVAL; + + tx->port = dln2->port; + tx->size = cpu_to_le16(data_len); + tx->attr = attr; + + dln2_spi_copy_to_buf(tx->buf, data, data_len, dln2->bpw); + + tx_len = sizeof(*tx) + data_len - DLN2_SPI_MAX_XFER_SIZE; + return dln2_transfer_tx(dln2->pdev, DLN2_SPI_WRITE, tx, tx_len); +} + +/* + * Perform one read operation. + */ +static int dln2_spi_read_one(struct dln2_spi *dln2, u8 *data, + u16 data_len, u8 attr) +{ + int ret; + struct { + u8 port; + __le16 size; + u8 attr; + } __packed tx; + struct { + __le16 size; + u8 buf[DLN2_SPI_MAX_XFER_SIZE]; + } __packed *rx = dln2->buf; + unsigned rx_len = sizeof(*rx); + + BUILD_BUG_ON(sizeof(*rx) > DLN2_SPI_BUF_SIZE); + + if (data_len > DLN2_SPI_MAX_XFER_SIZE) + return -EINVAL; + + tx.port = dln2->port; + tx.size = cpu_to_le16(data_len); + tx.attr = attr; + + ret = dln2_transfer(dln2->pdev, DLN2_SPI_READ, &tx, sizeof(tx), + rx, &rx_len); + if (ret < 0) + return ret; + if (rx_len < sizeof(rx->size) + data_len) + return -EPROTO; + if (le16_to_cpu(rx->size) != data_len) + return -EPROTO; + + dln2_spi_copy_from_buf(data, rx->buf, data_len, dln2->bpw); + + return 0; +} + +/* + * Perform one write & read operation. + */ +static int dln2_spi_read_write_one(struct dln2_spi *dln2, const u8 *tx_data, + u8 *rx_data, u16 data_len, u8 attr) +{ + int ret; + struct { + u8 port; + __le16 size; + u8 attr; + u8 buf[DLN2_SPI_MAX_XFER_SIZE]; + } __packed *tx; + struct { + __le16 size; + u8 buf[DLN2_SPI_MAX_XFER_SIZE]; + } __packed *rx; + unsigned tx_len, rx_len; + + BUILD_BUG_ON(sizeof(*tx) > DLN2_SPI_BUF_SIZE || + sizeof(*rx) > DLN2_SPI_BUF_SIZE); + + if (data_len > DLN2_SPI_MAX_XFER_SIZE) + return -EINVAL; + + /* + * Since this is a pseudo full-duplex communication, we're perfectly + * safe to use the same buffer for both tx and rx. When DLN2 sends the + * response back, with the rx data, we don't need the tx buffer anymore. + */ + tx = dln2->buf; + rx = dln2->buf; + + tx->port = dln2->port; + tx->size = cpu_to_le16(data_len); + tx->attr = attr; + + dln2_spi_copy_to_buf(tx->buf, tx_data, data_len, dln2->bpw); + + tx_len = sizeof(*tx) + data_len - DLN2_SPI_MAX_XFER_SIZE; + rx_len = sizeof(*rx); + + ret = dln2_transfer(dln2->pdev, DLN2_SPI_READ_WRITE, tx, tx_len, + rx, &rx_len); + if (ret < 0) + return ret; + if (rx_len < sizeof(rx->size) + data_len) + return -EPROTO; + if (le16_to_cpu(rx->size) != data_len) + return -EPROTO; + + dln2_spi_copy_from_buf(rx_data, rx->buf, data_len, dln2->bpw); + + return 0; +} + +/* + * Read/Write wrapper. It will automatically split an operation into multiple + * single ones due to device buffer constraints. + */ +static int dln2_spi_rdwr(struct dln2_spi *dln2, const u8 *tx_data, + u8 *rx_data, u16 data_len, u8 attr) { + int ret; + u16 len; + u8 temp_attr; + u16 remaining = data_len; + u16 offset; + + do { + if (remaining > DLN2_SPI_MAX_XFER_SIZE) { + len = DLN2_SPI_MAX_XFER_SIZE; + temp_attr = DLN2_SPI_ATTR_LEAVE_SS_LOW; + } else { + len = remaining; + temp_attr = attr; + } + + offset = data_len - remaining; + + if (tx_data && rx_data) { + ret = dln2_spi_read_write_one(dln2, + tx_data + offset, + rx_data + offset, + len, temp_attr); + } else if (tx_data) { + ret = dln2_spi_write_one(dln2, + tx_data + offset, + len, temp_attr); + } else if (rx_data) { + ret = dln2_spi_read_one(dln2, + rx_data + offset, + len, temp_attr); + } else { + return -EINVAL; + } + + if (ret < 0) + return ret; + + remaining -= len; + } while (remaining); + + return 0; +} + +static int dln2_spi_prepare_message(struct spi_master *master, + struct spi_message *message) +{ + int ret; + struct dln2_spi *dln2 = spi_master_get_devdata(master); + struct spi_device *spi = message->spi; + + if (dln2->cs != spi->chip_select) { + ret = dln2_spi_cs_set_one(dln2, spi->chip_select); + if (ret < 0) + return ret; + + dln2->cs = spi->chip_select; + } + + return 0; +} + +static int dln2_spi_transfer_setup(struct dln2_spi *dln2, u32 speed, + u8 bpw, u8 mode) +{ + int ret; + bool bus_setup_change; + + bus_setup_change = dln2->speed != speed || dln2->mode != mode || + dln2->bpw != bpw; + + if (!bus_setup_change) + return 0; + + ret = dln2_spi_enable(dln2, false); + if (ret < 0) + return ret; + + if (dln2->speed != speed) { + ret = dln2_spi_set_speed(dln2, speed); + if (ret < 0) + return ret; + + dln2->speed = speed; + } + + if (dln2->mode != mode) { + ret = dln2_spi_set_mode(dln2, mode & 0x3); + if (ret < 0) + return ret; + + dln2->mode = mode; + } + + if (dln2->bpw != bpw) { + ret = dln2_spi_set_bpw(dln2, bpw); + if (ret < 0) + return ret; + + dln2->bpw = bpw; + } + + ret = dln2_spi_enable(dln2, true); + if (ret < 0) + return ret; + + return 0; +} + +static int dln2_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct dln2_spi *dln2 = spi_master_get_devdata(master); + int status; + u8 attr = 0; + + status = dln2_spi_transfer_setup(dln2, xfer->speed_hz, + xfer->bits_per_word, + spi->mode); + if (status < 0) { + dev_err(&dln2->pdev->dev, "Cannot setup transfer\n"); + return status; + } + + if (!xfer->cs_change && !spi_transfer_is_last(master, xfer)) + attr = DLN2_SPI_ATTR_LEAVE_SS_LOW; + + status = dln2_spi_rdwr(dln2, xfer->tx_buf, xfer->rx_buf, + xfer->len, attr); + if (status < 0) + dev_err(&dln2->pdev->dev, "write/read failed!\n"); + + return status; +} + +static int dln2_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct dln2_spi *dln2; + struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev); + int ret; + + master = spi_alloc_master(&pdev->dev, sizeof(*dln2)); + if (!master) + return -ENOMEM; + + platform_set_drvdata(pdev, master); + + dln2 = spi_master_get_devdata(master); + + dln2->buf = devm_kmalloc(&pdev->dev, DLN2_SPI_BUF_SIZE, GFP_KERNEL); + if (!dln2->buf) { + ret = -ENOMEM; + goto exit_free_master; + } + + dln2->master = master; + dln2->pdev = pdev; + dln2->port = pdata->port; + /* cs/mode can never be 0xff, so the first transfer will set them */ + dln2->cs = 0xff; + dln2->mode = 0xff; + + /* disable SPI module before continuing with the setup */ + ret = dln2_spi_enable(dln2, false); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to disable SPI module\n"); + goto exit_free_master; + } + + ret = dln2_spi_get_cs_num(dln2, &master->num_chipselect); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get number of CS pins\n"); + goto exit_free_master; + } + + ret = dln2_spi_get_speed_range(dln2, + &master->min_speed_hz, + &master->max_speed_hz); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read bus min/max freqs\n"); + goto exit_free_master; + } + + ret = dln2_spi_get_supported_frame_sizes(dln2, + &master->bits_per_word_mask); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read supported frame sizes\n"); + goto exit_free_master; + } + + ret = dln2_spi_cs_enable_all(dln2, true); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to enable CS pins\n"); + goto exit_free_master; + } + + master->bus_num = -1; + master->mode_bits = SPI_CPOL | SPI_CPHA; + master->prepare_message = dln2_spi_prepare_message; + master->transfer_one = dln2_spi_transfer_one; + master->auto_runtime_pm = true; + + /* enable SPI module, we're good to go */ + ret = dln2_spi_enable(dln2, true); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to enable SPI module\n"); + goto exit_free_master; + } + + pm_runtime_set_autosuspend_delay(&pdev->dev, + DLN2_RPM_AUTOSUSPEND_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + ret = devm_spi_register_master(&pdev->dev, master); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register master\n"); + goto exit_register; + } + + return ret; + +exit_register: + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + + if (dln2_spi_enable(dln2, false) < 0) + dev_err(&pdev->dev, "Failed to disable SPI module\n"); +exit_free_master: + spi_master_put(master); + + return ret; +} + +static int dln2_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); + struct dln2_spi *dln2 = spi_master_get_devdata(master); + + pm_runtime_disable(&pdev->dev); + + if (dln2_spi_enable(dln2, false) < 0) + dev_err(&pdev->dev, "Failed to disable SPI module\n"); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int dln2_spi_suspend(struct device *dev) +{ + int ret; + struct spi_master *master = dev_get_drvdata(dev); + struct dln2_spi *dln2 = spi_master_get_devdata(master); + + ret = spi_master_suspend(master); + if (ret < 0) + return ret; + + if (!pm_runtime_suspended(dev)) { + ret = dln2_spi_enable(dln2, false); + if (ret < 0) + return ret; + } + + /* + * USB power may be cut off during sleep. Resetting the following + * parameters will force the board to be set up before first transfer. + */ + dln2->cs = 0xff; + dln2->speed = 0; + dln2->bpw = 0; + dln2->mode = 0xff; + + return 0; +} + +static int dln2_spi_resume(struct device *dev) +{ + int ret; + struct spi_master *master = dev_get_drvdata(dev); + struct dln2_spi *dln2 = spi_master_get_devdata(master); + + if (!pm_runtime_suspended(dev)) { + ret = dln2_spi_cs_enable_all(dln2, true); + if (ret < 0) + return ret; + + ret = dln2_spi_enable(dln2, true); + if (ret < 0) + return ret; + } + + return spi_master_resume(master); +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM_RUNTIME +static int dln2_spi_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct dln2_spi *dln2 = spi_master_get_devdata(master); + + return dln2_spi_enable(dln2, false); +} + +static int dln2_spi_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct dln2_spi *dln2 = spi_master_get_devdata(master); + + return dln2_spi_enable(dln2, true); +} +#endif /* CONFIG_PM_RUNTIME */ + +static const struct dev_pm_ops dln2_spi_pm = { + SET_SYSTEM_SLEEP_PM_OPS(dln2_spi_suspend, dln2_spi_resume) + SET_RUNTIME_PM_OPS(dln2_spi_runtime_suspend, + dln2_spi_runtime_resume, NULL) +}; + +static struct platform_driver spi_dln2_driver = { + .driver = { + .name = "dln2-spi", + .pm = &dln2_spi_pm, + }, + .probe = dln2_spi_probe, + .remove = dln2_spi_remove, +}; +module_platform_driver(spi_dln2_driver); + +MODULE_DESCRIPTION("Driver for the Diolan DLN2 SPI master interface"); +MODULE_AUTHOR("Laurentiu Palcu "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:dln2-spi"); -- cgit v0.10.2 From f2ecf2ef59b57bd495c40d8a3e9d03e80f66afa4 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 15 Dec 2014 15:42:33 +0800 Subject: ASoC: rt5670: Add runtime PM support This patch adds runtime PM support on rt5670 codec. Signed-off-by: Lin Mengdong Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index cd47ef1..78d85de 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -2734,18 +2735,26 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, } + pm_runtime_enable(&i2c->dev); + pm_request_idle(&i2c->dev); + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5670, rt5670_dai, ARRAY_SIZE(rt5670_dai)); if (ret < 0) goto err; + pm_runtime_put(&i2c->dev); + return 0; err: + pm_runtime_disable(&i2c->dev); + return ret; } static int rt5670_i2c_remove(struct i2c_client *i2c) { + pm_runtime_disable(&i2c->dev); snd_soc_unregister_codec(&i2c->dev); return 0; -- cgit v0.10.2 From 026e73683ad5665a45b01ca1a221fa87e0e8e6fb Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 15 Dec 2014 15:42:34 +0800 Subject: ASoC: rt5670: Keep sysclk on if JD func is used System clock is necessary for rt5670 JD function. We assume system clock source will be set in machine driver. So there are two things left we should do in codec driver. 1. Set sysclk to codec internal clock in probe since machine driver may not do that before JD function is registered. 2. Power up PLL once sysclk source is switched to PLL. Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 78d85de..0a027bc 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -2190,6 +2190,13 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai, if (freq == rt5670->sysclk && clk_id == rt5670->sysclk_src) return 0; + if (rt5670->pdata.jd_mode) { + if (clk_id == RT5670_SCLK_S_PLL1) + snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1"); + else + snd_soc_dapm_disable_pin(&codec->dapm, "PLL1"); + snd_soc_dapm_sync(&codec->dapm); + } switch (clk_id) { case RT5670_SCLK_S_MCLK: reg_val |= RT5670_SCLK_SRC_MCLK; @@ -2628,6 +2635,10 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, } if (rt5670->pdata.jd_mode) { + regmap_update_bits(rt5670->regmap, RT5670_GLB_CLK, + RT5670_SCLK_SRC_MASK, RT5670_SCLK_SRC_RCCLK); + rt5670->sysclk = 0; + rt5670->sysclk_src = RT5670_SCLK_S_RCCLK; regmap_update_bits(rt5670->regmap, RT5670_PWR_ANLG1, RT5670_PWR_MB, RT5670_PWR_MB); regmap_update_bits(rt5670->regmap, RT5670_PWR_ANLG2, -- cgit v0.10.2 From 33e8612f64d4973ae3617a01224ec02b7b879597 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 22 Dec 2014 13:39:54 +0100 Subject: livepatch: use FTRACE_OPS_FL_IPMODIFY Use the FTRACE_OPS_FL_IPMODIFY flag to prevent conflicts with other ftrace users who also modify regs->ip. Signed-off-by: Josh Poimboeuf Reviewed-by: Petr Mladek Acked-by: Masami Hiramatsu Signed-off-by: Jiri Kosina diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index 07a2db9..6f63879 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c @@ -641,7 +641,8 @@ static int klp_init_func(struct klp_object *obj, struct klp_func *func) ops->private = func; ops->func = klp_ftrace_handler; - ops->flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_DYNAMIC; + ops->flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_DYNAMIC | + FTRACE_OPS_FL_IPMODIFY; func->fops = ops; func->state = KLP_DISABLED; -- cgit v0.10.2 From 215cedec379b69427c457104f0c36b389edc471c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 1 Dec 2014 10:10:42 -0300 Subject: [media] media: remove emacs editor variables 1) This is not allowed by the kernel coding style 2) Just configure your editor correctly 3) It's really ugly Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml b/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml index 28a8c1e..a2017bf 100644 --- a/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml +++ b/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml @@ -212,11 +212,3 @@ standards set in the standards field. &return-value; - - diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml b/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml index b9fdfea..6e3cadd 100644 --- a/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml +++ b/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml @@ -131,11 +131,3 @@ is out of bounds or the pad number is invalid. - - diff --git a/drivers/media/common/btcx-risc.c b/drivers/media/common/btcx-risc.c index ac1b268..e67338a 100644 --- a/drivers/media/common/btcx-risc.c +++ b/drivers/media/common/btcx-risc.c @@ -252,9 +252,3 @@ EXPORT_SYMBOL(btcx_screen_clips); EXPORT_SYMBOL(btcx_align); EXPORT_SYMBOL(btcx_sort_clips); EXPORT_SYMBOL(btcx_calc_skips); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/common/btcx-risc.h b/drivers/media/common/btcx-risc.h index f8bc6e8..03583ef 100644 --- a/drivers/media/common/btcx-risc.h +++ b/drivers/media/common/btcx-risc.h @@ -26,9 +26,3 @@ void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips); void btcx_calc_skips(int line, int width, int *maxy, struct btcx_skiplist *skips, unsigned int *nskips, const struct v4l2_clip *clips, unsigned int nclips); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-frontends/au8522.h b/drivers/media/dvb-frontends/au8522.h index 83fe9a6..6122519 100644 --- a/drivers/media/dvb-frontends/au8522.h +++ b/drivers/media/dvb-frontends/au8522.h @@ -91,8 +91,3 @@ enum au8522_audio_input { }; #endif /* __AU8522_H__ */ - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/dvb-frontends/lg2160.c b/drivers/media/dvb-frontends/lg2160.c index 5fd14f8..99efeba 100644 --- a/drivers/media/dvb-frontends/lg2160.c +++ b/drivers/media/dvb-frontends/lg2160.c @@ -1456,9 +1456,3 @@ MODULE_DESCRIPTION("LG Electronics LG216x ATSC/MH Demodulator Driver"); MODULE_AUTHOR("Michael Krufky "); MODULE_LICENSE("GPL"); MODULE_VERSION("0.3"); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-frontends/lgdt3305.c b/drivers/media/dvb-frontends/lgdt3305.c index 92c891a..60df376 100644 --- a/drivers/media/dvb-frontends/lgdt3305.c +++ b/drivers/media/dvb-frontends/lgdt3305.c @@ -1215,9 +1215,3 @@ MODULE_DESCRIPTION("LG Electronics LGDT3304/5 ATSC/QAM-B Demodulator Driver"); MODULE_AUTHOR("Michael Krufky "); MODULE_LICENSE("GPL"); MODULE_VERSION("0.2"); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c index e046622..2e1a618 100644 --- a/drivers/media/dvb-frontends/lgdt330x.c +++ b/drivers/media/dvb-frontends/lgdt330x.c @@ -823,9 +823,3 @@ MODULE_AUTHOR("Wilson Michaels"); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(lgdt330x_attach); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-frontends/lgdt330x.h b/drivers/media/dvb-frontends/lgdt330x.h index ca0eab5..8bb3322 100644 --- a/drivers/media/dvb-frontends/lgdt330x.h +++ b/drivers/media/dvb-frontends/lgdt330x.h @@ -65,9 +65,3 @@ static inline struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* #endif // CONFIG_DVB_LGDT330X #endif /* LGDT330X_H */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-frontends/lgdt330x_priv.h b/drivers/media/dvb-frontends/lgdt330x_priv.h index 38c7669..1922f09 100644 --- a/drivers/media/dvb-frontends/lgdt330x_priv.h +++ b/drivers/media/dvb-frontends/lgdt330x_priv.h @@ -69,9 +69,3 @@ enum I2C_REG { }; #endif /* _LGDT330X_PRIV_ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-frontends/nxt200x.h b/drivers/media/dvb-frontends/nxt200x.h index b518d54..e38d01f 100644 --- a/drivers/media/dvb-frontends/nxt200x.h +++ b/drivers/media/dvb-frontends/nxt200x.h @@ -55,9 +55,3 @@ static inline struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* c #endif // CONFIG_DVB_NXT200X #endif /* NXT200X_H */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-frontends/or51132.c b/drivers/media/dvb-frontends/or51132.c index 5ef9218..cbbd259 100644 --- a/drivers/media/dvb-frontends/or51132.c +++ b/drivers/media/dvb-frontends/or51132.c @@ -623,9 +623,3 @@ MODULE_AUTHOR("Trent Piepho"); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(or51132_attach); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-frontends/or51132.h b/drivers/media/dvb-frontends/or51132.h index 9389583..cdb5be3 100644 --- a/drivers/media/dvb-frontends/or51132.h +++ b/drivers/media/dvb-frontends/or51132.h @@ -47,9 +47,3 @@ static inline struct dvb_frontend* or51132_attach(const struct or51132_config* c #endif // CONFIG_DVB_OR51132 #endif // OR51132_H - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-frontends/s5h1409.c b/drivers/media/dvb-frontends/s5h1409.c index f71b062..5ff474a 100644 --- a/drivers/media/dvb-frontends/s5h1409.c +++ b/drivers/media/dvb-frontends/s5h1409.c @@ -1021,9 +1021,3 @@ static struct dvb_frontend_ops s5h1409_ops = { MODULE_DESCRIPTION("Samsung S5H1409 QAM-B/ATSC Demodulator driver"); MODULE_AUTHOR("Steven Toth"); MODULE_LICENSE("GPL"); - - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/dvb-frontends/s5h1409.h b/drivers/media/dvb-frontends/s5h1409.h index 63b1e0a..9e143f5 100644 --- a/drivers/media/dvb-frontends/s5h1409.h +++ b/drivers/media/dvb-frontends/s5h1409.h @@ -81,8 +81,3 @@ static inline struct dvb_frontend *s5h1409_attach( #endif /* CONFIG_DVB_S5H1409 */ #endif /* __S5H1409_H__ */ - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/dvb-frontends/s5h1411.c b/drivers/media/dvb-frontends/s5h1411.c index 6cc4b7a..64f35fe 100644 --- a/drivers/media/dvb-frontends/s5h1411.c +++ b/drivers/media/dvb-frontends/s5h1411.c @@ -944,8 +944,3 @@ MODULE_PARM_DESC(debug, "Enable verbose debug messages"); MODULE_DESCRIPTION("Samsung S5H1411 QAM-B/ATSC Demodulator driver"); MODULE_AUTHOR("Steven Toth"); MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/dvb-frontends/s5h1411.h b/drivers/media/dvb-frontends/s5h1411.h index e4f5687..1d7deb6 100644 --- a/drivers/media/dvb-frontends/s5h1411.h +++ b/drivers/media/dvb-frontends/s5h1411.h @@ -83,8 +83,3 @@ static inline struct dvb_frontend *s5h1411_attach( #endif /* CONFIG_DVB_S5H1411 */ #endif /* __S5H1411_H__ */ - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c index 4d9c6bc..dcc68ec 100644 --- a/drivers/media/i2c/msp3400-driver.c +++ b/drivers/media/i2c/msp3400-driver.c @@ -904,11 +904,3 @@ static struct i2c_driver msp_driver = { }; module_i2c_driver(msp_driver); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/bt878.c b/drivers/media/pci/bt8xx/bt878.c index 1176583..0939d39 100644 --- a/drivers/media/pci/bt8xx/bt878.c +++ b/drivers/media/pci/bt8xx/bt878.c @@ -590,9 +590,3 @@ module_init(bt878_init_module); module_exit(bt878_cleanup_module); MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c index 4105560..c518677 100644 --- a/drivers/media/pci/bt8xx/bttv-cards.c +++ b/drivers/media/pci/bt8xx/bttv-cards.c @@ -5048,10 +5048,3 @@ int bttv_handle_chipset(struct bttv *btv) pci_write_config_byte(btv->c.pci, PCI_LATENCY_TIMER, latency); return 0; } - - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 4a8176c..665e46d 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -4429,9 +4429,3 @@ static void __exit bttv_cleanup_module(void) module_init(bttv_init_module); module_exit(bttv_cleanup_module); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/bttv-gpio.c b/drivers/media/pci/bt8xx/bttv-gpio.c index 3f364b7..25b9916 100644 --- a/drivers/media/pci/bt8xx/bttv-gpio.c +++ b/drivers/media/pci/bt8xx/bttv-gpio.c @@ -181,9 +181,3 @@ void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits) btwrite(data,BT848_GPIO_DATA); spin_unlock_irqrestore(&btv->gpio_lock,flags); } - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/bttv-if.c b/drivers/media/pci/bt8xx/bttv-if.c index a6a540d..538652e 100644 --- a/drivers/media/pci/bt8xx/bttv-if.c +++ b/drivers/media/pci/bt8xx/bttv-if.c @@ -113,9 +113,3 @@ int bttv_write_gpio(unsigned int card, unsigned long mask, unsigned long data) bttv_gpio_tracking(btv,"extern write"); return 0; } - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/bttv-risc.c b/drivers/media/pci/bt8xx/bttv-risc.c index 4d3f05a..3859dde 100644 --- a/drivers/media/pci/bt8xx/bttv-risc.c +++ b/drivers/media/pci/bt8xx/bttv-risc.c @@ -901,9 +901,3 @@ bttv_overlay_risc(struct bttv *btv, buf->vb.field = ov->field; return 0; } - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/bttv-vbi.c b/drivers/media/pci/bt8xx/bttv-vbi.c index b433267..e77129c 100644 --- a/drivers/media/pci/bt8xx/bttv-vbi.c +++ b/drivers/media/pci/bt8xx/bttv-vbi.c @@ -450,10 +450,3 @@ void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, unsigned int norm) /* See bttv_vbi_fmt_set(). */ f->end = tvnorm->vbistart[0] * 2 + 2; } - -/* ----------------------------------------------------------------------- */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/bttv.h b/drivers/media/pci/bt8xx/bttv.h index f081262..91301c3 100644 --- a/drivers/media/pci/bt8xx/bttv.h +++ b/drivers/media/pci/bt8xx/bttv.h @@ -378,8 +378,3 @@ extern void bttv_input_fini(struct bttv *dev); extern void bttv_input_irq(struct bttv *dev); #endif /* _BTTV_H_ */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/bttvp.h b/drivers/media/pci/bt8xx/bttvp.h index 9fe19488..e6e2c60 100644 --- a/drivers/media/pci/bt8xx/bttvp.h +++ b/drivers/media/pci/bt8xx/bttvp.h @@ -531,9 +531,3 @@ static inline unsigned int bttv_muxsel(const struct bttv *btv, #define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) #endif /* _BTTVP_H_ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c index dee177e..c38d5a1 100644 --- a/drivers/media/pci/cx88/cx88-core.c +++ b/drivers/media/pci/cx88/cx88-core.c @@ -1091,10 +1091,3 @@ EXPORT_SYMBOL(cx88_core_put); EXPORT_SYMBOL(cx88_ir_start); EXPORT_SYMBOL(cx88_ir_stop); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off - */ diff --git a/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c index 1c1f69e..a369b08 100644 --- a/drivers/media/pci/cx88/cx88-mpeg.c +++ b/drivers/media/pci/cx88/cx88-mpeg.c @@ -833,10 +833,3 @@ EXPORT_SYMBOL(cx8802_start_dma); EXPORT_SYMBOL(cx8802_register_driver); EXPORT_SYMBOL(cx8802_unregister_driver); EXPORT_SYMBOL(cx8802_get_driver); -/* ----------------------------------------------------------- */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off - */ diff --git a/drivers/media/pci/cx88/cx88-tvaudio.c b/drivers/media/pci/cx88/cx88-tvaudio.c index 424fd97..6bbce6a 100644 --- a/drivers/media/pci/cx88/cx88-tvaudio.c +++ b/drivers/media/pci/cx88/cx88-tvaudio.c @@ -1050,10 +1050,3 @@ EXPORT_SYMBOL(cx88_newstation); EXPORT_SYMBOL(cx88_set_stereo); EXPORT_SYMBOL(cx88_get_stereo); EXPORT_SYMBOL(cx88_audio_thread); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off - */ diff --git a/drivers/media/tuners/mt20xx.c b/drivers/media/tuners/mt20xx.c index 0e74e97..9e03104 100644 --- a/drivers/media/tuners/mt20xx.c +++ b/drivers/media/tuners/mt20xx.c @@ -660,11 +660,3 @@ EXPORT_SYMBOL_GPL(microtune_attach); MODULE_DESCRIPTION("Microtune tuner driver"); MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); MODULE_LICENSE("GPL"); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/tuners/mt2131.c b/drivers/media/tuners/mt2131.c index f83b0c1..6e2cdd2 100644 --- a/drivers/media/tuners/mt2131.c +++ b/drivers/media/tuners/mt2131.c @@ -294,8 +294,3 @@ EXPORT_SYMBOL(mt2131_attach); MODULE_AUTHOR("Steven Toth"); MODULE_DESCRIPTION("Microtune MT2131 silicon tuner driver"); MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/tuners/mt2131.h b/drivers/media/tuners/mt2131.h index 09ceaf6..837c854 100644 --- a/drivers/media/tuners/mt2131.h +++ b/drivers/media/tuners/mt2131.h @@ -47,8 +47,3 @@ static inline struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe, #endif /* CONFIG_MEDIA_TUNER_MT2131 */ #endif /* __MT2131_H__ */ - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/tuners/mt2131_priv.h b/drivers/media/tuners/mt2131_priv.h index 62aeedf..91283b5 100644 --- a/drivers/media/tuners/mt2131_priv.h +++ b/drivers/media/tuners/mt2131_priv.h @@ -41,8 +41,3 @@ struct mt2131_priv { }; #endif /* __MT2131_PRIV_H__ */ - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/tuners/mxl5007t.c b/drivers/media/tuners/mxl5007t.c index 1810ad6..f4ae04c 100644 --- a/drivers/media/tuners/mxl5007t.c +++ b/drivers/media/tuners/mxl5007t.c @@ -938,11 +938,3 @@ MODULE_DESCRIPTION("MaxLinear MxL5007T Silicon IC tuner driver"); MODULE_AUTHOR("Michael Krufky "); MODULE_LICENSE("GPL"); MODULE_VERSION("0.2"); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/tuners/mxl5007t.h b/drivers/media/tuners/mxl5007t.h index 37b0942..ae7037d 100644 --- a/drivers/media/tuners/mxl5007t.h +++ b/drivers/media/tuners/mxl5007t.h @@ -93,12 +93,3 @@ static inline struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, #endif #endif /* __MXL5007T_H__ */ - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ - diff --git a/drivers/media/tuners/tda18271-fe.c b/drivers/media/tuners/tda18271-fe.c index 4995b89..f862074 100644 --- a/drivers/media/tuners/tda18271-fe.c +++ b/drivers/media/tuners/tda18271-fe.c @@ -1355,11 +1355,3 @@ MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver"); MODULE_AUTHOR("Michael Krufky "); MODULE_LICENSE("GPL"); MODULE_VERSION("0.4"); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/tuners/tda18271-maps.c b/drivers/media/tuners/tda18271-maps.c index b62e925..1e89dd9 100644 --- a/drivers/media/tuners/tda18271-maps.c +++ b/drivers/media/tuners/tda18271-maps.c @@ -1305,11 +1305,3 @@ int tda18271_assign_map_layout(struct dvb_frontend *fe) return ret; } - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/tuners/tda18271-priv.h b/drivers/media/tuners/tda18271-priv.h index b36a7b7..cc80f54 100644 --- a/drivers/media/tuners/tda18271-priv.h +++ b/drivers/media/tuners/tda18271-priv.h @@ -226,11 +226,3 @@ extern int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq); extern int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq); #endif /* __TDA18271_PRIV_H__ */ - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/tuners/tda827x.c b/drivers/media/tuners/tda827x.c index 73453a2..edcb4a7 100644 --- a/drivers/media/tuners/tda827x.c +++ b/drivers/media/tuners/tda827x.c @@ -907,11 +907,3 @@ MODULE_DESCRIPTION("DVB TDA827x driver"); MODULE_AUTHOR("Hartmut Hackmann "); MODULE_AUTHOR("Michael Krufky "); MODULE_LICENSE("GPL"); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/tuners/tda8290.c b/drivers/media/tuners/tda8290.c index ab4106c..998e82b 100644 --- a/drivers/media/tuners/tda8290.c +++ b/drivers/media/tuners/tda8290.c @@ -881,11 +881,3 @@ EXPORT_SYMBOL_GPL(tda829x_probe); MODULE_DESCRIPTION("Philips/NXP TDA8290/TDA8295 analog IF demodulator driver"); MODULE_AUTHOR("Gerd Knorr, Hartmut Hackmann, Michael Krufky"); MODULE_LICENSE("GPL"); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/tuners/tda9887.c b/drivers/media/tuners/tda9887.c index 9823248..56be6c2 100644 --- a/drivers/media/tuners/tda9887.c +++ b/drivers/media/tuners/tda9887.c @@ -707,11 +707,3 @@ struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, EXPORT_SYMBOL_GPL(tda9887_attach); MODULE_LICENSE("GPL"); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/tuners/tuner-simple.c b/drivers/media/tuners/tuner-simple.c index ca274c2..8e9ce14 100644 --- a/drivers/media/tuners/tuner-simple.c +++ b/drivers/media/tuners/tuner-simple.c @@ -1148,11 +1148,3 @@ EXPORT_SYMBOL_GPL(simple_tuner_attach); MODULE_DESCRIPTION("Simple 4-control-bytes style tuner driver"); MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); MODULE_LICENSE("GPL"); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c index 0a98d04..ecefa5c 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c @@ -604,9 +604,3 @@ MODULE_DESCRIPTION("MaxLinear MxL111SF DVB-T demodulator driver"); MODULE_AUTHOR("Michael Krufky "); MODULE_LICENSE("GPL"); MODULE_VERSION("0.1"); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h index 2d4530f..0bd83e5 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h @@ -47,9 +47,3 @@ struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state, #endif /* CONFIG_DVB_USB_MXL111SF */ #endif /* __MXL111SF_DEMOD_H__ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c index a619410..2180c13 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c @@ -755,9 +755,3 @@ int mxl111sf_gpio_mode_switch(struct mxl111sf_state *state, unsigned int mode) } return 0; } - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h index b85a577..16fa4d4 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h @@ -48,9 +48,3 @@ int mxl111sf_config_pin_mux_modes(struct mxl111sf_state *state, enum mxl111sf_mux_config pin_mux_config); #endif /* _DVB_USB_MXL111SF_GPIO_H_ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c index a101d06..283495c 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c @@ -842,9 +842,3 @@ int mxl111sf_i2c_xfer(struct i2c_adapter *adap, return i == num ? num : -EREMOTEIO; } - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h index 4657621..c486fe0 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h @@ -27,9 +27,3 @@ int mxl111sf_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num); #endif /* _DVB_USB_MXL111SF_I2C_H_ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c index f6b3480..5b01911 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c @@ -335,9 +335,3 @@ int mxl111sf_idac_config(struct mxl111sf_state *state, return ret; } - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h index 0643738..25aa4a1 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h @@ -45,9 +45,3 @@ int mxl111sf_idac_config(struct mxl111sf_state *state, u8 current_value, u8 hysteresis_value); #endif /* _DVB_USB_MXL111SF_PHY_H_ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h index 89bf115..1f4bfbc 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h @@ -171,9 +171,3 @@ #define V6_DIG_RF_PWR_MSB_REG 0x47 #endif /* _DVB_USB_MXL111SF_REG_H_ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c index a8d2c70..444579b 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c @@ -515,11 +515,3 @@ MODULE_DESCRIPTION("MaxLinear MxL111SF CMOS tuner driver"); MODULE_AUTHOR("Michael Krufky "); MODULE_LICENSE("GPL"); MODULE_VERSION("0.1"); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h index 2046db2..e6caab2 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h @@ -77,12 +77,3 @@ struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe, #endif #endif /* __MXL111SF_TUNER_H__ */ - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ - diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c index c3447ea..bec12b0 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c @@ -1425,9 +1425,3 @@ MODULE_AUTHOR("Michael Krufky "); MODULE_DESCRIPTION("Driver for MaxLinear MxL111SF"); MODULE_VERSION("1.0"); MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.h b/drivers/media/usb/dvb-usb-v2/mxl111sf.h index 8516c01..ee70df1 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.h @@ -152,9 +152,3 @@ extern int dvb_usb_mxl111sf_debug; }) #endif /* _DVB_USB_MXL111SF_H_ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index abf8ab2..eafc5c8 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -1269,8 +1269,3 @@ MODULE_AUTHOR("Aapo Tahkola "); MODULE_DESCRIPTION("DVB Driver for ULI M920x"); MODULE_VERSION("0.1"); MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-audio.c b/drivers/media/usb/pvrusb2/pvrusb2-audio.c index cc06d5e..45276c6 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-audio.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-audio.c @@ -84,13 +84,3 @@ void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0); } } - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-audio.h b/drivers/media/usb/pvrusb2/pvrusb2-audio.h index e3e63d7..27cefb5 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-audio.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-audio.h @@ -25,13 +25,3 @@ #include "pvrusb2-hdw-internal.h" void pvr2_msp3400_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); #endif /* __PVRUSB2_AUDIO_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-context.c b/drivers/media/usb/pvrusb2/pvrusb2-context.c index c8761c7..924fc4c 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-context.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-context.c @@ -418,14 +418,3 @@ struct pvr2_ioread *pvr2_channel_create_mpeg_stream( pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key)); return cp; } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-context.h b/drivers/media/usb/pvrusb2/pvrusb2-context.h index d657e53..1c1d442 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-context.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-context.h @@ -83,12 +83,3 @@ int pvr2_context_global_init(void); void pvr2_context_global_done(void); #endif /* __PVRUSB2_CONTEXT_H */ -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c index 8832090..f82f0f0 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c @@ -82,14 +82,3 @@ void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) sd->ops->audio->s_routing(sd, input, 0, 0); } } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h index 53ba548..86c17be 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h @@ -36,13 +36,3 @@ void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); #endif /* __PVRUSB2_AUDIO_CS53L32A_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-ctrl.c b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.c index 7d5a713..958db17 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-ctrl.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.c @@ -596,14 +596,3 @@ int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *cptr, } while(0); LOCK_GIVE(cptr->hdw->big_lock); return ret; } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-ctrl.h b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.h index 794ff90..c175571 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-ctrl.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.h @@ -110,13 +110,3 @@ int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *, unsigned int *len); #endif /* __PVRUSB2_CTRL_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c index c514d0b..1a81aa7 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -152,15 +152,3 @@ void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) sd->ops->audio->s_routing(sd, (u32)aud_input, 0, 0); } } - - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h index e35c232..2eed7b7 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h @@ -40,13 +40,3 @@ void pvr2_cx25840_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd); #endif /* __PVRUSB2_CX2584X_V4L_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-debug.h b/drivers/media/usb/pvrusb2/pvrusb2-debug.h index be79249..4ef2ebc 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-debug.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-debug.h @@ -57,13 +57,3 @@ extern int pvrusb2_debug; #endif /* __PVRUSB2_HDW_INTERNAL_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c index 4279ebb..e4022bc 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c @@ -322,14 +322,3 @@ int pvr2_debugifc_docmd(struct pvr2_hdw *hdw,const char *buf, return 0; } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.h b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.h index 2f8d467..a8dfc55 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.h @@ -40,13 +40,3 @@ int pvr2_debugifc_docmd(struct pvr2_hdw *, const char *buf_ptr,unsigned int buf_size); #endif /* __PVRUSB2_DEBUGIFC_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-devattr.c b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c index adc501d3..06c4c3d 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c @@ -564,13 +564,3 @@ MODULE_FIRMWARE(PVR2_FIRMWARE_29xxx); MODULE_FIRMWARE(PVR2_FIRMWARE_24xxx); MODULE_FIRMWARE(PVR2_FIRMWARE_73xxx); MODULE_FIRMWARE(PVR2_FIRMWARE_75xxx); - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-devattr.h b/drivers/media/usb/pvrusb2/pvrusb2-devattr.h index 273c8d4..5aeefb6 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-devattr.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-devattr.h @@ -187,13 +187,3 @@ struct pvr2_device_desc { extern struct usb_device_id pvr2_device_table[]; #endif /* __PVRUSB2_HDW_INTERNAL_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c index 9515f3a..e1907cd 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c @@ -152,13 +152,3 @@ int pvr2_eeprom_analyze(struct pvr2_hdw *hdw) return 0; } - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-eeprom.h b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.h index cca3216..f1e33c8 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-eeprom.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.h @@ -27,13 +27,3 @@ struct pvr2_hdw; int pvr2_eeprom_analyze(struct pvr2_hdw *); #endif /* __PVRUSB2_EEPROM_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-encoder.c b/drivers/media/usb/pvrusb2/pvrusb2-encoder.c index f7702ae..593b3e9 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-encoder.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-encoder.c @@ -538,14 +538,3 @@ int pvr2_encoder_stop(struct pvr2_hdw *hdw) return status; } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-encoder.h b/drivers/media/usb/pvrusb2/pvrusb2-encoder.h index 232fefb..a2bfb48 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-encoder.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-encoder.h @@ -30,13 +30,3 @@ int pvr2_encoder_start(struct pvr2_hdw *); int pvr2_encoder_stop(struct pvr2_hdw *); #endif /* __PVRUSB2_ENCODER_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h b/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h index 614755e..06a15a6 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h @@ -60,13 +60,3 @@ #define FX2CMD_ONAIR_DTV_POWER_OFF 0xa3u #endif /* _PVRUSB2_FX2_CMD_H_ */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h index 036952f..1f9c028 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h @@ -394,13 +394,3 @@ unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *); void pvr2_hdw_status_poll(struct pvr2_hdw *); #endif /* __PVRUSB2_HDW_INTERNAL_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.h b/drivers/media/usb/pvrusb2/pvrusb2-hdw.h index 4184707..fc50379 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.h @@ -343,13 +343,3 @@ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw); int pvr2_upload_firmware2(struct pvr2_hdw *hdw); #endif /* __PVRUSB2_HDW_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c index b5e929f..4baa9d6 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c @@ -686,13 +686,3 @@ void pvr2_i2c_core_done(struct pvr2_hdw *hdw) hdw->i2c_linked = 0; } } - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h index 6a757692..a10a3e8 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h @@ -27,14 +27,3 @@ void pvr2_i2c_core_done(struct pvr2_hdw *); #endif /* __PVRUSB2_I2C_ADAPTER_H */ - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-io.c b/drivers/media/usb/pvrusb2/pvrusb2-io.c index 1e35474..0c08f22 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-io.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-io.c @@ -682,14 +682,3 @@ int pvr2_buffer_get_id(struct pvr2_buffer *bp) { return bp->id; } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-io.h b/drivers/media/usb/pvrusb2/pvrusb2-io.h index afb7e87..0c47c6a 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-io.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-io.h @@ -90,13 +90,3 @@ int pvr2_buffer_get_id(struct pvr2_buffer *); int pvr2_buffer_queue(struct pvr2_buffer *); #endif /* __PVRUSB2_IO_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-ioread.c b/drivers/media/usb/pvrusb2/pvrusb2-ioread.c index bba6115..cd995b5 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-ioread.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-ioread.c @@ -499,14 +499,3 @@ int pvr2_ioread_read(struct pvr2_ioread *cp,void __user *buf,unsigned int cnt) cp,req_cnt,ret); return ret; } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-ioread.h b/drivers/media/usb/pvrusb2/pvrusb2-ioread.h index 100e078..0b1f0fb 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-ioread.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-ioread.h @@ -36,13 +36,3 @@ int pvr2_ioread_read(struct pvr2_ioread *,void __user *buf,unsigned int cnt); int pvr2_ioread_avail(struct pvr2_ioread *); #endif /* __PVRUSB2_IOREAD_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-main.c b/drivers/media/usb/pvrusb2/pvrusb2-main.c index c1d9bb6..86be902 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-main.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-main.c @@ -169,14 +169,3 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_VERSION("0.9.1"); - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-std.c b/drivers/media/usb/pvrusb2/pvrusb2-std.c index 453627b..9a596a3 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-std.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-std.c @@ -398,14 +398,3 @@ v4l2_std_id pvr2_std_get_usable(void) { return CSTD_ALL; } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-std.h b/drivers/media/usb/pvrusb2/pvrusb2-std.h index a35c53d..ed4ec04 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-std.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-std.h @@ -47,13 +47,3 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, v4l2_std_id pvr2_std_get_usable(void); #endif /* __PVRUSB2_STD_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c index 6ef1335..06fe63c 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c @@ -848,14 +848,3 @@ static ssize_t debugcmd_store(struct device *class_dev, return count; } #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.h b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.h index 6d875bf..6f0579e 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.h @@ -34,13 +34,3 @@ struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *, struct pvr2_sysfs_class *); #endif /* __PVRUSB2_SYSFS_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-util.h b/drivers/media/usb/pvrusb2/pvrusb2-util.h index 92b7554..5465bf9 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-util.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-util.h @@ -50,13 +50,3 @@ #endif /* __PVRUSB2_UTIL_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index 1b158f1..422d79e 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -1360,13 +1360,3 @@ struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) pvr2_v4l2_destroy_no_lock(vp); return NULL; } - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.h b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.h index 34c011a..e455c95 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.h @@ -27,13 +27,3 @@ struct pvr2_v4l2; struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *); #endif /* __PVRUSB2_V4L2_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c index 2e205c9..139b397 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c @@ -101,14 +101,3 @@ void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) sd->ops->video->s_routing(sd, input, 0, 0); } } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h index 3b0bd5d..dacf3ec 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h @@ -36,13 +36,3 @@ void pvr2_saa7115_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); #endif /* __PVRUSB2_VIDEO_V4L_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-wm8775.c b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.c index 3ac8d75..f1df94a 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-wm8775.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.c @@ -56,15 +56,3 @@ void pvr2_wm8775_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) sd->ops->audio->s_routing(sd, input, 0, 0); } } - - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-wm8775.h b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.h index 0577bc7..a4ee12e 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-wm8775.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.h @@ -40,13 +40,3 @@ void pvr2_wm8775_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd); #endif /* __PVRUSB2_WM8775_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2.h b/drivers/media/usb/pvrusb2/pvrusb2.h index 240de9b..95f98a8 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2.h +++ b/drivers/media/usb/pvrusb2/pvrusb2.h @@ -30,13 +30,3 @@ #define PVR_NUM 20 #endif /* __PVRUSB2_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c index 302aa07..2144b7b 100644 --- a/drivers/media/usb/usbvision/usbvision-core.c +++ b/drivers/media/usb/usbvision/usbvision-core.c @@ -2502,11 +2502,3 @@ int usbvision_muxsel(struct usb_usbvision *usbvision, int channel) usbvision_set_audio(usbvision, audio[channel]); return 0; } - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/usbvision/usbvision-i2c.c b/drivers/media/usb/usbvision/usbvision-i2c.c index ba262a3..26dbcb1 100644 --- a/drivers/media/usb/usbvision/usbvision-i2c.c +++ b/drivers/media/usb/usbvision/usbvision-i2c.c @@ -445,11 +445,3 @@ static struct i2c_adapter i2c_adap_template = { .owner = THIS_MODULE, .name = "usbvision", }; - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 693d5f4..cd2fbf1 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -1716,11 +1716,3 @@ static void __exit usbvision_exit(void) module_init(usbvision_init); module_exit(usbvision_exit); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/usbvision/usbvision.h b/drivers/media/usb/usbvision/usbvision.h index a0c73cf..77aeb1e 100644 --- a/drivers/media/usb/usbvision/usbvision.h +++ b/drivers/media/usb/usbvision/usbvision.h @@ -517,11 +517,3 @@ int usbvision_power_off(struct usb_usbvision *usbvision); int usbvision_power_on(struct usb_usbvision *usbvision); #endif /* __LINUX_USBVISION_H */ - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 9aa530a..a13cc61 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -1033,10 +1033,3 @@ MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab "); MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2"); MODULE_LICENSE("GPL"); MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR); - - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/include/media/videobuf-dvb.h b/include/media/videobuf-dvb.h index d63965a..c3bfa47 100644 --- a/include/media/videobuf-dvb.h +++ b/include/media/videobuf-dvb.h @@ -56,9 +56,3 @@ struct videobuf_dvb_frontend * videobuf_dvb_get_frontend(struct videobuf_dvb_fro int videobuf_dvb_find_frontend(struct videobuf_dvb_frontends *f, struct dvb_frontend *p); #endif /* _VIDEOBUF_DVB_H_ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ -- cgit v0.10.2 From 83737691e586cd2767fa4fc87cd41251d1a49e9e Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 22 Dec 2014 21:04:31 +0100 Subject: x86, mce: Fix sparse errors Make stuff used in mce.c only, static. Signed-off-by: Borislav Petkov diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index 323e02c..4c5cd75 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -115,7 +115,7 @@ static void (*quirk_no_way_out)(int bank, struct mce *m, struct pt_regs *regs); * CPU/chipset specific EDAC code can register a notifier call here to print * MCE errors in a human-readable form. */ -ATOMIC_NOTIFIER_HEAD(x86_mce_decoder_chain); +static ATOMIC_NOTIFIER_HEAD(x86_mce_decoder_chain); /* Do initial initialization of a struct mce */ void mce_setup(struct mce *m) @@ -529,7 +529,7 @@ static void mce_schedule_work(void) schedule_work(this_cpu_ptr(&mce_work)); } -DEFINE_PER_CPU(struct irq_work, mce_irq_work); +static DEFINE_PER_CPU(struct irq_work, mce_irq_work); static void mce_irq_work_cb(struct irq_work *entry) { @@ -1012,7 +1012,7 @@ static void mce_clear_state(unsigned long *toclear) */ #define MCE_INFO_MAX 16 -struct mce_info { +static struct mce_info { atomic_t inuse; struct task_struct *t; __u64 paddr; -- cgit v0.10.2 From 1a023feb9c880bdf61502c5f8ef7b499133de8da Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 4 Dec 2014 06:54:52 -0300 Subject: [media] v4l2 subdevs: replace get/set_crop by get/set_selection The crop and selection pad ops are duplicates. Replace all uses of get/set_crop by get/set_selection. This will make it possible to drop get/set_crop altogether. Signed-off-by: Hans Verkuil Acked-by: Sylwester Nawrocki Acked-by: Laurent Pinchart Cc: Prabhakar Lad Cc: Philipp Zabel Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c index 45b3fca..7643122 100644 --- a/drivers/media/i2c/mt9m032.c +++ b/drivers/media/i2c/mt9m032.c @@ -422,22 +422,25 @@ done: return ret; } -static int mt9m032_get_pad_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9m032_get_pad_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct mt9m032 *sensor = to_mt9m032(subdev); + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + mutex_lock(&sensor->lock); - crop->rect = *__mt9m032_get_pad_crop(sensor, fh, crop->which); + sel->r = *__mt9m032_get_pad_crop(sensor, fh, sel->which); mutex_unlock(&sensor->lock); return 0; } -static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9m032_set_pad_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct mt9m032 *sensor = to_mt9m032(subdev); struct v4l2_mbus_framefmt *format; @@ -445,9 +448,12 @@ static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev, struct v4l2_rect rect; int ret = 0; + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + mutex_lock(&sensor->lock); - if (sensor->streaming && crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (sensor->streaming && sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { ret = -EBUSY; goto done; } @@ -455,13 +461,13 @@ static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev, /* Clamp the crop rectangle boundaries and align them to a multiple of 2 * pixels to ensure a GRBG Bayer pattern. */ - rect.left = clamp(ALIGN(crop->rect.left, 2), MT9M032_COLUMN_START_MIN, + rect.left = clamp(ALIGN(sel->r.left, 2), MT9M032_COLUMN_START_MIN, MT9M032_COLUMN_START_MAX); - rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN, + rect.top = clamp(ALIGN(sel->r.top, 2), MT9M032_ROW_START_MIN, MT9M032_ROW_START_MAX); - rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), + rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), MT9M032_COLUMN_SIZE_MIN, MT9M032_COLUMN_SIZE_MAX); - rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), + rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), MT9M032_ROW_SIZE_MIN, MT9M032_ROW_SIZE_MAX); rect.width = min_t(unsigned int, rect.width, @@ -469,21 +475,21 @@ static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev, rect.height = min_t(unsigned int, rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9m032_get_pad_crop(sensor, fh, crop->which); + __crop = __mt9m032_get_pad_crop(sensor, fh, sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - format = __mt9m032_get_pad_format(sensor, fh, crop->which); + format = __mt9m032_get_pad_format(sensor, fh, sel->which); format->width = rect.width; format->height = rect.height; } *__crop = rect; - crop->rect = rect; + sel->r = rect; - if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) ret = mt9m032_update_geom_timing(sensor); done: @@ -690,8 +696,8 @@ static const struct v4l2_subdev_pad_ops mt9m032_pad_ops = { .enum_frame_size = mt9m032_enum_frame_size, .get_fmt = mt9m032_get_pad_format, .set_fmt = mt9m032_set_pad_format, - .set_crop = mt9m032_set_pad_crop, - .get_crop = mt9m032_get_pad_crop, + .set_selection = mt9m032_set_pad_selection, + .get_selection = mt9m032_get_pad_selection, }; static const struct v4l2_subdev_ops mt9m032_ops = { diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index edb76bd..e3acae9 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -581,37 +581,42 @@ static int mt9p031_set_format(struct v4l2_subdev *subdev, return 0; } -static int mt9p031_get_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9p031_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); - crop->rect = *__mt9p031_get_pad_crop(mt9p031, fh, crop->pad, - crop->which); + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + sel->r = *__mt9p031_get_pad_crop(mt9p031, fh, sel->pad, sel->which); return 0; } -static int mt9p031_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9p031_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); struct v4l2_mbus_framefmt *__format; struct v4l2_rect *__crop; struct v4l2_rect rect; + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 * pixels to ensure a GRBG Bayer pattern. */ - rect.left = clamp(ALIGN(crop->rect.left, 2), MT9P031_COLUMN_START_MIN, + rect.left = clamp(ALIGN(sel->r.left, 2), MT9P031_COLUMN_START_MIN, MT9P031_COLUMN_START_MAX); - rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN, + rect.top = clamp(ALIGN(sel->r.top, 2), MT9P031_ROW_START_MIN, MT9P031_ROW_START_MAX); - rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), + rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), MT9P031_WINDOW_WIDTH_MIN, MT9P031_WINDOW_WIDTH_MAX); - rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), + rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), MT9P031_WINDOW_HEIGHT_MIN, MT9P031_WINDOW_HEIGHT_MAX); @@ -620,20 +625,20 @@ static int mt9p031_set_crop(struct v4l2_subdev *subdev, rect.height = min_t(unsigned int, rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which); + __crop = __mt9p031_get_pad_crop(mt9p031, fh, sel->pad, sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - __format = __mt9p031_get_pad_format(mt9p031, fh, crop->pad, - crop->which); + __format = __mt9p031_get_pad_format(mt9p031, fh, sel->pad, + sel->which); __format->width = rect.width; __format->height = rect.height; } *__crop = rect; - crop->rect = rect; + sel->r = rect; return 0; } @@ -980,8 +985,8 @@ static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = { .enum_frame_size = mt9p031_enum_frame_size, .get_fmt = mt9p031_get_format, .set_fmt = mt9p031_set_format, - .get_crop = mt9p031_get_crop, - .set_crop = mt9p031_set_crop, + .get_selection = mt9p031_get_selection, + .set_selection = mt9p031_set_selection, }; static struct v4l2_subdev_ops mt9p031_subdev_ops = { diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c index d9e9889..f6ca636 100644 --- a/drivers/media/i2c/mt9t001.c +++ b/drivers/media/i2c/mt9t001.c @@ -401,39 +401,44 @@ static int mt9t001_set_format(struct v4l2_subdev *subdev, return 0; } -static int mt9t001_get_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9t001_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct mt9t001 *mt9t001 = to_mt9t001(subdev); - crop->rect = *__mt9t001_get_pad_crop(mt9t001, fh, crop->pad, - crop->which); + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + sel->r = *__mt9t001_get_pad_crop(mt9t001, fh, sel->pad, sel->which); return 0; } -static int mt9t001_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9t001_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct mt9t001 *mt9t001 = to_mt9t001(subdev); struct v4l2_mbus_framefmt *__format; struct v4l2_rect *__crop; struct v4l2_rect rect; + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 * pixels. */ - rect.left = clamp(ALIGN(crop->rect.left, 2), + rect.left = clamp(ALIGN(sel->r.left, 2), MT9T001_COLUMN_START_MIN, MT9T001_COLUMN_START_MAX); - rect.top = clamp(ALIGN(crop->rect.top, 2), + rect.top = clamp(ALIGN(sel->r.top, 2), MT9T001_ROW_START_MIN, MT9T001_ROW_START_MAX); - rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), + rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), MT9T001_WINDOW_WIDTH_MIN + 1, MT9T001_WINDOW_WIDTH_MAX + 1); - rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), + rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), MT9T001_WINDOW_HEIGHT_MIN + 1, MT9T001_WINDOW_HEIGHT_MAX + 1); @@ -442,20 +447,20 @@ static int mt9t001_set_crop(struct v4l2_subdev *subdev, rect.height = min_t(unsigned int, rect.height, MT9T001_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9t001_get_pad_crop(mt9t001, fh, crop->pad, crop->which); + __crop = __mt9t001_get_pad_crop(mt9t001, fh, sel->pad, sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - __format = __mt9t001_get_pad_format(mt9t001, fh, crop->pad, - crop->which); + __format = __mt9t001_get_pad_format(mt9t001, fh, sel->pad, + sel->which); __format->width = rect.width; __format->height = rect.height; } *__crop = rect; - crop->rect = rect; + sel->r = rect; return 0; } @@ -819,8 +824,8 @@ static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = { .enum_frame_size = mt9t001_enum_frame_size, .get_fmt = mt9t001_get_format, .set_fmt = mt9t001_set_format, - .get_crop = mt9t001_get_crop, - .set_crop = mt9t001_set_crop, + .get_selection = mt9t001_get_selection, + .set_selection = mt9t001_set_selection, }; static struct v4l2_subdev_ops mt9t001_subdev_ops = { diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 93687c1..bd3f979 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -552,39 +552,44 @@ static int mt9v032_set_format(struct v4l2_subdev *subdev, return 0; } -static int mt9v032_get_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9v032_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); - crop->rect = *__mt9v032_get_pad_crop(mt9v032, fh, crop->pad, - crop->which); + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + sel->r = *__mt9v032_get_pad_crop(mt9v032, fh, sel->pad, sel->which); return 0; } -static int mt9v032_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9v032_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); struct v4l2_mbus_framefmt *__format; struct v4l2_rect *__crop; struct v4l2_rect rect; + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + /* Clamp the crop rectangle boundaries and align them to a non multiple * of 2 pixels to ensure a GRBG Bayer pattern. */ - rect.left = clamp(ALIGN(crop->rect.left + 1, 2) - 1, + rect.left = clamp(ALIGN(sel->r.left + 1, 2) - 1, MT9V032_COLUMN_START_MIN, MT9V032_COLUMN_START_MAX); - rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1, + rect.top = clamp(ALIGN(sel->r.top + 1, 2) - 1, MT9V032_ROW_START_MIN, MT9V032_ROW_START_MAX); - rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), + rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), MT9V032_WINDOW_WIDTH_MIN, MT9V032_WINDOW_WIDTH_MAX); - rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), + rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), MT9V032_WINDOW_HEIGHT_MIN, MT9V032_WINDOW_HEIGHT_MAX); @@ -593,17 +598,17 @@ static int mt9v032_set_crop(struct v4l2_subdev *subdev, rect.height = min_t(unsigned int, rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which); + __crop = __mt9v032_get_pad_crop(mt9v032, fh, sel->pad, sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - __format = __mt9v032_get_pad_format(mt9v032, fh, crop->pad, - crop->which); + __format = __mt9v032_get_pad_format(mt9v032, fh, sel->pad, + sel->which); __format->width = rect.width; __format->height = rect.height; - if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { mt9v032->hratio = 1; mt9v032->vratio = 1; mt9v032_configure_pixel_rate(mt9v032); @@ -611,7 +616,7 @@ static int mt9v032_set_crop(struct v4l2_subdev *subdev, } *__crop = rect; - crop->rect = rect; + sel->r = rect; return 0; } @@ -844,8 +849,8 @@ static struct v4l2_subdev_pad_ops mt9v032_subdev_pad_ops = { .enum_frame_size = mt9v032_enum_frame_size, .get_fmt = mt9v032_get_format, .set_fmt = mt9v032_set_format, - .get_crop = mt9v032_get_crop, - .set_crop = mt9v032_set_crop, + .get_selection = mt9v032_get_selection, + .set_selection = mt9v032_set_selection, }; static struct v4l2_subdev_ops mt9v032_subdev_ops = { diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c index 2851581..19edafb 100644 --- a/drivers/media/i2c/s5k6aa.c +++ b/drivers/media/i2c/s5k6aa.c @@ -1161,17 +1161,21 @@ static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return ret; } -static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int s5k6aa_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct s5k6aa *s5k6aa = to_s5k6aa(sd); struct v4l2_rect *rect; - memset(crop->reserved, 0, sizeof(crop->reserved)); + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + memset(sel->reserved, 0, sizeof(sel->reserved)); mutex_lock(&s5k6aa->lock); - rect = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); - crop->rect = *rect; + rect = __s5k6aa_get_crop_rect(s5k6aa, fh, sel->which); + sel->r = *rect; mutex_unlock(&s5k6aa->lock); v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n", @@ -1180,35 +1184,39 @@ static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return 0; } -static int s5k6aa_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int s5k6aa_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct s5k6aa *s5k6aa = to_s5k6aa(sd); struct v4l2_mbus_framefmt *mf; unsigned int max_x, max_y; struct v4l2_rect *crop_r; + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + mutex_lock(&s5k6aa->lock); - crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); + crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, sel->which); - if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { mf = &s5k6aa->preset->mbus_fmt; s5k6aa->apply_crop = 1; } else { mf = v4l2_subdev_get_try_format(fh, 0); } - v4l_bound_align_image(&crop->rect.width, mf->width, + v4l_bound_align_image(&sel->r.width, mf->width, S5K6AA_WIN_WIDTH_MAX, 1, - &crop->rect.height, mf->height, + &sel->r.height, mf->height, S5K6AA_WIN_HEIGHT_MAX, 1, 0); - max_x = (S5K6AA_WIN_WIDTH_MAX - crop->rect.width) & ~1; - max_y = (S5K6AA_WIN_HEIGHT_MAX - crop->rect.height) & ~1; + max_x = (S5K6AA_WIN_WIDTH_MAX - sel->r.width) & ~1; + max_y = (S5K6AA_WIN_HEIGHT_MAX - sel->r.height) & ~1; - crop->rect.left = clamp_t(unsigned int, crop->rect.left, 0, max_x); - crop->rect.top = clamp_t(unsigned int, crop->rect.top, 0, max_y); + sel->r.left = clamp_t(unsigned int, sel->r.left, 0, max_x); + sel->r.top = clamp_t(unsigned int, sel->r.top, 0, max_y); - *crop_r = crop->rect; + *crop_r = sel->r; mutex_unlock(&s5k6aa->lock); @@ -1224,8 +1232,8 @@ static const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = { .enum_frame_interval = s5k6aa_enum_frame_interval, .get_fmt = s5k6aa_get_fmt, .set_fmt = s5k6aa_set_fmt, - .get_crop = s5k6aa_get_crop, - .set_crop = s5k6aa_set_crop, + .get_selection = s5k6aa_get_selection, + .set_selection = s5k6aa_set_selection, }; static const struct v4l2_subdev_video_ops s5k6aa_video_ops = { diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c index 0ba0bf2..bcf762b 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_isif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c @@ -1535,7 +1535,7 @@ isif_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, } /* - * isif_pad_set_crop() - set crop rectangle on pad + * isif_pad_set_selection() - set crop rectangle on pad * @sd: VPFE isif V4L2 subdevice * @fh: V4L2 subdev file handle * @code: pointer to v4l2_subdev_mbus_code_enum structure @@ -1543,35 +1543,36 @@ isif_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, * Return 0 on success, -EINVAL if pad is invalid */ static int -isif_pad_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +isif_pad_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - /* check wether its a valid pad */ - if (crop->pad != ISIF_PAD_SINK) + /* check whether it's a valid pad and target */ + if (sel->pad != ISIF_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - format = __isif_get_format(vpfe_isif, fh, crop->pad, crop->which); + format = __isif_get_format(vpfe_isif, fh, sel->pad, sel->which); if (format == NULL) return -EINVAL; /* check wether crop rect is within limits */ - if (crop->rect.top < 0 || crop->rect.left < 0 || - (crop->rect.left + crop->rect.width > + if (sel->r.top < 0 || sel->r.left < 0 || + (sel->r.left + sel->r.width > vpfe_isif->formats[ISIF_PAD_SINK].width) || - (crop->rect.top + crop->rect.height > + (sel->r.top + sel->r.height > vpfe_isif->formats[ISIF_PAD_SINK].height)) { - crop->rect.left = 0; - crop->rect.top = 0; - crop->rect.width = format->width; - crop->rect.height = format->height; + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = format->width; + sel->r.height = format->height; } /* adjust the width to 16 pixel boundary */ - crop->rect.width = ((crop->rect.width + 15) & ~0xf); - vpfe_isif->crop = crop->rect; - if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sel->r.width = ((sel->r.width + 15) & ~0xf); + vpfe_isif->crop = sel->r; + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { isif_set_image_window(vpfe_isif); } else { struct v4l2_rect *rect; @@ -1583,7 +1584,7 @@ isif_pad_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, } /* - * isif_pad_get_crop() - get crop rectangle on pad + * isif_pad_get_selection() - get crop rectangle on pad * @sd: VPFE isif V4L2 subdevice * @fh: V4L2 subdev file handle * @code: pointer to v4l2_subdev_mbus_code_enum structure @@ -1591,22 +1592,23 @@ isif_pad_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, * Return 0 on success, -EINVAL if pad is invalid */ static int -isif_pad_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +isif_pad_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); - /* check wether its a valid pad */ - if (crop->pad != ISIF_PAD_SINK) + /* check whether it's a valid pad and target */ + if (sel->pad != ISIF_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - if (crop->which == V4L2_SUBDEV_FORMAT_TRY) { + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_rect *rect; rect = v4l2_subdev_get_try_crop(fh, ISIF_PAD_SINK); - memcpy(&crop->rect, rect, sizeof(*rect)); + memcpy(&sel->r, rect, sizeof(*rect)); } else { - crop->rect = vpfe_isif->crop; + sel->r = vpfe_isif->crop; } return 0; @@ -1626,7 +1628,7 @@ isif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_subdev_format format; - struct v4l2_subdev_crop crop; + struct v4l2_subdev_selection sel; memset(&format, 0, sizeof(format)); format.pad = ISIF_PAD_SINK; @@ -1644,12 +1646,13 @@ isif_init_formats(struct v4l2_subdev *sd, format.format.height = MAX_HEIGHT; isif_set_format(sd, fh, &format); - memset(&crop, 0, sizeof(crop)); - crop.pad = ISIF_PAD_SINK; - crop.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; - crop.rect.width = MAX_WIDTH; - crop.rect.height = MAX_HEIGHT; - isif_pad_set_crop(sd, fh, &crop); + memset(&sel, 0, sizeof(sel)); + sel.pad = ISIF_PAD_SINK; + sel.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + sel.target = V4L2_SEL_TGT_CROP; + sel.r.width = MAX_WIDTH; + sel.r.height = MAX_HEIGHT; + isif_pad_set_selection(sd, fh, &sel); return 0; } @@ -1675,8 +1678,8 @@ static const struct v4l2_subdev_pad_ops isif_v4l2_pad_ops = { .enum_frame_size = isif_enum_frame_size, .get_fmt = isif_get_format, .set_fmt = isif_set_format, - .set_crop = isif_pad_set_crop, - .get_crop = isif_pad_get_crop, + .set_selection = isif_pad_set_selection, + .get_selection = isif_pad_get_selection, }; /* subdev operations */ -- cgit v0.10.2 From 05b9cc3eeffcb2b0d6720930fc144058d4ace1aa Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 4 Dec 2014 06:54:53 -0300 Subject: [media] v4l2-subdev: drop get/set_crop pad ops Drop the duplicate get/set_crop pad ops and only use get/set_selection. It makes no sense to have two duplicate ops in the internal subdev API. Signed-off-by: Hans Verkuil Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 543631c..19a034e 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -283,10 +283,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (rval) return rval; - rval = v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop); - if (rval != -ENOIOCTLCMD) - return rval; - memset(&sel, 0, sizeof(sel)); sel.which = crop->which; sel.pad = crop->pad; @@ -308,10 +304,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (rval) return rval; - rval = v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop); - if (rval != -ENOIOCTLCMD) - return rval; - memset(&sel, 0, sizeof(sel)); sel.which = crop->which; sel.pad = crop->pad; diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 5860292..b052184 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -503,10 +503,6 @@ struct v4l2_subdev_pad_ops { struct v4l2_subdev_format *format); int (*set_fmt)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *format); - int (*set_crop)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop); - int (*get_crop)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop); int (*get_selection)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_selection *sel); int (*set_selection)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, -- cgit v0.10.2 From cb03893e3e55924c87943435ab6c7b38e507466c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 4 Dec 2014 06:54:54 -0300 Subject: [media] v4l2-subdev: drop unused op enum_mbus_fmt Weird, this op isn't used at all. Seems to be orphaned code. Remove it. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index b052184..5beeb87 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -342,8 +342,6 @@ struct v4l2_subdev_video_ops { struct v4l2_dv_timings *timings); int (*enum_mbus_fmt)(struct v4l2_subdev *sd, unsigned int index, u32 *code); - int (*enum_mbus_fsizes)(struct v4l2_subdev *sd, - struct v4l2_frmsizeenum *fsize); int (*g_mbus_fmt)(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt); int (*try_mbus_fmt)(struct v4l2_subdev *sd, -- cgit v0.10.2 From 584ca0252abf31388cacbdb3018aab2c70362d8d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 4 Dec 2014 06:54:56 -0300 Subject: [media] media/i2c/Kconfig: drop superfluous MEDIA_CONTROLLER These drivers depend on VIDEO_V4L2_SUBDEV_API, which in turn depends on MEDIA_CONTROLLER. So it is sufficient to just depend on VIDEO_V4L2_SUBDEV_API. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 205d713..ca84543 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -196,7 +196,7 @@ config VIDEO_ADV7183 config VIDEO_ADV7604 tristate "Analog Devices ADV7604 decoder" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API ---help--- Support for the Analog Devices ADV7604 video decoder. @@ -208,7 +208,7 @@ config VIDEO_ADV7604 config VIDEO_ADV7842 tristate "Analog Devices ADV7842 decoder" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API ---help--- Support for the Analog Devices ADV7842 video decoder. @@ -422,7 +422,7 @@ config VIDEO_ADV7393 config VIDEO_ADV7511 tristate "Analog Devices ADV7511 encoder" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API ---help--- Support for the Analog Devices ADV7511 video encoder. -- cgit v0.10.2 From 2db73d4482da9bd979edee345e95bb3a88441b30 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 18 Dec 2014 15:04:19 +0200 Subject: spi: pxa2xx: Remove unused define Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 05c623c..e1dc8618 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -45,8 +45,6 @@ MODULE_DESCRIPTION("PXA2xx SSP SPI Controller"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:pxa2xx-spi"); -#define MAX_BUSES 3 - #define TIMOUT_DFLT 1000 /* -- cgit v0.10.2 From 7566bcc76b15186172c4db0414cf30c8a61e4a73 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 18 Dec 2014 15:04:20 +0200 Subject: spi: pxa2xx: Move is_lpss_ssp() tests to caller Move is_lpss_ssp() tests from functions to caller. Although this aims to improve readability it also saves a few code bytes on x86. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index e1dc8618..cc08500 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -251,9 +251,6 @@ static void lpss_ssp_setup(struct driver_data *drv_data) unsigned offset = 0x400; u32 value, orig; - if (!is_lpss_ssp(drv_data)) - return; - /* * Perform auto-detection of the LPSS SSP private registers. They * can be either at 1k or 2k offset from the base address. @@ -302,9 +299,6 @@ static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable) { u32 value; - if (!is_lpss_ssp(drv_data)) - return; - value = __lpss_ssp_read_priv(drv_data, SPI_CS_CONTROL); if (enable) value &= ~SPI_CS_CONTROL_CS_HIGH; @@ -332,7 +326,8 @@ static void cs_assert(struct driver_data *drv_data) return; } - lpss_ssp_cs_control(drv_data, true); + if (is_lpss_ssp(drv_data)) + lpss_ssp_cs_control(drv_data, true); } static void cs_deassert(struct driver_data *drv_data) @@ -352,7 +347,8 @@ static void cs_deassert(struct driver_data *drv_data) return; } - lpss_ssp_cs_control(drv_data, false); + if (is_lpss_ssp(drv_data)) + lpss_ssp_cs_control(drv_data, false); } int pxa2xx_spi_flush(struct driver_data *drv_data) @@ -1415,7 +1411,8 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) if (!is_quark_x1000_ssp(drv_data)) write_SSPSP(0, drv_data->ioaddr); - lpss_ssp_setup(drv_data); + if (is_lpss_ssp(drv_data)) + lpss_ssp_setup(drv_data); tasklet_init(&drv_data->pump_transfers, pump_transfers, (unsigned long)drv_data); -- cgit v0.10.2 From c4827bb859cbe8afad9287c9dd4e7162119d3d59 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 18 Dec 2014 15:04:21 +0200 Subject: spi: pxa2xx: Add definition for Intel Quark DDS_RATE register Intel Quark DDS_RATE register is defined only in register access macro. Add a definition for it to common SSP register definitions for preparing to cleanup those macros. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index 77aed9e..dab545b 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -37,6 +37,7 @@ #define SSDR (0x10) /* SSP Data Write/Data Read Register */ #define SSTO (0x28) /* SSP Time Out Register */ +#define DDS_RATE (0x28) /* SSP DDS Clock Rate Register (Intel Quark) */ #define SSPSP (0x2C) /* SSP Programmable Serial Protocol */ #define SSTSA (0x30) /* SSP Tx Timeslot Active */ #define SSRSA (0x34) /* SSP Rx Timeslot Active */ -- cgit v0.10.2 From 8e8dd9fb25f6dab6ac3f19f445a51b2c71e08d5c Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 18 Dec 2014 15:04:22 +0200 Subject: spi: pxa2xx: Pass driver data instead of ioaddr to wait_ssp_rx_stall() Pass pointer to struct driver_data instead of ioaddr to wait_ssp_rx_stall() for preparing to register access macro cleanup. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx-pxadma.c b/drivers/spi/spi-pxa2xx-pxadma.c index e8a26f2..aaa5fc6 100644 --- a/drivers/spi/spi-pxa2xx-pxadma.c +++ b/drivers/spi/spi-pxa2xx-pxadma.c @@ -118,11 +118,11 @@ static void pxa2xx_spi_unmap_dma_buffers(struct driver_data *drv_data) drv_data->dma_mapped = 0; } -static int wait_ssp_rx_stall(void const __iomem *ioaddr) +static int wait_ssp_rx_stall(struct driver_data *drv_data) { unsigned long limit = loops_per_jiffy << 1; - while ((read_SSSR(ioaddr) & SSSR_BSY) && --limit) + while ((read_SSSR(drv_data->ioaddr) & SSSR_BSY) && --limit) cpu_relax(); return limit; @@ -228,7 +228,7 @@ void pxa2xx_spi_dma_handler(int channel, void *data) && (drv_data->ssp_type == PXA25x_SSP)) { /* Wait for rx to stall */ - if (wait_ssp_rx_stall(drv_data->ioaddr) == 0) + if (wait_ssp_rx_stall(drv_data) == 0) dev_err(&drv_data->pdev->dev, "dma_handler: ssp rx stall failed\n"); -- cgit v0.10.2 From c039dd275e88989478abe5f28e4e15648c28ce33 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 18 Dec 2014 15:04:23 +0200 Subject: spi: pxa2xx: Cleanup register access macros Currently SSP registers are accessed by having an own read and write macros for each register. For instance read_SSSR(iobase) and write_SSSR(iobase). In my opinion this hurts readability and requires new macros to be defined for each new added register. Let's define and use instead common pxa2xx_spi_read() and pxa2xx_spi_write() accessors. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx-dma.c b/drivers/spi/spi-pxa2xx-dma.c index 62a9297..66a1739 100644 --- a/drivers/spi/spi-pxa2xx-dma.c +++ b/drivers/spi/spi-pxa2xx-dma.c @@ -111,23 +111,24 @@ static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data, * by using ->dma_running. */ if (atomic_dec_and_test(&drv_data->dma_running)) { - void __iomem *reg = drv_data->ioaddr; - /* * If the other CPU is still handling the ROR interrupt we * might not know about the error yet. So we re-check the * ROR bit here before we clear the status register. */ if (!error) { - u32 status = read_SSSR(reg) & drv_data->mask_sr; + u32 status = pxa2xx_spi_read(drv_data, SSSR) + & drv_data->mask_sr; error = status & SSSR_ROR; } /* Clear status & disable interrupts */ - write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); + pxa2xx_spi_write(drv_data, SSCR1, + pxa2xx_spi_read(drv_data, SSCR1) + & ~drv_data->dma_cr1); write_SSSR_CS(drv_data, drv_data->clear_sr); if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(0, reg); + pxa2xx_spi_write(drv_data, SSTO, 0); if (!error) { pxa2xx_spi_unmap_dma_buffers(drv_data); @@ -139,7 +140,9 @@ static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data, msg->state = pxa2xx_spi_next_transfer(drv_data); } else { /* In case we got an error we disable the SSP now */ - write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); + pxa2xx_spi_write(drv_data, SSCR0, + pxa2xx_spi_read(drv_data, SSCR0) + & ~SSCR0_SSE); msg->state = ERROR_STATE; } @@ -247,7 +250,7 @@ irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data) { u32 status; - status = read_SSSR(drv_data->ioaddr) & drv_data->mask_sr; + status = pxa2xx_spi_read(drv_data, SSSR) & drv_data->mask_sr; if (status & SSSR_ROR) { dev_err(&drv_data->pdev->dev, "FIFO overrun\n"); diff --git a/drivers/spi/spi-pxa2xx-pxadma.c b/drivers/spi/spi-pxa2xx-pxadma.c index aaa5fc6..6c82d49 100644 --- a/drivers/spi/spi-pxa2xx-pxadma.c +++ b/drivers/spi/spi-pxa2xx-pxadma.c @@ -122,7 +122,7 @@ static int wait_ssp_rx_stall(struct driver_data *drv_data) { unsigned long limit = loops_per_jiffy << 1; - while ((read_SSSR(drv_data->ioaddr) & SSSR_BSY) && --limit) + while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_BSY) && --limit) cpu_relax(); return limit; @@ -141,17 +141,18 @@ static int wait_dma_channel_stop(int channel) static void pxa2xx_spi_dma_error_stop(struct driver_data *drv_data, const char *msg) { - void __iomem *reg = drv_data->ioaddr; - /* Stop and reset */ DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; write_SSSR_CS(drv_data, drv_data->clear_sr); - write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); + pxa2xx_spi_write(drv_data, SSCR1, + pxa2xx_spi_read(drv_data, SSCR1) + & ~drv_data->dma_cr1); if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(0, reg); + pxa2xx_spi_write(drv_data, SSTO, 0); pxa2xx_spi_flush(drv_data); - write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); + pxa2xx_spi_write(drv_data, SSCR0, + pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE); pxa2xx_spi_unmap_dma_buffers(drv_data); @@ -163,11 +164,12 @@ static void pxa2xx_spi_dma_error_stop(struct driver_data *drv_data, static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; struct spi_message *msg = drv_data->cur_msg; /* Clear and disable interrupts on SSP and DMA channels*/ - write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); + pxa2xx_spi_write(drv_data, SSCR1, + pxa2xx_spi_read(drv_data, SSCR1) + & ~drv_data->dma_cr1); write_SSSR_CS(drv_data, drv_data->clear_sr); DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; @@ -240,9 +242,8 @@ void pxa2xx_spi_dma_handler(int channel, void *data) irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data) { u32 irq_status; - void __iomem *reg = drv_data->ioaddr; - irq_status = read_SSSR(reg) & drv_data->mask_sr; + irq_status = pxa2xx_spi_read(drv_data, SSSR) & drv_data->mask_sr; if (irq_status & SSSR_ROR) { pxa2xx_spi_dma_error_stop(drv_data, "dma_transfer: fifo overrun"); @@ -252,7 +253,7 @@ irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data) /* Check for false positive timeout */ if ((irq_status & SSSR_TINT) && (DCSR(drv_data->tx_channel) & DCSR_RUN)) { - write_SSSR(SSSR_TINT, reg); + pxa2xx_spi_write(drv_data, SSSR, SSSR_TINT); return IRQ_HANDLED; } @@ -261,7 +262,7 @@ irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data) /* Clear and disable timeout interrupt, do the rest in * dma_transfer_complete */ if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(0, reg); + pxa2xx_spi_write(drv_data, SSTO, 0); /* finish this transfer, start the next */ pxa2xx_spi_dma_transfer_complete(drv_data); diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index cc08500..b00db8b 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -160,7 +160,6 @@ pxa2xx_spi_get_rx_default_thre(const struct driver_data *drv_data) static bool pxa2xx_spi_txfifo_full(const struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; u32 mask; switch (drv_data->ssp_type) { @@ -172,7 +171,7 @@ static bool pxa2xx_spi_txfifo_full(const struct driver_data *drv_data) break; } - return (read_SSSR(reg) & mask) == mask; + return (pxa2xx_spi_read(drv_data, SSSR) & mask) == mask; } static void pxa2xx_spi_clear_rx_thre(const struct driver_data *drv_data, @@ -312,7 +311,7 @@ static void cs_assert(struct driver_data *drv_data) struct chip_data *chip = drv_data->cur_chip; if (drv_data->ssp_type == CE4100_SSP) { - write_SSSR(drv_data->cur_chip->frm, drv_data->ioaddr); + pxa2xx_spi_write(drv_data, SSSR, drv_data->cur_chip->frm); return; } @@ -355,13 +354,10 @@ int pxa2xx_spi_flush(struct driver_data *drv_data) { unsigned long limit = loops_per_jiffy << 1; - void __iomem *reg = drv_data->ioaddr; - do { - while (read_SSSR(reg) & SSSR_RNE) { - read_SSDR(reg); - } - } while ((read_SSSR(reg) & SSSR_BSY) && --limit); + while (pxa2xx_spi_read(drv_data, SSSR) & SSSR_RNE) + pxa2xx_spi_read(drv_data, SSDR); + } while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_BSY) && --limit); write_SSSR_CS(drv_data, SSSR_ROR); return limit; @@ -369,14 +365,13 @@ int pxa2xx_spi_flush(struct driver_data *drv_data) static int null_writer(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; u8 n_bytes = drv_data->n_bytes; if (pxa2xx_spi_txfifo_full(drv_data) || (drv_data->tx == drv_data->tx_end)) return 0; - write_SSDR(0, reg); + pxa2xx_spi_write(drv_data, SSDR, 0); drv_data->tx += n_bytes; return 1; @@ -384,12 +379,11 @@ static int null_writer(struct driver_data *drv_data) static int null_reader(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; u8 n_bytes = drv_data->n_bytes; - while ((read_SSSR(reg) & SSSR_RNE) - && (drv_data->rx < drv_data->rx_end)) { - read_SSDR(reg); + while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_RNE) + && (drv_data->rx < drv_data->rx_end)) { + pxa2xx_spi_read(drv_data, SSDR); drv_data->rx += n_bytes; } @@ -398,13 +392,11 @@ static int null_reader(struct driver_data *drv_data) static int u8_writer(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; - if (pxa2xx_spi_txfifo_full(drv_data) || (drv_data->tx == drv_data->tx_end)) return 0; - write_SSDR(*(u8 *)(drv_data->tx), reg); + pxa2xx_spi_write(drv_data, SSDR, *(u8 *)(drv_data->tx)); ++drv_data->tx; return 1; @@ -412,11 +404,9 @@ static int u8_writer(struct driver_data *drv_data) static int u8_reader(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; - - while ((read_SSSR(reg) & SSSR_RNE) - && (drv_data->rx < drv_data->rx_end)) { - *(u8 *)(drv_data->rx) = read_SSDR(reg); + while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_RNE) + && (drv_data->rx < drv_data->rx_end)) { + *(u8 *)(drv_data->rx) = pxa2xx_spi_read(drv_data, SSDR); ++drv_data->rx; } @@ -425,13 +415,11 @@ static int u8_reader(struct driver_data *drv_data) static int u16_writer(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; - if (pxa2xx_spi_txfifo_full(drv_data) || (drv_data->tx == drv_data->tx_end)) return 0; - write_SSDR(*(u16 *)(drv_data->tx), reg); + pxa2xx_spi_write(drv_data, SSDR, *(u16 *)(drv_data->tx)); drv_data->tx += 2; return 1; @@ -439,11 +427,9 @@ static int u16_writer(struct driver_data *drv_data) static int u16_reader(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; - - while ((read_SSSR(reg) & SSSR_RNE) - && (drv_data->rx < drv_data->rx_end)) { - *(u16 *)(drv_data->rx) = read_SSDR(reg); + while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_RNE) + && (drv_data->rx < drv_data->rx_end)) { + *(u16 *)(drv_data->rx) = pxa2xx_spi_read(drv_data, SSDR); drv_data->rx += 2; } @@ -452,13 +438,11 @@ static int u16_reader(struct driver_data *drv_data) static int u32_writer(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; - if (pxa2xx_spi_txfifo_full(drv_data) || (drv_data->tx == drv_data->tx_end)) return 0; - write_SSDR(*(u32 *)(drv_data->tx), reg); + pxa2xx_spi_write(drv_data, SSDR, *(u32 *)(drv_data->tx)); drv_data->tx += 4; return 1; @@ -466,11 +450,9 @@ static int u32_writer(struct driver_data *drv_data) static int u32_reader(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; - - while ((read_SSSR(reg) & SSSR_RNE) - && (drv_data->rx < drv_data->rx_end)) { - *(u32 *)(drv_data->rx) = read_SSDR(reg); + while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_RNE) + && (drv_data->rx < drv_data->rx_end)) { + *(u32 *)(drv_data->rx) = pxa2xx_spi_read(drv_data, SSDR); drv_data->rx += 4; } @@ -546,27 +528,25 @@ static void giveback(struct driver_data *drv_data) static void reset_sccr1(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; struct chip_data *chip = drv_data->cur_chip; u32 sccr1_reg; - sccr1_reg = read_SSCR1(reg) & ~drv_data->int_cr1; + sccr1_reg = pxa2xx_spi_read(drv_data, SSCR1) & ~drv_data->int_cr1; sccr1_reg &= ~SSCR1_RFT; sccr1_reg |= chip->threshold; - write_SSCR1(sccr1_reg, reg); + pxa2xx_spi_write(drv_data, SSCR1, sccr1_reg); } static void int_error_stop(struct driver_data *drv_data, const char* msg) { - void __iomem *reg = drv_data->ioaddr; - /* Stop and reset SSP */ write_SSSR_CS(drv_data, drv_data->clear_sr); reset_sccr1(drv_data); if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(0, reg); + pxa2xx_spi_write(drv_data, SSTO, 0); pxa2xx_spi_flush(drv_data); - write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); + pxa2xx_spi_write(drv_data, SSCR0, + pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE); dev_err(&drv_data->pdev->dev, "%s\n", msg); @@ -576,13 +556,11 @@ static void int_error_stop(struct driver_data *drv_data, const char* msg) static void int_transfer_complete(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; - /* Stop SSP */ write_SSSR_CS(drv_data, drv_data->clear_sr); reset_sccr1(drv_data); if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(0, reg); + pxa2xx_spi_write(drv_data, SSTO, 0); /* Update total byte transferred return count actual bytes read */ drv_data->cur_msg->actual_length += drv_data->len - @@ -601,12 +579,10 @@ static void int_transfer_complete(struct driver_data *drv_data) static irqreturn_t interrupt_transfer(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; - - u32 irq_mask = (read_SSCR1(reg) & SSCR1_TIE) ? - drv_data->mask_sr : drv_data->mask_sr & ~SSSR_TFS; + u32 irq_mask = (pxa2xx_spi_read(drv_data, SSCR1) & SSCR1_TIE) ? + drv_data->mask_sr : drv_data->mask_sr & ~SSSR_TFS; - u32 irq_status = read_SSSR(reg) & irq_mask; + u32 irq_status = pxa2xx_spi_read(drv_data, SSSR) & irq_mask; if (irq_status & SSSR_ROR) { int_error_stop(drv_data, "interrupt_transfer: fifo overrun"); @@ -614,7 +590,7 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) } if (irq_status & SSSR_TINT) { - write_SSSR(SSSR_TINT, reg); + pxa2xx_spi_write(drv_data, SSSR, SSSR_TINT); if (drv_data->read(drv_data)) { int_transfer_complete(drv_data); return IRQ_HANDLED; @@ -638,7 +614,7 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) u32 bytes_left; u32 sccr1_reg; - sccr1_reg = read_SSCR1(reg); + sccr1_reg = pxa2xx_spi_read(drv_data, SSCR1); sccr1_reg &= ~SSCR1_TIE; /* @@ -664,7 +640,7 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) pxa2xx_spi_set_rx_thre(drv_data, &sccr1_reg, rx_thre); } - write_SSCR1(sccr1_reg, reg); + pxa2xx_spi_write(drv_data, SSCR1, sccr1_reg); } /* We did something */ @@ -674,7 +650,6 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) static irqreturn_t ssp_int(int irq, void *dev_id) { struct driver_data *drv_data = dev_id; - void __iomem *reg = drv_data->ioaddr; u32 sccr1_reg; u32 mask = drv_data->mask_sr; u32 status; @@ -694,11 +669,11 @@ static irqreturn_t ssp_int(int irq, void *dev_id) * are all set to one. That means that the device is already * powered off. */ - status = read_SSSR(reg); + status = pxa2xx_spi_read(drv_data, SSSR); if (status == ~0) return IRQ_NONE; - sccr1_reg = read_SSCR1(reg); + sccr1_reg = pxa2xx_spi_read(drv_data, SSCR1); /* Ignore possible writes if we don't need to write */ if (!(sccr1_reg & SSCR1_TIE)) @@ -709,10 +684,14 @@ static irqreturn_t ssp_int(int irq, void *dev_id) if (!drv_data->cur_msg) { - write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); - write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg); + pxa2xx_spi_write(drv_data, SSCR0, + pxa2xx_spi_read(drv_data, SSCR0) + & ~SSCR0_SSE); + pxa2xx_spi_write(drv_data, SSCR1, + pxa2xx_spi_read(drv_data, SSCR1) + & ~drv_data->int_cr1); if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(0, reg); + pxa2xx_spi_write(drv_data, SSTO, 0); write_SSSR_CS(drv_data, drv_data->clear_sr); dev_err(&drv_data->pdev->dev, @@ -781,7 +760,6 @@ static void pump_transfers(unsigned long data) struct spi_transfer *transfer = NULL; struct spi_transfer *previous = NULL; struct chip_data *chip = NULL; - void __iomem *reg = drv_data->ioaddr; u32 clk_div = 0; u8 bits = 0; u32 speed = 0; @@ -925,7 +903,7 @@ static void pump_transfers(unsigned long data) /* Clear status and start DMA engine */ cr1 = chip->cr1 | dma_thresh | drv_data->dma_cr1; - write_SSSR(drv_data->clear_sr, reg); + pxa2xx_spi_write(drv_data, SSSR, drv_data->clear_sr); pxa2xx_spi_dma_start(drv_data); } else { @@ -938,39 +916,43 @@ static void pump_transfers(unsigned long data) } if (is_lpss_ssp(drv_data)) { - if ((read_SSIRF(reg) & 0xff) != chip->lpss_rx_threshold) - write_SSIRF(chip->lpss_rx_threshold, reg); - if ((read_SSITF(reg) & 0xffff) != chip->lpss_tx_threshold) - write_SSITF(chip->lpss_tx_threshold, reg); + if ((pxa2xx_spi_read(drv_data, SSIRF) & 0xff) + != chip->lpss_rx_threshold) + pxa2xx_spi_write(drv_data, SSIRF, + chip->lpss_rx_threshold); + if ((pxa2xx_spi_read(drv_data, SSITF) & 0xffff) + != chip->lpss_tx_threshold) + pxa2xx_spi_write(drv_data, SSITF, + chip->lpss_tx_threshold); } if (is_quark_x1000_ssp(drv_data) && - (read_DDS_RATE(reg) != chip->dds_rate)) - write_DDS_RATE(chip->dds_rate, reg); + (pxa2xx_spi_read(drv_data, DDS_RATE) != chip->dds_rate)) + pxa2xx_spi_write(drv_data, DDS_RATE, chip->dds_rate); /* see if we need to reload the config registers */ - if ((read_SSCR0(reg) != cr0) || - (read_SSCR1(reg) & change_mask) != (cr1 & change_mask)) { - + if ((pxa2xx_spi_read(drv_data, SSCR0) != cr0) + || (pxa2xx_spi_read(drv_data, SSCR1) & change_mask) + != (cr1 & change_mask)) { /* stop the SSP, and update the other bits */ - write_SSCR0(cr0 & ~SSCR0_SSE, reg); + pxa2xx_spi_write(drv_data, SSCR0, cr0 & ~SSCR0_SSE); if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(chip->timeout, reg); + pxa2xx_spi_write(drv_data, SSTO, chip->timeout); /* first set CR1 without interrupt and service enables */ - write_SSCR1(cr1 & change_mask, reg); + pxa2xx_spi_write(drv_data, SSCR1, cr1 & change_mask); /* restart the SSP */ - write_SSCR0(cr0, reg); + pxa2xx_spi_write(drv_data, SSCR0, cr0); } else { if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(chip->timeout, reg); + pxa2xx_spi_write(drv_data, SSTO, chip->timeout); } cs_assert(drv_data); /* after chip select, release the data by enabling service * requests and interrupts, without changing any mode bits */ - write_SSCR1(cr1, reg); + pxa2xx_spi_write(drv_data, SSCR1, cr1); } static int pxa2xx_spi_transfer_one_message(struct spi_master *master, @@ -999,8 +981,8 @@ static int pxa2xx_spi_unprepare_transfer(struct spi_master *master) struct driver_data *drv_data = spi_master_get_devdata(master); /* Disable the SSP now */ - write_SSCR0(read_SSCR0(drv_data->ioaddr) & ~SSCR0_SSE, - drv_data->ioaddr); + pxa2xx_spi_write(drv_data, SSCR0, + pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE); return 0; } @@ -1283,6 +1265,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) struct driver_data *drv_data; struct ssp_device *ssp; int status; + u32 tmp; platform_info = dev_get_platdata(dev); if (!platform_info) { @@ -1380,36 +1363,32 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) drv_data->max_clk_rate = clk_get_rate(ssp->clk); /* Load default SSP configuration */ - write_SSCR0(0, drv_data->ioaddr); + pxa2xx_spi_write(drv_data, SSCR0, 0); switch (drv_data->ssp_type) { case QUARK_X1000_SSP: - write_SSCR1(QUARK_X1000_SSCR1_RxTresh( - RX_THRESH_QUARK_X1000_DFLT) | - QUARK_X1000_SSCR1_TxTresh( - TX_THRESH_QUARK_X1000_DFLT), - drv_data->ioaddr); + tmp = QUARK_X1000_SSCR1_RxTresh(RX_THRESH_QUARK_X1000_DFLT) + | QUARK_X1000_SSCR1_TxTresh(TX_THRESH_QUARK_X1000_DFLT); + pxa2xx_spi_write(drv_data, SSCR1, tmp); /* using the Motorola SPI protocol and use 8 bit frame */ - write_SSCR0(QUARK_X1000_SSCR0_Motorola - | QUARK_X1000_SSCR0_DataSize(8), - drv_data->ioaddr); + pxa2xx_spi_write(drv_data, SSCR0, + QUARK_X1000_SSCR0_Motorola + | QUARK_X1000_SSCR0_DataSize(8)); break; default: - write_SSCR1(SSCR1_RxTresh(RX_THRESH_DFLT) | - SSCR1_TxTresh(TX_THRESH_DFLT), - drv_data->ioaddr); - write_SSCR0(SSCR0_SCR(2) - | SSCR0_Motorola - | SSCR0_DataSize(8), - drv_data->ioaddr); + tmp = SSCR1_RxTresh(RX_THRESH_DFLT) | + SSCR1_TxTresh(TX_THRESH_DFLT); + pxa2xx_spi_write(drv_data, SSCR1, tmp); + tmp = SSCR0_SCR(2) | SSCR0_Motorola | SSCR0_DataSize(8); + pxa2xx_spi_write(drv_data, SSCR0, tmp); break; } if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(0, drv_data->ioaddr); + pxa2xx_spi_write(drv_data, SSTO, 0); if (!is_quark_x1000_ssp(drv_data)) - write_SSPSP(0, drv_data->ioaddr); + pxa2xx_spi_write(drv_data, SSPSP, 0); if (is_lpss_ssp(drv_data)) lpss_ssp_setup(drv_data); @@ -1455,7 +1434,7 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); /* Disable the SSP at the peripheral and SOC level */ - write_SSCR0(0, drv_data->ioaddr); + pxa2xx_spi_write(drv_data, SSCR0, 0); clk_disable_unprepare(ssp->clk); /* Release DMA */ @@ -1492,7 +1471,7 @@ static int pxa2xx_spi_suspend(struct device *dev) status = spi_master_suspend(drv_data->master); if (status != 0) return status; - write_SSCR0(0, drv_data->ioaddr); + pxa2xx_spi_write(drv_data, SSCR0, 0); if (!pm_runtime_suspended(dev)) clk_disable_unprepare(ssp->clk); diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h index 6bec59c..85a58c9 100644 --- a/drivers/spi/spi-pxa2xx.h +++ b/drivers/spi/spi-pxa2xx.h @@ -115,23 +115,17 @@ struct chip_data { void (*cs_control)(u32 command); }; -#define DEFINE_SSP_REG(reg, off) \ -static inline u32 read_##reg(void const __iomem *p) \ -{ return __raw_readl(p + (off)); } \ -\ -static inline void write_##reg(u32 v, void __iomem *p) \ -{ __raw_writel(v, p + (off)); } - -DEFINE_SSP_REG(SSCR0, 0x00) -DEFINE_SSP_REG(SSCR1, 0x04) -DEFINE_SSP_REG(SSSR, 0x08) -DEFINE_SSP_REG(SSITR, 0x0c) -DEFINE_SSP_REG(SSDR, 0x10) -DEFINE_SSP_REG(DDS_RATE, 0x28) /* DDS Clock Rate */ -DEFINE_SSP_REG(SSTO, 0x28) -DEFINE_SSP_REG(SSPSP, 0x2c) -DEFINE_SSP_REG(SSITF, SSITF) -DEFINE_SSP_REG(SSIRF, SSIRF) +static inline u32 pxa2xx_spi_read(const struct driver_data *drv_data, + unsigned reg) +{ + return __raw_readl(drv_data->ioaddr + reg); +} + +static inline void pxa2xx_spi_write(const struct driver_data *drv_data, + unsigned reg, u32 val) +{ + __raw_writel(val, drv_data->ioaddr + reg); +} #define START_STATE ((void *)0) #define RUNNING_STATE ((void *)1) @@ -155,13 +149,11 @@ static inline int pxa25x_ssp_comp(struct driver_data *drv_data) static inline void write_SSSR_CS(struct driver_data *drv_data, u32 val) { - void __iomem *reg = drv_data->ioaddr; - if (drv_data->ssp_type == CE4100_SSP || drv_data->ssp_type == QUARK_X1000_SSP) - val |= read_SSSR(reg) & SSSR_ALT_FRM_MASK; + val |= pxa2xx_spi_read(drv_data, SSSR) & SSSR_ALT_FRM_MASK; - write_SSSR(val, reg); + pxa2xx_spi_write(drv_data, SSSR, val); } extern int pxa2xx_spi_flush(struct driver_data *drv_data); -- cgit v0.10.2 From 74d50da3e47f9e0b9eba2be4372556aa82d0f05d Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 22 Dec 2014 13:40:20 +0100 Subject: livepatch: MAINTAINERS: add git tree location Update MAINTAINERS entry for live patching infrastructure so that it points to git tree hosted at kernel.org. Signed-off-by: Jiri Kosina diff --git a/MAINTAINERS b/MAINTAINERS index afe93ea..cff1f33 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5797,6 +5797,7 @@ F: arch/x86/kernel/livepatch.c F: Documentation/ABI/testing/sysfs-kernel-livepatch F: samples/livepatch/ L: live-patching@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/livepatching.git LLC (802.2) M: Arnaldo Carvalho de Melo -- cgit v0.10.2 From cf6ab6d9143b157786bf29bca5c32e55234bb07d Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Mon, 15 Dec 2014 20:13:31 -0500 Subject: tracing: Add ref count to tracer for when they are being read by pipe When one of the trace pipe files are being read (by either the trace_pipe or trace_pipe_raw), do not allow the current_trace to change. By adding a ref count that is incremented when the pipe files are opened, will prevent the current_trace from being changed. This will allow for the removal of the global trace_types_lock from reading the pipe buffers (which is currently a bottle neck). Signed-off-by: Steven Rostedt diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 2e76797..ed3fba1 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4140,6 +4140,12 @@ static int tracing_set_tracer(struct trace_array *tr, const char *buf) goto out; } + /* If trace pipe files are being read, we can't change the tracer */ + if (tr->current_trace->ref) { + ret = -EBUSY; + goto out; + } + trace_branch_disable(); tr->current_trace->enabled--; @@ -4363,6 +4369,8 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp) iter->trace->pipe_open(iter); nonseekable_open(inode, filp); + + tr->current_trace->ref++; out: mutex_unlock(&trace_types_lock); return ret; @@ -4382,6 +4390,8 @@ static int tracing_release_pipe(struct inode *inode, struct file *file) mutex_lock(&trace_types_lock); + tr->current_trace->ref--; + if (iter->trace->pipe_close) iter->trace->pipe_close(iter); @@ -5331,6 +5341,8 @@ static int tracing_buffers_open(struct inode *inode, struct file *filp) filp->private_data = info; + tr->current_trace->ref++; + mutex_unlock(&trace_types_lock); ret = nonseekable_open(inode, filp); @@ -5437,6 +5449,8 @@ static int tracing_buffers_release(struct inode *inode, struct file *file) mutex_lock(&trace_types_lock); + iter->tr->current_trace->ref--; + __trace_array_put(iter->tr); if (info->spare) @@ -6416,7 +6430,7 @@ static int instance_delete(const char *name) goto out_unlock; ret = -EBUSY; - if (tr->ref) + if (tr->ref || (tr->current_trace && tr->current_trace->ref)) goto out_unlock; list_del(&tr->list); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 8de48ba..0eddfeb 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -388,6 +388,7 @@ struct tracer { struct tracer *next; struct tracer_flags *flags; int enabled; + int ref; bool print_max; bool allow_instances; #ifdef CONFIG_TRACER_MAX_TRACE -- cgit v0.10.2 From b2e5dda14fbc2ca1155c614c500c6de6f5b4ba8c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 23 Dec 2014 00:27:41 +0000 Subject: spi/dln2: Fix for PM_RUNTIME removal Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-dln2.c b/drivers/spi/spi-dln2.c index 7b35454..cede063 100644 --- a/drivers/spi/spi-dln2.c +++ b/drivers/spi/spi-dln2.c @@ -850,7 +850,7 @@ static int dln2_spi_resume(struct device *dev) } #endif /* CONFIG_PM_SLEEP */ -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM static int dln2_spi_runtime_suspend(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); -- cgit v0.10.2 From 574732c73d155320f9358d9ee5d84beb0f4ecee2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 23 Dec 2014 15:05:36 +1030 Subject: param: initialize store function to NULL if not available. I rebased Kees' 'param: do not set store func without write perm' on top of my 'params: cleanup sysfs allocation'. However, my patch uses krealloc which doesn't zero memory, leaving .store unset. Reported-by: Sasha Levin Cc: Kees Cook Signed-off-by: Rusty Russell diff --git a/kernel/params.c b/kernel/params.c index 0af9b2c..bd65d136 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -648,6 +648,8 @@ static __modinit int add_sysfs_param(struct module_kobject *mk, /* Do not allow runtime DAC changes to make param writable. */ if ((kp->perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) mk->mp->attrs[mk->mp->num].mattr.store = param_attr_store; + else + mk->mp->attrs[mk->mp->num].mattr.store = NULL; mk->mp->attrs[mk->mp->num].mattr.attr.name = (char *)name; mk->mp->attrs[mk->mp->num].mattr.attr.mode = kp->perm; mk->mp->num++; -- cgit v0.10.2 From d716ff71dd12bc6328f84a9ec1c3647daf01c827 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Mon, 15 Dec 2014 22:31:07 -0500 Subject: tracing: Remove taking of trace_types_lock in pipe files Taking the global mutex "trace_types_lock" in the trace_pipe files causes a bottle neck as most the pipe files can be read per cpu and there's no reason to serialize them. The current_trace variable was given a ref count and it can not change when the ref count is not zero. Opening the trace_pipe files will up the ref count (and decremented on close), so that the lock no longer needs to be taken when accessing the current_trace variable. Signed-off-by: Steven Rostedt diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index ed3fba1..7669b1f 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4332,17 +4332,7 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp) } trace_seq_init(&iter->seq); - - /* - * We make a copy of the current tracer to avoid concurrent - * changes on it while we are reading. - */ - iter->trace = kmalloc(sizeof(*iter->trace), GFP_KERNEL); - if (!iter->trace) { - ret = -ENOMEM; - goto fail; - } - *iter->trace = *tr->current_trace; + iter->trace = tr->current_trace; if (!alloc_cpumask_var(&iter->started, GFP_KERNEL)) { ret = -ENOMEM; @@ -4399,7 +4389,6 @@ static int tracing_release_pipe(struct inode *inode, struct file *file) free_cpumask_var(iter->started); mutex_destroy(&iter->mutex); - kfree(iter->trace); kfree(iter); trace_array_put(tr); @@ -4432,7 +4421,7 @@ tracing_poll_pipe(struct file *filp, poll_table *poll_table) return trace_poll(iter, filp, poll_table); } -/* Must be called with trace_types_lock mutex held. */ +/* Must be called with iter->mutex held. */ static int tracing_wait_pipe(struct file *filp) { struct trace_iterator *iter = filp->private_data; @@ -4477,7 +4466,6 @@ tracing_read_pipe(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { struct trace_iterator *iter = filp->private_data; - struct trace_array *tr = iter->tr; ssize_t sret; /* return any leftover data */ @@ -4487,12 +4475,6 @@ tracing_read_pipe(struct file *filp, char __user *ubuf, trace_seq_init(&iter->seq); - /* copy the tracer to avoid using a global lock all around */ - mutex_lock(&trace_types_lock); - if (unlikely(iter->trace->name != tr->current_trace->name)) - *iter->trace = *tr->current_trace; - mutex_unlock(&trace_types_lock); - /* * Avoid more than one consumer on a single file descriptor * This is just a matter of traces coherency, the ring buffer itself @@ -4652,7 +4634,6 @@ static ssize_t tracing_splice_read_pipe(struct file *filp, .ops = &tracing_pipe_buf_ops, .spd_release = tracing_spd_release_pipe, }; - struct trace_array *tr = iter->tr; ssize_t ret; size_t rem; unsigned int i; @@ -4660,12 +4641,6 @@ static ssize_t tracing_splice_read_pipe(struct file *filp, if (splice_grow_spd(pipe, &spd)) return -ENOMEM; - /* copy the tracer to avoid using a global lock all around */ - mutex_lock(&trace_types_lock); - if (unlikely(iter->trace->name != tr->current_trace->name)) - *iter->trace = *tr->current_trace; - mutex_unlock(&trace_types_lock); - mutex_lock(&iter->mutex); if (iter->trace->splice_read) { @@ -5373,21 +5348,16 @@ tracing_buffers_read(struct file *filp, char __user *ubuf, if (!count) return 0; - mutex_lock(&trace_types_lock); - #ifdef CONFIG_TRACER_MAX_TRACE - if (iter->snapshot && iter->tr->current_trace->use_max_tr) { - size = -EBUSY; - goto out_unlock; - } + if (iter->snapshot && iter->tr->current_trace->use_max_tr) + return -EBUSY; #endif if (!info->spare) info->spare = ring_buffer_alloc_read_page(iter->trace_buffer->buffer, iter->cpu_file); - size = -ENOMEM; if (!info->spare) - goto out_unlock; + return -ENOMEM; /* Do we have previous read data to read? */ if (info->read < PAGE_SIZE) @@ -5403,21 +5373,16 @@ tracing_buffers_read(struct file *filp, char __user *ubuf, if (ret < 0) { if (trace_empty(iter)) { - if ((filp->f_flags & O_NONBLOCK)) { - size = -EAGAIN; - goto out_unlock; - } - mutex_unlock(&trace_types_lock); + if ((filp->f_flags & O_NONBLOCK)) + return -EAGAIN; + ret = wait_on_pipe(iter, false); - mutex_lock(&trace_types_lock); - if (ret) { - size = ret; - goto out_unlock; - } + if (ret) + return ret; + goto again; } - size = 0; - goto out_unlock; + return 0; } info->read = 0; @@ -5427,18 +5392,14 @@ tracing_buffers_read(struct file *filp, char __user *ubuf, size = count; ret = copy_to_user(ubuf, info->spare + info->read, size); - if (ret == size) { - size = -EFAULT; - goto out_unlock; - } + if (ret == size) + return -EFAULT; + size -= ret; *ppos += size; info->read += size; - out_unlock: - mutex_unlock(&trace_types_lock); - return size; } @@ -5536,30 +5497,20 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos, int entries, size, i; ssize_t ret = 0; - mutex_lock(&trace_types_lock); - #ifdef CONFIG_TRACER_MAX_TRACE - if (iter->snapshot && iter->tr->current_trace->use_max_tr) { - ret = -EBUSY; - goto out; - } + if (iter->snapshot && iter->tr->current_trace->use_max_tr) + return -EBUSY; #endif - if (splice_grow_spd(pipe, &spd)) { - ret = -ENOMEM; - goto out; - } + if (splice_grow_spd(pipe, &spd)) + return -ENOMEM; - if (*ppos & (PAGE_SIZE - 1)) { - ret = -EINVAL; - goto out; - } + if (*ppos & (PAGE_SIZE - 1)) + return -EINVAL; if (len & (PAGE_SIZE - 1)) { - if (len < PAGE_SIZE) { - ret = -EINVAL; - goto out; - } + if (len < PAGE_SIZE) + return -EINVAL; len &= PAGE_MASK; } @@ -5620,25 +5571,20 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos, /* did we read anything? */ if (!spd.nr_pages) { if (ret) - goto out; + return ret; + + if ((file->f_flags & O_NONBLOCK) || (flags & SPLICE_F_NONBLOCK)) + return -EAGAIN; - if ((file->f_flags & O_NONBLOCK) || (flags & SPLICE_F_NONBLOCK)) { - ret = -EAGAIN; - goto out; - } - mutex_unlock(&trace_types_lock); ret = wait_on_pipe(iter, true); - mutex_lock(&trace_types_lock); if (ret) - goto out; + return ret; goto again; } ret = splice_to_pipe(pipe, &spd); splice_shrink_spd(&spd); -out: - mutex_unlock(&trace_types_lock); return ret; } -- cgit v0.10.2 From 85b89af07d509531cbbb51adb9ec65f90f304473 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 21 Dec 2014 15:25:28 +0200 Subject: mac80211: fix dot11MulticastTransmittedFrameCount tested address dot11MulticastTransmittedFrameCount should be updated according to the DA, which might be different from A1. Checking A1 results in the counter being 0 in case of station, as to-DS data frames use A1 for the BSSID. This behaviour is defined in state machines, specifically in the sta_tx_dcf_3.1d(10) description of 802.11-2012. Signed-off-by: Eliad Peller [rewrite commit message] Signed-off-by: Johannes Berg diff --git a/net/mac80211/status.c b/net/mac80211/status.c index d64037c..7d4e930 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -862,7 +862,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) (info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED)) { if (ieee80211_is_first_frag(hdr->seq_ctrl)) { local->dot11TransmittedFrameCount++; - if (is_multicast_ether_addr(hdr->addr1)) + if (is_multicast_ether_addr(ieee80211_get_DA(hdr))) local->dot11MulticastTransmittedFrameCount++; if (retry_count > 0) local->dot11RetryCount++; -- cgit v0.10.2 From 6ca7a8a15035add0a4f9b2fd658118d41dbeb20c Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Sun, 21 Dec 2014 15:02:23 +0100 Subject: x86/fpu: Use a symbolic name for asm operand Fix up the else-case in fpu_fxsave() which seems like it has been overlooked. Correct comment style in restore_fpu_checking() while at it. Signed-off-by: Borislav Petkov Cc: Linus Torvalds Link: http://lkml.kernel.org/r/1419170543-11393-1-git-send-email-bp@alien8.de Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/fpu-internal.h b/arch/x86/include/asm/fpu-internal.h index e97622f..0dbc082 100644 --- a/arch/x86/include/asm/fpu-internal.h +++ b/arch/x86/include/asm/fpu-internal.h @@ -207,7 +207,7 @@ static inline void fpu_fxsave(struct fpu *fpu) if (config_enabled(CONFIG_X86_32)) asm volatile( "fxsave %[fx]" : [fx] "=m" (fpu->state->fxsave)); else if (config_enabled(CONFIG_AS_FXSAVEQ)) - asm volatile("fxsaveq %0" : "=m" (fpu->state->fxsave)); + asm volatile("fxsaveq %[fx]" : [fx] "=m" (fpu->state->fxsave)); else { /* Using "rex64; fxsave %0" is broken because, if the memory * operand uses any extended registers for addressing, a second @@ -290,9 +290,11 @@ static inline int fpu_restore_checking(struct fpu *fpu) static inline int restore_fpu_checking(struct task_struct *tsk) { - /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception - is pending. Clear the x87 state here by setting it to fixed - values. "m" is a random variable that should be in L1 */ + /* + * AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception is + * pending. Clear the x87 state here by setting it to fixed values. + * "m" is a random variable that should be in L1. + */ if (unlikely(static_cpu_has_bug_safe(X86_BUG_FXSAVE_LEAK))) { asm volatile( "fnclex\n\t" -- cgit v0.10.2 From 2b261f9f7bd9333e62140ca7e02a2f4ffec5e44f Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 21 Dec 2014 13:58:18 +0100 Subject: x86/platform: Remove unused function from apb_timer.c Remove the function is_apbt_capable() that is not used anywhere. This was partially found by using a static code analysis program called 'cppcheck'. Signed-off-by: Rickard Strandqvist Reviewed-by: Jiang Liu Cc: Christoph Lameter Cc: Tejun Heo Link: http://lkml.kernel.org/r/1419166698-2470-1-git-send-email-rickard_strandqvist@spectrumdigital.se Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/apb_timer.c b/arch/x86/kernel/apb_timer.c index b708738..6a7c23f 100644 --- a/arch/x86/kernel/apb_timer.c +++ b/arch/x86/kernel/apb_timer.c @@ -135,14 +135,6 @@ static inline void apbt_clear_mapping(void) apbt_virt_address = NULL; } -/* - * APBT timer interrupt enable / disable - */ -static inline int is_apbt_capable(void) -{ - return apbt_virt_address ? 1 : 0; -} - static int __init apbt_clockevent_register(void) { struct sfi_timer_table_entry *mtmr; -- cgit v0.10.2 From aadec012eabf0e5147103cfd2ffde27e5aad65e2 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 25 Sep 2014 09:22:00 -0300 Subject: [media] omap3isp: Fix division by 0 If the requested clock rate passed to the XCLK set_rate or round_rate operation is 0, the driver will try to divide by 0. Fix this. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 51c2129..deca809 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -220,6 +220,9 @@ static u32 isp_xclk_calc_divider(unsigned long *rate, unsigned long parent_rate) return ISPTCTRL_CTRL_DIV_BYPASS; } + if (*rate == 0) + *rate = 1; + divider = DIV_ROUND_CLOSEST(parent_rate, *rate); if (divider >= ISPTCTRL_CTRL_DIV_BYPASS) divider = ISPTCTRL_CTRL_DIV_BYPASS - 1; -- cgit v0.10.2 From 3e90f789739e5685da0d0b716b04e49e17671877 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 14 Nov 2013 01:03:10 -0300 Subject: [media] v4l: omap4iss: Enable DMABUF support Enable DMABUF import and export operations using videobuf2. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index cdee596..2085f69 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -773,6 +773,14 @@ iss_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) } static int +iss_video_expbuf(struct file *file, void *fh, struct v4l2_exportbuffer *e) +{ + struct iss_video_fh *vfh = to_iss_video_fh(fh); + + return vb2_expbuf(&vfh->queue, e); +} + +static int iss_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) { struct iss_video_fh *vfh = to_iss_video_fh(fh); @@ -1021,6 +1029,7 @@ static const struct v4l2_ioctl_ops iss_video_ioctl_ops = { .vidioc_reqbufs = iss_video_reqbufs, .vidioc_querybuf = iss_video_querybuf, .vidioc_qbuf = iss_video_qbuf, + .vidioc_expbuf = iss_video_expbuf, .vidioc_dqbuf = iss_video_dqbuf, .vidioc_streamon = iss_video_streamon, .vidioc_streamoff = iss_video_streamoff, @@ -1071,7 +1080,7 @@ static int iss_video_open(struct file *file) q = &handle->queue; q->type = video->type; - q->io_modes = VB2_MMAP; + q->io_modes = VB2_MMAP | VB2_DMABUF; q->drv_priv = handle; q->ops = &iss_video_vb2ops; q->mem_ops = &vb2_dma_contig_memops; -- cgit v0.10.2 From dd162547f10132b8ea63adb7f795acc5020616fd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Jul 2014 18:31:24 -0300 Subject: [media] v4l: omap4iss: Remove bogus frame number propagation Frame number propagation tries to increase the robustness of the frame number counter by using sources less likely to be missed than the end of frame interrupts, such as hardware frame counters or start of frame interrupts. Increasing the frame number in the IPIPE ISIF and resizer end of frame interrupt handlers is pointless as it doesn't bring any improvement. Don't do it. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c index 32a7483..3943fae 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.c +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -242,23 +242,6 @@ static void ipipeif_isr_buffer(struct iss_ipipeif_device *ipipeif) } /* - * ipipeif_isif0_isr - Handle ISIF0 event - * @ipipeif: Pointer to ISP IPIPEIF device. - * - * Executes LSC deferred enablement before next frame starts. - */ -static void ipipeif_isif0_isr(struct iss_ipipeif_device *ipipeif) -{ - struct iss_pipeline *pipe = - to_iss_pipeline(&ipipeif->subdev.entity); - if (pipe->do_propagation) - atomic_inc(&pipe->frame_number); - - if (ipipeif->output & IPIPEIF_OUTPUT_MEMORY) - ipipeif_isr_buffer(ipipeif); -} - -/* * omap4iss_ipipeif_isr - Configure ipipeif during interframe time. * @ipipeif: Pointer to ISP IPIPEIF device. * @events: IPIPEIF events @@ -269,8 +252,9 @@ void omap4iss_ipipeif_isr(struct iss_ipipeif_device *ipipeif, u32 events) &ipipeif->stopping)) return; - if (events & ISP5_IRQ_ISIF_INT(0)) - ipipeif_isif0_isr(ipipeif); + if ((events & ISP5_IRQ_ISIF_INT(0)) && + (ipipeif->output & IPIPEIF_OUTPUT_MEMORY)) + ipipeif_isr_buffer(ipipeif); } /* ----------------------------------------------------------------------------- diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index 88522a8..3ab9728 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -283,22 +283,6 @@ static void resizer_isr_buffer(struct iss_resizer_device *resizer) } /* - * resizer_isif0_isr - Handle ISIF0 event - * @resizer: Pointer to ISP RESIZER device. - * - * Executes LSC deferred enablement before next frame starts. - */ -static void resizer_int_dma_isr(struct iss_resizer_device *resizer) -{ - struct iss_pipeline *pipe = - to_iss_pipeline(&resizer->subdev.entity); - if (pipe->do_propagation) - atomic_inc(&pipe->frame_number); - - resizer_isr_buffer(resizer); -} - -/* * omap4iss_resizer_isr - Configure resizer during interframe time. * @resizer: Pointer to ISP RESIZER device. * @events: RESIZER events @@ -322,7 +306,7 @@ void omap4iss_resizer_isr(struct iss_resizer_device *resizer, u32 events) return; if (events & ISP5_IRQ_RSZ_INT_DMA) - resizer_int_dma_isr(resizer); + resizer_isr_buffer(resizer); } /* ----------------------------------------------------------------------------- -- cgit v0.10.2 From 707acfc072676d4b9f5db8d2082e2f0c79c34bb4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Jul 2014 18:44:15 -0300 Subject: [media] v4l: omap4iss: csi2: Perform real frame number propagation Compute the pipeline frame number from the frame number sent by the sensor instead of incrementing the frame number in software. This improves dropped frames detection. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index cc1dfad..d6534f5 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -612,7 +612,12 @@ static int iss_pipeline_enable(struct iss_pipeline *pipe, ret = v4l2_subdev_call(subdev, video, s_stream, mode); if (ret < 0 && ret != -ENOIOCTLCMD) return ret; + + if (subdev == &iss->csi2a.subdev || + subdev == &iss->csi2b.subdev) + pipe->do_propagation = true; } + iss_print_status(pipe->output->iss); return 0; } diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index 21971c6..2d96fb3 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -319,6 +319,8 @@ static void csi2_ctx_config(struct iss_csi2_device *csi2, { u32 reg = 0; + ctx->frame = 0; + /* Set up CSI2_CTx_CTRL1 */ if (ctx->eof_enabled) reg = CSI2_CTX_CTRL1_EOF_EN; @@ -396,21 +398,18 @@ static void csi2_timing_config(struct iss_csi2_device *csi2, */ static void csi2_irq_ctx_set(struct iss_csi2_device *csi2, int enable) { - u32 reg = CSI2_CTX_IRQ_FE; + const u32 mask = CSI2_CTX_IRQ_FE | CSI2_CTX_IRQ_FS; int i; - if (csi2->use_fs_irq) - reg |= CSI2_CTX_IRQ_FS; - for (i = 0; i < 8; i++) { iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_IRQSTATUS(i), - reg); + mask); if (enable) iss_reg_set(csi2->iss, csi2->regs1, - CSI2_CTX_IRQENABLE(i), reg); + CSI2_CTX_IRQENABLE(i), mask); else iss_reg_clr(csi2->iss, csi2->regs1, - CSI2_CTX_IRQENABLE(i), reg); + CSI2_CTX_IRQENABLE(i), mask); } } @@ -679,8 +678,34 @@ static void csi2_isr_ctx(struct iss_csi2_device *csi2, if (status & CSI2_CTX_IRQ_FS) { struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity); - if (pipe->do_propagation) + u16 frame; + u16 delta; + + frame = iss_reg_read(csi2->iss, csi2->regs1, + CSI2_CTX_CTRL2(ctx->ctxnum)) + >> CSI2_CTX_CTRL2_FRAME_SHIFT; + + if (frame == 0) { + /* A zero value means that the counter isn't implemented + * by the source. Increment the frame number in software + * in that case. + */ atomic_inc(&pipe->frame_number); + } else { + /* Extend the 16 bit frame number to 32 bits by + * computing the delta between two consecutive CSI2 + * frame numbers and adding it to the software frame + * number. The hardware counter starts at 1 and wraps + * from 0xffff to 1 without going through 0, so subtract + * 1 when the counter wraps. + */ + delta = frame - ctx->frame; + if (frame < ctx->frame) + delta--; + ctx->frame = frame; + + atomic_add(delta, &pipe->frame_number); + } } if (!(status & CSI2_CTX_IRQ_FE)) @@ -1039,7 +1064,6 @@ static int csi2_set_stream(struct v4l2_subdev *sd, int enable) { struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct iss_device *iss = csi2->iss; - struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity); struct iss_video *video_out = &csi2->video_out; int ret = 0; @@ -1058,7 +1082,6 @@ static int csi2_set_stream(struct v4l2_subdev *sd, int enable) if (omap4iss_csiphy_acquire(csi2->phy) < 0) return -ENODEV; - csi2->use_fs_irq = pipe->do_propagation; csi2_configure(csi2); csi2_print_status(csi2); diff --git a/drivers/staging/media/omap4iss/iss_csi2.h b/drivers/staging/media/omap4iss/iss_csi2.h index 971aa7b..3b37978 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.h +++ b/drivers/staging/media/omap4iss/iss_csi2.h @@ -82,6 +82,7 @@ struct iss_csi2_ctx_cfg { u8 virtual_id; u16 format_id; /* as in CSI2_CTx_CTRL2[9:0] */ u8 dpcm_predictor; /* 1: simple, 0: advanced */ + u16 frame; /* Fields in CSI2_CTx_CTRL1/3 - Shadowed */ u16 alpha; @@ -137,7 +138,6 @@ struct iss_csi2_device { u32 output; /* output to IPIPEIF, memory or both? */ bool dpcm_decompress; unsigned int frame_skip; - bool use_fs_irq; struct iss_csiphy *phy; struct iss_csi2_ctx_cfg contexts[ISS_CSI2_MAX_CTX_NUM + 1]; diff --git a/drivers/staging/media/omap4iss/iss_regs.h b/drivers/staging/media/omap4iss/iss_regs.h index efd0291..d2b6b6a 100644 --- a/drivers/staging/media/omap4iss/iss_regs.h +++ b/drivers/staging/media/omap4iss/iss_regs.h @@ -215,6 +215,8 @@ #define CSI2_CTX_CTRL1_CTX_EN (1 << 0) #define CSI2_CTX_CTRL2(i) (0x74 + (0x20 * i)) +#define CSI2_CTX_CTRL2_FRAME_MASK (0xffff << 16) +#define CSI2_CTX_CTRL2_FRAME_SHIFT 16 #define CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT 13 #define CSI2_CTX_CTRL2_USER_DEF_MAP_MASK \ (0x3 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT) -- cgit v0.10.2 From 9587a3fc4333efd9cfd56a0754cabf76bdcdb3b0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 28 Oct 2014 09:07:37 -0300 Subject: [media] v4l: omap4iss: Stop started entities when pipeline start fails If an entity can't be started when starting a pipeline we need to clean up by stopping all entities that have been successfully started. Otherwise the hardware and software states won't match, potentially leading to crashes (for instance due to the CSI2 receiver receiving interrupts with a NULL pipeline pointer). Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index d6534f5..44b81a2 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -560,6 +560,61 @@ static int iss_pipeline_link_notify(struct media_link *link, u32 flags, */ /* + * iss_pipeline_disable - Disable streaming on a pipeline + * @pipe: ISS pipeline + * @until: entity at which to stop pipeline walk + * + * Walk the entities chain starting at the pipeline output video node and stop + * all modules in the chain. Wait synchronously for the modules to be stopped if + * necessary. + * + * If the until argument isn't NULL, stop the pipeline walk when reaching the + * until entity. This is used to disable a partially started pipeline due to a + * subdev start error. + */ +static int iss_pipeline_disable(struct iss_pipeline *pipe, + struct media_entity *until) +{ + struct iss_device *iss = pipe->output->iss; + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + int failure = 0; + int ret; + + entity = &pipe->output->video.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_entity_remote_pad(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + entity = pad->entity; + if (entity == until) + break; + + subdev = media_entity_to_v4l2_subdev(entity); + ret = v4l2_subdev_call(subdev, video, s_stream, 0); + if (ret < 0) { + dev_dbg(iss->dev, "%s: module stop timeout.\n", + subdev->name); + /* If the entity failed to stopped, assume it has + * crashed. Mark it as such, the ISS will be reset when + * applications will release it. + */ + iss->crashed |= 1U << subdev->entity.id; + failure = -ETIMEDOUT; + } + } + + return failure; +} + +/* * iss_pipeline_enable - Enable streaming on a pipeline * @pipe: ISS pipeline * @mode: Stream mode (single shot or continuous) @@ -610,8 +665,10 @@ static int iss_pipeline_enable(struct iss_pipeline *pipe, subdev = media_entity_to_v4l2_subdev(entity); ret = v4l2_subdev_call(subdev, video, s_stream, mode); - if (ret < 0 && ret != -ENOIOCTLCMD) + if (ret < 0 && ret != -ENOIOCTLCMD) { + iss_pipeline_disable(pipe, entity); return ret; + } if (subdev == &iss->csi2a.subdev || subdev == &iss->csi2b.subdev) @@ -623,53 +680,6 @@ static int iss_pipeline_enable(struct iss_pipeline *pipe, } /* - * iss_pipeline_disable - Disable streaming on a pipeline - * @pipe: ISS pipeline - * - * Walk the entities chain starting at the pipeline output video node and stop - * all modules in the chain. Wait synchronously for the modules to be stopped if - * necessary. - */ -static int iss_pipeline_disable(struct iss_pipeline *pipe) -{ - struct iss_device *iss = pipe->output->iss; - struct media_entity *entity; - struct media_pad *pad; - struct v4l2_subdev *subdev; - int failure = 0; - int ret; - - entity = &pipe->output->video.entity; - while (1) { - pad = &entity->pads[0]; - if (!(pad->flags & MEDIA_PAD_FL_SINK)) - break; - - pad = media_entity_remote_pad(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) - break; - - entity = pad->entity; - subdev = media_entity_to_v4l2_subdev(entity); - - ret = v4l2_subdev_call(subdev, video, s_stream, 0); - if (ret < 0) { - dev_dbg(iss->dev, "%s: module stop timeout.\n", - subdev->name); - /* If the entity failed to stopped, assume it has - * crashed. Mark it as such, the ISS will be reset when - * applications will release it. - */ - iss->crashed |= 1U << subdev->entity.id; - failure = -ETIMEDOUT; - } - } - - return failure; -} - -/* * omap4iss_pipeline_set_stream - Enable/disable streaming on a pipeline * @pipe: ISS pipeline * @state: Stream state (stopped, single shot or continuous) @@ -687,7 +697,7 @@ int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe, int ret; if (state == ISS_PIPELINE_STREAM_STOPPED) - ret = iss_pipeline_disable(pipe); + ret = iss_pipeline_disable(pipe, NULL); else ret = iss_pipeline_enable(pipe, state); -- cgit v0.10.2 From 5fb3f55504d5d9b6ca97131091707afd351fe1dc Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Wed, 26 Nov 2014 20:25:44 -0300 Subject: [media] media: usb: uvc: use vb2_ops_wait_prepare/finish helper This patch drops driver specific wait_prepare() and wait_finish() callbacks from vb2_ops and instead uses the the helpers vb2_ops_wait_prepare/finish() provided by the vb2 core, the lock member of the queue needs to be initalized to a mutex so that vb2 helpers vb2_ops_wait_prepare/finish() can make use of it. Signed-off-by: Lad, Prabhakar Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index cc96072..10c554e 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -143,20 +143,6 @@ static void uvc_buffer_finish(struct vb2_buffer *vb) uvc_video_clock_update(stream, &vb->v4l2_buf, buf); } -static void uvc_wait_prepare(struct vb2_queue *vq) -{ - struct uvc_video_queue *queue = vb2_get_drv_priv(vq); - - mutex_unlock(&queue->mutex); -} - -static void uvc_wait_finish(struct vb2_queue *vq) -{ - struct uvc_video_queue *queue = vb2_get_drv_priv(vq); - - mutex_lock(&queue->mutex); -} - static int uvc_start_streaming(struct vb2_queue *vq, unsigned int count) { struct uvc_video_queue *queue = vb2_get_drv_priv(vq); @@ -195,8 +181,8 @@ static struct vb2_ops uvc_queue_qops = { .buf_prepare = uvc_buffer_prepare, .buf_queue = uvc_buffer_queue, .buf_finish = uvc_buffer_finish, - .wait_prepare = uvc_wait_prepare, - .wait_finish = uvc_wait_finish, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .start_streaming = uvc_start_streaming, .stop_streaming = uvc_stop_streaming, }; @@ -214,6 +200,7 @@ int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, queue->queue.mem_ops = &vb2_vmalloc_memops; queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC | V4L2_BUF_FLAG_TSTAMP_SRC_SOE; + queue->queue.lock = &queue->mutex; ret = vb2_queue_init(&queue->queue); if (ret) return ret; -- cgit v0.10.2 From 1b8dc32286a1a4fb73cfc7793ef103f3993a6478 Mon Sep 17 00:00:00 2001 From: William Manley Date: Mon, 8 Dec 2014 15:57:58 -0300 Subject: [media] uvcvideo: Add GUID for BGR 8:8:8 The Magewell XI100DUSB-HDMI[1] video capture device reports the pixel format "e436eb7d-524f-11ce-9f53-0020af0ba770". This is its GUID for BGR 8:8:8. The UVC 1.5 spec[2] only defines GUIDs for YUY2, NV12, M420 and I420. This seems to be an extension documented in the Microsoft Windows Media Format SDK[3] - or at least the Media Format SDK was the only hit that Google gave when searching for the GUID. This Media Format SDK defines this GUID as corresponding to `MEDIASUBTYPE_RGB24`. Note though, the XI100DUSB outputs BGR e.g. byte-reversed. I don't know if its the capture device in error or Microsoft mean BGR when they say RGB. [1]: http://www.magewell.com/hardware/dongles/xi100dusb-hdmi/xi100dusb-hdmi_features.html?lang=en [2]: http://www.usb.org/developers/docs/devclass_docs/USB_Video_Class_1_5.zip [3]: http://msdn.microsoft.com/en-gb/library/windows/desktop/dd757532(v=vs.85).aspx Signed-off-by: William Manley Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 6a4b0b8..cf27006 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -138,6 +138,11 @@ static struct uvc_format_desc uvc_fmts[] = { .fcc = V4L2_PIX_FMT_RGB565, }, { + .name = "BGR 8:8:8 (BGR3)", + .guid = UVC_GUID_FORMAT_BGR3, + .fcc = V4L2_PIX_FMT_BGR24, + }, + { .name = "H.264", .guid = UVC_GUID_FORMAT_H264, .fcc = V4L2_PIX_FMT_H264, diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index f0a04b5..c63e5b5 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -109,6 +109,9 @@ #define UVC_GUID_FORMAT_RGBP \ { 'R', 'G', 'B', 'P', 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_BGR3 \ + { 0x7d, 0xeb, 0x36, 0xe4, 0x4f, 0x52, 0xce, 0x11, \ + 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70} #define UVC_GUID_FORMAT_M420 \ { 'M', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} -- cgit v0.10.2 From 32d17597d3e299ffe8b07e3afc12f8074e7ae483 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 Apr 2014 08:13:18 -0300 Subject: [media] v4l: vsp1: Remove support for platform data Now that all platforms instantiate the VSP1 through DT, platform data support isn't needed anymore. Signed-off-by: Laurent Pinchart Acked-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 765bffb..480a174 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -223,7 +223,7 @@ config VIDEO_SH_VEU config VIDEO_RENESAS_VSP1 tristate "Renesas VSP1 Video Processing Engine" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA - depends on ARCH_SHMOBILE || COMPILE_TEST + depends on (ARCH_SHMOBILE && OF) || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG ---help--- This is a V4L2 driver for the Renesas VSP1 video processing engine. diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 1246719..989e96f 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -16,7 +16,6 @@ #include #include #include -#include #include #include @@ -40,9 +39,20 @@ struct vsp1_uds; #define VSP1_MAX_UDS 3 #define VSP1_MAX_WPF 4 +#define VSP1_HAS_LIF (1 << 0) +#define VSP1_HAS_LUT (1 << 1) +#define VSP1_HAS_SRU (1 << 2) + +struct vsp1_platform_data { + unsigned int features; + unsigned int rpf_count; + unsigned int uds_count; + unsigned int wpf_count; +}; + struct vsp1_device { struct device *dev; - struct vsp1_platform_data *pdata; + struct vsp1_platform_data pdata; void __iomem *mmio; struct clk *clock; diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 5eb16e8..913485a 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -40,7 +40,7 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data) irqreturn_t ret = IRQ_NONE; unsigned int i; - for (i = 0; i < vsp1->pdata->wpf_count; ++i) { + for (i = 0; i < vsp1->pdata.wpf_count; ++i) { struct vsp1_rwpf *wpf = vsp1->wpf[i]; struct vsp1_pipeline *pipe; u32 status; @@ -181,7 +181,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities); - if (vsp1->pdata->features & VSP1_HAS_LIF) { + if (vsp1->pdata.features & VSP1_HAS_LIF) { vsp1->lif = vsp1_lif_create(vsp1); if (IS_ERR(vsp1->lif)) { ret = PTR_ERR(vsp1->lif); @@ -191,7 +191,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->lif->entity.list_dev, &vsp1->entities); } - if (vsp1->pdata->features & VSP1_HAS_LUT) { + if (vsp1->pdata.features & VSP1_HAS_LUT) { vsp1->lut = vsp1_lut_create(vsp1); if (IS_ERR(vsp1->lut)) { ret = PTR_ERR(vsp1->lut); @@ -201,7 +201,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->lut->entity.list_dev, &vsp1->entities); } - for (i = 0; i < vsp1->pdata->rpf_count; ++i) { + for (i = 0; i < vsp1->pdata.rpf_count; ++i) { struct vsp1_rwpf *rpf; rpf = vsp1_rpf_create(vsp1, i); @@ -214,7 +214,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&rpf->entity.list_dev, &vsp1->entities); } - if (vsp1->pdata->features & VSP1_HAS_SRU) { + if (vsp1->pdata.features & VSP1_HAS_SRU) { vsp1->sru = vsp1_sru_create(vsp1); if (IS_ERR(vsp1->sru)) { ret = PTR_ERR(vsp1->sru); @@ -224,7 +224,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->sru->entity.list_dev, &vsp1->entities); } - for (i = 0; i < vsp1->pdata->uds_count; ++i) { + for (i = 0; i < vsp1->pdata.uds_count; ++i) { struct vsp1_uds *uds; uds = vsp1_uds_create(vsp1, i); @@ -237,7 +237,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&uds->entity.list_dev, &vsp1->entities); } - for (i = 0; i < vsp1->pdata->wpf_count; ++i) { + for (i = 0; i < vsp1->pdata.wpf_count; ++i) { struct vsp1_rwpf *wpf; wpf = vsp1_wpf_create(vsp1, i); @@ -261,7 +261,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) goto done; } - if (vsp1->pdata->features & VSP1_HAS_LIF) { + if (vsp1->pdata.features & VSP1_HAS_LIF) { ret = media_entity_create_link( &vsp1->wpf[0]->entity.subdev.entity, RWPF_PAD_SOURCE, &vsp1->lif->entity.subdev.entity, LIF_PAD_SINK, 0); @@ -294,7 +294,7 @@ static int vsp1_device_init(struct vsp1_device *vsp1) /* Reset any channel that might be running. */ status = vsp1_read(vsp1, VI6_STATUS); - for (i = 0; i < vsp1->pdata->wpf_count; ++i) { + for (i = 0; i < vsp1->pdata.wpf_count; ++i) { unsigned int timeout; if (!(status & VI6_STATUS_SYS_ACT(i))) @@ -318,10 +318,10 @@ static int vsp1_device_init(struct vsp1_device *vsp1) vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) | (8 << VI6_CLK_DCSWT_CSTRW_SHIFT)); - for (i = 0; i < vsp1->pdata->rpf_count; ++i) + for (i = 0; i < vsp1->pdata.rpf_count; ++i) vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED); - for (i = 0; i < vsp1->pdata->uds_count; ++i) + for (i = 0; i < vsp1->pdata.uds_count; ++i) vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED); vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED); @@ -428,28 +428,36 @@ static const struct dev_pm_ops vsp1_pm_ops = { * Platform Driver */ -static int vsp1_validate_platform_data(struct platform_device *pdev, - struct vsp1_platform_data *pdata) +static int vsp1_parse_dt(struct vsp1_device *vsp1) { - if (pdata == NULL) { - dev_err(&pdev->dev, "missing platform data\n"); - return -EINVAL; - } + struct device_node *np = vsp1->dev->of_node; + struct vsp1_platform_data *pdata = &vsp1->pdata; + + if (of_property_read_bool(np, "renesas,has-lif")) + pdata->features |= VSP1_HAS_LIF; + if (of_property_read_bool(np, "renesas,has-lut")) + pdata->features |= VSP1_HAS_LUT; + if (of_property_read_bool(np, "renesas,has-sru")) + pdata->features |= VSP1_HAS_SRU; + + of_property_read_u32(np, "renesas,#rpf", &pdata->rpf_count); + of_property_read_u32(np, "renesas,#uds", &pdata->uds_count); + of_property_read_u32(np, "renesas,#wpf", &pdata->wpf_count); if (pdata->rpf_count <= 0 || pdata->rpf_count > VSP1_MAX_RPF) { - dev_err(&pdev->dev, "invalid number of RPF (%u)\n", + dev_err(vsp1->dev, "invalid number of RPF (%u)\n", pdata->rpf_count); return -EINVAL; } if (pdata->uds_count <= 0 || pdata->uds_count > VSP1_MAX_UDS) { - dev_err(&pdev->dev, "invalid number of UDS (%u)\n", + dev_err(vsp1->dev, "invalid number of UDS (%u)\n", pdata->uds_count); return -EINVAL; } if (pdata->wpf_count <= 0 || pdata->wpf_count > VSP1_MAX_WPF) { - dev_err(&pdev->dev, "invalid number of WPF (%u)\n", + dev_err(vsp1->dev, "invalid number of WPF (%u)\n", pdata->wpf_count); return -EINVAL; } @@ -457,33 +465,6 @@ static int vsp1_validate_platform_data(struct platform_device *pdev, return 0; } -static struct vsp1_platform_data * -vsp1_get_platform_data(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct vsp1_platform_data *pdata; - - if (!IS_ENABLED(CONFIG_OF) || np == NULL) - return pdev->dev.platform_data; - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (pdata == NULL) - return NULL; - - if (of_property_read_bool(np, "renesas,has-lif")) - pdata->features |= VSP1_HAS_LIF; - if (of_property_read_bool(np, "renesas,has-lut")) - pdata->features |= VSP1_HAS_LUT; - if (of_property_read_bool(np, "renesas,has-sru")) - pdata->features |= VSP1_HAS_SRU; - - of_property_read_u32(np, "renesas,#rpf", &pdata->rpf_count); - of_property_read_u32(np, "renesas,#uds", &pdata->uds_count); - of_property_read_u32(np, "renesas,#wpf", &pdata->wpf_count); - - return pdata; -} - static int vsp1_probe(struct platform_device *pdev) { struct vsp1_device *vsp1; @@ -499,11 +480,7 @@ static int vsp1_probe(struct platform_device *pdev) mutex_init(&vsp1->lock); INIT_LIST_HEAD(&vsp1->entities); - vsp1->pdata = vsp1_get_platform_data(pdev); - if (vsp1->pdata == NULL) - return -ENODEV; - - ret = vsp1_validate_platform_data(pdev, vsp1->pdata); + ret = vsp1_parse_dt(vsp1); if (ret < 0) return ret; diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 6e05776..b1089d0 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -280,7 +280,7 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) * except for the WPF0 source link if a LIF is present. */ flags = MEDIA_LNK_FL_ENABLED; - if (!(vsp1->pdata->features & VSP1_HAS_LIF) || index != 0) + if (!(vsp1->pdata.features & VSP1_HAS_LIF) || index != 0) flags |= MEDIA_LNK_FL_IMMUTABLE; ret = media_entity_create_link(&wpf->entity.subdev.entity, diff --git a/include/linux/platform_data/vsp1.h b/include/linux/platform_data/vsp1.h deleted file mode 100644 index 63170e26..0000000 --- a/include/linux/platform_data/vsp1.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * vsp1.h -- R-Car VSP1 Platform Data - * - * Copyright (C) 2013 Renesas Corporation - * - * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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. - */ -#ifndef __PLATFORM_VSP1_H__ -#define __PLATFORM_VSP1_H__ - -#define VSP1_HAS_LIF (1 << 0) -#define VSP1_HAS_LUT (1 << 1) -#define VSP1_HAS_SRU (1 << 2) - -struct vsp1_platform_data { - unsigned int features; - unsigned int rpf_count; - unsigned int uds_count; - unsigned int wpf_count; -}; - -#endif /* __PLATFORM_VSP1_H__ */ -- cgit v0.10.2 From 857161fc7d3734f8fd8707f1bc756c1158694371 Mon Sep 17 00:00:00 2001 From: Takanari Hayama Date: Wed, 26 Nov 2014 22:25:01 -0300 Subject: [media] v4l: vsp1: Reset VSP1 RPF source address Source address of VSP1 RPF needs to be reset whenever crop offsets are recalculated. This correctly reflects a crop setting even VIDIOC_QBUF is called before VIDIOC_STREAMON is called. Signed-off-by: Takanari Hayama Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index d14d26b..3294529 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -106,11 +106,22 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) + crop->left * fmtinfo->bpp[0] / 8; pstride = format->plane_fmt[0].bytesperline << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT; + + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, + rpf->buf_addr[0] + rpf->offsets[0]); + if (format->num_planes > 1) { rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline + crop->left * fmtinfo->bpp[1] / 8; pstride |= format->plane_fmt[1].bytesperline << VI6_RPF_SRCM_PSTRIDE_C_SHIFT; + + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, + rpf->buf_addr[1] + rpf->offsets[1]); + + if (format->num_planes > 2) + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, + rpf->buf_addr[2] + rpf->offsets[1]); } vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride); @@ -179,6 +190,13 @@ static void rpf_vdev_queue(struct vsp1_video *video, struct vsp1_video_buffer *buf) { struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video); + unsigned int i; + + for (i = 0; i < 3; ++i) + rpf->buf_addr[i] = buf->addr[i]; + + if (!vsp1_entity_is_streaming(&rpf->entity)) + return; vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, buf->addr[0] + rpf->offsets[0]); diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 28dd9e7..2cf1f13 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -39,6 +39,7 @@ struct vsp1_rwpf { struct v4l2_rect crop; unsigned int offsets[2]; + dma_addr_t buf_addr[3]; }; static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev) -- cgit v0.10.2 From 5d0beeec59e303c76160ddd67fa73dcfc5d76de0 Mon Sep 17 00:00:00 2001 From: Takanari Hayama Date: Wed, 26 Nov 2014 22:25:02 -0300 Subject: [media] v4l: vsp1: Always enable virtual RPF when BRU is in use Regardless of a number of inputs, we should always enable virtual RPF when BRU is used. This allows the case when there's only one input to BRU, and a size of the input is smaller than a size of an output of BRU. Signed-off-by: Takanari Hayama Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index b1089d0..1d2b3a2 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -92,19 +92,20 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) return 0; } - /* Sources. If the pipeline has a single input configure it as the - * master layer. Otherwise configure all inputs as sub-layers and - * select the virtual RPF as the master layer. + /* Sources. If the pipeline has a single input and BRU is not used, + * configure it as the master layer. Otherwise configure all + * inputs as sub-layers and select the virtual RPF as the master + * layer. */ for (i = 0; i < pipe->num_inputs; ++i) { struct vsp1_rwpf *input = pipe->inputs[i]; - srcrpf |= pipe->num_inputs == 1 + srcrpf |= (!pipe->bru && pipe->num_inputs == 1) ? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index) : VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index); } - if (pipe->num_inputs > 1) + if (pipe->bru || pipe->num_inputs > 1) srcrpf |= VI6_WPF_SRCRPF_VIRACT_MST; vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, srcrpf); -- cgit v0.10.2 From 5ede94c7055392bb55c6d2e26f1912f68ef4b875 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 12 Dec 2014 10:27:54 -0300 Subject: [media] cx25821: remove bogus btcx_risc dependency Those btcx_risc functions are meant for use with bttv, other drivers should just allocate the memory themselves. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx25821/Kconfig b/drivers/media/pci/cx25821/Kconfig index 6439a84..0e69cab 100644 --- a/drivers/media/pci/cx25821/Kconfig +++ b/drivers/media/pci/cx25821/Kconfig @@ -2,7 +2,6 @@ config VIDEO_CX25821 tristate "Conexant cx25821 support" depends on VIDEO_DEV && PCI && I2C select I2C_ALGOBIT - select VIDEO_BTCX select VIDEOBUF_DMA_SG ---help--- This is a video4linux driver for Conexant 25821 based diff --git a/drivers/media/pci/cx25821/Makefile b/drivers/media/pci/cx25821/Makefile index fb76c3d..5872feb 100644 --- a/drivers/media/pci/cx25821/Makefile +++ b/drivers/media/pci/cx25821/Makefile @@ -6,4 +6,3 @@ obj-$(CONFIG_VIDEO_CX25821) += cx25821.o obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o ccflags-y += -Idrivers/media/i2c -ccflags-y += -Idrivers/media/common diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c index 2dd5bca..27e6493 100644 --- a/drivers/media/pci/cx25821/cx25821-alsa.c +++ b/drivers/media/pci/cx25821/cx25821-alsa.c @@ -63,7 +63,7 @@ static int devno; struct cx25821_audio_buffer { unsigned int bpl; - struct btcx_riscmem risc; + struct cx25821_riscmem risc; struct videobuf_dmabuf dma; }; @@ -330,12 +330,14 @@ out: static int dsp_buffer_free(struct cx25821_audio_dev *chip) { + struct cx25821_riscmem *risc = &chip->buf->risc; + BUG_ON(!chip->dma_size); dprintk(2, "Freeing buffer\n"); videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc); videobuf_dma_free(chip->dma_risc); - btcx_riscmem_free(chip->pci, &chip->buf->risc); + pci_free_consistent(chip->pci, risc->size, risc->cpu, risc->dma); kfree(chip->buf); chip->dma_risc = NULL; diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index 389fffd..c8c65b7 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -979,6 +979,27 @@ void cx25821_dev_unregister(struct cx25821_dev *dev) } EXPORT_SYMBOL(cx25821_dev_unregister); +int cx25821_riscmem_alloc(struct pci_dev *pci, + struct cx25821_riscmem *risc, + unsigned int size) +{ + __le32 *cpu; + dma_addr_t dma = 0; + + if (NULL != risc->cpu && risc->size < size) + pci_free_consistent(pci, risc->size, risc->cpu, risc->dma); + if (NULL == risc->cpu) { + cpu = pci_zalloc_consistent(pci, size, &dma); + if (NULL == cpu) + return -ENOMEM; + risc->cpu = cpu; + risc->dma = dma; + risc->size = size; + } + return 0; +} +EXPORT_SYMBOL(cx25821_riscmem_alloc); + static __le32 *cx25821_risc_field(__le32 * rp, struct scatterlist *sglist, unsigned int offset, u32 sync_line, unsigned int bpl, unsigned int padding, @@ -1035,7 +1056,7 @@ static __le32 *cx25821_risc_field(__le32 * rp, struct scatterlist *sglist, return rp; } -int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, +int cx25821_risc_buffer(struct pci_dev *pci, struct cx25821_riscmem *risc, struct scatterlist *sglist, unsigned int top_offset, unsigned int bottom_offset, unsigned int bpl, unsigned int padding, unsigned int lines) @@ -1059,7 +1080,7 @@ int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines); instructions += 2; - rc = btcx_riscmem_alloc(pci, risc, instructions * 12); + rc = cx25821_riscmem_alloc(pci, risc, instructions * 12); if (rc < 0) return rc; @@ -1146,7 +1167,7 @@ static __le32 *cx25821_risc_field_audio(__le32 * rp, struct scatterlist *sglist, } int cx25821_risc_databuffer_audio(struct pci_dev *pci, - struct btcx_riscmem *risc, + struct cx25821_riscmem *risc, struct scatterlist *sglist, unsigned int bpl, unsigned int lines, unsigned int lpi) @@ -1163,7 +1184,7 @@ int cx25821_risc_databuffer_audio(struct pci_dev *pci, instructions = 1 + (bpl * lines) / PAGE_SIZE + lines; instructions += 1; - rc = btcx_riscmem_alloc(pci, risc, instructions * 12); + rc = cx25821_riscmem_alloc(pci, risc, instructions * 12); if (rc < 0) return rc; @@ -1179,13 +1200,13 @@ int cx25821_risc_databuffer_audio(struct pci_dev *pci, } EXPORT_SYMBOL(cx25821_risc_databuffer_audio); -int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, +int cx25821_risc_stopper(struct pci_dev *pci, struct cx25821_riscmem *risc, u32 reg, u32 mask, u32 value) { __le32 *rp; int rc; - rc = btcx_riscmem_alloc(pci, risc, 4 * 16); + rc = cx25821_riscmem_alloc(pci, risc, 4 * 16); if (rc < 0) return rc; @@ -1211,7 +1232,8 @@ void cx25821_free_buffer(struct videobuf_queue *q, struct cx25821_buffer *buf) videobuf_waiton(q, &buf->vb, 0, 0); videobuf_dma_unmap(q->dev, dma); videobuf_dma_free(dma); - btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); + pci_free_consistent(to_pci_dev(q->dev), + buf->risc.size, buf->risc.cpu, buf->risc.dma); buf->vb.state = VIDEOBUF_NEEDS_INIT; } diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index 3a419f1..3eda1a1 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -1017,11 +1017,13 @@ void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num) cx_clear(PCI_INT_MSK, 1); if (video_is_registered(&dev->channels[chan_num].vdev)) { + struct cx25821_riscmem *risc = + &dev->channels[chan_num].dma_vidq.stopper; + video_unregister_device(&dev->channels[chan_num].vdev); v4l2_ctrl_handler_free(&dev->channels[chan_num].hdl); - btcx_riscmem_free(dev->pci, - &dev->channels[chan_num].dma_vidq.stopper); + pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma); } } diff --git a/drivers/media/pci/cx25821/cx25821.h b/drivers/media/pci/cx25821/cx25821.h index 90bdc19..38beec2 100644 --- a/drivers/media/pci/cx25821/cx25821.h +++ b/drivers/media/pci/cx25821/cx25821.h @@ -36,7 +36,6 @@ #include #include -#include "btcx-risc.h" #include "cx25821-reg.h" #include "cx25821-medusa-reg.h" #include "cx25821-sram.h" @@ -111,6 +110,13 @@ enum cx25821_src_sel_type { CX25821_SRC_SEL_PARALLEL_MPEG_VIDEO }; +struct cx25821_riscmem { + unsigned int size; + __le32 *cpu; + __le32 *jmp; + dma_addr_t dma; +}; + /* buffer for one video frame */ struct cx25821_buffer { /* common v4l buffer stuff -- must be first */ @@ -118,7 +124,7 @@ struct cx25821_buffer { /* cx25821 specific */ unsigned int bpl; - struct btcx_riscmem risc; + struct cx25821_riscmem risc; const struct cx25821_fmt *fmt; u32 count; }; @@ -161,7 +167,7 @@ struct cx25821_dmaqueue { struct list_head active; struct list_head queued; struct timer_list timeout; - struct btcx_riscmem stopper; + struct cx25821_riscmem stopper; u32 count; }; @@ -405,20 +411,23 @@ extern int cx25821_sram_channel_setup(struct cx25821_dev *dev, const struct sram_channel *ch, unsigned int bpl, u32 risc); -extern int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, +extern int cx25821_riscmem_alloc(struct pci_dev *pci, + struct cx25821_riscmem *risc, + unsigned int size); +extern int cx25821_risc_buffer(struct pci_dev *pci, struct cx25821_riscmem *risc, struct scatterlist *sglist, unsigned int top_offset, unsigned int bottom_offset, unsigned int bpl, unsigned int padding, unsigned int lines); extern int cx25821_risc_databuffer_audio(struct pci_dev *pci, - struct btcx_riscmem *risc, + struct cx25821_riscmem *risc, struct scatterlist *sglist, unsigned int bpl, unsigned int lines, unsigned int lpi); extern void cx25821_free_buffer(struct videobuf_queue *q, struct cx25821_buffer *buf); -extern int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, +extern int cx25821_risc_stopper(struct pci_dev *pci, struct cx25821_riscmem *risc, u32 reg, u32 mask, u32 value); extern void cx25821_sram_channel_dump(struct cx25821_dev *dev, const struct sram_channel *ch); -- cgit v0.10.2 From 1e4518086ae863814a3481a180264a1ac89a730b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 12 Dec 2014 10:27:55 -0300 Subject: [media] cx231xx: remove btcx_riscmem reference Remove this comment block, it's unused. This removes the btcx_riscmem reference as well, since that will no longer be available to other drivers except bttv. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index f9e262eb..6d6f3ee 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -532,15 +532,7 @@ struct cx231xx_video_mode { unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ u16 end_point_addr; }; -/* -struct cx23885_dmaqueue { - struct list_head active; - struct list_head queued; - struct timer_list timeout; - struct btcx_riscmem stopper; - u32 count; -}; -*/ + struct cx231xx_tsport { struct cx231xx *dev; -- cgit v0.10.2 From 90ca8bef9082c2036a200dd2cd17924944cd339b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 12 Dec 2014 10:27:56 -0300 Subject: [media] btcx-risc: move to bt8xx The btcx-risc module is no longer used by other drivers except for bttv. So move it from common to bt8xx and make it part of the bttv driver instead of as a separate module. This module should never have been a common module since most of the code has always been bttv specific. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index b85f88c..21154dd 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -8,10 +8,6 @@ comment "common driver options" config VIDEO_CX2341X tristate -config VIDEO_BTCX - depends on PCI - tristate - config VIDEO_TVEEPROM tristate depends on I2C diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index d208de3..89b795d 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1,5 +1,4 @@ obj-y += b2c2/ saa7146/ siano/ obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o -obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o obj-$(CONFIG_CYPRESS_FIRMWARE) += cypress_firmware.o diff --git a/drivers/media/common/btcx-risc.c b/drivers/media/common/btcx-risc.c deleted file mode 100644 index e67338a..0000000 --- a/drivers/media/common/btcx-risc.c +++ /dev/null @@ -1,254 +0,0 @@ -/* - - btcx-risc.c - - bt848/bt878/cx2388x risc code generator. - - (c) 2000-03 Gerd Knorr [SuSE Labs] - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include -#include -#include -#include -#include -#include - -#include "btcx-risc.h" - -MODULE_DESCRIPTION("some code shared by bttv and cx88xx drivers"); -MODULE_AUTHOR("Gerd Knorr"); -MODULE_LICENSE("GPL"); - -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug,"debug messages, default is 0 (no)"); - -/* ---------------------------------------------------------- */ -/* allocate/free risc memory */ - -static int memcnt; - -void btcx_riscmem_free(struct pci_dev *pci, - struct btcx_riscmem *risc) -{ - if (NULL == risc->cpu) - return; - if (debug) { - memcnt--; - printk("btcx: riscmem free [%d] dma=%lx\n", - memcnt, (unsigned long)risc->dma); - } - pci_free_consistent(pci, risc->size, risc->cpu, risc->dma); - memset(risc,0,sizeof(*risc)); -} - -int btcx_riscmem_alloc(struct pci_dev *pci, - struct btcx_riscmem *risc, - unsigned int size) -{ - __le32 *cpu; - dma_addr_t dma = 0; - - if (NULL != risc->cpu && risc->size < size) - btcx_riscmem_free(pci,risc); - if (NULL == risc->cpu) { - cpu = pci_alloc_consistent(pci, size, &dma); - if (NULL == cpu) - return -ENOMEM; - risc->cpu = cpu; - risc->dma = dma; - risc->size = size; - if (debug) { - memcnt++; - printk("btcx: riscmem alloc [%d] dma=%lx cpu=%p size=%d\n", - memcnt, (unsigned long)dma, cpu, size); - } - } - memset(risc->cpu,0,risc->size); - return 0; -} - -/* ---------------------------------------------------------- */ -/* screen overlay helpers */ - -int -btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, - struct v4l2_clip *clips, unsigned int n) -{ - if (win->left < 0) { - /* left */ - clips[n].c.left = 0; - clips[n].c.top = 0; - clips[n].c.width = -win->left; - clips[n].c.height = win->height; - n++; - } - if (win->left + win->width > swidth) { - /* right */ - clips[n].c.left = swidth - win->left; - clips[n].c.top = 0; - clips[n].c.width = win->width - clips[n].c.left; - clips[n].c.height = win->height; - n++; - } - if (win->top < 0) { - /* top */ - clips[n].c.left = 0; - clips[n].c.top = 0; - clips[n].c.width = win->width; - clips[n].c.height = -win->top; - n++; - } - if (win->top + win->height > sheight) { - /* bottom */ - clips[n].c.left = 0; - clips[n].c.top = sheight - win->top; - clips[n].c.width = win->width; - clips[n].c.height = win->height - clips[n].c.top; - n++; - } - return n; -} - -int -btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int mask) -{ - s32 nx,nw,dx; - unsigned int i; - - /* fixup window */ - nx = (win->left + mask) & ~mask; - nw = (win->width) & ~mask; - if (nx + nw > win->left + win->width) - nw -= mask+1; - dx = nx - win->left; - win->left = nx; - win->width = nw; - if (debug) - printk(KERN_DEBUG "btcx: window align %dx%d+%d+%d [dx=%d]\n", - win->width, win->height, win->left, win->top, dx); - - /* fixup clips */ - for (i = 0; i < n; i++) { - nx = (clips[i].c.left-dx) & ~mask; - nw = (clips[i].c.width) & ~mask; - if (nx + nw < clips[i].c.left-dx + clips[i].c.width) - nw += mask+1; - clips[i].c.left = nx; - clips[i].c.width = nw; - if (debug) - printk(KERN_DEBUG "btcx: clip align %dx%d+%d+%d\n", - clips[i].c.width, clips[i].c.height, - clips[i].c.left, clips[i].c.top); - } - return 0; -} - -void -btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips) -{ - struct v4l2_clip swap; - int i,j,n; - - if (nclips < 2) - return; - for (i = nclips-2; i >= 0; i--) { - for (n = 0, j = 0; j <= i; j++) { - if (clips[j].c.left > clips[j+1].c.left) { - swap = clips[j]; - clips[j] = clips[j+1]; - clips[j+1] = swap; - n++; - } - } - if (0 == n) - break; - } -} - -void -btcx_calc_skips(int line, int width, int *maxy, - struct btcx_skiplist *skips, unsigned int *nskips, - const struct v4l2_clip *clips, unsigned int nclips) -{ - unsigned int clip,skip; - int end, maxline; - - skip=0; - maxline = 9999; - for (clip = 0; clip < nclips; clip++) { - - /* sanity checks */ - if (clips[clip].c.left + clips[clip].c.width <= 0) - continue; - if (clips[clip].c.left > (signed)width) - break; - - /* vertical range */ - if (line > clips[clip].c.top+clips[clip].c.height-1) - continue; - if (line < clips[clip].c.top) { - if (maxline > clips[clip].c.top-1) - maxline = clips[clip].c.top-1; - continue; - } - if (maxline > clips[clip].c.top+clips[clip].c.height-1) - maxline = clips[clip].c.top+clips[clip].c.height-1; - - /* horizontal range */ - if (0 == skip || clips[clip].c.left > skips[skip-1].end) { - /* new one */ - skips[skip].start = clips[clip].c.left; - if (skips[skip].start < 0) - skips[skip].start = 0; - skips[skip].end = clips[clip].c.left + clips[clip].c.width; - if (skips[skip].end > width) - skips[skip].end = width; - skip++; - } else { - /* overlaps -- expand last one */ - end = clips[clip].c.left + clips[clip].c.width; - if (skips[skip-1].end < end) - skips[skip-1].end = end; - if (skips[skip-1].end > width) - skips[skip-1].end = width; - } - } - *nskips = skip; - *maxy = maxline; - - if (debug) { - printk(KERN_DEBUG "btcx: skips line %d-%d:",line,maxline); - for (skip = 0; skip < *nskips; skip++) { - printk(" %d-%d",skips[skip].start,skips[skip].end); - } - printk("\n"); - } -} - -/* ---------------------------------------------------------- */ - -EXPORT_SYMBOL(btcx_riscmem_alloc); -EXPORT_SYMBOL(btcx_riscmem_free); - -EXPORT_SYMBOL(btcx_screen_clips); -EXPORT_SYMBOL(btcx_align); -EXPORT_SYMBOL(btcx_sort_clips); -EXPORT_SYMBOL(btcx_calc_skips); diff --git a/drivers/media/pci/bt8xx/Kconfig b/drivers/media/pci/bt8xx/Kconfig index 61d09e0..496cf6b 100644 --- a/drivers/media/pci/bt8xx/Kconfig +++ b/drivers/media/pci/bt8xx/Kconfig @@ -2,7 +2,6 @@ config VIDEO_BT848 tristate "BT848 Video For Linux" depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L2 select I2C_ALGOBIT - select VIDEO_BTCX select VIDEOBUF_DMA_SG depends on RC_CORE select VIDEO_TUNER diff --git a/drivers/media/pci/bt8xx/Makefile b/drivers/media/pci/bt8xx/Makefile index f9fe7c4..2d4c3dd 100644 --- a/drivers/media/pci/bt8xx/Makefile +++ b/drivers/media/pci/bt8xx/Makefile @@ -1,6 +1,6 @@ bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \ bttv-risc.o bttv-vbi.o bttv-i2c.o bttv-gpio.o \ - bttv-input.o bttv-audio-hook.o + bttv-input.o bttv-audio-hook.o btcx-risc.o obj-$(CONFIG_VIDEO_BT848) += bttv.o obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o diff --git a/drivers/media/pci/bt8xx/btcx-risc.c b/drivers/media/pci/bt8xx/btcx-risc.c new file mode 100644 index 0000000..00f0880 --- /dev/null +++ b/drivers/media/pci/bt8xx/btcx-risc.c @@ -0,0 +1,240 @@ +/* + + btcx-risc.c + + bt848/bt878/cx2388x risc code generator. + + (c) 2000-03 Gerd Knorr [SuSE Labs] + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "btcx-risc.h" + +static unsigned int btcx_debug; +module_param(btcx_debug, int, 0644); +MODULE_PARM_DESC(btcx_debug,"debug messages, default is 0 (no)"); + +/* ---------------------------------------------------------- */ +/* allocate/free risc memory */ + +static int memcnt; + +void btcx_riscmem_free(struct pci_dev *pci, + struct btcx_riscmem *risc) +{ + if (NULL == risc->cpu) + return; + if (btcx_debug) { + memcnt--; + printk("btcx: riscmem free [%d] dma=%lx\n", + memcnt, (unsigned long)risc->dma); + } + pci_free_consistent(pci, risc->size, risc->cpu, risc->dma); + memset(risc,0,sizeof(*risc)); +} + +int btcx_riscmem_alloc(struct pci_dev *pci, + struct btcx_riscmem *risc, + unsigned int size) +{ + __le32 *cpu; + dma_addr_t dma = 0; + + if (NULL != risc->cpu && risc->size < size) + btcx_riscmem_free(pci,risc); + if (NULL == risc->cpu) { + cpu = pci_alloc_consistent(pci, size, &dma); + if (NULL == cpu) + return -ENOMEM; + risc->cpu = cpu; + risc->dma = dma; + risc->size = size; + if (btcx_debug) { + memcnt++; + printk("btcx: riscmem alloc [%d] dma=%lx cpu=%p size=%d\n", + memcnt, (unsigned long)dma, cpu, size); + } + } + memset(risc->cpu,0,risc->size); + return 0; +} + +/* ---------------------------------------------------------- */ +/* screen overlay helpers */ + +int +btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, + struct v4l2_clip *clips, unsigned int n) +{ + if (win->left < 0) { + /* left */ + clips[n].c.left = 0; + clips[n].c.top = 0; + clips[n].c.width = -win->left; + clips[n].c.height = win->height; + n++; + } + if (win->left + win->width > swidth) { + /* right */ + clips[n].c.left = swidth - win->left; + clips[n].c.top = 0; + clips[n].c.width = win->width - clips[n].c.left; + clips[n].c.height = win->height; + n++; + } + if (win->top < 0) { + /* top */ + clips[n].c.left = 0; + clips[n].c.top = 0; + clips[n].c.width = win->width; + clips[n].c.height = -win->top; + n++; + } + if (win->top + win->height > sheight) { + /* bottom */ + clips[n].c.left = 0; + clips[n].c.top = sheight - win->top; + clips[n].c.width = win->width; + clips[n].c.height = win->height - clips[n].c.top; + n++; + } + return n; +} + +int +btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int mask) +{ + s32 nx,nw,dx; + unsigned int i; + + /* fixup window */ + nx = (win->left + mask) & ~mask; + nw = (win->width) & ~mask; + if (nx + nw > win->left + win->width) + nw -= mask+1; + dx = nx - win->left; + win->left = nx; + win->width = nw; + if (btcx_debug) + printk(KERN_DEBUG "btcx: window align %dx%d+%d+%d [dx=%d]\n", + win->width, win->height, win->left, win->top, dx); + + /* fixup clips */ + for (i = 0; i < n; i++) { + nx = (clips[i].c.left-dx) & ~mask; + nw = (clips[i].c.width) & ~mask; + if (nx + nw < clips[i].c.left-dx + clips[i].c.width) + nw += mask+1; + clips[i].c.left = nx; + clips[i].c.width = nw; + if (btcx_debug) + printk(KERN_DEBUG "btcx: clip align %dx%d+%d+%d\n", + clips[i].c.width, clips[i].c.height, + clips[i].c.left, clips[i].c.top); + } + return 0; +} + +void +btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips) +{ + struct v4l2_clip swap; + int i,j,n; + + if (nclips < 2) + return; + for (i = nclips-2; i >= 0; i--) { + for (n = 0, j = 0; j <= i; j++) { + if (clips[j].c.left > clips[j+1].c.left) { + swap = clips[j]; + clips[j] = clips[j+1]; + clips[j+1] = swap; + n++; + } + } + if (0 == n) + break; + } +} + +void +btcx_calc_skips(int line, int width, int *maxy, + struct btcx_skiplist *skips, unsigned int *nskips, + const struct v4l2_clip *clips, unsigned int nclips) +{ + unsigned int clip,skip; + int end, maxline; + + skip=0; + maxline = 9999; + for (clip = 0; clip < nclips; clip++) { + + /* sanity checks */ + if (clips[clip].c.left + clips[clip].c.width <= 0) + continue; + if (clips[clip].c.left > (signed)width) + break; + + /* vertical range */ + if (line > clips[clip].c.top+clips[clip].c.height-1) + continue; + if (line < clips[clip].c.top) { + if (maxline > clips[clip].c.top-1) + maxline = clips[clip].c.top-1; + continue; + } + if (maxline > clips[clip].c.top+clips[clip].c.height-1) + maxline = clips[clip].c.top+clips[clip].c.height-1; + + /* horizontal range */ + if (0 == skip || clips[clip].c.left > skips[skip-1].end) { + /* new one */ + skips[skip].start = clips[clip].c.left; + if (skips[skip].start < 0) + skips[skip].start = 0; + skips[skip].end = clips[clip].c.left + clips[clip].c.width; + if (skips[skip].end > width) + skips[skip].end = width; + skip++; + } else { + /* overlaps -- expand last one */ + end = clips[clip].c.left + clips[clip].c.width; + if (skips[skip-1].end < end) + skips[skip-1].end = end; + if (skips[skip-1].end > width) + skips[skip-1].end = width; + } + } + *nskips = skip; + *maxy = maxline; + + if (btcx_debug) { + printk(KERN_DEBUG "btcx: skips line %d-%d:",line,maxline); + for (skip = 0; skip < *nskips; skip++) { + printk(" %d-%d",skips[skip].start,skips[skip].end); + } + printk("\n"); + } +} diff --git a/drivers/media/pci/bt8xx/btcx-risc.h b/drivers/media/pci/bt8xx/btcx-risc.h new file mode 100644 index 0000000..1ed7a00 --- /dev/null +++ b/drivers/media/pci/bt8xx/btcx-risc.h @@ -0,0 +1,26 @@ +struct btcx_riscmem { + unsigned int size; + __le32 *cpu; + __le32 *jmp; + dma_addr_t dma; +}; + +struct btcx_skiplist { + int start; + int end; +}; + +int btcx_riscmem_alloc(struct pci_dev *pci, + struct btcx_riscmem *risc, + unsigned int size); +void btcx_riscmem_free(struct pci_dev *pci, + struct btcx_riscmem *risc); + +int btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, + struct v4l2_clip *clips, unsigned int n); +int btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, + unsigned int n, int mask); +void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips); +void btcx_calc_skips(int line, int width, int *maxy, + struct btcx_skiplist *skips, unsigned int *nskips, + const struct v4l2_clip *clips, unsigned int nclips); -- cgit v0.10.2 From 8d8e6d6005de1f2421b7846bfb50782b09cd63a7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 12 Dec 2014 10:27:57 -0300 Subject: [media] cx28521: drop videobuf abuse in cx25821-alsa The alsa driver uses videobuf low-level functions that are not available in vb2, so replace them by driver-specific functions. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c index 27e6493..24f964b 100644 --- a/drivers/media/pci/cx25821/cx25821-alsa.c +++ b/drivers/media/pci/cx25821/cx25821-alsa.c @@ -64,7 +64,10 @@ static int devno; struct cx25821_audio_buffer { unsigned int bpl; struct cx25821_riscmem risc; - struct videobuf_dmabuf dma; + void *vaddr; + struct scatterlist *sglist; + int sglen; + int nr_pages; }; struct cx25821_audio_dev { @@ -87,8 +90,6 @@ struct cx25821_audio_dev { unsigned int period_size; unsigned int num_periods; - struct videobuf_dmabuf *dma_risc; - struct cx25821_audio_buffer *buf; struct snd_pcm_substream *substream; @@ -142,6 +143,83 @@ MODULE_PARM_DESC(debug, "enable debug messages"); #define PCI_MSK_AUD_EXT (1 << 4) #define PCI_MSK_AUD_INT (1 << 3) + +static int cx25821_alsa_dma_init(struct cx25821_audio_dev *chip, int nr_pages) +{ + struct cx25821_audio_buffer *buf = chip->buf; + struct page *pg; + int i; + + buf->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); + if (NULL == buf->vaddr) { + dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); + return -ENOMEM; + } + + dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", + (unsigned long)buf->vaddr, + nr_pages << PAGE_SHIFT); + + memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT); + buf->nr_pages = nr_pages; + + buf->sglist = vzalloc(buf->nr_pages * sizeof(*buf->sglist)); + if (NULL == buf->sglist) + goto vzalloc_err; + + sg_init_table(buf->sglist, buf->nr_pages); + for (i = 0; i < buf->nr_pages; i++) { + pg = vmalloc_to_page(buf->vaddr + i * PAGE_SIZE); + if (NULL == pg) + goto vmalloc_to_page_err; + sg_set_page(&buf->sglist[i], pg, PAGE_SIZE, 0); + } + return 0; + +vmalloc_to_page_err: + vfree(buf->sglist); + buf->sglist = NULL; +vzalloc_err: + vfree(buf->vaddr); + buf->vaddr = NULL; + return -ENOMEM; +} + +static int cx25821_alsa_dma_map(struct cx25821_audio_dev *dev) +{ + struct cx25821_audio_buffer *buf = dev->buf; + + buf->sglen = dma_map_sg(&dev->pci->dev, buf->sglist, + buf->nr_pages, PCI_DMA_FROMDEVICE); + + if (0 == buf->sglen) { + pr_warn("%s: cx25821_alsa_map_sg failed\n", __func__); + return -ENOMEM; + } + return 0; +} + +static int cx25821_alsa_dma_unmap(struct cx25821_audio_dev *dev) +{ + struct cx25821_audio_buffer *buf = dev->buf; + + if (!buf->sglen) + return 0; + + dma_unmap_sg(&dev->pci->dev, buf->sglist, buf->sglen, PCI_DMA_FROMDEVICE); + buf->sglen = 0; + return 0; +} + +static int cx25821_alsa_dma_free(struct cx25821_audio_buffer *buf) +{ + vfree(buf->sglist); + buf->sglist = NULL; + vfree(buf->vaddr); + buf->vaddr = NULL; + return 0; +} + /* * BOARD Specific: Sets audio DMA */ @@ -335,12 +413,12 @@ static int dsp_buffer_free(struct cx25821_audio_dev *chip) BUG_ON(!chip->dma_size); dprintk(2, "Freeing buffer\n"); - videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc); - videobuf_dma_free(chip->dma_risc); + cx25821_alsa_dma_unmap(chip); + cx25821_alsa_dma_free(chip->buf); pci_free_consistent(chip->pci, risc->size, risc->cpu, risc->dma); kfree(chip->buf); - chip->dma_risc = NULL; + chip->buf = NULL; chip->dma_size = 0; return 0; @@ -432,8 +510,6 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); - struct videobuf_dmabuf *dma; - struct cx25821_audio_buffer *buf; int ret; @@ -457,19 +533,18 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, chip->period_size = AUDIO_LINE_SIZE; buf->bpl = chip->period_size; + chip->buf = buf; - dma = &buf->dma; - videobuf_dma_init(dma); - ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, + ret = cx25821_alsa_dma_init(chip, (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT)); if (ret < 0) goto error; - ret = videobuf_dma_map(&chip->pci->dev, dma); + ret = cx25821_alsa_dma_map(chip); if (ret < 0) goto error; - ret = cx25821_risc_databuffer_audio(chip->pci, &buf->risc, dma->sglist, + ret = cx25821_risc_databuffer_audio(chip->pci, &buf->risc, buf->sglist, chip->period_size, chip->num_periods, 1); if (ret < 0) { pr_info("DEBUG: ERROR after cx25821_risc_databuffer_audio()\n"); @@ -481,16 +556,14 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - chip->buf = buf; - chip->dma_risc = dma; - - substream->runtime->dma_area = chip->dma_risc->vaddr; + substream->runtime->dma_area = chip->buf->vaddr; substream->runtime->dma_bytes = chip->dma_size; substream->runtime->dma_addr = 0; return 0; error: + chip->buf = NULL; kfree(buf); return ret; } -- cgit v0.10.2 From b671ae6bdc1ad77a63536c6f74940976b9b89322 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 12 Dec 2014 10:27:58 -0300 Subject: [media] cx25821: convert to vb2 This patch converts the cx25821 driver from the old videobuf framework to the new vb2 framework. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx25821/Kconfig b/drivers/media/pci/cx25821/Kconfig index 0e69cab..1755d3d 100644 --- a/drivers/media/pci/cx25821/Kconfig +++ b/drivers/media/pci/cx25821/Kconfig @@ -2,7 +2,7 @@ config VIDEO_CX25821 tristate "Conexant cx25821 support" depends on VIDEO_DEV && PCI && I2C select I2C_ALGOBIT - select VIDEOBUF_DMA_SG + select VIDEOBUF2_DMA_SG ---help--- This is a video4linux driver for Conexant 25821 based TV cards. diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index c8c65b7..c1ea24e 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -874,10 +874,9 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) if (dev->pci->device != 0x8210) { pr_info("%s(): Exiting. Incorrect Hardware device = 0x%02x\n", __func__, dev->pci->device); - return -1; - } else { - pr_info("Athena Hardware device = 0x%02x\n", dev->pci->device); + return -ENODEV; } + pr_info("Athena Hardware device = 0x%02x\n", dev->pci->device); /* Apply a sensible clock frequency for the PCIe bridge */ dev->clk_freq = 28000000; @@ -1003,11 +1002,17 @@ EXPORT_SYMBOL(cx25821_riscmem_alloc); static __le32 *cx25821_risc_field(__le32 * rp, struct scatterlist *sglist, unsigned int offset, u32 sync_line, unsigned int bpl, unsigned int padding, - unsigned int lines) + unsigned int lines, bool jump) { struct scatterlist *sg; unsigned int line, todo; + if (jump) { + *(rp++) = cpu_to_le32(RISC_JUMP); + *(rp++) = cpu_to_le32(0); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + } + /* sync instruction */ if (sync_line != NO_SYNC_LINE) *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); @@ -1073,13 +1078,13 @@ int cx25821_risc_buffer(struct pci_dev *pci, struct cx25821_riscmem *risc, fields++; /* estimate risc mem: worst case is one write per page border + - one write per scan line + syncs + jump (all 2 dwords). Padding + one write per scan line + syncs + jump (all 3 dwords). Padding can cause next bpl to start close to a page border. First DMA region may be smaller than PAGE_SIZE */ /* write and jump need and extra dword */ instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines); - instructions += 2; + instructions += 5; rc = cx25821_riscmem_alloc(pci, risc, instructions * 12); if (rc < 0) @@ -1090,17 +1095,17 @@ int cx25821_risc_buffer(struct pci_dev *pci, struct cx25821_riscmem *risc, if (UNSET != top_offset) { rp = cx25821_risc_field(rp, sglist, top_offset, 0, bpl, padding, - lines); + lines, true); } if (UNSET != bottom_offset) { rp = cx25821_risc_field(rp, sglist, bottom_offset, 0x200, bpl, - padding, lines); + padding, lines, UNSET == top_offset); } /* save pointer to jmp instruction address */ risc->jmp = rp; - BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); + BUG_ON((risc->jmp - risc->cpu + 3) * sizeof(*risc->cpu) > risc->size); return 0; } @@ -1200,41 +1205,14 @@ int cx25821_risc_databuffer_audio(struct pci_dev *pci, } EXPORT_SYMBOL(cx25821_risc_databuffer_audio); -int cx25821_risc_stopper(struct pci_dev *pci, struct cx25821_riscmem *risc, - u32 reg, u32 mask, u32 value) +void cx25821_free_buffer(struct cx25821_dev *dev, struct cx25821_buffer *buf) { - __le32 *rp; - int rc; - - rc = cx25821_riscmem_alloc(pci, risc, 4 * 16); - - if (rc < 0) - return rc; - - /* write risc instructions */ - rp = risc->cpu; - - *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ1); - *(rp++) = cpu_to_le32(reg); - *(rp++) = cpu_to_le32(value); - *(rp++) = cpu_to_le32(mask); - *(rp++) = cpu_to_le32(RISC_JUMP); - *(rp++) = cpu_to_le32(risc->dma); - *(rp++) = cpu_to_le32(0); /* bits 63-32 */ - return 0; -} - -void cx25821_free_buffer(struct videobuf_queue *q, struct cx25821_buffer *buf) -{ - struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); - BUG_ON(in_interrupt()); - videobuf_waiton(q, &buf->vb, 0, 0); - videobuf_dma_unmap(q->dev, dma); - videobuf_dma_free(dma); - pci_free_consistent(to_pci_dev(q->dev), + if (WARN_ON(buf->risc.size == 0)) + return; + pci_free_consistent(dev->pci, buf->risc.size, buf->risc.cpu, buf->risc.dma); - buf->vb.state = VIDEOBUF_NEEDS_INIT; + memset(&buf->risc, 0, sizeof(buf->risc)); } static irqreturn_t cx25821_irq(int irq, void *dev_id) @@ -1319,14 +1297,15 @@ static int cx25821_initdev(struct pci_dev *pci_dev, goto fail_unregister_device; } + dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev); + if (IS_ERR(dev->alloc_ctx)) { + err = PTR_ERR(dev->alloc_ctx); + goto fail_unregister_pci; + } err = cx25821_dev_setup(dev); - if (err) { - if (err == -EBUSY) - goto fail_unregister_device; - else - goto fail_unregister_pci; - } + if (err) + goto fail_free_ctx; /* print pci info */ pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); @@ -1356,6 +1335,8 @@ fail_irq: pr_info("cx25821_initdev() can't get IRQ !\n"); cx25821_dev_unregister(dev); +fail_free_ctx: + vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); fail_unregister_pci: pci_disable_device(pci_dev); fail_unregister_device: @@ -1379,6 +1360,7 @@ static void cx25821_finidev(struct pci_dev *pci_dev) free_irq(pci_dev->irq, dev); cx25821_dev_unregister(dev); + vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); v4l2_device_unregister(v4l2_dev); kfree(dev); } diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index 3eda1a1..3497946 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -3,7 +3,7 @@ * * Copyright (C) 2009 Conexant Systems Inc. * Authors , - * Based on Steven Toth cx23885 driver + * Based on Steven Toth cx25821 driver * Parts adapted/taken from Eduardo Moscoso Rubino * Copyright (C) 2009 Eduardo Moscoso Rubino * @@ -46,10 +46,6 @@ static unsigned int irq_debug; module_param(irq_debug, int, 0644); MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]"); -static unsigned int vid_limit = 16; -module_param(vid_limit, int, 0644); -MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); - #define FORMAT_FLAGS_PACKED 0x01 static const struct cx25821_fmt formats[] = { @@ -76,41 +72,6 @@ static const struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc) return NULL; } -void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, - u32 count) -{ - struct cx25821_buffer *buf; - int bc; - - for (bc = 0;; bc++) { - if (list_empty(&q->active)) { - dprintk(1, "bc=%d (=0: active empty)\n", bc); - break; - } - - buf = list_entry(q->active.next, struct cx25821_buffer, - vb.queue); - - /* count comes from the hw and it is 16bit wide -- - * this trick handles wrap-arounds correctly for - * up to 32767 buffers in flight... */ - if ((s16) (count - buf->count) < 0) - break; - - v4l2_get_timestamp(&buf->vb.ts); - buf->vb.state = VIDEOBUF_DONE; - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); - } - - if (list_empty(&q->active)) - del_timer(&q->timeout); - else - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - if (bc != 1) - pr_err("%s: %d buffers handled (should be 1)\n", __func__, bc); -} - int cx25821_start_video_dma(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, struct cx25821_buffer *buf, @@ -123,7 +84,6 @@ int cx25821_start_video_dma(struct cx25821_dev *dev, /* reset counter */ cx_write(channel->gpcnt_ctl, 3); - q->count = 1; /* enable irq */ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << channel->i)); @@ -139,86 +99,8 @@ int cx25821_start_video_dma(struct cx25821_dev *dev, return 0; } -static int cx25821_restart_video_queue(struct cx25821_dev *dev, - struct cx25821_dmaqueue *q, - const struct sram_channel *channel) -{ - struct cx25821_buffer *buf, *prev; - struct list_head *item; - - if (!list_empty(&q->active)) { - buf = list_entry(q->active.next, struct cx25821_buffer, - vb.queue); - - cx25821_start_video_dma(dev, q, buf, channel); - - list_for_each(item, &q->active) { - buf = list_entry(item, struct cx25821_buffer, vb.queue); - buf->count = q->count++; - } - - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - return 0; - } - - prev = NULL; - for (;;) { - if (list_empty(&q->queued)) - return 0; - - buf = list_entry(q->queued.next, struct cx25821_buffer, - vb.queue); - - if (NULL == prev) { - list_move_tail(&buf->vb.queue, &q->active); - cx25821_start_video_dma(dev, q, buf, channel); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - } else if (prev->vb.width == buf->vb.width && - prev->vb.height == buf->vb.height && - prev->fmt == buf->fmt) { - list_move_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */ - } else { - return 0; - } - prev = buf; - } -} - -static void cx25821_vid_timeout(unsigned long data) -{ - struct cx25821_data *timeout_data = (struct cx25821_data *)data; - struct cx25821_dev *dev = timeout_data->dev; - const struct sram_channel *channel = timeout_data->channel; - struct cx25821_dmaqueue *q = &dev->channels[channel->i].dma_vidq; - struct cx25821_buffer *buf; - unsigned long flags; - - /* cx25821_sram_channel_dump(dev, channel); */ - cx_clear(channel->dma_ctl, 0x11); - - spin_lock_irqsave(&dev->slock, flags); - while (!list_empty(&q->active)) { - buf = list_entry(q->active.next, struct cx25821_buffer, - vb.queue); - list_del(&buf->vb.queue); - - buf->vb.state = VIDEOBUF_ERROR; - wake_up(&buf->vb.done); - } - - cx25821_restart_video_queue(dev, q, channel); - spin_unlock_irqrestore(&dev->slock, flags); -} - int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) { - u32 count = 0; int handled = 0; u32 mask; const struct sram_channel *channel = dev->channels[chan_num].sram_channels; @@ -239,317 +121,197 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) /* risc1 y */ if (status & FLD_VID_DST_RISC1) { - spin_lock(&dev->slock); - count = cx_read(channel->gpcnt); - cx25821_video_wakeup(dev, &dev->channels[channel->i].dma_vidq, - count); - spin_unlock(&dev->slock); - handled++; - } + struct cx25821_dmaqueue *dmaq = + &dev->channels[channel->i].dma_vidq; + struct cx25821_buffer *buf; - /* risc2 y */ - if (status & 0x10) { - dprintk(2, "stopper video\n"); spin_lock(&dev->slock); - cx25821_restart_video_queue(dev, - &dev->channels[channel->i].dma_vidq, channel); + if (!list_empty(&dmaq->active)) { + buf = list_entry(dmaq->active.next, + struct cx25821_buffer, queue); + + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + buf->vb.v4l2_buf.sequence = dmaq->count++; + list_del(&buf->queue); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); + } spin_unlock(&dev->slock); handled++; } return handled; } -static int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count, - unsigned int *size) +static int cx25821_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct cx25821_channel *chan = q->priv_data; - - *size = chan->fmt->depth * chan->width * chan->height >> 3; - - if (0 == *count) - *count = 32; - - if (*size * *count > vid_limit * 1024 * 1024) - *count = (vid_limit * 1024 * 1024) / *size; + struct cx25821_channel *chan = q->drv_priv; + *num_planes = 1; + sizes[0] = (chan->fmt->depth * chan->width * chan->height) >> 3; + alloc_ctxs[0] = chan->dev->alloc_ctx; return 0; } -static int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) +static int cx25821_buffer_prepare(struct vb2_buffer *vb) { - struct cx25821_channel *chan = q->priv_data; + struct cx25821_channel *chan = vb->vb2_queue->drv_priv; struct cx25821_dev *dev = chan->dev; struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb); - int rc, init_buffer = 0; + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); u32 line0_offset; - struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); int bpl_local = LINE_SIZE_D1; + int ret; - BUG_ON(NULL == chan->fmt); - if (chan->width < 48 || chan->width > 720 || - chan->height < 32 || chan->height > 576) - return -EINVAL; - - buf->vb.size = (chan->width * chan->height * chan->fmt->depth) >> 3; + if (chan->pixel_formats == PIXEL_FRMT_411) + buf->bpl = (chan->fmt->depth * chan->width) >> 3; + else + buf->bpl = (chan->fmt->depth >> 3) * chan->width; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + if (vb2_plane_size(vb, 0) < chan->height * buf->bpl) return -EINVAL; + vb2_set_plane_payload(vb, 0, chan->height * buf->bpl); + buf->vb.v4l2_buf.field = chan->field; - if (buf->fmt != chan->fmt || - buf->vb.width != chan->width || - buf->vb.height != chan->height || buf->vb.field != field) { - buf->fmt = chan->fmt; - buf->vb.width = chan->width; - buf->vb.height = chan->height; - buf->vb.field = field; - init_buffer = 1; - } + if (chan->pixel_formats == PIXEL_FRMT_411) { + bpl_local = buf->bpl; + } else { + bpl_local = buf->bpl; /* Default */ - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - init_buffer = 1; - rc = videobuf_iolock(q, &buf->vb, NULL); - if (0 != rc) { - printk(KERN_DEBUG pr_fmt("videobuf_iolock failed!\n")); - goto fail; + if (chan->use_cif_resolution) { + if (dev->tvnorm & V4L2_STD_625_50) + bpl_local = 352 << 1; + else + bpl_local = chan->cif_width << 1; } } - dprintk(1, "init_buffer=%d\n", init_buffer); - - if (init_buffer) { - if (chan->pixel_formats == PIXEL_FRMT_411) - buf->bpl = (buf->fmt->depth * buf->vb.width) >> 3; - else - buf->bpl = (buf->fmt->depth >> 3) * (buf->vb.width); - - if (chan->pixel_formats == PIXEL_FRMT_411) { - bpl_local = buf->bpl; - } else { - bpl_local = buf->bpl; /* Default */ - - if (chan->use_cif_resolution) { - if (dev->tvnorm & V4L2_STD_625_50) - bpl_local = 352 << 1; - else - bpl_local = chan->cif_width << 1; - } - } - - switch (buf->vb.field) { - case V4L2_FIELD_TOP: - cx25821_risc_buffer(dev->pci, &buf->risc, - dma->sglist, 0, UNSET, - buf->bpl, 0, buf->vb.height); - break; - case V4L2_FIELD_BOTTOM: - cx25821_risc_buffer(dev->pci, &buf->risc, - dma->sglist, UNSET, 0, - buf->bpl, 0, buf->vb.height); - break; - case V4L2_FIELD_INTERLACED: - /* All other formats are top field first */ - line0_offset = 0; - dprintk(1, "top field first\n"); - - cx25821_risc_buffer(dev->pci, &buf->risc, - dma->sglist, line0_offset, - bpl_local, bpl_local, bpl_local, - buf->vb.height >> 1); - break; - case V4L2_FIELD_SEQ_TB: - cx25821_risc_buffer(dev->pci, &buf->risc, - dma->sglist, - 0, buf->bpl * (buf->vb.height >> 1), - buf->bpl, 0, buf->vb.height >> 1); - break; - case V4L2_FIELD_SEQ_BT: - cx25821_risc_buffer(dev->pci, &buf->risc, - dma->sglist, - buf->bpl * (buf->vb.height >> 1), 0, - buf->bpl, 0, buf->vb.height >> 1); - break; - default: - BUG(); - } + switch (chan->field) { + case V4L2_FIELD_TOP: + ret = cx25821_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, 0, UNSET, + buf->bpl, 0, chan->height); + break; + case V4L2_FIELD_BOTTOM: + ret = cx25821_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, UNSET, 0, + buf->bpl, 0, chan->height); + break; + case V4L2_FIELD_INTERLACED: + /* All other formats are top field first */ + line0_offset = 0; + dprintk(1, "top field first\n"); + + ret = cx25821_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, line0_offset, + bpl_local, bpl_local, bpl_local, + chan->height >> 1); + break; + case V4L2_FIELD_SEQ_TB: + ret = cx25821_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, + 0, buf->bpl * (chan->height >> 1), + buf->bpl, 0, chan->height >> 1); + break; + case V4L2_FIELD_SEQ_BT: + ret = cx25821_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, + buf->bpl * (chan->height >> 1), 0, + buf->bpl, 0, chan->height >> 1); + break; + default: + WARN_ON(1); + ret = -EINVAL; + break; } dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", - buf, buf->vb.i, chan->width, chan->height, chan->fmt->depth, - chan->fmt->name, (unsigned long)buf->risc.dma); - - buf->vb.state = VIDEOBUF_PREPARED; - - return 0; + buf, buf->vb.v4l2_buf.index, chan->width, chan->height, + chan->fmt->depth, chan->fmt->name, + (unsigned long)buf->risc.dma); -fail: - cx25821_free_buffer(q, buf); - return rc; + return ret; } -static void cx25821_buffer_release(struct videobuf_queue *q, - struct videobuf_buffer *vb) +static void cx25821_buffer_finish(struct vb2_buffer *vb) { struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb); + struct cx25821_channel *chan = vb->vb2_queue->drv_priv; + struct cx25821_dev *dev = chan->dev; - cx25821_free_buffer(q, buf); -} - -static int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct cx25821_channel *chan = video_drvdata(file); - - return videobuf_mmap_mapper(&chan->vidq, vma); + cx25821_free_buffer(dev, buf); } - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +static void cx25821_buffer_queue(struct vb2_buffer *vb) { struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb); - struct cx25821_buffer *prev; - struct cx25821_channel *chan = vq->priv_data; + struct cx25821_channel *chan = vb->vb2_queue->drv_priv; struct cx25821_dev *dev = chan->dev; + struct cx25821_buffer *prev; struct cx25821_dmaqueue *q = &dev->channels[chan->id].dma_vidq; - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); - buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - - dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); - - if (!list_empty(&q->queued)) { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, - buf->vb.i); - - } else if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx25821_start_video_dma(dev, q, buf, chan->sram_channels); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", - buf, buf->vb.i, buf->count, q->count); + buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 12); + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 12); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + if (list_empty(&q->active)) { + list_add_tail(&buf->queue, &q->active); } else { + buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1); prev = list_entry(q->active.prev, struct cx25821_buffer, - vb.queue); - if (prev->vb.width == buf->vb.width - && prev->vb.height == buf->vb.height - && prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n", - buf, buf->vb.i, buf->count); - - } else { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, - buf->vb.i); - } + queue); + list_add_tail(&buf->queue, &q->active); + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); } - - if (list_empty(&q->active)) - dprintk(2, "active queue empty!\n"); } -static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = cx25821_buffer_setup, - .buf_prepare = cx25821_buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = cx25821_buffer_release, -}; - -static ssize_t video_read(struct file *file, char __user * data, size_t count, - loff_t *ppos) +static int cx25821_start_streaming(struct vb2_queue *q, unsigned int count) { - struct v4l2_fh *fh = file->private_data; - struct cx25821_channel *chan = video_drvdata(file); + struct cx25821_channel *chan = q->drv_priv; struct cx25821_dev *dev = chan->dev; - int err = 0; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - if (chan->streaming_fh && chan->streaming_fh != fh) { - err = -EBUSY; - goto unlock; - } - chan->streaming_fh = fh; + struct cx25821_dmaqueue *dmaq = &dev->channels[chan->id].dma_vidq; + struct cx25821_buffer *buf = list_entry(dmaq->active.next, + struct cx25821_buffer, queue); - err = videobuf_read_one(&chan->vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); -unlock: - mutex_unlock(&dev->lock); - return err; -} - -static unsigned int video_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx25821_channel *chan = video_drvdata(file); - unsigned long req_events = poll_requested_events(wait); - unsigned int res = v4l2_ctrl_poll(file, wait); - - if (req_events & (POLLIN | POLLRDNORM)) - res |= videobuf_poll_stream(file, &chan->vidq, wait); - return res; - - /* This doesn't belong in poll(). This can be done - * much better with vb2. We keep this code here as a - * reminder. - if ((res & POLLIN) && buf->vb.state == VIDEOBUF_DONE) { - struct cx25821_dev *dev = chan->dev; - - if (dev && chan->use_cif_resolution) { - u8 cam_id = *((char *)buf->vb.baddr + 3); - memcpy((char *)buf->vb.baddr, - (char *)buf->vb.baddr + (chan->width * 2), - (chan->width * 2)); - *((char *)buf->vb.baddr + 3) = cam_id; - } - } - */ + dmaq->count = 0; + cx25821_start_video_dma(dev, dmaq, buf, chan->sram_channels); + return 0; } -static int video_release(struct file *file) +static void cx25821_stop_streaming(struct vb2_queue *q) { - struct cx25821_channel *chan = video_drvdata(file); - struct v4l2_fh *fh = file->private_data; + struct cx25821_channel *chan = q->drv_priv; struct cx25821_dev *dev = chan->dev; - const struct sram_channel *sram_ch = - dev->channels[0].sram_channels; - - mutex_lock(&dev->lock); - /* stop the risc engine and fifo */ - cx_write(sram_ch->dma_ctl, 0); /* FIFO and RISC disable */ + struct cx25821_dmaqueue *dmaq = &dev->channels[chan->id].dma_vidq; + unsigned long flags; - /* stop video capture */ - if (chan->streaming_fh == fh) { - videobuf_queue_cancel(&chan->vidq); - chan->streaming_fh = NULL; - } + cx_write(chan->sram_channels->dma_ctl, 0); /* FIFO and RISC disable */ + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&dmaq->active)) { + struct cx25821_buffer *buf = list_entry(dmaq->active.next, + struct cx25821_buffer, queue); - if (chan->vidq.read_buf) { - cx25821_buffer_release(&chan->vidq, chan->vidq.read_buf); - kfree(chan->vidq.read_buf); + list_del(&buf->queue); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); } - - videobuf_mmap_free(&chan->vidq); - mutex_unlock(&dev->lock); - - return v4l2_fh_release(file); + spin_unlock_irqrestore(&dev->slock, flags); } +static struct vb2_ops cx25821_video_qops = { + .queue_setup = cx25821_queue_setup, + .buf_prepare = cx25821_buffer_prepare, + .buf_finish = cx25821_buffer_finish, + .buf_queue = cx25821_buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = cx25821_start_streaming, + .stop_streaming = cx25821_stop_streaming, +}; + /* VIDEO IOCTLS */ static int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv, @@ -571,7 +333,7 @@ static int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width = chan->width; f->fmt.pix.height = chan->height; - f->fmt.pix.field = chan->vidq.field; + f->fmt.pix.field = chan->field; f->fmt.pix.pixelformat = chan->fmt->fourcc; f->fmt.pix.bytesperline = (chan->width * chan->fmt->depth) >> 3; f->fmt.pix.sizeimage = chan->height * f->fmt.pix.bytesperline; @@ -632,7 +394,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, return err; chan->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); - chan->vidq.field = f->fmt.pix.field; + chan->field = f->fmt.pix.field; chan->width = f->fmt.pix.width; chan->height = f->fmt.pix.height; @@ -654,47 +416,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_channel *chan = video_drvdata(file); - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (chan->streaming_fh && chan->streaming_fh != priv) - return -EBUSY; - chan->streaming_fh = priv; - - return videobuf_streamon(&chan->vidq); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_channel *chan = video_drvdata(file); - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (chan->streaming_fh && chan->streaming_fh != priv) - return -EBUSY; - if (chan->streaming_fh == NULL) - return 0; - - chan->streaming_fh = NULL; - return videobuf_streamoff(&chan->vidq); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - int ret_val = 0; - struct cx25821_channel *chan = video_drvdata(file); - - ret_val = videobuf_dqbuf(&chan->vidq, p, file->f_flags & O_NONBLOCK); - p->sequence = chan->dma_vidq.count; - - return ret_val; -} - static int vidioc_log_status(struct file *file, void *priv) { struct cx25821_channel *chan = video_drvdata(file); @@ -729,29 +450,6 @@ static int cx25821_vidioc_querycap(struct file *file, void *priv, return 0; } -static int cx25821_vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct cx25821_channel *chan = video_drvdata(file); - - return videobuf_reqbufs(&chan->vidq, p); -} - -static int cx25821_vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct cx25821_channel *chan = video_drvdata(file); - - return videobuf_querybuf(&chan->vidq, p); -} - -static int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct cx25821_channel *chan = video_drvdata(file); - - return videobuf_qbuf(&chan->vidq, p); -} - static int cx25821_vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorms) { struct cx25821_channel *chan = video_drvdata(file); @@ -880,7 +578,7 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *priv, return err; chan->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); - chan->vidq.field = f->fmt.pix.field; + chan->field = f->fmt.pix.field; chan->width = f->fmt.pix.width; chan->height = f->fmt.pix.height; if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) @@ -890,52 +588,6 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *priv, return 0; } -static ssize_t video_write(struct file *file, const char __user *data, size_t count, - loff_t *ppos) -{ - struct cx25821_channel *chan = video_drvdata(file); - struct cx25821_dev *dev = chan->dev; - struct v4l2_fh *fh = file->private_data; - int err = 0; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - if (chan->streaming_fh && chan->streaming_fh != fh) { - err = -EBUSY; - goto unlock; - } - if (!chan->streaming_fh) { - err = cx25821_vidupstream_init(chan, chan->pixel_formats); - if (err) - goto unlock; - chan->streaming_fh = fh; - } - - err = cx25821_write_frame(chan, data, count); - count -= err; - *ppos += err; - -unlock: - mutex_unlock(&dev->lock); - return err; -} - -static int video_out_release(struct file *file) -{ - struct cx25821_channel *chan = video_drvdata(file); - struct cx25821_dev *dev = chan->dev; - struct v4l2_fh *fh = file->private_data; - - mutex_lock(&dev->lock); - if (chan->streaming_fh == fh) { - cx25821_stop_upstream_video(chan); - chan->streaming_fh = NULL; - } - mutex_unlock(&dev->lock); - - return v4l2_fh_release(file); -} - static const struct v4l2_ctrl_ops cx25821_ctrl_ops = { .s_ctrl = cx25821_s_ctrl, }; @@ -943,11 +595,11 @@ static const struct v4l2_ctrl_ops cx25821_ctrl_ops = { static const struct v4l2_file_operations video_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, - .release = video_release, - .read = video_read, - .poll = video_poll, - .mmap = cx25821_video_mmap, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { @@ -956,17 +608,18 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = cx25821_vidioc_reqbufs, - .vidioc_querybuf = cx25821_vidioc_querybuf, - .vidioc_qbuf = cx25821_vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_g_std = cx25821_vidioc_g_std, .vidioc_s_std = cx25821_vidioc_s_std, .vidioc_enum_input = cx25821_vidioc_enum_input, .vidioc_g_input = cx25821_vidioc_g_input, .vidioc_s_input = cx25821_vidioc_s_input, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, .vidioc_log_status = vidioc_log_status, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, @@ -984,9 +637,11 @@ static const struct video_device cx25821_video_device = { static const struct v4l2_file_operations video_out_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, - .write = video_write, - .release = video_out_release, + .release = vb2_fop_release, + .write = vb2_fop_write, + .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, }; static const struct v4l2_ioctl_ops video_out_ioctl_ops = { @@ -1017,13 +672,8 @@ void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num) cx_clear(PCI_INT_MSK, 1); if (video_is_registered(&dev->channels[chan_num].vdev)) { - struct cx25821_riscmem *risc = - &dev->channels[chan_num].dma_vidq.stopper; - video_unregister_device(&dev->channels[chan_num].vdev); v4l2_ctrl_handler_free(&dev->channels[chan_num].hdl); - - pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma); } } @@ -1041,6 +691,7 @@ int cx25821_video_register(struct cx25821_dev *dev) struct cx25821_channel *chan = &dev->channels[i]; struct video_device *vdev = &chan->vdev; struct v4l2_ctrl_handler *hdl = &chan->hdl; + struct vb2_queue *q; bool is_output = i > SRAM_CH08; if (i == SRAM_CH08) /* audio channel */ @@ -1068,11 +719,9 @@ int cx25821_video_register(struct cx25821_dev *dev) chan->out->chan = chan; } - cx25821_risc_stopper(dev->pci, &chan->dma_vidq.stopper, - chan->sram_channels->dma_ctl, 0x11, 0); - chan->sram_channels = &cx25821_sram_channels[i]; chan->width = 720; + chan->field = V4L2_FIELD_INTERLACED; if (dev->tvnorm & V4L2_STD_625_50) chan->height = 576; else @@ -1086,19 +735,27 @@ int cx25821_video_register(struct cx25821_dev *dev) cx_write(chan->sram_channels->int_stat, 0xffffffff); INIT_LIST_HEAD(&chan->dma_vidq.active); - INIT_LIST_HEAD(&chan->dma_vidq.queued); - chan->timeout_data.dev = dev; - chan->timeout_data.channel = &cx25821_sram_channels[i]; - chan->dma_vidq.timeout.function = cx25821_vid_timeout; - chan->dma_vidq.timeout.data = (unsigned long)&chan->timeout_data; - init_timer(&chan->dma_vidq.timeout); + q = &chan->vidq; + + q->type = is_output ? V4L2_BUF_TYPE_VIDEO_OUTPUT : + V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + q->io_modes |= is_output ? VB2_WRITE : VB2_READ; + q->gfp_flags = GFP_DMA32; + q->min_buffers_needed = 2; + q->drv_priv = chan; + q->buf_struct_size = sizeof(struct cx25821_buffer); + q->ops = &cx25821_video_qops; + q->mem_ops = &vb2_dma_sg_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &dev->lock; - if (!is_output) - videobuf_queue_sg_init(&chan->vidq, &cx25821_video_qops, &dev->pci->dev, - &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, sizeof(struct cx25821_buffer), - chan, &dev->lock); + if (!is_output) { + err = vb2_queue_init(q); + if (err < 0) + goto fail_unreg; + } /* register v4l devices */ *vdev = is_output ? cx25821_video_out_device : cx25821_video_device; @@ -1108,6 +765,7 @@ int cx25821_video_register(struct cx25821_dev *dev) else vdev->vfl_dir = VFL_DIR_TX; vdev->lock = &dev->lock; + vdev->queue = q; snprintf(vdev->name, sizeof(vdev->name), "%s #%d", dev->name, i); video_set_drvdata(vdev, chan); diff --git a/drivers/media/pci/cx25821/cx25821.h b/drivers/media/pci/cx25821/cx25821.h index 38beec2..34c5ff1 100644 --- a/drivers/media/pci/cx25821/cx25821.h +++ b/drivers/media/pci/cx25821/cx25821.h @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include "cx25821-reg.h" #include "cx25821-medusa-reg.h" @@ -120,13 +120,13 @@ struct cx25821_riscmem { /* buffer for one video frame */ struct cx25821_buffer { /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; + struct vb2_buffer vb; + struct list_head queue; /* cx25821 specific */ unsigned int bpl; struct cx25821_riscmem risc; const struct cx25821_fmt *fmt; - u32 count; }; enum port { @@ -165,17 +165,9 @@ struct cx25821_i2c { struct cx25821_dmaqueue { struct list_head active; - struct list_head queued; - struct timer_list timeout; - struct cx25821_riscmem stopper; u32 count; }; -struct cx25821_data { - struct cx25821_dev *dev; - const struct sram_channel *channel; -}; - struct cx25821_dev; struct cx25821_channel; @@ -213,18 +205,17 @@ struct cx25821_video_out_data { struct cx25821_channel { unsigned id; struct cx25821_dev *dev; - struct v4l2_fh *streaming_fh; struct v4l2_ctrl_handler hdl; - struct cx25821_data timeout_data; struct video_device vdev; struct cx25821_dmaqueue dma_vidq; - struct videobuf_queue vidq; + struct vb2_queue vidq; const struct sram_channel *sram_channels; const struct cx25821_fmt *fmt; + unsigned field; unsigned int width, height; int pixel_formats; int use_cif_resolution; @@ -250,6 +241,7 @@ struct cx25821_dev { int hwrevision; /* used by cx25821-alsa */ struct snd_card *card; + void *alloc_ctx; u32 clk_freq; @@ -425,10 +417,8 @@ extern int cx25821_risc_databuffer_audio(struct pci_dev *pci, struct scatterlist *sglist, unsigned int bpl, unsigned int lines, unsigned int lpi); -extern void cx25821_free_buffer(struct videobuf_queue *q, +extern void cx25821_free_buffer(struct cx25821_dev *dev, struct cx25821_buffer *buf); -extern int cx25821_risc_stopper(struct pci_dev *pci, struct cx25821_riscmem *risc, - u32 reg, u32 mask, u32 value); extern void cx25821_sram_channel_dump(struct cx25821_dev *dev, const struct sram_channel *ch); extern void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev, -- cgit v0.10.2 From 11c8a2df2cf8c213e1970d548aee067887971ef8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 12 Dec 2014 10:27:59 -0300 Subject: [media] cx25821: add create_bufs support Add support for the VIDIOC_CREATE_BUFS ioctl. This was missing in this driver and in vb2 it's trivial to add. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index 3497946..827c3c0 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -146,9 +146,13 @@ static int cx25821_queue_setup(struct vb2_queue *q, const struct v4l2_format *fm unsigned int sizes[], void *alloc_ctxs[]) { struct cx25821_channel *chan = q->drv_priv; + unsigned size = (chan->fmt->depth * chan->width * chan->height) >> 3; + + if (fmt && fmt->fmt.pix.sizeimage < size) + return -EINVAL; *num_planes = 1; - sizes[0] = (chan->fmt->depth * chan->width * chan->height) >> 3; + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : size; alloc_ctxs[0] = chan->dev->alloc_ctx; return 0; } @@ -610,6 +614,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_reqbufs = vb2_ioctl_reqbufs, .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, .vidioc_querybuf = vb2_ioctl_querybuf, .vidioc_qbuf = vb2_ioctl_qbuf, .vidioc_dqbuf = vb2_ioctl_dqbuf, -- cgit v0.10.2 From b6f21dc3541a3660acfec1e789a011e6d4154173 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 12 Dec 2014 10:28:00 -0300 Subject: [media] cx25821: remove video output support The video output functionality never worked for this driver. Now remove the creation of the output video nodes as well to prevent users from thinking that video output is available, when it isn't. To correctly implement this the video output should use vb2 as well, and that requires rewriting the output DMA setup. But without hardware to test I am not able to do that. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx25821/Makefile b/drivers/media/pci/cx25821/Makefile index 5872feb..c8f8598 100644 --- a/drivers/media/pci/cx25821/Makefile +++ b/drivers/media/pci/cx25821/Makefile @@ -1,6 +1,6 @@ cx25821-y := cx25821-core.o cx25821-cards.o cx25821-i2c.o \ cx25821-gpio.o cx25821-medusa-video.o \ - cx25821-video.o cx25821-video-upstream.o + cx25821-video.o obj-$(CONFIG_VIDEO_CX25821) += cx25821.o obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index c1ea24e..559f829 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -965,11 +965,15 @@ void cx25821_dev_unregister(struct cx25821_dev *dev) release_mem_region(dev->base_io_addr, pci_resource_len(dev->pci, 0)); - for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; i++) { + for (i = 0; i < MAX_VID_CAP_CHANNEL_NUM - 1; i++) { if (i == SRAM_CH08) /* audio channel */ continue; + /* + * TODO: enable when video output is properly + * supported. if (i == SRAM_CH09 || i == SRAM_CH10) cx25821_free_mem_upstream(&dev->channels[i]); + */ cx25821_video_unregister(dev, i); } diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index 827c3c0..7bc495e 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -692,7 +692,7 @@ int cx25821_video_register(struct cx25821_dev *dev) spin_lock_init(&dev->slock); - for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; ++i) { + for (i = 0; i < MAX_VID_CAP_CHANNEL_NUM - 1; ++i) { struct cx25821_channel *chan = &dev->channels[i]; struct video_device *vdev = &chan->vdev; struct v4l2_ctrl_handler *hdl = &chan->hdl; diff --git a/drivers/media/pci/cx25821/cx25821.h b/drivers/media/pci/cx25821/cx25821.h index 34c5ff1..d81a08a 100644 --- a/drivers/media/pci/cx25821/cx25821.h +++ b/drivers/media/pci/cx25821/cx25821.h @@ -88,6 +88,13 @@ #define CX25821_BOARD_CONEXANT_ATHENA10 1 #define MAX_VID_CHANNEL_NUM 12 + +/* + * Maximum capture-only channels. This can go away once video/audio output + * is fully supported in this driver. + */ +#define MAX_VID_CAP_CHANNEL_NUM 10 + #define VID_CHANNEL_NUM 8 struct cx25821_fmt { -- cgit v0.10.2 From e476f4e15d859d1112872667983918bdfa4e663b Mon Sep 17 00:00:00 2001 From: Andrey Utkin Date: Wed, 5 Nov 2014 17:11:14 -0300 Subject: [media] solo6x10: just pass frame motion flag from hardware, drop additional handling as complicated and unstable Dropping code (introduced in 316d9e84a72069e04e483de0d5934c1d75f6a44c) which intends to make raising of motion events more "smooth"(?). It made motion event never appear in my installation. That code is complicated, so I couldn't figure out quickly how to fix it, so dropping it seems better to me. Another justification is that anyway application would implement "motion signal stabilization" if required, it is not necessarily kernel driver's job. Signed-off-by: Andrey Utkin Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c index 6e933d3..1c9688f 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c @@ -239,8 +239,6 @@ static int solo_enc_on(struct solo_enc_dev *solo_enc) if (solo_enc->bw_weight > solo_dev->enc_bw_remain) return -EBUSY; solo_enc->sequence = 0; - solo_enc->motion_last_state = false; - solo_enc->frames_since_last_motion = 0; solo_dev->enc_bw_remain -= solo_enc->bw_weight; if (solo_enc->type == SOLO_ENC_TYPE_EXT) @@ -529,36 +527,12 @@ static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc, } if (!ret) { - bool send_event = false; - vb->v4l2_buf.sequence = solo_enc->sequence++; vb->v4l2_buf.timestamp.tv_sec = vop_sec(vh); vb->v4l2_buf.timestamp.tv_usec = vop_usec(vh); /* Check for motion flags */ - if (solo_is_motion_on(solo_enc)) { - /* It takes a few frames for the hardware to detect - * motion. Once it does it clears the motion detection - * register and it takes again a few frames before - * motion is seen. This means in practice that when the - * motion field is 1, it will go back to 0 for the next - * frame. This leads to motion detection event being - * sent all the time, which is not what we want. - * Instead wait a few frames before deciding that the - * motion has halted. After some experimentation it - * turns out that waiting for 5 frames works well. - */ - if (enc_buf->motion == 0 && - solo_enc->motion_last_state && - solo_enc->frames_since_last_motion++ > 5) - send_event = true; - else if (enc_buf->motion) { - solo_enc->frames_since_last_motion = 0; - send_event = !solo_enc->motion_last_state; - } - } - - if (send_event) { + if (solo_is_motion_on(solo_enc) && enc_buf->motion) { struct v4l2_event ev = { .type = V4L2_EVENT_MOTION_DET, .u.motion_det = { @@ -568,8 +542,6 @@ static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc, }, }; - solo_enc->motion_last_state = enc_buf->motion; - solo_enc->frames_since_last_motion = 0; v4l2_event_queue(solo_enc->vfd, &ev); } } diff --git a/drivers/media/pci/solo6x10/solo6x10.h b/drivers/media/pci/solo6x10/solo6x10.h index bd8edfa..6c9bc70 100644 --- a/drivers/media/pci/solo6x10/solo6x10.h +++ b/drivers/media/pci/solo6x10/solo6x10.h @@ -159,8 +159,6 @@ struct solo_enc_dev { u16 motion_thresh; bool motion_global; bool motion_enabled; - bool motion_last_state; - u8 frames_since_last_motion; u16 width; u16 height; -- cgit v0.10.2 From 21a7e0596a63abdf12a9591c29359f7f15e18c16 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 1 Dec 2014 10:10:43 -0300 Subject: [media] media: drivers shouldn't touch debug field in video_device The debug field in struct video_device is for internal use only and drivers should mix that with their own debug module options. It is handled by the V4L2 core and users can set it using /sys/class/video4linux//debug. It has been deprecated for some time now, so it is time to remove it completely from the drivers. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 665e46d..6eed8f7 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3884,7 +3884,6 @@ static struct video_device *vdev_init(struct bttv *btv, *vfd = *template; vfd->v4l2_dev = &btv->c.v4l2_dev; vfd->release = video_device_release; - vfd->debug = bttv_debug; video_set_drvdata(vfd, btv); snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)", btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "", diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c index d3c79d9..b6be46e 100644 --- a/drivers/media/pci/cx88/cx88-blackbird.c +++ b/drivers/media/pci/cx88/cx88-blackbird.c @@ -1234,6 +1234,3 @@ static void __exit blackbird_fini(void) module_init(blackbird_init); module_exit(blackbird_fini); - -module_param_named(video_debug,cx8802_mpeg_template.debug, int, 0644); -MODULE_PARM_DESC(debug,"enable debug messages [video]"); diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 193373f..dd5b141 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1913,7 +1913,6 @@ int mccic_register(struct mcam_camera *cam) mutex_lock(&cam->s_mutex); cam->vdev = mcam_v4l_template; - cam->vdev.debug = 0; cam->vdev.v4l2_dev = &cam->v4l2_dev; video_set_drvdata(&cam->vdev, cam); ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 53ca12c..ecea76f 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -2062,7 +2062,6 @@ static struct video_device *cx231xx_vdev_init(struct cx231xx *dev, *vfd = *template; vfd->v4l2_dev = &dev->v4l2_dev; vfd->release = video_device_release; - vfd->debug = video_debug; vfd->lock = &dev->lock; snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index cf7f58b..3691b39 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2192,7 +2192,6 @@ static struct video_device *vfd = *template; vfd->v4l2_dev = &dev->v4l2->v4l2_dev; - vfd->debug = video_debug; vfd->lock = &dev->lock; if (dev->board.is_webcam) vfd->tvnorms = 0; diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index a4762910..65a326c 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -38,10 +38,6 @@ #include "stk1160.h" #include "stk1160-reg.h" -static unsigned int vidioc_debug; -module_param(vidioc_debug, int, 0644); -MODULE_PARM_DESC(vidioc_debug, "enable debug messages [vidioc]"); - static bool keep_buffers; module_param(keep_buffers, bool, 0644); MODULE_PARM_DESC(keep_buffers, "don't release buffers upon stop streaming"); @@ -659,7 +655,6 @@ int stk1160_video_register(struct stk1160 *dev) /* Initialize video_device with a template structure */ dev->vdev = v4l_template; - dev->vdev.debug = vidioc_debug; dev->vdev.queue = &dev->vb_vidq; /* diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index 3588dc3..e08fa58 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -1262,7 +1262,6 @@ static int stk_register_video_device(struct stk_camera *dev) dev->vdev = stk_v4l_data; dev->vdev.lock = &dev->lock; - dev->vdev.debug = debug; dev->vdev.v4l2_dev = &dev->v4l2_dev; video_set_drvdata(&dev->vdev, dev); err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 793577f..0f14d3c 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -941,7 +941,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, fmt = format_by_fourcc(f->fmt.pix.pixelformat); if (NULL == fmt) { - dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Fourcc format (0x%08x)" + dprintk(dev, 2, "Fourcc format (0x%08x)" " invalid.\n", f->fmt.pix.pixelformat); return -EINVAL; } @@ -1622,7 +1622,6 @@ static struct video_device *vdev_init(struct tm6000_core *dev, *vfd = *template; vfd->v4l2_dev = &dev->v4l2_dev; vfd->release = video_device_release; - vfd->debug = tm6000_debug; vfd->lock = &dev->lock; snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 5c00627..ca85031 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -1454,8 +1454,6 @@ static int zr364xx_probe(struct usb_interface *intf, cam->vdev.v4l2_dev = &cam->v4l2_dev; cam->vdev.ctrl_handler = &cam->ctrl_handler; video_set_drvdata(&cam->vdev, cam); - if (debug) - cam->vdev.debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; cam->udev = udev; diff --git a/drivers/staging/media/tlg2300/pd-common.h b/drivers/staging/media/tlg2300/pd-common.h index 9e23ad32..04c5aac 100644 --- a/drivers/staging/media/tlg2300/pd-common.h +++ b/drivers/staging/media/tlg2300/pd-common.h @@ -250,7 +250,6 @@ void free_all_urb_generic(struct urb **urb_array, int num); /* misc */ void poseidon_delete(struct kref *kref); extern int debug_mode; -void set_debug_mode(struct video_device *vfd, int debug_mode); #ifdef CONFIG_PM #define in_hibernation(pd) (pd->msg.event == PM_EVENT_FREEZE) diff --git a/drivers/staging/media/tlg2300/pd-radio.c b/drivers/staging/media/tlg2300/pd-radio.c index b391194..c0567b5 100644 --- a/drivers/staging/media/tlg2300/pd-radio.c +++ b/drivers/staging/media/tlg2300/pd-radio.c @@ -98,12 +98,9 @@ static int poseidon_fm_open(struct file *filp) usb_autopm_get_interface(p->interface); if (0 == p->state) { - struct video_device *vfd = &p->radio_data.fm_dev; - /* default pre-emphasis */ if (p->radio_data.pre_emphasis == 0) p->radio_data.pre_emphasis = TLG_TUNE_ASTD_FM_EUR; - set_debug_mode(vfd, debug_mode); ret = poseidon_check_mode_radio(p); if (ret < 0) { diff --git a/drivers/staging/media/tlg2300/pd-video.c b/drivers/staging/media/tlg2300/pd-video.c index 8cd7f02..c0c3c1c 100644 --- a/drivers/staging/media/tlg2300/pd-video.c +++ b/drivers/staging/media/tlg2300/pd-video.c @@ -1299,15 +1299,6 @@ static int pm_video_resume(struct poseidon *pd) } #endif -void set_debug_mode(struct video_device *vfd, int debug_mode) -{ - vfd->debug = 0; - if (debug_mode & 0x1) - vfd->debug = V4L2_DEBUG_IOCTL; - if (debug_mode & 0x2) - vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; -} - static void init_video_context(struct running_context *context) { context->sig_index = 0; @@ -1354,7 +1345,6 @@ static int pd_video_open(struct file *file) front->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; pd->video_data.users++; - set_debug_mode(vfd, debug_mode); videobuf_queue_vmalloc_init(&front->q, &pd_video_qops, NULL, &front->queue_lock, -- cgit v0.10.2 From 17028cdb74bf8bb593aebe5550bc90325fa9af88 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 1 Dec 2014 10:10:44 -0300 Subject: [media] v4l2 core: improve debug flag handling The old debug field is renamed to dev_debug to ensure that existing drivers (including out-of-tree drivers) that try to use the old name will no longer compile. A comment has also been added that makes it explicit that drivers shouldn't use this field. Additional bits have been added to the debug flag to be more fine-grained when debugging, especially when dealing with streaming ioctls and read, write and poll. You want to enable those explicitly to prevent flooding the log when streaming unless you actually want to do that. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index a13cc61..86bb93f 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -47,15 +47,15 @@ static ssize_t index_show(struct device *cd, } static DEVICE_ATTR_RO(index); -static ssize_t debug_show(struct device *cd, +static ssize_t dev_debug_show(struct device *cd, struct device_attribute *attr, char *buf) { struct video_device *vdev = to_video_device(cd); - return sprintf(buf, "%i\n", vdev->debug); + return sprintf(buf, "%i\n", vdev->dev_debug); } -static ssize_t debug_store(struct device *cd, struct device_attribute *attr, +static ssize_t dev_debug_store(struct device *cd, struct device_attribute *attr, const char *buf, size_t len) { struct video_device *vdev = to_video_device(cd); @@ -66,10 +66,10 @@ static ssize_t debug_store(struct device *cd, struct device_attribute *attr, if (res) return res; - vdev->debug = value; + vdev->dev_debug = value; return len; } -static DEVICE_ATTR_RW(debug); +static DEVICE_ATTR_RW(dev_debug); static ssize_t name_show(struct device *cd, struct device_attribute *attr, char *buf) @@ -82,7 +82,7 @@ static DEVICE_ATTR_RO(name); static struct attribute *video_device_attrs[] = { &dev_attr_name.attr, - &dev_attr_debug.attr, + &dev_attr_dev_debug.attr, &dev_attr_index.attr, NULL, }; @@ -304,7 +304,8 @@ static ssize_t v4l2_read(struct file *filp, char __user *buf, return -EINVAL; if (video_is_registered(vdev)) ret = vdev->fops->read(filp, buf, sz, off); - if (vdev->debug) + if ((vdev->dev_debug & V4L2_DEV_DEBUG_FOP) && + (vdev->dev_debug & V4L2_DEV_DEBUG_STREAMING)) printk(KERN_DEBUG "%s: read: %zd (%d)\n", video_device_node_name(vdev), sz, ret); return ret; @@ -320,7 +321,8 @@ static ssize_t v4l2_write(struct file *filp, const char __user *buf, return -EINVAL; if (video_is_registered(vdev)) ret = vdev->fops->write(filp, buf, sz, off); - if (vdev->debug) + if ((vdev->dev_debug & V4L2_DEV_DEBUG_FOP) && + (vdev->dev_debug & V4L2_DEV_DEBUG_STREAMING)) printk(KERN_DEBUG "%s: write: %zd (%d)\n", video_device_node_name(vdev), sz, ret); return ret; @@ -335,7 +337,7 @@ static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) return DEFAULT_POLLMASK; if (video_is_registered(vdev)) res = vdev->fops->poll(filp, poll); - if (vdev->debug > 2) + if (vdev->dev_debug & V4L2_DEV_DEBUG_POLL) printk(KERN_DEBUG "%s: poll: %08x\n", video_device_node_name(vdev), res); return res; @@ -404,7 +406,7 @@ static unsigned long v4l2_get_unmapped_area(struct file *filp, if (!video_is_registered(vdev)) return -ENODEV; ret = vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags); - if (vdev->debug) + if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP) printk(KERN_DEBUG "%s: get_unmapped_area (%d)\n", video_device_node_name(vdev), ret); return ret; @@ -420,7 +422,7 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) return -ENODEV; if (video_is_registered(vdev)) ret = vdev->fops->mmap(filp, vm); - if (vdev->debug) + if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP) printk(KERN_DEBUG "%s: mmap (%d)\n", video_device_node_name(vdev), ret); return ret; @@ -450,7 +452,7 @@ static int v4l2_open(struct inode *inode, struct file *filp) ret = -ENODEV; } - if (vdev->debug) + if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP) printk(KERN_DEBUG "%s: open (%d)\n", video_device_node_name(vdev), ret); /* decrease the refcount in case of an error */ @@ -467,7 +469,7 @@ static int v4l2_release(struct inode *inode, struct file *filp) if (vdev->fops->release) ret = vdev->fops->release(filp); - if (vdev->debug) + if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP) printk(KERN_DEBUG "%s: release\n", video_device_node_name(vdev)); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index faac2f4..b084072 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2339,7 +2339,7 @@ static long __video_do_ioctl(struct file *file, const struct v4l2_ioctl_info *info; void *fh = file->private_data; struct v4l2_fh *vfh = NULL; - int debug = vfd->debug; + int dev_debug = vfd->dev_debug; long ret = -ENOTTY; if (ops == NULL) { @@ -2388,11 +2388,15 @@ static long __video_do_ioctl(struct file *file, } done: - if (debug) { + if (dev_debug & (V4L2_DEV_DEBUG_IOCTL | V4L2_DEV_DEBUG_IOCTL_ARG)) { + if (!(dev_debug & V4L2_DEV_DEBUG_STREAMING) && + (cmd == VIDIOC_QBUF || cmd == VIDIOC_DQBUF)) + return ret; + v4l_printk_ioctl(video_device_node_name(vfd), cmd); if (ret < 0) pr_cont(": error %ld", ret); - if (debug == V4L2_DEBUG_IOCTL) + if (!(dev_debug & V4L2_DEV_DEBUG_IOCTL_ARG)) pr_cont("\n"); else if (_IOC_DIR(cmd) == _IOC_NONE) info->debug(arg, write_only); diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index eb76cfd..3e4fddf 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -124,7 +124,8 @@ struct video_device spinlock_t fh_lock; /* Lock for all v4l2_fhs */ struct list_head fh_list; /* List of struct v4l2_fh */ - int debug; /* Activates debug level*/ + /* Internal device debug flags, not for use by drivers */ + int dev_debug; /* Video standard vars */ v4l2_std_id tvnorms; /* Supported tv norms */ diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 53605f0..8537983 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -291,9 +291,18 @@ struct v4l2_ioctl_ops { /* v4l debugging and diagnostics */ -/* Debug bitmask flags to be used on V4L2 */ -#define V4L2_DEBUG_IOCTL 0x01 -#define V4L2_DEBUG_IOCTL_ARG 0x02 +/* Device debug flags to be used with the video device debug attribute */ + +/* Just log the ioctl name + error code */ +#define V4L2_DEV_DEBUG_IOCTL 0x01 +/* Log the ioctl name arguments + error code */ +#define V4L2_DEV_DEBUG_IOCTL_ARG 0x02 +/* Log the file operations open, release, mmap and get_unmapped_area */ +#define V4L2_DEV_DEBUG_FOP 0x04 +/* Log the read and write file operations and the VIDIOC_(D)QBUF ioctls */ +#define V4L2_DEV_DEBUG_STREAMING 0x08 +/* Log poll() */ +#define V4L2_DEV_DEBUG_POLL 0x10 /* Video standard functions */ extern const char *v4l2_norm_to_name(v4l2_std_id id); -- cgit v0.10.2 From 88f414f476bdf9def8123fec49890a3c94b5fb53 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 1 Dec 2014 10:10:45 -0300 Subject: [media] v4l2-framework.txt: document debug attribute The debug attribute in /sys/class/video4linux//debug was never documented. Add this. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index a11dff0..f586e29 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -793,8 +793,10 @@ video_register_device_no_warn() instead. Whenever a device node is created some attributes are also created for you. If you look in /sys/class/video4linux you see the devices. Go into e.g. -video0 and you will see 'name' and 'index' attributes. The 'name' attribute -is the 'name' field of the video_device struct. +video0 and you will see 'name', 'debug' and 'index' attributes. The 'name' +attribute is the 'name' field of the video_device struct. The 'debug' attribute +can be used to enable core debugging. See the next section for more detailed +information on this. The 'index' attribute is the index of the device node: for each call to video_register_device() the index is just increased by 1. The first video @@ -816,6 +818,25 @@ video_device was embedded in it. The vdev->release() callback will never be called if the registration failed, nor should you ever attempt to unregister the device if the registration failed. +video device debugging +---------------------- + +The 'debug' attribute that is created for each video, vbi, radio or swradio +device in /sys/class/video4linux// allows you to enable logging of +file operations. + +It is a bitmask and the following bits can be set: + +0x01: Log the ioctl name and error code. VIDIOC_(D)QBUF ioctls are only logged + if bit 0x08 is also set. +0x02: Log the ioctl name arguments and error code. VIDIOC_(D)QBUF ioctls are + only logged if bit 0x08 is also set. +0x04: Log the file operations open, release, read, write, mmap and + get_unmapped_area. The read and write operations are only logged if + bit 0x08 is also set. +0x08: Log the read and write file operations and the VIDIOC_QBUF and + VIDIOC_DQBUF ioctls. +0x10: Log the poll file operation. video_device cleanup -------------------- -- cgit v0.10.2 From e5b30145e56a20caeffed553d1239e401e4fb4e2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 13 Dec 2014 08:52:51 -0300 Subject: [media] av7110: fix sparse warning drivers/media/pci/ttpci/av7110.c:1226:15: warning: memset with byte count of 192512 Instead of memsetting this in one go, loop over each line and memset each line separately. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index c1f0617..45199a1 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -1219,11 +1219,14 @@ static int stop_ts_capture(struct av7110 *budget) static int start_ts_capture(struct av7110 *budget) { + unsigned y; + dprintk(2, "budget: %p\n", budget); if (budget->feeding1) return ++budget->feeding1; - memset(budget->grabbing, 0x00, TS_BUFLEN); + for (y = 0; y < TS_HEIGHT; y++) + memset(budget->grabbing + y * TS_WIDTH, 0x00, TS_WIDTH); budget->ttbp = 0; SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */ SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */ -- cgit v0.10.2 From 65adb86d6142f1233e84c81112907f4caa6afffd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 13 Dec 2014 08:52:52 -0300 Subject: [media] budget-core: fix sparse warnings Fixes these sparse warnings. drivers/media/pci/ttpci/budget-core.c:250:17: warning: context imbalance in 'ttpci_budget_debiread' - different lock contexts for basic block drivers/media/pci/ttpci/budget-core.c:289:17: warning: context imbalance in 'ttpci_budget_debiwrite' - different lock contexts for basic block To be honest, the new code does look better than the old. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c index 37d02fe..23e0549 100644 --- a/drivers/media/pci/ttpci/budget-core.c +++ b/drivers/media/pci/ttpci/budget-core.c @@ -231,63 +231,59 @@ static void vpeirq(unsigned long data) } -int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, - int uselocks, int nobusyloop) +static int ttpci_budget_debiread_nolock(struct budget *budget, u32 config, + int addr, int count, int nobusyloop) { struct saa7146_dev *saa = budget->dev; - int result = 0; - unsigned long flags = 0; - - if (count > 4 || count <= 0) - return 0; - - if (uselocks) - spin_lock_irqsave(&budget->debilock, flags); + int result; - if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { - if (uselocks) - spin_unlock_irqrestore(&budget->debilock, flags); + result = saa7146_wait_for_debi_done(saa, nobusyloop); + if (result < 0) return result; - } saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff)); saa7146_write(saa, DEBI_CONFIG, config); saa7146_write(saa, DEBI_PAGE, 0); saa7146_write(saa, MC2, (2 << 16) | 2); - if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { - if (uselocks) - spin_unlock_irqrestore(&budget->debilock, flags); + result = saa7146_wait_for_debi_done(saa, nobusyloop); + if (result < 0) return result; - } result = saa7146_read(saa, DEBI_AD); result &= (0xffffffffUL >> ((4 - count) * 8)); - - if (uselocks) - spin_unlock_irqrestore(&budget->debilock, flags); - return result; } -int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, - int count, u32 value, int uselocks, int nobusyloop) +int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, + int uselocks, int nobusyloop) { - struct saa7146_dev *saa = budget->dev; - unsigned long flags = 0; - int result; - if (count > 4 || count <= 0) return 0; - if (uselocks) - spin_lock_irqsave(&budget->debilock, flags); + if (uselocks) { + unsigned long flags; + int result; - if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { - if (uselocks) - spin_unlock_irqrestore(&budget->debilock, flags); + spin_lock_irqsave(&budget->debilock, flags); + result = ttpci_budget_debiread_nolock(budget, config, addr, + count, nobusyloop); + spin_unlock_irqrestore(&budget->debilock, flags); return result; } + return ttpci_budget_debiread_nolock(budget, config, addr, + count, nobusyloop); +} + +static int ttpci_budget_debiwrite_nolock(struct budget *budget, u32 config, + int addr, int count, u32 value, int nobusyloop) +{ + struct saa7146_dev *saa = budget->dev; + int result; + + result = saa7146_wait_for_debi_done(saa, nobusyloop); + if (result < 0) + return result; saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x00000 | (addr & 0xffff)); saa7146_write(saa, DEBI_CONFIG, config); @@ -295,15 +291,28 @@ int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, saa7146_write(saa, DEBI_AD, value); saa7146_write(saa, MC2, (2 << 16) | 2); - if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { - if (uselocks) - spin_unlock_irqrestore(&budget->debilock, flags); - return result; - } + result = saa7146_wait_for_debi_done(saa, nobusyloop); + return result < 0 ? result : 0; +} - if (uselocks) +int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, + int count, u32 value, int uselocks, int nobusyloop) +{ + if (count > 4 || count <= 0) + return 0; + + if (uselocks) { + unsigned long flags; + int result; + + spin_lock_irqsave(&budget->debilock, flags); + result = ttpci_budget_debiwrite_nolock(budget, config, addr, + count, value, nobusyloop); spin_unlock_irqrestore(&budget->debilock, flags); - return 0; + return result; + } + return ttpci_budget_debiwrite_nolock(budget, config, addr, + count, value, nobusyloop); } -- cgit v0.10.2 From d832672f2038d7b36dc08636468cbbed4afe625b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 13 Dec 2014 08:52:53 -0300 Subject: [media] ivtv: fix sparse warning Fix this warning: drivers/media/pci/ivtv/ivtv-irq.c:418:9: warning: context imbalance in 'ivtv_dma_stream_dec_prepare' - different lock contexts for basic block sparse didn't quite understand the locking scheme, so rewrite it to keep sparse happy. Signed-off-by: Hans Verkuil Cc: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/ivtv/ivtv-irq.c b/drivers/media/pci/ivtv/ivtv-irq.c index ab6d5d2..e7d7017 100644 --- a/drivers/media/pci/ivtv/ivtv-irq.c +++ b/drivers/media/pci/ivtv/ivtv-irq.c @@ -357,7 +357,6 @@ void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock) u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET; int y_done = 0; int bytes_written = 0; - unsigned long flags = 0; int idx = 0; IVTV_DEBUG_HI_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset); @@ -407,16 +406,21 @@ void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock) /* Sync Hardware SG List of buffers */ ivtv_stream_sync_for_device(s); - if (lock) + if (lock) { + unsigned long flags = 0; + spin_lock_irqsave(&itv->dma_reg_lock, flags); - if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) { - ivtv_dma_dec_start(s); - } - else { - set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags); - } - if (lock) + if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) + ivtv_dma_dec_start(s); + else + set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags); spin_unlock_irqrestore(&itv->dma_reg_lock, flags); + } else { + if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) + ivtv_dma_dec_start(s); + else + set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags); + } } static void ivtv_dma_enc_start_xfer(struct ivtv_stream *s) -- cgit v0.10.2 From 7c424dd14fcf4c2905778ecd3f1ad10c5a097b5b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 13 Dec 2014 08:52:54 -0300 Subject: [media] videobuf2-vmalloc: fix sparse warning Fix this warning: drivers/media/v4l2-core/videobuf2-vmalloc.c:98:28: warning: incorrect type in assignment (different address spaces) drivers/media/v4l2-core/videobuf2-vmalloc.c:158:28: warning: incorrect type in argument 1 (different address spaces) The warning is correct, but we have no other choice here to forcibly cast. At least it is now explicit that such a cast is needed. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c index fba944e..7f6d41b 100644 --- a/drivers/media/v4l2-core/videobuf2-vmalloc.c +++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c @@ -95,7 +95,7 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, if (vb2_get_contig_userptr(vaddr, size, &vma, &physp)) goto fail_pages_array_alloc; buf->vma = vma; - buf->vaddr = ioremap_nocache(physp, size); + buf->vaddr = (__force void *)ioremap_nocache(physp, size); if (!buf->vaddr) goto fail_pages_array_alloc; } else { @@ -155,7 +155,7 @@ static void vb2_vmalloc_put_userptr(void *buf_priv) kfree(buf->pages); } else { vb2_put_vma(buf->vma); - iounmap(buf->vaddr); + iounmap((__force void __iomem *)buf->vaddr); } kfree(buf); } -- cgit v0.10.2 From 50fd5e8c1e02ac471a0a4f3aaa87684a5152a0b4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 13 Dec 2014 08:52:56 -0300 Subject: [media] hd29l2: fix sparse error and warnings drivers/media/dvb-frontends/hd29l2.c:29:18: warning: Variable length array is used. drivers/media/dvb-frontends/hd29l2.c:34:32: error: cannot size expression drivers/media/dvb-frontends/hd29l2.c:125:5: warning: symbol 'hd29l2_rd_reg_mask' was not declared. Should it be static? Variable length arrays are frowned upon, so replace with a fixed length and check that there won't be a buffer overrun. Signed-off-by: Hans Verkuil Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/hd29l2.c b/drivers/media/dvb-frontends/hd29l2.c index d7b9d54..67c8e6d 100644 --- a/drivers/media/dvb-frontends/hd29l2.c +++ b/drivers/media/dvb-frontends/hd29l2.c @@ -22,20 +22,24 @@ #include "hd29l2_priv.h" +#define HD29L2_MAX_LEN (3) + /* write multiple registers */ static int hd29l2_wr_regs(struct hd29l2_priv *priv, u8 reg, u8 *val, int len) { int ret; - u8 buf[2 + len]; + u8 buf[2 + HD29L2_MAX_LEN]; struct i2c_msg msg[1] = { { .addr = priv->cfg.i2c_addr, .flags = 0, - .len = sizeof(buf), + .len = 2 + len, .buf = buf, } }; + if (len > HD29L2_MAX_LEN) + return -EINVAL; buf[0] = 0x00; buf[1] = reg; memcpy(&buf[2], val, len); @@ -118,7 +122,7 @@ static int hd29l2_wr_reg_mask(struct hd29l2_priv *priv, u8 reg, u8 val, u8 mask) } /* read single register with mask */ -int hd29l2_rd_reg_mask(struct hd29l2_priv *priv, u8 reg, u8 *val, u8 mask) +static int hd29l2_rd_reg_mask(struct hd29l2_priv *priv, u8 reg, u8 *val, u8 mask) { int ret, i; u8 tmp; -- cgit v0.10.2 From d3acd83e6ea42146189c0b60de24fd89bcb92e4b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 13 Dec 2014 08:52:57 -0300 Subject: [media] m5mols: fix sparse warnings drivers/media/i2c/m5mols/m5mols_core.c:128:24: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:128:24: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:128:24: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:128:24: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:130:24: warning: cast to restricted __be32 drivers/media/i2c/m5mols/m5mols_core.c:130:24: warning: cast to restricted __be32 drivers/media/i2c/m5mols/m5mols_core.c:130:24: warning: cast to restricted __be32 drivers/media/i2c/m5mols/m5mols_core.c:130:24: warning: cast to restricted __be32 drivers/media/i2c/m5mols/m5mols_core.c:130:24: warning: cast to restricted __be32 drivers/media/i2c/m5mols/m5mols_core.c:130:24: warning: cast to restricted __be32 drivers/media/i2c/m5mols/m5mols_core.c:457:19: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:457:19: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:457:19: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:457:19: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:458:19: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:458:19: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:458:19: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:458:19: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:459:22: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:459:22: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:459:22: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:459:22: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:460:20: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:460:20: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:460:20: warning: cast to restricted __be16 drivers/media/i2c/m5mols/m5mols_core.c:460:20: warning: cast to restricted __be16 The be16_to_cpu conversions in m5mols_get_version() are not needed since the data is already using cpu endianness. This was never noticed since these version fields are never used. Signed-off-by: Hans Verkuil Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c index 2820f7c..6ed16e5 100644 --- a/drivers/media/i2c/m5mols/m5mols_core.c +++ b/drivers/media/i2c/m5mols/m5mols_core.c @@ -125,9 +125,9 @@ static u32 m5mols_swap_byte(u8 *data, u8 length) if (length == 1) return *data; else if (length == 2) - return be16_to_cpu(*((u16 *)data)); + return be16_to_cpu(*((__be16 *)data)); else - return be32_to_cpu(*((u32 *)data)); + return be32_to_cpu(*((__be32 *)data)); } /** @@ -454,11 +454,6 @@ static int m5mols_get_version(struct v4l2_subdev *sd) return ret; } - ver->fw = be16_to_cpu(ver->fw); - ver->hw = be16_to_cpu(ver->hw); - ver->param = be16_to_cpu(ver->param); - ver->awb = be16_to_cpu(ver->awb); - v4l2_info(sd, "Manufacturer\t[%s]\n", is_manufacturer(info, REG_SAMSUNG_ELECTRO) ? "Samsung Electro-Machanics" : -- cgit v0.10.2 From b75f2d16cb0d3f75104f8565dd5f713dbf4a074d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 13 Dec 2014 08:52:58 -0300 Subject: [media] s5k4ecgx: fix sparse warnings drivers/media/i2c/s5k4ecgx.c:223:16: warning: cast to restricted __be16 drivers/media/i2c/s5k4ecgx.c:223:16: warning: cast to restricted __be16 drivers/media/i2c/s5k4ecgx.c:223:16: warning: cast to restricted __be16 drivers/media/i2c/s5k4ecgx.c:223:16: warning: cast to restricted __be16 drivers/media/i2c/s5k4ecgx.c:344:20: warning: cast to restricted __le32 drivers/media/i2c/s5k4ecgx.c:354:20: warning: cast to restricted __le32 drivers/media/i2c/s5k4ecgx.c:364:24: warning: cast to restricted __le32 drivers/media/i2c/s5k4ecgx.c:366:23: warning: cast to restricted __le16 The get_unaligned_le*() functions return the value using cpu endianness, so calling le*_to_cpu is wrong. It hasn't been not noticed because this code has only been run on little endian systems, so le*_to_cpu doesn't do anything. Signed-off-by: Hans Verkuil Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c index d1c50c9..7007131 100644 --- a/drivers/media/i2c/s5k4ecgx.c +++ b/drivers/media/i2c/s5k4ecgx.c @@ -220,7 +220,7 @@ static int s5k4ecgx_i2c_read(struct i2c_client *client, u16 addr, u16 *val) msg[1].buf = rbuf; ret = i2c_transfer(client->adapter, msg, 2); - *val = be16_to_cpu(*((u16 *)rbuf)); + *val = be16_to_cpu(*((__be16 *)rbuf)); v4l2_dbg(4, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val); @@ -341,7 +341,7 @@ static int s5k4ecgx_load_firmware(struct v4l2_subdev *sd) v4l2_err(sd, "Failed to read firmware %s\n", S5K4ECGX_FIRMWARE); return err; } - regs_num = le32_to_cpu(get_unaligned_le32(fw->data)); + regs_num = get_unaligned_le32(fw->data); v4l2_dbg(3, debug, sd, "FW: %s size %zu register sets %d\n", S5K4ECGX_FIRMWARE, fw->size, regs_num); @@ -351,8 +351,7 @@ static int s5k4ecgx_load_firmware(struct v4l2_subdev *sd) err = -EINVAL; goto fw_out; } - crc_file = le32_to_cpu(get_unaligned_le32(fw->data + - regs_num * FW_RECORD_SIZE)); + crc_file = get_unaligned_le32(fw->data + regs_num * FW_RECORD_SIZE); crc = crc32_le(~0, fw->data, regs_num * FW_RECORD_SIZE); if (crc != crc_file) { v4l2_err(sd, "FW: invalid crc (%#x:%#x)\n", crc, crc_file); @@ -361,9 +360,9 @@ static int s5k4ecgx_load_firmware(struct v4l2_subdev *sd) } ptr = fw->data + FW_RECORD_SIZE; for (i = 1; i < regs_num; i++) { - addr = le32_to_cpu(get_unaligned_le32(ptr)); + addr = get_unaligned_le32(ptr); ptr += sizeof(u32); - val = le16_to_cpu(get_unaligned_le16(ptr)); + val = get_unaligned_le16(ptr); ptr += sizeof(u16); if (addr - addr_inc != 2) err = s5k4ecgx_write(client, addr, val); -- cgit v0.10.2 From 7c7586bb3c132f93b27148fe686dbd979f64d902 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 13 Dec 2014 08:52:59 -0300 Subject: [media] s5k6aa: fix sparse warnings drivers/media/i2c/s5k6aa.c:351:16: warning: cast to restricted __be16 drivers/media/i2c/s5k6aa.c:351:16: warning: cast to restricted __be16 drivers/media/i2c/s5k6aa.c:351:16: warning: cast to restricted __be16 drivers/media/i2c/s5k6aa.c:351:16: warning: cast to restricted __be16 Signed-off-by: Hans Verkuil Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c index 19edafb..b1c5832 100644 --- a/drivers/media/i2c/s5k6aa.c +++ b/drivers/media/i2c/s5k6aa.c @@ -348,7 +348,7 @@ static int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val) msg[1].buf = rbuf; ret = i2c_transfer(client->adapter, msg, 2); - *val = be16_to_cpu(*((u16 *)rbuf)); + *val = be16_to_cpu(*((__be16 *)rbuf)); v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val); -- cgit v0.10.2 From 0348bb1a7aa6093e9aead41d50c792576839a3a1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 13 Dec 2014 08:53:00 -0300 Subject: [media] s5k5baf: fix sparse warnings drivers/media/i2c/s5k5baf.c:1796:33: warning: duplicate const drivers/media/i2c/s5k5baf.c:379:24: warning: cast to restricted __le16 drivers/media/i2c/s5k5baf.c:437:11: warning: incorrect type in assignment (different base types) drivers/media/i2c/s5k5baf.c:445:16: warning: incorrect type in return expression (different base types) Signed-off-by: Hans Verkuil Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index 60a74d8..a3d7d03 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -353,7 +353,7 @@ static struct v4l2_rect s5k5baf_cis_rect = { * */ static int s5k5baf_fw_parse(struct device *dev, struct s5k5baf_fw **fw, - size_t count, const u16 *data) + size_t count, const __le16 *data) { struct s5k5baf_fw *f; u16 *d, i, *end; @@ -421,6 +421,7 @@ static u16 s5k5baf_i2c_read(struct s5k5baf *state, u16 addr) { struct i2c_client *c = v4l2_get_subdevdata(&state->sd); __be16 w, r; + u16 res; struct i2c_msg msg[] = { { .addr = c->addr, .flags = 0, .len = 2, .buf = (u8 *)&w }, @@ -434,15 +435,15 @@ static u16 s5k5baf_i2c_read(struct s5k5baf *state, u16 addr) w = cpu_to_be16(addr); ret = i2c_transfer(c->adapter, msg, 2); - r = be16_to_cpu(r); + res = be16_to_cpu(r); - v4l2_dbg(3, debug, c, "i2c_read: 0x%04x : 0x%04x\n", addr, r); + v4l2_dbg(3, debug, c, "i2c_read: 0x%04x : 0x%04x\n", addr, res); if (ret != 2) { v4l2_err(c, "i2c_read: error during transfer (%d)\n", ret); state->error = ret; } - return r; + return res; } static void s5k5baf_i2c_write(struct s5k5baf *state, u16 addr, u16 val) @@ -1037,7 +1038,7 @@ static int s5k5baf_load_setfile(struct s5k5baf *state) } ret = s5k5baf_fw_parse(&c->dev, &state->fw, fw->size / 2, - (u16 *)fw->data); + (__le16 *)fw->data); release_firmware(fw); @@ -1793,7 +1794,7 @@ static const struct v4l2_subdev_ops s5k5baf_subdev_ops = { static int s5k5baf_configure_gpios(struct s5k5baf *state) { - static const char const *name[] = { "S5K5BAF_STBY", "S5K5BAF_RST" }; + static const char * const name[] = { "S5K5BAF_STBY", "S5K5BAF_RST" }; struct i2c_client *c = v4l2_get_subdevdata(&state->sd); struct s5k5baf_gpio *g = state->gpios; int ret, i; -- cgit v0.10.2 From 9571a17fc253e1b362217352d7faa815ba5b5a3e Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Tue, 16 Dec 2014 20:17:20 -0300 Subject: [media] media: au0828 VBI support comment cleanup This driver supports VBI and the comment "VBI support is not yet working" is inaccurate. Remove it. Signed-off-by: Shuah Khan Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 5f337b1..8a7a547 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -22,7 +22,6 @@ /* Developer Notes: * - * VBI support is not yet working * The hardware scaler supported is unimplemented * AC97 audio support is unimplemented (only i2s audio mode) * -- cgit v0.10.2 From 94acbe12f589bfcb37ed8a12c5efd14140044642 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 15 Dec 2014 10:55:37 -0300 Subject: [media] VIDEO_CAFE_CCIC should select VIDEOBUF2_DMA_SG If VIDEO_CAFE_CCIC=y, but VIDEOBUF2_DMA_SG=m: drivers/built-in.o: In function `mcam_v4l_open': mcam-core.c:(.text+0x1c2e81): undefined reference to `vb2_dma_sg_memops' mcam-core.c:(.text+0x1c2eb0): undefined reference to `vb2_dma_sg_init_ctx' drivers/built-in.o: In function `mcam_v4l_release': mcam-core.c:(.text+0x1c34bf): undefined reference to `vb2_dma_sg_cleanup_ctx' Reported-by: Fengguang Wu Signed-off-by: Geert Uytterhoeven Acked-by: Jonathan Corbet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/marvell-ccic/Kconfig b/drivers/media/platform/marvell-ccic/Kconfig index 6265d36..3d16656 100644 --- a/drivers/media/platform/marvell-ccic/Kconfig +++ b/drivers/media/platform/marvell-ccic/Kconfig @@ -5,6 +5,7 @@ config VIDEO_CAFE_CCIC select VIDEO_OV7670 select VIDEOBUF2_VMALLOC select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_DMA_SG ---help--- This is a video4linux2 driver for the Marvell 88ALP01 integrated CMOS camera controller. This is the controller found on first- -- cgit v0.10.2 From a2391a80fba2f4d9a12727e4dbd5d4cbde31469c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 7 Dec 2014 19:56:38 -0300 Subject: [media] DocBook: v4l: Fix raw bayer pixel format documentation wording The documentation began with "The following four pixel formats"... but the format definitions preceded this sentence. Replace it with "These four pixel formats". Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/pixfmt-srggb10.xml b/Documentation/DocBook/media/v4l/pixfmt-srggb10.xml index c1c62a9..f34d03e 100644 --- a/Documentation/DocBook/media/v4l/pixfmt-srggb10.xml +++ b/Documentation/DocBook/media/v4l/pixfmt-srggb10.xml @@ -17,7 +17,7 @@ Description - The following four pixel formats are raw sRGB / Bayer formats with + These four pixel formats are raw sRGB / Bayer formats with 10 bits per colour. Each colour component is stored in a 16-bit word, with 6 unused high bits filled with zeros. Each n-pixel row contains n/2 green samples and n/2 blue or red samples, with alternating red and blue rows. Bytes are diff --git a/Documentation/DocBook/media/v4l/pixfmt-srggb10alaw8.xml b/Documentation/DocBook/media/v4l/pixfmt-srggb10alaw8.xml index 29acc20..d2e5845 100644 --- a/Documentation/DocBook/media/v4l/pixfmt-srggb10alaw8.xml +++ b/Documentation/DocBook/media/v4l/pixfmt-srggb10alaw8.xml @@ -25,7 +25,7 @@ Description - The following four pixel formats are raw sRGB / Bayer + These four pixel formats are raw sRGB / Bayer formats with 10 bits per color compressed to 8 bits each, using the A-LAW algorithm. Each color component consumes 8 bits of memory. In other respects this format is similar to diff --git a/Documentation/DocBook/media/v4l/pixfmt-srggb10dpcm8.xml b/Documentation/DocBook/media/v4l/pixfmt-srggb10dpcm8.xml index 2d3f0b1a..bde8987 100644 --- a/Documentation/DocBook/media/v4l/pixfmt-srggb10dpcm8.xml +++ b/Documentation/DocBook/media/v4l/pixfmt-srggb10dpcm8.xml @@ -18,7 +18,7 @@ Description - The following four pixel formats are raw sRGB / Bayer formats + These four pixel formats are raw sRGB / Bayer formats with 10 bits per colour compressed to 8 bits each, using DPCM compression. DPCM, differential pulse-code modulation, is lossy. Each colour component consumes 8 bits of memory. In other respects diff --git a/Documentation/DocBook/media/v4l/pixfmt-srggb12.xml b/Documentation/DocBook/media/v4l/pixfmt-srggb12.xml index 96947f1..0c8e4ad 100644 --- a/Documentation/DocBook/media/v4l/pixfmt-srggb12.xml +++ b/Documentation/DocBook/media/v4l/pixfmt-srggb12.xml @@ -17,7 +17,7 @@ Description - The following four pixel formats are raw sRGB / Bayer formats with + These four pixel formats are raw sRGB / Bayer formats with 12 bits per colour. Each colour component is stored in a 16-bit word, with 4 unused high bits filled with zeros. Each n-pixel row contains n/2 green samples and n/2 blue or red samples, with alternating red and blue rows. Bytes are -- cgit v0.10.2 From cbb77bc281d697764dbb035157b643a8cbc9df30 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 7 Dec 2014 20:17:49 -0300 Subject: [media] DocBook: v4l: Rearrange raw bayer format definitions, remove bad comment Rearrange 12-bit raw bayer format definitions after 10-bit ones. Also remove the comment related to 16-bit bayer formats, it was simply wrong. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index d279c1b..f0b94b8 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -463,10 +463,6 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SGBRG10 v4l2_fourcc('G', 'B', '1', '0') /* 10 GBGB.. RGRG.. */ #define V4L2_PIX_FMT_SGRBG10 v4l2_fourcc('B', 'A', '1', '0') /* 10 GRGR.. BGBG.. */ #define V4L2_PIX_FMT_SRGGB10 v4l2_fourcc('R', 'G', '1', '0') /* 10 RGRG.. GBGB.. */ -#define V4L2_PIX_FMT_SBGGR12 v4l2_fourcc('B', 'G', '1', '2') /* 12 BGBG.. GRGR.. */ -#define V4L2_PIX_FMT_SGBRG12 v4l2_fourcc('G', 'B', '1', '2') /* 12 GBGB.. RGRG.. */ -#define V4L2_PIX_FMT_SGRBG12 v4l2_fourcc('B', 'A', '1', '2') /* 12 GRGR.. BGBG.. */ -#define V4L2_PIX_FMT_SRGGB12 v4l2_fourcc('R', 'G', '1', '2') /* 12 RGRG.. GBGB.. */ /* 10bit raw bayer a-law compressed to 8 bits */ #define V4L2_PIX_FMT_SBGGR10ALAW8 v4l2_fourcc('a', 'B', 'A', '8') #define V4L2_PIX_FMT_SGBRG10ALAW8 v4l2_fourcc('a', 'G', 'A', '8') @@ -477,10 +473,10 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SGBRG10DPCM8 v4l2_fourcc('b', 'G', 'A', '8') #define V4L2_PIX_FMT_SGRBG10DPCM8 v4l2_fourcc('B', 'D', '1', '0') #define V4L2_PIX_FMT_SRGGB10DPCM8 v4l2_fourcc('b', 'R', 'A', '8') - /* - * 10bit raw bayer, expanded to 16 bits - * xxxxrrrrrrrrrrxxxxgggggggggg xxxxggggggggggxxxxbbbbbbbbbb... - */ +#define V4L2_PIX_FMT_SBGGR12 v4l2_fourcc('B', 'G', '1', '2') /* 12 BGBG.. GRGR.. */ +#define V4L2_PIX_FMT_SGBRG12 v4l2_fourcc('G', 'B', '1', '2') /* 12 GBGB.. RGRG.. */ +#define V4L2_PIX_FMT_SGRBG12 v4l2_fourcc('B', 'A', '1', '2') /* 12 GRGR.. BGBG.. */ +#define V4L2_PIX_FMT_SRGGB12 v4l2_fourcc('R', 'G', '1', '2') /* 12 RGRG.. GBGB.. */ #define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B', 'Y', 'R', '2') /* 16 BGBG.. GRGR.. */ /* compressed formats */ -- cgit v0.10.2 From 4353e36ee84d936859eb6d65ecd9d3076edd11bc Mon Sep 17 00:00:00 2001 From: Aviv Greenberg Date: Wed, 3 Dec 2014 08:14:09 -0300 Subject: [media] v4l: Add packed Bayer raw10 pixel formats These formats are just like 10-bit raw bayer formats that exist already, but the pixels are not padded to byte boundaries. Instead, the eight high order bits of four consecutive pixels are stored in four bytes, followed by a byte of two low order bits of each of the four pixels. Signed-off-by: Aviv Greenberg Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/pixfmt-srggb10p.xml b/Documentation/DocBook/media/v4l/pixfmt-srggb10p.xml new file mode 100644 index 0000000..30aa635 --- /dev/null +++ b/Documentation/DocBook/media/v4l/pixfmt-srggb10p.xml @@ -0,0 +1,99 @@ + + + V4L2_PIX_FMT_SRGGB10P ('pRAA'), + V4L2_PIX_FMT_SGRBG10P ('pgAA'), + V4L2_PIX_FMT_SGBRG10P ('pGAA'), + V4L2_PIX_FMT_SBGGR10P ('pBAA'), + + &manvol; + + + V4L2_PIX_FMT_SRGGB10P + V4L2_PIX_FMT_SGRBG10P + V4L2_PIX_FMT_SGBRG10P + V4L2_PIX_FMT_SBGGR10P + 10-bit packed Bayer formats + + + Description + + These four pixel formats are packed raw sRGB / + Bayer formats with 10 bits per colour. Every four consecutive + colour components are packed into 5 bytes. Each of the first 4 + bytes contain the 8 high order bits of the pixels, and the + fifth byte contains the two least significants bits of each + pixel, in the same order. + + Each n-pixel row contains n/2 green samples and n/2 blue + or red samples, with alternating green-red and green-blue + rows. They are conventionally described as GRGR... BGBG..., + RGRG... GBGB..., etc. Below is an example of one of these + formats: + + + <constant>V4L2_PIX_FMT_SBGGR10P</constant> 4 × 4 + pixel image + + + Byte Order. + Each cell is one byte. + + + + + + start + 0: + B00high + G01high + B02high + G03high + B00low(bits 7--6) + G01low(bits 5--4) + B02low(bits 3--2) + G03low(bits 1--0) + + + + start + 5: + G10high + R11high + G12high + R13high + G10low(bits 7--6) + R11low(bits 5--4) + G12low(bits 3--2) + R13low(bits 1--0) + + + + start + 10: + B20high + G21high + B22high + G23high + B20low(bits 7--6) + G21low(bits 5--4) + B22low(bits 3--2) + G23low(bits 1--0) + + + + start + 15: + G30high + R31high + G32high + R33high + G30low(bits 7--6) + R31low(bits 5--4) + G32low(bits 3--2) + R33low(bits 1--0) + + + + + + + + + + diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml index d5eca4b..5e0352c 100644 --- a/Documentation/DocBook/media/v4l/pixfmt.xml +++ b/Documentation/DocBook/media/v4l/pixfmt.xml @@ -1405,6 +1405,7 @@ access the palette, this must be done with ioctls of the Linux framebuffer API.< &sub-srggb8; &sub-sbggr16; &sub-srggb10; + &sub-srggb10p; &sub-srggb10alaw8; &sub-srggb10dpcm8; &sub-srggb12; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index f0b94b8..fbdc360 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -463,6 +463,11 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SGBRG10 v4l2_fourcc('G', 'B', '1', '0') /* 10 GBGB.. RGRG.. */ #define V4L2_PIX_FMT_SGRBG10 v4l2_fourcc('B', 'A', '1', '0') /* 10 GRGR.. BGBG.. */ #define V4L2_PIX_FMT_SRGGB10 v4l2_fourcc('R', 'G', '1', '0') /* 10 RGRG.. GBGB.. */ + /* 10bit raw bayer packed, 5 bytes for every 4 pixels */ +#define V4L2_PIX_FMT_SBGGR10P v4l2_fourcc('p', 'B', 'A', 'A') +#define V4L2_PIX_FMT_SGBRG10P v4l2_fourcc('p', 'G', 'A', 'A') +#define V4L2_PIX_FMT_SGRBG10P v4l2_fourcc('p', 'g', 'A', 'A') +#define V4L2_PIX_FMT_SRGGB10P v4l2_fourcc('p', 'R', 'A', 'A') /* 10bit raw bayer a-law compressed to 8 bits */ #define V4L2_PIX_FMT_SBGGR10ALAW8 v4l2_fourcc('a', 'B', 'A', '8') #define V4L2_PIX_FMT_SGBRG10ALAW8 v4l2_fourcc('a', 'G', 'A', '8') -- cgit v0.10.2 From 88eab472ec21f01d3e36ff926f8bd4f742687075 Mon Sep 17 00:00:00 2001 From: Marcelo Leitner Date: Wed, 3 Dec 2014 17:30:19 -0200 Subject: netfilter: conntrack: adjust nf_conntrack_buckets default value Manually bumping either nf_conntrack_buckets or nf_conntrack_max has become a common task as our Linux servers tend to serve more and more clients/applications, so let's adjust nf_conntrack_buckets this to a more updated value. Now for systems with more than 4GB of memory, nf_conntrack_buckets becomes 65536 instead of 16384, resulting in nf_conntrack_max=256k entries. Signed-off-by: Marcelo Ricardo Leitner Acked-by: Jesper Dangaard Brouer Signed-off-by: Pablo Neira Ayuso diff --git a/Documentation/networking/nf_conntrack-sysctl.txt b/Documentation/networking/nf_conntrack-sysctl.txt index 70da508..f55599c 100644 --- a/Documentation/networking/nf_conntrack-sysctl.txt +++ b/Documentation/networking/nf_conntrack-sysctl.txt @@ -11,7 +11,8 @@ nf_conntrack_buckets - INTEGER (read-only) Size of hash table. If not specified as parameter during module loading, the default size is calculated by dividing total memory by 16384 to determine the number of buckets but the hash table will - never have fewer than 32 or more than 16384 buckets. + never have fewer than 32 and limited to 16384 buckets. For systems + with more than 4GB of memory it will be 65536 buckets. nf_conntrack_checksum - BOOLEAN 0 - disabled diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index a116748..da58cd4 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1624,13 +1624,18 @@ int nf_conntrack_init_start(void) for (i = 0; i < CONNTRACK_LOCKS; i++) spin_lock_init(&nf_conntrack_locks[i]); - /* Idea from tcp.c: use 1/16384 of memory. On i386: 32MB - * machine has 512 buckets. >= 1GB machines have 16384 buckets. */ if (!nf_conntrack_htable_size) { + /* Idea from tcp.c: use 1/16384 of memory. + * On i386: 32MB machine has 512 buckets. + * >= 1GB machines have 16384 buckets. + * >= 4GB machines have 65536 buckets. + */ nf_conntrack_htable_size = (((totalram_pages << PAGE_SHIFT) / 16384) / sizeof(struct hlist_head)); - if (totalram_pages > (1024 * 1024 * 1024 / PAGE_SIZE)) + if (totalram_pages > (4 * (1024 * 1024 * 1024 / PAGE_SIZE))) + nf_conntrack_htable_size = 65536; + else if (totalram_pages > (1024 * 1024 * 1024 / PAGE_SIZE)) nf_conntrack_htable_size = 16384; if (nf_conntrack_htable_size < 32) nf_conntrack_htable_size = 32; -- cgit v0.10.2 From 372e28661a5c1cda34598314e2e341a7529d6219 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 16 Dec 2014 12:17:13 -0800 Subject: netfilter: xt_osf: Use continue to reduce indentation Invert logic in test to use continue. This routine already uses continue, use it a bit more to minimize > 80 column long lines and unnecessary indentation. No change in compiled object file. Other miscellanea: o Remove trailing whitespace o Realign arguments to multiline statement Signed-off-by: Joe Perches Acked-by: Evgeniy Polyakov Signed-off-by: Pablo Neira Ayuso diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c index c529161..0778855 100644 --- a/net/netfilter/xt_osf.c +++ b/net/netfilter/xt_osf.c @@ -225,6 +225,8 @@ xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p) rcu_read_lock(); list_for_each_entry_rcu(kf, &xt_osf_fingers[df], finger_entry) { + int foptsize, optnum; + f = &kf->finger; if (!(info->flags & XT_OSF_LOG) && strcmp(info->genre, f->genre)) @@ -233,110 +235,109 @@ xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p) optp = _optp; fmatch = FMATCH_WRONG; - if (totlen == f->ss && xt_osf_ttl(skb, info, f->ttl)) { - int foptsize, optnum; + if (totlen != f->ss || !xt_osf_ttl(skb, info, f->ttl)) + continue; - /* - * Should not happen if userspace parser was written correctly. - */ - if (f->wss.wc >= OSF_WSS_MAX) - continue; + /* + * Should not happen if userspace parser was written correctly. + */ + if (f->wss.wc >= OSF_WSS_MAX) + continue; - /* Check options */ + /* Check options */ - foptsize = 0; - for (optnum = 0; optnum < f->opt_num; ++optnum) - foptsize += f->opt[optnum].length; + foptsize = 0; + for (optnum = 0; optnum < f->opt_num; ++optnum) + foptsize += f->opt[optnum].length; - if (foptsize > MAX_IPOPTLEN || - optsize > MAX_IPOPTLEN || - optsize != foptsize) - continue; + if (foptsize > MAX_IPOPTLEN || + optsize > MAX_IPOPTLEN || + optsize != foptsize) + continue; - check_WSS = f->wss.wc; + check_WSS = f->wss.wc; - for (optnum = 0; optnum < f->opt_num; ++optnum) { - if (f->opt[optnum].kind == (*optp)) { - __u32 len = f->opt[optnum].length; - const __u8 *optend = optp + len; - int loop_cont = 0; + for (optnum = 0; optnum < f->opt_num; ++optnum) { + if (f->opt[optnum].kind == (*optp)) { + __u32 len = f->opt[optnum].length; + const __u8 *optend = optp + len; + int loop_cont = 0; - fmatch = FMATCH_OK; + fmatch = FMATCH_OK; - switch (*optp) { - case OSFOPT_MSS: - mss = optp[3]; - mss <<= 8; - mss |= optp[2]; + switch (*optp) { + case OSFOPT_MSS: + mss = optp[3]; + mss <<= 8; + mss |= optp[2]; - mss = ntohs((__force __be16)mss); - break; - case OSFOPT_TS: - loop_cont = 1; - break; - } + mss = ntohs((__force __be16)mss); + break; + case OSFOPT_TS: + loop_cont = 1; + break; + } - optp = optend; - } else - fmatch = FMATCH_OPT_WRONG; + optp = optend; + } else + fmatch = FMATCH_OPT_WRONG; - if (fmatch != FMATCH_OK) - break; - } + if (fmatch != FMATCH_OK) + break; + } - if (fmatch != FMATCH_OPT_WRONG) { - fmatch = FMATCH_WRONG; + if (fmatch != FMATCH_OPT_WRONG) { + fmatch = FMATCH_WRONG; - switch (check_WSS) { - case OSF_WSS_PLAIN: - if (f->wss.val == 0 || window == f->wss.val) - fmatch = FMATCH_OK; - break; - case OSF_WSS_MSS: - /* - * Some smart modems decrease mangle MSS to - * SMART_MSS_2, so we check standard, decreased - * and the one provided in the fingerprint MSS - * values. - */ + switch (check_WSS) { + case OSF_WSS_PLAIN: + if (f->wss.val == 0 || window == f->wss.val) + fmatch = FMATCH_OK; + break; + case OSF_WSS_MSS: + /* + * Some smart modems decrease mangle MSS to + * SMART_MSS_2, so we check standard, decreased + * and the one provided in the fingerprint MSS + * values. + */ #define SMART_MSS_1 1460 #define SMART_MSS_2 1448 - if (window == f->wss.val * mss || - window == f->wss.val * SMART_MSS_1 || - window == f->wss.val * SMART_MSS_2) - fmatch = FMATCH_OK; - break; - case OSF_WSS_MTU: - if (window == f->wss.val * (mss + 40) || - window == f->wss.val * (SMART_MSS_1 + 40) || - window == f->wss.val * (SMART_MSS_2 + 40)) - fmatch = FMATCH_OK; - break; - case OSF_WSS_MODULO: - if ((window % f->wss.val) == 0) - fmatch = FMATCH_OK; - break; - } + if (window == f->wss.val * mss || + window == f->wss.val * SMART_MSS_1 || + window == f->wss.val * SMART_MSS_2) + fmatch = FMATCH_OK; + break; + case OSF_WSS_MTU: + if (window == f->wss.val * (mss + 40) || + window == f->wss.val * (SMART_MSS_1 + 40) || + window == f->wss.val * (SMART_MSS_2 + 40)) + fmatch = FMATCH_OK; + break; + case OSF_WSS_MODULO: + if ((window % f->wss.val) == 0) + fmatch = FMATCH_OK; + break; } + } - if (fmatch != FMATCH_OK) - continue; + if (fmatch != FMATCH_OK) + continue; - fcount++; + fcount++; - if (info->flags & XT_OSF_LOG) - nf_log_packet(net, p->family, p->hooknum, skb, - p->in, p->out, NULL, - "%s [%s:%s] : %pI4:%d -> %pI4:%d hops=%d\n", - f->genre, f->version, f->subtype, - &ip->saddr, ntohs(tcp->source), - &ip->daddr, ntohs(tcp->dest), - f->ttl - ip->ttl); + if (info->flags & XT_OSF_LOG) + nf_log_packet(net, p->family, p->hooknum, skb, + p->in, p->out, NULL, + "%s [%s:%s] : %pI4:%d -> %pI4:%d hops=%d\n", + f->genre, f->version, f->subtype, + &ip->saddr, ntohs(tcp->source), + &ip->daddr, ntohs(tcp->dest), + f->ttl - ip->ttl); - if ((info->flags & XT_OSF_LOG) && - info->loglevel == XT_OSF_LOGLEVEL_FIRST) - break; - } + if ((info->flags & XT_OSF_LOG) && + info->loglevel == XT_OSF_LOGLEVEL_FIRST) + break; } rcu_read_unlock(); -- cgit v0.10.2 From 29867e2febad863fb3cef99ad894cd992df5e665 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 18 Oct 2014 06:11:19 -0300 Subject: [media] smiapp: Remove FSF's address from the license header Remove FSF's address information from the license header in the smiapp driver and the smiapp-pll PLL calculator. This should no longer be needed, and would be rendered outdated in case the FSF chooses to relocate its office. Signed-off-by: Sakari Ailus Cc: Timo Ahonen Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c index e40d902..2b84d09 100644 --- a/drivers/media/i2c/smiapp-pll.c +++ b/drivers/media/i2c/smiapp-pll.c @@ -14,12 +14,6 @@ * 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 - * */ #include diff --git a/drivers/media/i2c/smiapp-pll.h b/drivers/media/i2c/smiapp-pll.h index e8f035a..77f7ff2f 100644 --- a/drivers/media/i2c/smiapp-pll.h +++ b/drivers/media/i2c/smiapp-pll.h @@ -14,12 +14,6 @@ * 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 - * */ #ifndef SMIAPP_PLL_H diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 0df5070e..cee1a9a 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -18,12 +18,6 @@ * 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 - * */ #include diff --git a/drivers/media/i2c/smiapp/smiapp-limits.c b/drivers/media/i2c/smiapp/smiapp-limits.c index 847cb23..784b114 100644 --- a/drivers/media/i2c/smiapp/smiapp-limits.c +++ b/drivers/media/i2c/smiapp/smiapp-limits.c @@ -14,12 +14,6 @@ * 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 - * */ #include "smiapp.h" diff --git a/drivers/media/i2c/smiapp/smiapp-limits.h b/drivers/media/i2c/smiapp/smiapp-limits.h index 343e9c3..b201248 100644 --- a/drivers/media/i2c/smiapp/smiapp-limits.h +++ b/drivers/media/i2c/smiapp/smiapp-limits.h @@ -14,12 +14,6 @@ * 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 - * */ #define SMIAPP_LIMIT_ANALOGUE_GAIN_CAPABILITY 0 diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.c b/drivers/media/i2c/smiapp/smiapp-quirk.c index e0bee87..dd4ae6f 100644 --- a/drivers/media/i2c/smiapp/smiapp-quirk.c +++ b/drivers/media/i2c/smiapp/smiapp-quirk.c @@ -14,12 +14,6 @@ * 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 - * */ #include diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.h b/drivers/media/i2c/smiapp/smiapp-quirk.h index 46e9ea8..3a3c3e5 100644 --- a/drivers/media/i2c/smiapp/smiapp-quirk.h +++ b/drivers/media/i2c/smiapp/smiapp-quirk.h @@ -14,12 +14,6 @@ * 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 - * */ #ifndef __SMIAPP_QUIRK__ diff --git a/drivers/media/i2c/smiapp/smiapp-reg-defs.h b/drivers/media/i2c/smiapp/smiapp-reg-defs.h index c488ef0..f928d4c 100644 --- a/drivers/media/i2c/smiapp/smiapp-reg-defs.h +++ b/drivers/media/i2c/smiapp/smiapp-reg-defs.h @@ -14,12 +14,6 @@ * 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 - * */ #define SMIAPP_REG_MK_U8(r) ((SMIAPP_REG_8BIT << 16) | (r)) #define SMIAPP_REG_MK_U16(r) ((SMIAPP_REG_16BIT << 16) | (r)) diff --git a/drivers/media/i2c/smiapp/smiapp-reg.h b/drivers/media/i2c/smiapp/smiapp-reg.h index b0dcbb8..4c8b406 100644 --- a/drivers/media/i2c/smiapp/smiapp-reg.h +++ b/drivers/media/i2c/smiapp/smiapp-reg.h @@ -14,12 +14,6 @@ * 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 - * */ #ifndef __SMIAPP_REG_H_ diff --git a/drivers/media/i2c/smiapp/smiapp-regs.c b/drivers/media/i2c/smiapp/smiapp-regs.c index a209800..6b6c20b 100644 --- a/drivers/media/i2c/smiapp/smiapp-regs.c +++ b/drivers/media/i2c/smiapp/smiapp-regs.c @@ -14,12 +14,6 @@ * 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 - * */ #include diff --git a/drivers/media/i2c/smiapp/smiapp-regs.h b/drivers/media/i2c/smiapp/smiapp-regs.h index 3552112..6dd0e49 100644 --- a/drivers/media/i2c/smiapp/smiapp-regs.h +++ b/drivers/media/i2c/smiapp/smiapp-regs.h @@ -14,12 +14,6 @@ * 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 - * */ #ifndef SMIAPP_REGS_H diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h index f88f8ec..8fded46 100644 --- a/drivers/media/i2c/smiapp/smiapp.h +++ b/drivers/media/i2c/smiapp/smiapp.h @@ -14,12 +14,6 @@ * 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 - * */ #ifndef __SMIAPP_PRIV_H_ -- cgit v0.10.2 From fd2bfdc863c7912e4865f9d993b2ab1108645fd5 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 16 Nov 2014 12:08:44 -0300 Subject: [media] smiapp: List include/uapi/linux/smiapp.h in MAINTAINERS This is part of the smiapp driver. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index ddb9ac8..f7d04bac 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8826,6 +8826,7 @@ F: drivers/media/i2c/smiapp/ F: include/media/smiapp.h F: drivers/media/i2c/smiapp-pll.c F: drivers/media/i2c/smiapp-pll.h +F: include/uapi/linux/smiapp.h SMM665 HARDWARE MONITOR DRIVER M: Guenter Roeck -- cgit v0.10.2 From 8c20ee6e92e435609f8d7dd0c2b4b77c8dbb3f18 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 18 Oct 2014 06:16:20 -0300 Subject: [media] smiapp-pll: include linux/device.h in smiapp-pll.c, not in smiapp-pll.h struct device has a forward declaration in the header already. The header is only needed in the .c file. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c index 2b84d09..e3348db 100644 --- a/drivers/media/i2c/smiapp-pll.c +++ b/drivers/media/i2c/smiapp-pll.c @@ -16,6 +16,7 @@ * General Public License for more details. */ +#include #include #include #include diff --git a/drivers/media/i2c/smiapp-pll.h b/drivers/media/i2c/smiapp-pll.h index 77f7ff2f..b98d143 100644 --- a/drivers/media/i2c/smiapp-pll.h +++ b/drivers/media/i2c/smiapp-pll.h @@ -19,8 +19,6 @@ #ifndef SMIAPP_PLL_H #define SMIAPP_PLL_H -#include - /* CSI-2 or CCP-2 */ #define SMIAPP_PLL_BUS_TYPE_CSI2 0x00 #define SMIAPP_PLL_BUS_TYPE_PARALLEL 0x01 -- cgit v0.10.2 From ec176a6f142bb403b8a9b977a6919b556e80ea99 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 30 Nov 2013 16:35:48 -0300 Subject: [media] smiapp: Use types better suitable for DT Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/smiapp.h b/include/media/smiapp.h index 0b8f124..268a3cd 100644 --- a/include/media/smiapp.h +++ b/include/media/smiapp.h @@ -65,19 +65,19 @@ struct smiapp_platform_data { unsigned short i2c_addr_dfl; /* Default i2c addr */ unsigned short i2c_addr_alt; /* Alternate i2c addr */ - unsigned int nvm_size; /* bytes */ - unsigned int ext_clk; /* sensor external clk */ + uint32_t nvm_size; /* bytes */ + uint32_t ext_clk; /* sensor external clk */ unsigned int lanes; /* Number of CSI-2 lanes */ - u8 csi_signalling_mode; /* SMIAPP_CSI_SIGNALLING_MODE_* */ - const s64 *op_sys_clock; + uint32_t csi_signalling_mode; /* SMIAPP_CSI_SIGNALLING_MODE_* */ + uint64_t *op_sys_clock; enum smiapp_module_board_orient module_board_orient; struct smiapp_flash_strobe_parms *strobe_setup; int (*set_xclk)(struct v4l2_subdev *sd, int hz); - int xshutdown; /* gpio or SMIAPP_NO_XSHUTDOWN */ + int32_t xshutdown; /* gpio or SMIAPP_NO_XSHUTDOWN */ }; #endif /* __SMIAPP_H_ */ -- cgit v0.10.2 From 3d9bb7a620f061fd94da043bd9e69a65a9510981 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 8 Dec 2014 17:49:31 -0300 Subject: [media] smiapp: Don't give the source sub-device a temporary name The source sub-device's name will be overwritten shortly. Don't give it a name in the meantime. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index cee1a9a..3d5fba4 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2458,8 +2458,6 @@ static int smiapp_identify_module(struct v4l2_subdev *subdev) minfo->name, minfo->manufacturer_id, minfo->model_id, minfo->revision_number_major); - strlcpy(subdev->name, sensor->minfo.name, sizeof(subdev->name)); - return 0; } -- cgit v0.10.2 From f73108eb8e804180cb7f834649c670ba8b892777 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 30 Nov 2013 17:51:15 -0300 Subject: [media] smiapp: Register async subdev Register and unregister async sub-device for DT. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 3d5fba4..2c65ff3 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2942,8 +2942,21 @@ static int smiapp_probe(struct i2c_client *client, sensor->src->sensor = sensor; sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE; - return media_entity_init(&sensor->src->sd.entity, 2, + rval = media_entity_init(&sensor->src->sd.entity, 2, sensor->src->pads, 0); + if (rval < 0) + return rval; + + rval = v4l2_async_register_subdev(&sensor->src->sd); + if (rval < 0) + goto out_media_entity_cleanup; + + return 0; + +out_media_entity_cleanup: + media_entity_cleanup(&sensor->src->sd.entity); + + return rval; } static int smiapp_remove(struct i2c_client *client) @@ -2952,6 +2965,8 @@ static int smiapp_remove(struct i2c_client *client) struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); unsigned int i; + v4l2_async_unregister_subdev(subdev); + if (sensor->power_count) { if (gpio_is_valid(sensor->platform_data->xshutdown)) gpio_set_value(sensor->platform_data->xshutdown, 0); -- cgit v0.10.2 From 575ea5b3a41c3facb6e7973fdce865d208bf89c9 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 26 Jan 2014 21:13:13 -0300 Subject: [media] smiapp: The sensor only needs a single clock, name may be NULL The SMIA compatible sensors only need a single clock. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 2c65ff3..3dd32b1 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2482,7 +2482,7 @@ static int smiapp_registered(struct v4l2_subdev *subdev) } if (!sensor->platform_data->set_xclk) { - sensor->ext_clk = devm_clk_get(&client->dev, "ext_clk"); + sensor->ext_clk = devm_clk_get(&client->dev, NULL); if (IS_ERR(sensor->ext_clk)) { dev_err(&client->dev, "could not get clock\n"); return PTR_ERR(sensor->ext_clk); -- cgit v0.10.2 From 0f8e2537852f39414e00093934cea154917d2ccf Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 8 Nov 2014 08:38:10 -0300 Subject: [media] of: v4l: Document link-frequencies property in video-interfaces.txt link-frequencies is a 64-bit unsigned integer array of allowed link frequencies. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/video-interfaces.txt b/Documentation/devicetree/bindings/media/video-interfaces.txt index ce719f8..52a14cf 100644 --- a/Documentation/devicetree/bindings/media/video-interfaces.txt +++ b/Documentation/devicetree/bindings/media/video-interfaces.txt @@ -103,6 +103,9 @@ Optional endpoint properties array contains only one entry. - clock-noncontinuous: a boolean property to allow MIPI CSI-2 non-continuous clock mode. +- link-frequencies: Allowed data bus frequencies. For MIPI CSI-2, for + instance, this is the actual frequency of the bus, not bits per clock per + lane value. An array of 64-bit unsigned integers. Example -- cgit v0.10.2 From 8aefc4d1c6acb03adfada4eaf7dd66c4d91c34f6 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Mon, 22 Dec 2014 19:36:15 +0100 Subject: netfilter: log: remove unnecessary sizeof(char) sizeof(char) is always 1. Suggested-by: Joe Perches Signed-off-by: Fabian Frederick Signed-off-by: Pablo Neira Ayuso diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index 43c926c..0d8448f 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -425,8 +425,7 @@ static int netfilter_log_sysctl_init(struct net *net) nf_log_sysctl_table[i].procname = nf_log_sysctl_fnames[i]; nf_log_sysctl_table[i].data = NULL; - nf_log_sysctl_table[i].maxlen = - NFLOGGER_NAME_LEN * sizeof(char); + nf_log_sysctl_table[i].maxlen = NFLOGGER_NAME_LEN; nf_log_sysctl_table[i].mode = 0644; nf_log_sysctl_table[i].proc_handler = nf_log_proc_dostring; -- cgit v0.10.2 From a2cec3c0199ab4d7c0d83dee7fc69bd22eef7e12 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 6 Nov 2014 18:16:13 -0300 Subject: [media] of: smiapp: Add documentation Document the smiapp device tree properties. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/i2c/nokia,smia.txt b/Documentation/devicetree/bindings/media/i2c/nokia,smia.txt new file mode 100644 index 0000000..855e1fa --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/nokia,smia.txt @@ -0,0 +1,63 @@ +SMIA/SMIA++ sensor + +SMIA (Standard Mobile Imaging Architecture) is an image sensor standard +defined jointly by Nokia and ST. SMIA++, defined by Nokia, is an extension +of that. These definitions are valid for both types of sensors. + +More detailed documentation can be found in +Documentation/devicetree/bindings/media/video-interfaces.txt . + + +Mandatory properties +-------------------- + +- compatible: "nokia,smia" +- reg: I2C address (0x10, or an alternative address) +- vana-supply: Analogue voltage supply (VANA), typically 2,8 volts (sensor + dependent). +- clocks: External clock to the sensor +- clock-frequency: Frequency of the external clock to the sensor +- link-frequencies: List of allowed data link frequencies. An array of + 64-bit elements. + + +Optional properties +------------------- + +- nokia,nvm-size: The size of the NVM, in bytes. If the size is not given, + the NVM contents will not be read. +- reset-gpios: XSHUTDOWN GPIO + + +Endpoint node mandatory properties +---------------------------------- + +- clock-lanes: <0> +- data-lanes: <1..n> +- remote-endpoint: A phandle to the bus receiver's endpoint node. + + +Example +------- + +&i2c2 { + clock-frequency = <400000>; + + smiapp_1: camera@10 { + compatible = "nokia,smia"; + reg = <0x10>; + reset-gpios = <&gpio3 20 0>; + vana-supply = <&vaux3>; + clocks = <&omap3_isp 0>; + clock-frequency = <9600000>; + nokia,nvm-size = <512>; /* 8 * 64 */ + link-frequencies = /bits/ 64 <199200000 210000000 499200000>; + port { + smiapp_1_1: endpoint { + clock-lanes = <0>; + data-lanes = <1 2>; + remote-endpoint = <&csi2a_ep>; + }; + }; + }; +}; diff --git a/MAINTAINERS b/MAINTAINERS index f7d04bac..dc2d912 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8827,6 +8827,7 @@ F: include/media/smiapp.h F: drivers/media/i2c/smiapp-pll.c F: drivers/media/i2c/smiapp-pll.h F: include/uapi/linux/smiapp.h +F: Documentation/devicetree/bindings/media/i2c/nokia,smia.txt SMM665 HARDWARE MONITOR DRIVER M: Guenter Roeck -- cgit v0.10.2 From 390a5fa5bd07fd683f8f8a2b2959400242e0b76f Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 30 Nov 2013 17:51:15 -0300 Subject: [media] smiapp: Obtain device information from the Device Tree if OF node exists Platform data support is retained. of_property_read_u64_array() isn't used yet as it's not in yet. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 3dd32b1..a6bc5ef 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -25,11 +25,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include "smiapp.h" @@ -2919,19 +2921,121 @@ static int smiapp_resume(struct device *dev) #endif /* CONFIG_PM */ +static struct smiapp_platform_data *smiapp_get_pdata(struct device *dev) +{ + struct smiapp_platform_data *pdata; + struct v4l2_of_endpoint bus_cfg; + struct device_node *ep; + struct property *prop; + __be32 *val; + uint32_t asize; + unsigned int i; + int rval; + + if (!dev->of_node) + return dev->platform_data; + + ep = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!ep) + return NULL; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + rval = -ENOMEM; + goto out_err; + } + + v4l2_of_parse_endpoint(ep, &bus_cfg); + + switch (bus_cfg.bus_type) { + case V4L2_MBUS_CSI2: + pdata->csi_signalling_mode = SMIAPP_CSI_SIGNALLING_MODE_CSI2; + break; + /* FIXME: add CCP2 support. */ + default: + rval = -EINVAL; + goto out_err; + } + + pdata->lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + dev_dbg(dev, "lanes %u\n", pdata->lanes); + + /* xshutdown GPIO is optional */ + pdata->xshutdown = of_get_named_gpio(dev->of_node, "reset-gpios", 0); + + /* NVM size is not mandatory */ + of_property_read_u32(dev->of_node, "nokia,nvm-size", + &pdata->nvm_size); + + rval = of_property_read_u32(dev->of_node, "clock-frequency", + &pdata->ext_clk); + if (rval) { + dev_warn(dev, "can't get clock-frequency\n"); + goto out_err; + } + + dev_dbg(dev, "reset %d, nvm %d, clk %d, csi %d\n", pdata->xshutdown, + pdata->nvm_size, pdata->ext_clk, pdata->csi_signalling_mode); + + rval = of_get_property( + dev->of_node, "link-frequencies", &asize) ? 0 : -ENOENT; + if (rval) { + dev_warn(dev, "can't get link-frequencies array size\n"); + goto out_err; + } + + pdata->op_sys_clock = devm_kzalloc(dev, asize, GFP_KERNEL); + if (!pdata->op_sys_clock) { + rval = -ENOMEM; + goto out_err; + } + + asize /= sizeof(*pdata->op_sys_clock); + /* + * Read a 64-bit array --- this will be replaced with a + * of_property_read_u64_array() once it's merged. + */ + prop = of_find_property(dev->of_node, "link-frequencies", NULL); + if (!prop) + goto out_err; + if (!prop->value) + goto out_err; + if (asize * sizeof(*pdata->op_sys_clock) > prop->length) + goto out_err; + val = prop->value; + if (IS_ERR(val)) + goto out_err; + + for (i = 0; i < asize; i++) + pdata->op_sys_clock[i] = of_read_number(val + i * 2, 2); + + for (; asize > 0; asize--) + dev_dbg(dev, "freq %d: %lld\n", asize - 1, + pdata->op_sys_clock[asize - 1]); + + of_node_put(ep); + return pdata; + +out_err: + of_node_put(ep); + return NULL; +} + static int smiapp_probe(struct i2c_client *client, const struct i2c_device_id *devid) { struct smiapp_sensor *sensor; + struct smiapp_platform_data *pdata = smiapp_get_pdata(&client->dev); + int rval; - if (client->dev.platform_data == NULL) + if (pdata == NULL) return -ENODEV; sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); if (sensor == NULL) return -ENOMEM; - sensor->platform_data = client->dev.platform_data; + sensor->platform_data = pdata; mutex_init(&sensor->mutex); mutex_init(&sensor->power_mutex); sensor->src = &sensor->ssds[sensor->ssds_used]; @@ -2990,6 +3094,11 @@ static int smiapp_remove(struct i2c_client *client) return 0; } +static const struct of_device_id smiapp_of_table[] = { + { .compatible = "nokia,smia" }, + { }, +}; + static const struct i2c_device_id smiapp_id_table[] = { { SMIAPP_NAME, 0 }, { }, @@ -3003,6 +3112,7 @@ static const struct dev_pm_ops smiapp_pm_ops = { static struct i2c_driver smiapp_i2c_driver = { .driver = { + .of_match_table = smiapp_of_table, .name = SMIAPP_NAME, .pm = &smiapp_pm_ops, }, -- cgit v0.10.2 From 7095108b1674d3e0fd02b3fb51c9b302346c0260 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 14 Sep 2014 17:59:00 -0300 Subject: [media] smiapp: Split sub-device initialisation off from the registered callback The registered callback is called by the V4L2 async framework after the bound callback. This allows separating the functionality in the registered callback so that on DT based systems only sub-device registration is done there. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index a6bc5ef..7b13617 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2467,6 +2467,56 @@ static const struct v4l2_subdev_ops smiapp_ops; static const struct v4l2_subdev_internal_ops smiapp_internal_ops; static const struct media_entity_operations smiapp_entity_ops; +static int smiapp_register_subdevs(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct smiapp_subdev *ssds[] = { + sensor->scaler, + sensor->binner, + sensor->pixel_array, + }; + unsigned int i; + int rval; + + for (i = 0; i < SMIAPP_SUBDEVS - 1; i++) { + struct smiapp_subdev *this = ssds[i + 1]; + struct smiapp_subdev *last = ssds[i]; + + if (!last) + continue; + + rval = media_entity_init(&this->sd.entity, + this->npads, this->pads, 0); + if (rval) { + dev_err(&client->dev, + "media_entity_init failed\n"); + return rval; + } + + rval = media_entity_create_link(&this->sd.entity, + this->source_pad, + &last->sd.entity, + last->sink_pad, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (rval) { + dev_err(&client->dev, + "media_entity_create_link failed\n"); + return rval; + } + + rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, + &this->sd); + if (rval) { + dev_err(&client->dev, + "v4l2_device_register_subdev failed\n"); + return rval; + } + } + + return 0; +} + static int smiapp_registered(struct v4l2_subdev *subdev) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); @@ -2705,37 +2755,13 @@ static int smiapp_registered(struct v4l2_subdev *subdev) this->sd.owner = THIS_MODULE; v4l2_set_subdevdata(&this->sd, client); - rval = media_entity_init(&this->sd.entity, - this->npads, this->pads, 0); - if (rval) { - dev_err(&client->dev, - "media_entity_init failed\n"); - goto out_nvm_release; - } - - rval = media_entity_create_link(&this->sd.entity, - this->source_pad, - &last->sd.entity, - last->sink_pad, - MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); - if (rval) { - dev_err(&client->dev, - "media_entity_create_link failed\n"); - goto out_nvm_release; - } - - rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, - &this->sd); - if (rval) { - dev_err(&client->dev, - "v4l2_device_register_subdev failed\n"); - goto out_nvm_release; - } - last = this; } + rval = smiapp_register_subdevs(sensor); + if (rval) + goto out_nvm_release; + dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile); sensor->pixel_array->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; -- cgit v0.10.2 From 4c94468459884f10834b436620d2bd591c4cb105 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 14 Sep 2014 18:53:06 -0300 Subject: [media] smiapp: Fully probe the device in probe In the case of platform data, ISPs that provide clocks to the sensor must probe before the sensor does. Accessing the sensor does require the clocks, and thus, probe cannot access the sensor in such a system. This limitation does not exist in the case of the DT. Perform all initialisation except Media entity initialisation, link creation and sub-device registration in probe. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 7b13617..91ab71c 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2334,10 +2334,9 @@ static DEVICE_ATTR(ident, S_IRUGO, smiapp_sysfs_ident_read, NULL); * V4L2 subdev core operations */ -static int smiapp_identify_module(struct v4l2_subdev *subdev) +static int smiapp_identify_module(struct smiapp_sensor *sensor) { - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); struct smiapp_module_info *minfo = &sensor->minfo; unsigned int i; int rval = 0; @@ -2517,10 +2516,17 @@ static int smiapp_register_subdevs(struct smiapp_sensor *sensor) return 0; } -static int smiapp_registered(struct v4l2_subdev *subdev) +static void smiapp_cleanup(struct smiapp_sensor *sensor) { - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + device_remove_file(&client->dev, &dev_attr_nvm); + device_remove_file(&client->dev, &dev_attr_ident); +} + +static int smiapp_init(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); struct smiapp_pll *pll = &sensor->pll; struct smiapp_subdev *last = NULL; u32 tmp; @@ -2566,7 +2572,7 @@ static int smiapp_registered(struct v4l2_subdev *subdev) if (rval) return -ENODEV; - rval = smiapp_identify_module(subdev); + rval = smiapp_identify_module(sensor); if (rval) { rval = -ENODEV; goto out_power_off; @@ -2646,13 +2652,13 @@ static int smiapp_registered(struct v4l2_subdev *subdev) if (sensor->nvm == NULL) { dev_err(&client->dev, "nvm buf allocation failed\n"); rval = -ENOMEM; - goto out_ident_release; + goto out_cleanup; } if (device_create_file(&client->dev, &dev_attr_nvm) != 0) { dev_err(&client->dev, "sysfs nvm entry failed\n"); rval = -EBUSY; - goto out_ident_release; + goto out_cleanup; } } @@ -2696,7 +2702,7 @@ static int smiapp_registered(struct v4l2_subdev *subdev) rval = smiapp_get_mbus_formats(sensor); if (rval) { rval = -ENODEV; - goto out_nvm_release; + goto out_cleanup; } for (i = 0; i < SMIAPP_SUBDEVS; i++) { @@ -2758,10 +2764,6 @@ static int smiapp_registered(struct v4l2_subdev *subdev) last = this; } - rval = smiapp_register_subdevs(sensor); - if (rval) - goto out_nvm_release; - dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile); sensor->pixel_array->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; @@ -2770,14 +2772,14 @@ static int smiapp_registered(struct v4l2_subdev *subdev) smiapp_read_frame_fmt(sensor); rval = smiapp_init_controls(sensor); if (rval < 0) - goto out_nvm_release; + goto out_cleanup; mutex_lock(&sensor->mutex); rval = smiapp_update_mode(sensor); mutex_unlock(&sensor->mutex); if (rval) { dev_err(&client->dev, "update mode failed\n"); - goto out_nvm_release; + goto out_cleanup; } sensor->streaming = false; @@ -2787,23 +2789,39 @@ static int smiapp_registered(struct v4l2_subdev *subdev) rval = smiapp_read(sensor, SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, &tmp); sensor->flash_capability = tmp; if (rval) - goto out_nvm_release; + goto out_cleanup; smiapp_power_off(sensor); return 0; -out_nvm_release: - device_remove_file(&client->dev, &dev_attr_nvm); - -out_ident_release: - device_remove_file(&client->dev, &dev_attr_ident); +out_cleanup: + smiapp_cleanup(sensor); out_power_off: smiapp_power_off(sensor); return rval; } +static int smiapp_registered(struct v4l2_subdev *subdev) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + int rval; + + if (!client->dev.of_node) { + rval = smiapp_init(sensor); + if (rval) + return rval; + } + + rval = smiapp_register_subdevs(sensor); + if (rval) + smiapp_cleanup(sensor); + + return rval; +} + static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct smiapp_subdev *ssd = to_smiapp_subdev(sd); @@ -3077,6 +3095,12 @@ static int smiapp_probe(struct i2c_client *client, if (rval < 0) return rval; + if (client->dev.of_node) { + rval = smiapp_init(sensor); + if (rval) + goto out_media_entity_cleanup; + } + rval = v4l2_async_register_subdev(&sensor->src->sd); if (rval < 0) goto out_media_entity_cleanup; -- cgit v0.10.2 From 0691b40e88350cbfa686ca25f98ab614f1919d73 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 3 Oct 2014 11:21:52 -0300 Subject: [media] smiapp: Access flash capabilities through limits The flash capability register is already read as part of the limit registers. Do no access it separately; instead use the value from the limits. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 91ab71c..22eddd6 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -1483,7 +1483,7 @@ static int smiapp_start_streaming(struct smiapp_sensor *sensor) if (rval < 0) goto out; - if ((sensor->flash_capability & + if ((sensor->limits[SMIAPP_LIMIT_FLASH_MODE_CAPABILITY] & (SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE | SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE)) && sensor->platform_data->strobe_setup != NULL && @@ -2529,7 +2529,6 @@ static int smiapp_init(struct smiapp_sensor *sensor) struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); struct smiapp_pll *pll = &sensor->pll; struct smiapp_subdev *last = NULL; - u32 tmp; unsigned int i; int rval; @@ -2785,12 +2784,6 @@ static int smiapp_init(struct smiapp_sensor *sensor) sensor->streaming = false; sensor->dev_init_done = true; - /* check flash capability */ - rval = smiapp_read(sensor, SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, &tmp); - sensor->flash_capability = tmp; - if (rval) - goto out_cleanup; - smiapp_power_off(sensor); return 0; diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h index 8fded46..ed010a8 100644 --- a/drivers/media/i2c/smiapp/smiapp.h +++ b/drivers/media/i2c/smiapp/smiapp.h @@ -216,7 +216,6 @@ struct smiapp_sensor { u8 scaling_mode; u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */ - u8 flash_capability; u8 frame_skip; int power_count; -- cgit v0.10.2 From f7350a034af4531f6df7e66cd5661f0f9a0ec4d6 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 3 Oct 2014 11:20:37 -0300 Subject: [media] smiapp: Free control handlers in sub-device cleanup Also call smiapp_cleanup() in smiapp_remove(), replacing code that did the same than the function. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 22eddd6..d0a55fb 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2522,6 +2522,8 @@ static void smiapp_cleanup(struct smiapp_sensor *sensor) device_remove_file(&client->dev, &dev_attr_nvm); device_remove_file(&client->dev, &dev_attr_ident); + + smiapp_free_controls(sensor); } static int smiapp_init(struct smiapp_sensor *sensor) @@ -3124,15 +3126,11 @@ static int smiapp_remove(struct i2c_client *client) sensor->power_count = 0; } - device_remove_file(&client->dev, &dev_attr_ident); - if (sensor->nvm) - device_remove_file(&client->dev, &dev_attr_nvm); - for (i = 0; i < sensor->ssds_used; i++) { v4l2_device_unregister_subdev(&sensor->ssds[i].sd); media_entity_cleanup(&sensor->ssds[i].sd.entity); } - smiapp_free_controls(sensor); + smiapp_cleanup(sensor); return 0; } -- cgit v0.10.2 From 6208aebd33d341d5068ab0ccbff8035ff4034c08 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 9 Dec 2014 14:35:43 -0300 Subject: [media] smiapp: Clean up smiapp_init_controls() Clean up smiapp_init_controls() by adding newlines to appropriate places and by removing superfluous error handling. The caller will clean up control handlers in any case if the function fails. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index d0a55fb..8361070 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -527,6 +527,7 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 12); if (rval) return rval; + sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; sensor->analog_gain = v4l2_ctrl_new_std( @@ -585,8 +586,7 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) dev_err(&client->dev, "pixel array controls initialization failed (%d)\n", sensor->pixel_array->ctrl_handler.error); - rval = sensor->pixel_array->ctrl_handler.error; - goto error; + return sensor->pixel_array->ctrl_handler.error; } sensor->pixel_array->sd.ctrl_handler = @@ -596,7 +596,8 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) rval = v4l2_ctrl_handler_init(&sensor->src->ctrl_handler, 0); if (rval) - goto error; + return rval; + sensor->src->ctrl_handler.lock = &sensor->mutex; for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++); @@ -614,20 +615,12 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) dev_err(&client->dev, "src controls initialization failed (%d)\n", sensor->src->ctrl_handler.error); - rval = sensor->src->ctrl_handler.error; - goto error; + return sensor->src->ctrl_handler.error; } - sensor->src->sd.ctrl_handler = - &sensor->src->ctrl_handler; + sensor->src->sd.ctrl_handler = &sensor->src->ctrl_handler; return 0; - -error: - v4l2_ctrl_handler_free(&sensor->pixel_array->ctrl_handler); - v4l2_ctrl_handler_free(&sensor->src->ctrl_handler); - - return rval; } static void smiapp_free_controls(struct smiapp_sensor *sensor) -- cgit v0.10.2 From 2e9f3c1c4443b2e46ef15e021cdf693b16c5231d Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 9 Dec 2014 13:48:58 -0300 Subject: [media] smiapp: Separate late controls from the rest The default values and limits for certain controls need the knowledge of available media bus codes or link frequencies. Create such controls later on, so that most of the initialisation of the sensor has already been done when the init quirk is called. [mchehab@osg.samsung.com: make checkpatch.pl happier] Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 8361070..fb8f8a8 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -519,9 +519,6 @@ static const struct v4l2_ctrl_ops smiapp_ctrl_ops = { static int smiapp_init_controls(struct smiapp_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned long *valid_link_freqs = &sensor->valid_link_freqs[ - sensor->csi_format->compressed - SMIAPP_COMPRESSED_BASE]; - unsigned int max, i; int rval; rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 12); @@ -573,15 +570,6 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) ARRAY_SIZE(smiapp_test_patterns) - 1, 0, 0, smiapp_test_patterns); - for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) { - int max_value = (1 << sensor->csi_format->width) - 1; - sensor->test_data[i] = - v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, - &smiapp_ctrl_ops, V4L2_CID_TEST_PATTERN_RED + i, - 0, max_value, 1, max_value); - } - if (sensor->pixel_array->ctrl_handler.error) { dev_err(&client->dev, "pixel array controls initialization failed (%d)\n", @@ -600,13 +588,6 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) sensor->src->ctrl_handler.lock = &sensor->mutex; - for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++); - - sensor->link_freq = v4l2_ctrl_new_int_menu( - &sensor->src->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_LINK_FREQ, __fls(*valid_link_freqs), - __ffs(*valid_link_freqs), sensor->platform_data->op_sys_clock); - sensor->pixel_rate_csi = v4l2_ctrl_new_std( &sensor->src->ctrl_handler, &smiapp_ctrl_ops, V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1); @@ -623,6 +604,35 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) return 0; } +/* + * For controls that require information on available media bus codes + * and linke frequencies. + */ +static int smiapp_init_late_controls(struct smiapp_sensor *sensor) +{ + unsigned long *valid_link_freqs = &sensor->valid_link_freqs[ + sensor->csi_format->compressed - SMIAPP_COMPRESSED_BASE]; + unsigned int max, i; + + for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) { + int max_value = (1 << sensor->csi_format->width) - 1; + + sensor->test_data[i] = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, + &smiapp_ctrl_ops, V4L2_CID_TEST_PATTERN_RED + i, + 0, max_value, 1, max_value); + } + + for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++); + + sensor->link_freq = v4l2_ctrl_new_int_menu( + &sensor->src->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_LINK_FREQ, __fls(*valid_link_freqs), + __ffs(*valid_link_freqs), sensor->platform_data->op_sys_clock); + + return sensor->src->ctrl_handler.error; +} + static void smiapp_free_controls(struct smiapp_sensor *sensor) { unsigned int i; @@ -2768,6 +2778,12 @@ static int smiapp_init(struct smiapp_sensor *sensor) if (rval < 0) goto out_cleanup; + rval = smiapp_init_late_controls(sensor); + if (rval) { + rval = -ENODEV; + goto out_cleanup; + } + mutex_lock(&sensor->mutex); rval = smiapp_update_mode(sensor); mutex_unlock(&sensor->mutex); -- cgit v0.10.2 From 5313c00266700d231d9b386d9c0e4faad710c729 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 9 Dec 2014 14:22:31 -0300 Subject: [media] smiapp: Move enumerating available media bus codes later As the controls creation is separated in two sections, the available media bus codes and link frequencies can be enumerated later on. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index fb8f8a8..aa27a1b 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2703,12 +2703,6 @@ static int smiapp_init(struct smiapp_sensor *sensor) if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS; - rval = smiapp_get_mbus_formats(sensor); - if (rval) { - rval = -ENODEV; - goto out_cleanup; - } - for (i = 0; i < SMIAPP_SUBDEVS; i++) { struct { struct smiapp_subdev *ssd; @@ -2778,6 +2772,12 @@ static int smiapp_init(struct smiapp_sensor *sensor) if (rval < 0) goto out_cleanup; + rval = smiapp_get_mbus_formats(sensor); + if (rval) { + rval = -ENODEV; + goto out_cleanup; + } + rval = smiapp_init_late_controls(sensor); if (rval) { rval = -ENODEV; -- cgit v0.10.2 From 0d825a1226466512f44d4a81ce11ad15ca9334b0 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 3 Oct 2014 11:38:32 -0300 Subject: [media] smiapp: Replace pll_flags quirk with more generic init quirk The pll_flags quirk just returned the extra PLL flags the sensor required, but the init quirk is far more versatile. It can be used to perform any extra initialisation needed by the sensor, including allocating memory for sensor specific struct and creating sensor specific new controls. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index aa27a1b..b3c8125 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2697,7 +2697,6 @@ static int smiapp_init(struct smiapp_sensor *sensor) pll->bus_type = SMIAPP_PLL_BUS_TYPE_CSI2; pll->csi2.lanes = sensor->platform_data->lanes; pll->ext_clk_freq_hz = sensor->platform_data->ext_clk; - pll->flags = smiapp_call_quirk(sensor, pll_flags); pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; /* Profile 0 sensors have no separate OP clock branch. */ if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) @@ -2772,6 +2771,10 @@ static int smiapp_init(struct smiapp_sensor *sensor) if (rval < 0) goto out_cleanup; + rval = smiapp_call_quirk(sensor, init); + if (rval) + goto out_cleanup; + rval = smiapp_get_mbus_formats(sensor); if (rval) { rval = -ENODEV; diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.c b/drivers/media/i2c/smiapp/smiapp-quirk.c index dd4ae6f..abf9ea7 100644 --- a/drivers/media/i2c/smiapp/smiapp-quirk.c +++ b/drivers/media/i2c/smiapp/smiapp-quirk.c @@ -214,9 +214,11 @@ static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor) return smiapp_write_8(sensor, 0x3328, 0x80); } -static unsigned long jt8ev1_pll_flags(struct smiapp_sensor *sensor) +static int jt8ev1_init(struct smiapp_sensor *sensor) { - return SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; + sensor->pll.flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; + + return 0; } const struct smiapp_quirk smiapp_jt8ev1_quirk = { @@ -224,7 +226,7 @@ const struct smiapp_quirk smiapp_jt8ev1_quirk = { .post_poweron = jt8ev1_post_poweron, .pre_streamon = jt8ev1_pre_streamon, .post_streamoff = jt8ev1_post_streamoff, - .pll_flags = jt8ev1_pll_flags, + .init = jt8ev1_init, }; static int tcm8500md_limits(struct smiapp_sensor *sensor) diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.h b/drivers/media/i2c/smiapp/smiapp-quirk.h index 3a3c3e5..a24eb43 100644 --- a/drivers/media/i2c/smiapp/smiapp-quirk.h +++ b/drivers/media/i2c/smiapp/smiapp-quirk.h @@ -29,6 +29,9 @@ struct smiapp_sensor; * @post_poweron: Called always after the sensor has been fully powered on. * @pre_streamon: Called just before streaming is enabled. * @post_streamon: Called right after stopping streaming. + * @pll_flags: Return flags for the PLL calculator. + * @init: Quirk initialisation, called the last in probe(). This is + * also appropriate for adding sensor specific controls, for instance. * @reg_access: Register access quirk. The quirk may divert the access * to another register, or no register at all. * @@ -47,6 +50,7 @@ struct smiapp_quirk { int (*pre_streamon)(struct smiapp_sensor *sensor); int (*post_streamoff)(struct smiapp_sensor *sensor); unsigned long (*pll_flags)(struct smiapp_sensor *sensor); + int (*init)(struct smiapp_sensor *sensor); int (*reg_access)(struct smiapp_sensor *sensor, bool write, u32 *reg, u32 *val); unsigned long flags; -- cgit v0.10.2 From 3b1635e02e524e2cc7bded4b998d35074b68065e Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 9 Dec 2014 18:43:16 -0300 Subject: [media] smiapp: Add parentheses to macro arguments used in macros This makes the macros a little bit safer. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.h b/drivers/media/i2c/smiapp/smiapp-quirk.h index a24eb43..dac5566 100644 --- a/drivers/media/i2c/smiapp/smiapp-quirk.h +++ b/drivers/media/i2c/smiapp/smiapp-quirk.h @@ -72,14 +72,14 @@ void smiapp_replace_limit(struct smiapp_sensor *sensor, .val = _val, \ } -#define smiapp_call_quirk(_sensor, _quirk, ...) \ - (_sensor->minfo.quirk && \ - _sensor->minfo.quirk->_quirk ? \ - _sensor->minfo.quirk->_quirk(_sensor, ##__VA_ARGS__) : 0) +#define smiapp_call_quirk(sensor, _quirk, ...) \ + ((sensor)->minfo.quirk && \ + (sensor)->minfo.quirk->_quirk ? \ + (sensor)->minfo.quirk->_quirk(sensor, ##__VA_ARGS__) : 0) -#define smiapp_needs_quirk(_sensor, _quirk) \ - (_sensor->minfo.quirk ? \ - _sensor->minfo.quirk->flags & _quirk : 0) +#define smiapp_needs_quirk(sensor, _quirk) \ + ((sensor)->minfo.quirk ? \ + (sensor)->minfo.quirk->flags & _quirk : 0) extern const struct smiapp_quirk smiapp_jt8ev1_quirk; extern const struct smiapp_quirk smiapp_imx125es_quirk; -- cgit v0.10.2 From 417d2e507edcb5cf15eb344f86bd3dd28737f24e Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Tue, 9 Dec 2014 16:43:44 -0300 Subject: [media] media: platform: add VPFE capture driver support for AM437X This patch adds Video Processing Front End (VPFE) driver for AM437X family of devices Driver supports the following: - V4L2 API using MMAP buffer access based on videobuf2 api - Asynchronous sensor/decoder sub device registration - DT support Signed-off-by: Benoit Parrot Signed-off-by: Darren Etheridge Signed-off-by: Lad, Prabhakar [hans.verkuil@cisco.com: swapped two lines to fix vpfe_release() & add pinctrl include] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/ti-am437x-vpfe.txt b/Documentation/devicetree/bindings/media/ti-am437x-vpfe.txt new file mode 100644 index 0000000..3932e76 --- /dev/null +++ b/Documentation/devicetree/bindings/media/ti-am437x-vpfe.txt @@ -0,0 +1,61 @@ +Texas Instruments AM437x CAMERA (VPFE) +-------------------------------------- + +The Video Processing Front End (VPFE) is a key component for image capture +applications. The capture module provides the system interface and the +processing capability to connect RAW image-sensor modules and video decoders +to the AM437x device. + +Required properties: +- compatible: must be "ti,am437x-vpfe" +- reg: physical base address and length of the registers set for the device; +- interrupts: should contain IRQ line for the VPFE; +- ti,am437x-vpfe-interface: can be one of the following, + 0 - Raw Bayer Interface. + 1 - 8 Bit BT656 Interface. + 2 - 10 Bit BT656 Interface. + 3 - YCbCr 8 Bit Interface. + 4 - YCbCr 16 Bit Interface. + +VPFE supports a single port node with parallel bus. It should contain one +'port' child node with child 'endpoint' node. Please refer to the bindings +defined in Documentation/devicetree/bindings/media/video-interfaces.txt. + +Example: + vpfe: vpfe@f0034000 { + compatible = "ti,am437x-vpfe"; + reg = <0x48328000 0x2000>; + interrupts = ; + + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&vpfe_pins_default>; + pinctrl-1 = <&vpfe_pins_sleep>; + + port { + #address-cells = <1>; + #size-cells = <0>; + + vpfe0_ep: endpoint { + remote-endpoint = <&ov2659_1>; + ti,am437x-vpfe-interface = <0>; + bus-width = <8>; + hsync-active = <0>; + vsync-active = <0>; + }; + }; + }; + + i2c1: i2c@4802a000 { + + ov2659@30 { + compatible = "ti,ov2659"; + reg = <0x30>; + + port { + ov2659_1: endpoint { + remote-endpoint = <&vpfe0_ep>; + bus-width = <8>; + mclk-frequency = <12000000>; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index dc2d912..4318f34 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8745,6 +8745,15 @@ S: Maintained F: drivers/media/platform/davinci/ F: include/media/davinci/ +TI AM437X VPFE DRIVER +M: Lad, Prabhakar +L: linux-media@vger.kernel.org +W: http://linuxtv.org/ +Q: http://patchwork.linuxtv.org/project/linux-media/list/ +T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git +S: Maintained +F: drivers/media/platform/am437x/ + SIS 190 ETHERNET DRIVER M: Francois Romieu L: netdev@vger.kernel.org diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 480a174..71e8873 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -118,6 +118,7 @@ config VIDEO_S3C_CAMIF source "drivers/media/platform/soc_camera/Kconfig" source "drivers/media/platform/exynos4-is/Kconfig" source "drivers/media/platform/s5p-tv/Kconfig" +source "drivers/media/platform/am437x/Kconfig" endif # V4L_PLATFORM_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index a49936b..3ec1547 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -46,4 +46,6 @@ obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/ obj-y += omap/ +obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x/ + ccflags-y += -I$(srctree)/drivers/media/i2c diff --git a/drivers/media/platform/am437x/Kconfig b/drivers/media/platform/am437x/Kconfig new file mode 100644 index 0000000..7b023a76 --- /dev/null +++ b/drivers/media/platform/am437x/Kconfig @@ -0,0 +1,11 @@ +config VIDEO_AM437X_VPFE + tristate "TI AM437x VPFE video capture driver" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on SOC_AM43XX || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + help + Support for AM437x Video Processing Front End based Video + Capture Driver. + + To compile this driver as a module, choose M here. The module + will be called am437x-vpfe. diff --git a/drivers/media/platform/am437x/Makefile b/drivers/media/platform/am437x/Makefile new file mode 100644 index 0000000..d11fff1 --- /dev/null +++ b/drivers/media/platform/am437x/Makefile @@ -0,0 +1,3 @@ +# Makefile for AM437x VPFE driver + +obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x-vpfe.o diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c new file mode 100644 index 0000000..e01ac22 --- /dev/null +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -0,0 +1,2778 @@ +/* + * TI VPFE capture Driver + * + * Copyright (C) 2013 - 2014 Texas Instruments, Inc. + * + * Benoit Parrot + * Lad, Prabhakar + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "am437x-vpfe.h" + +#define VPFE_MODULE_NAME "vpfe" +#define VPFE_VERSION "0.1.0" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level 0-8"); + +#define vpfe_dbg(level, dev, fmt, arg...) \ + v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ##arg) +#define vpfe_info(dev, fmt, arg...) \ + v4l2_info(&dev->v4l2_dev, fmt, ##arg) +#define vpfe_err(dev, fmt, arg...) \ + v4l2_err(&dev->v4l2_dev, fmt, ##arg) + +/* standard information */ +struct vpfe_standard { + v4l2_std_id std_id; + unsigned int width; + unsigned int height; + struct v4l2_fract pixelaspect; + int frame_format; +}; + +const struct vpfe_standard vpfe_standards[] = { + {V4L2_STD_525_60, 720, 480, {11, 10}, 1}, + {V4L2_STD_625_50, 720, 576, {54, 59}, 1}, +}; + +struct bus_format { + unsigned int width; + unsigned int bpp; +}; + +/* + * struct vpfe_fmt - VPFE media bus format information + * @name: V4L2 format description + * @code: V4L2 media bus format code + * @shifted: V4L2 media bus format code for the same pixel layout but + * shifted to be 8 bits per pixel. =0 if format is not shiftable. + * @pixelformat: V4L2 pixel format FCC identifier + * @width: Bits per pixel (when transferred over a bus) + * @bpp: Bytes per pixel (when stored in memory) + * @supported: Indicates format supported by subdev + */ +struct vpfe_fmt { + const char *name; + u32 fourcc; + u32 code; + struct bus_format l; + struct bus_format s; + bool supported; + u32 index; +}; + +static struct vpfe_fmt formats[] = { + { + .name = "YUV 4:2:2 packed, YCbYCr", + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .l.width = 10, + .l.bpp = 4, + .s.width = 8, + .s.bpp = 2, + .supported = false, + }, { + .name = "YUV 4:2:2 packed, CbYCrY", + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .l.width = 10, + .l.bpp = 4, + .s.width = 8, + .s.bpp = 2, + .supported = false, + }, { + .name = "YUV 4:2:2 packed, YCrYCb", + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .l.width = 10, + .l.bpp = 4, + .s.width = 8, + .s.bpp = 2, + .supported = false, + }, { + .name = "YUV 4:2:2 packed, CrYCbY", + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .l.width = 10, + .l.bpp = 4, + .s.width = 8, + .s.bpp = 2, + .supported = false, + }, { + .name = "RAW8 BGGR", + .fourcc = V4L2_PIX_FMT_SBGGR8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .l.width = 10, + .l.bpp = 2, + .s.width = 8, + .s.bpp = 1, + .supported = false, + }, { + .name = "RAW8 GBRG", + .fourcc = V4L2_PIX_FMT_SGBRG8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .l.width = 10, + .l.bpp = 2, + .s.width = 8, + .s.bpp = 1, + .supported = false, + }, { + .name = "RAW8 GRBG", + .fourcc = V4L2_PIX_FMT_SGRBG8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .l.width = 10, + .l.bpp = 2, + .s.width = 8, + .s.bpp = 1, + .supported = false, + }, { + .name = "RAW8 RGGB", + .fourcc = V4L2_PIX_FMT_SRGGB8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .l.width = 10, + .l.bpp = 2, + .s.width = 8, + .s.bpp = 1, + .supported = false, + }, { + .name = "RGB565 (LE)", + .fourcc = V4L2_PIX_FMT_RGB565, + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .l.width = 10, + .l.bpp = 4, + .s.width = 8, + .s.bpp = 2, + .supported = false, + }, { + .name = "RGB565 (BE)", + .fourcc = V4L2_PIX_FMT_RGB565X, + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .l.width = 10, + .l.bpp = 4, + .s.width = 8, + .s.bpp = 2, + .supported = false, + }, +}; + +static int +__vpfe_get_format(struct vpfe_device *vpfe, + struct v4l2_format *format, unsigned int *bpp); + +static struct vpfe_fmt *find_format_by_code(unsigned int code) +{ + struct vpfe_fmt *fmt; + unsigned int k; + + for (k = 0; k < ARRAY_SIZE(formats); k++) { + fmt = &formats[k]; + if (fmt->code == code) + return fmt; + } + + return NULL; +} + +static struct vpfe_fmt *find_format_by_pix(unsigned int pixelformat) +{ + struct vpfe_fmt *fmt; + unsigned int k; + + for (k = 0; k < ARRAY_SIZE(formats); k++) { + fmt = &formats[k]; + if (fmt->fourcc == pixelformat) + return fmt; + } + + return NULL; +} + +static void +mbus_to_pix(struct vpfe_device *vpfe, + const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix, unsigned int *bpp) +{ + struct vpfe_subdev_info *sdinfo = vpfe->current_subdev; + unsigned int bus_width = sdinfo->vpfe_param.bus_width; + struct vpfe_fmt *fmt; + + fmt = find_format_by_code(mbus->code); + if (WARN_ON(fmt == NULL)) { + pr_err("Invalid mbus code set\n"); + *bpp = 1; + return; + } + + memset(pix, 0, sizeof(*pix)); + v4l2_fill_pix_format(pix, mbus); + pix->pixelformat = fmt->fourcc; + *bpp = (bus_width == 10) ? fmt->l.bpp : fmt->s.bpp; + + /* pitch should be 32 bytes aligned */ + pix->bytesperline = ALIGN(pix->width * *bpp, 32); + pix->sizeimage = pix->bytesperline * pix->height; +} + +static void pix_to_mbus(struct vpfe_device *vpfe, + struct v4l2_pix_format *pix_fmt, + struct v4l2_mbus_framefmt *mbus_fmt) +{ + struct vpfe_fmt *fmt; + + fmt = find_format_by_pix(pix_fmt->pixelformat); + if (!fmt) { + /* default to first entry */ + vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n", + pix_fmt->pixelformat); + fmt = &formats[0]; + } + + memset(mbus_fmt, 0, sizeof(*mbus_fmt)); + v4l2_fill_mbus_format(mbus_fmt, pix_fmt, fmt->code); +} + +/* Print Four-character-code (FOURCC) */ +static char *print_fourcc(u32 fmt) +{ + static char code[5]; + + code[0] = (unsigned char)(fmt & 0xff); + code[1] = (unsigned char)((fmt >> 8) & 0xff); + code[2] = (unsigned char)((fmt >> 16) & 0xff); + code[3] = (unsigned char)((fmt >> 24) & 0xff); + code[4] = '\0'; + + return code; +} + +static int +cmp_v4l2_format(const struct v4l2_format *lhs, const struct v4l2_format *rhs) +{ + return lhs->type == rhs->type && + lhs->fmt.pix.width == rhs->fmt.pix.width && + lhs->fmt.pix.height == rhs->fmt.pix.height && + lhs->fmt.pix.pixelformat == rhs->fmt.pix.pixelformat && + lhs->fmt.pix.field == rhs->fmt.pix.field && + lhs->fmt.pix.colorspace == rhs->fmt.pix.colorspace && + lhs->fmt.pix.ycbcr_enc == rhs->fmt.pix.ycbcr_enc && + lhs->fmt.pix.quantization == rhs->fmt.pix.quantization; +} + +static inline u32 vpfe_reg_read(struct vpfe_ccdc *ccdc, u32 offset) +{ + return ioread32(ccdc->ccdc_cfg.base_addr + offset); +} + +static inline void vpfe_reg_write(struct vpfe_ccdc *ccdc, u32 val, u32 offset) +{ + iowrite32(val, ccdc->ccdc_cfg.base_addr + offset); +} + +static inline struct vpfe_device *to_vpfe(struct vpfe_ccdc *ccdc) +{ + return container_of(ccdc, struct vpfe_device, ccdc); +} + +static inline struct vpfe_cap_buffer *to_vpfe_buffer(struct vb2_buffer *vb) +{ + return container_of(vb, struct vpfe_cap_buffer, vb); +} + +static inline void vpfe_pcr_enable(struct vpfe_ccdc *ccdc, int flag) +{ + vpfe_reg_write(ccdc, !!flag, VPFE_PCR); +} + +static void vpfe_config_enable(struct vpfe_ccdc *ccdc, int flag) +{ + unsigned int cfg; + + if (!flag) { + cfg = vpfe_reg_read(ccdc, VPFE_CONFIG); + cfg &= ~(VPFE_CONFIG_EN_ENABLE << VPFE_CONFIG_EN_SHIFT); + } else { + cfg = VPFE_CONFIG_EN_ENABLE << VPFE_CONFIG_EN_SHIFT; + } + + vpfe_reg_write(ccdc, cfg, VPFE_CONFIG); +} + +static void vpfe_ccdc_setwin(struct vpfe_ccdc *ccdc, + struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, + int bpp) +{ + int horz_start, horz_nr_pixels; + int vert_start, vert_nr_lines; + int val, mid_img; + + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left * bpp; + horz_nr_pixels = (image_win->width * bpp) - 1; + vpfe_reg_write(ccdc, (horz_start << VPFE_HORZ_INFO_SPH_SHIFT) | + horz_nr_pixels, VPFE_HORZ_INFO); + + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* Since first line doesn't have any data */ + vert_start += 1; + /* configure VDINT0 */ + val = (vert_start << VPFE_VDINT_VDINT0_SHIFT); + } else { + /* Since first line doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* + * configure VDINT0 and VDINT1. VDINT1 will be at half + * of image height + */ + mid_img = vert_start + (image_win->height / 2); + val = (vert_start << VPFE_VDINT_VDINT0_SHIFT) | + (mid_img & VPFE_VDINT_VDINT1_MASK); + } + + vpfe_reg_write(ccdc, val, VPFE_VDINT); + + vpfe_reg_write(ccdc, (vert_start << VPFE_VERT_START_SLV0_SHIFT) | + vert_start, VPFE_VERT_START); + vpfe_reg_write(ccdc, vert_nr_lines, VPFE_VERT_LINES); +} + +static void vpfe_reg_dump(struct vpfe_ccdc *ccdc) +{ + struct vpfe_device *vpfe = to_vpfe(ccdc); + + vpfe_dbg(3, vpfe, "ALAW: 0x%x\n", vpfe_reg_read(ccdc, VPFE_ALAW)); + vpfe_dbg(3, vpfe, "CLAMP: 0x%x\n", vpfe_reg_read(ccdc, VPFE_CLAMP)); + vpfe_dbg(3, vpfe, "DCSUB: 0x%x\n", vpfe_reg_read(ccdc, VPFE_DCSUB)); + vpfe_dbg(3, vpfe, "BLKCMP: 0x%x\n", vpfe_reg_read(ccdc, VPFE_BLKCMP)); + vpfe_dbg(3, vpfe, "COLPTN: 0x%x\n", vpfe_reg_read(ccdc, VPFE_COLPTN)); + vpfe_dbg(3, vpfe, "SDOFST: 0x%x\n", vpfe_reg_read(ccdc, VPFE_SDOFST)); + vpfe_dbg(3, vpfe, "SYN_MODE: 0x%x\n", + vpfe_reg_read(ccdc, VPFE_SYNMODE)); + vpfe_dbg(3, vpfe, "HSIZE_OFF: 0x%x\n", + vpfe_reg_read(ccdc, VPFE_HSIZE_OFF)); + vpfe_dbg(3, vpfe, "HORZ_INFO: 0x%x\n", + vpfe_reg_read(ccdc, VPFE_HORZ_INFO)); + vpfe_dbg(3, vpfe, "VERT_START: 0x%x\n", + vpfe_reg_read(ccdc, VPFE_VERT_START)); + vpfe_dbg(3, vpfe, "VERT_LINES: 0x%x\n", + vpfe_reg_read(ccdc, VPFE_VERT_LINES)); +} + +static int +vpfe_ccdc_validate_param(struct vpfe_ccdc *ccdc, + struct vpfe_ccdc_config_params_raw *ccdcparam) +{ + struct vpfe_device *vpfe = to_vpfe(ccdc); + u8 max_gamma, max_data; + + if (!ccdcparam->alaw.enable) + return 0; + + max_gamma = ccdc_gamma_width_max_bit(ccdcparam->alaw.gamma_wd); + max_data = ccdc_data_size_max_bit(ccdcparam->data_sz); + + if (ccdcparam->alaw.gamma_wd > VPFE_CCDC_GAMMA_BITS_09_0 || + ccdcparam->alaw.gamma_wd < VPFE_CCDC_GAMMA_BITS_15_6 || + max_gamma > max_data) { + vpfe_dbg(1, vpfe, "Invalid data line select\n"); + return -EINVAL; + } + + return 0; +} + +static void +vpfe_ccdc_update_raw_params(struct vpfe_ccdc *ccdc, + struct vpfe_ccdc_config_params_raw *raw_params) +{ + struct vpfe_ccdc_config_params_raw *config_params = + &ccdc->ccdc_cfg.bayer.config_params; + + config_params = raw_params; +} + +/* + * vpfe_ccdc_restore_defaults() + * This function will write defaults to all CCDC registers + */ +static void vpfe_ccdc_restore_defaults(struct vpfe_ccdc *ccdc) +{ + int i; + + /* Disable CCDC */ + vpfe_pcr_enable(ccdc, 0); + + /* set all registers to default value */ + for (i = 4; i <= 0x94; i += 4) + vpfe_reg_write(ccdc, 0, i); + + vpfe_reg_write(ccdc, VPFE_NO_CULLING, VPFE_CULLING); + vpfe_reg_write(ccdc, VPFE_CCDC_GAMMA_BITS_11_2, VPFE_ALAW); +} + +static int vpfe_ccdc_close(struct vpfe_ccdc *ccdc, struct device *dev) +{ + int dma_cntl, i, pcr; + + /* If the CCDC module is still busy wait for it to be done */ + for (i = 0; i < 10; i++) { + usleep_range(5000, 6000); + pcr = vpfe_reg_read(ccdc, VPFE_PCR); + if (!pcr) + break; + + /* make sure it it is disabled */ + vpfe_pcr_enable(ccdc, 0); + } + + /* Disable CCDC by resetting all register to default POR values */ + vpfe_ccdc_restore_defaults(ccdc); + + /* if DMA_CNTL overflow bit is set. Clear it + * It appears to take a while for this to become quiescent ~20ms + */ + for (i = 0; i < 10; i++) { + dma_cntl = vpfe_reg_read(ccdc, VPFE_DMA_CNTL); + if (!(dma_cntl & VPFE_DMA_CNTL_OVERFLOW)) + break; + + /* Clear the overflow bit */ + vpfe_reg_write(ccdc, dma_cntl, VPFE_DMA_CNTL); + usleep_range(5000, 6000); + } + + /* Disabled the module at the CONFIG level */ + vpfe_config_enable(ccdc, 0); + + pm_runtime_put_sync(dev); + + return 0; +} + +static int vpfe_ccdc_set_params(struct vpfe_ccdc *ccdc, void __user *params) +{ + struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); + struct vpfe_ccdc_config_params_raw raw_params; + int x; + + if (ccdc->ccdc_cfg.if_type != VPFE_RAW_BAYER) + return -EINVAL; + + x = copy_from_user(&raw_params, params, sizeof(raw_params)); + if (x) { + vpfe_dbg(1, vpfe, + "vpfe_ccdc_set_params: error in copying ccdc params, %d\n", + x); + return -EFAULT; + } + + if (!vpfe_ccdc_validate_param(ccdc, &raw_params)) { + vpfe_ccdc_update_raw_params(ccdc, &raw_params); + return 0; + } + + return -EINVAL; +} + +/* + * vpfe_ccdc_config_ycbcr() + * This function will configure CCDC for YCbCr video capture + */ +static void vpfe_ccdc_config_ycbcr(struct vpfe_ccdc *ccdc) +{ + struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); + struct ccdc_params_ycbcr *params = &ccdc->ccdc_cfg.ycbcr; + u32 syn_mode; + + vpfe_dbg(3, vpfe, "vpfe_ccdc_config_ycbcr:\n"); + /* + * first restore the CCDC registers to default values + * This is important since we assume default values to be set in + * a lot of registers that we didn't touch + */ + vpfe_ccdc_restore_defaults(ccdc); + + /* + * configure pixel format, frame format, configure video frame + * format, enable output to SDRAM, enable internal timing generator + * and 8bit pack mode + */ + syn_mode = (((params->pix_fmt & VPFE_SYN_MODE_INPMOD_MASK) << + VPFE_SYN_MODE_INPMOD_SHIFT) | + ((params->frm_fmt & VPFE_SYN_FLDMODE_MASK) << + VPFE_SYN_FLDMODE_SHIFT) | VPFE_VDHDEN_ENABLE | + VPFE_WEN_ENABLE | VPFE_DATA_PACK_ENABLE); + + /* setup BT.656 sync mode */ + if (params->bt656_enable) { + vpfe_reg_write(ccdc, VPFE_REC656IF_BT656_EN, VPFE_REC656IF); + + /* + * configure the FID, VD, HD pin polarity, + * fld,hd pol positive, vd negative, 8-bit data + */ + syn_mode |= VPFE_SYN_MODE_VD_POL_NEGATIVE; + if (ccdc->ccdc_cfg.if_type == VPFE_BT656_10BIT) + syn_mode |= VPFE_SYN_MODE_10BITS; + else + syn_mode |= VPFE_SYN_MODE_8BITS; + } else { + /* y/c external sync mode */ + syn_mode |= (((params->fid_pol & VPFE_FID_POL_MASK) << + VPFE_FID_POL_SHIFT) | + ((params->hd_pol & VPFE_HD_POL_MASK) << + VPFE_HD_POL_SHIFT) | + ((params->vd_pol & VPFE_VD_POL_MASK) << + VPFE_VD_POL_SHIFT)); + } + vpfe_reg_write(ccdc, syn_mode, VPFE_SYNMODE); + + /* configure video window */ + vpfe_ccdc_setwin(ccdc, ¶ms->win, + params->frm_fmt, params->bytesperpixel); + + /* + * configure the order of y cb cr in SDRAM, and disable latch + * internal register on vsync + */ + if (ccdc->ccdc_cfg.if_type == VPFE_BT656_10BIT) + vpfe_reg_write(ccdc, + (params->pix_order << VPFE_CCDCFG_Y8POS_SHIFT) | + VPFE_LATCH_ON_VSYNC_DISABLE | + VPFE_CCDCFG_BW656_10BIT, VPFE_CCDCFG); + else + vpfe_reg_write(ccdc, + (params->pix_order << VPFE_CCDCFG_Y8POS_SHIFT) | + VPFE_LATCH_ON_VSYNC_DISABLE, VPFE_CCDCFG); + + /* + * configure the horizontal line offset. This should be a + * on 32 byte boundary. So clear LSB 5 bits + */ + vpfe_reg_write(ccdc, params->bytesperline, VPFE_HSIZE_OFF); + + /* configure the memory line offset */ + if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) + /* two fields are interleaved in memory */ + vpfe_reg_write(ccdc, VPFE_SDOFST_FIELD_INTERLEAVED, + VPFE_SDOFST); +} + +static void +vpfe_ccdc_config_black_clamp(struct vpfe_ccdc *ccdc, + struct vpfe_ccdc_black_clamp *bclamp) +{ + u32 val; + + if (!bclamp->enable) { + /* configure DCSub */ + val = (bclamp->dc_sub) & VPFE_BLK_DC_SUB_MASK; + vpfe_reg_write(ccdc, val, VPFE_DCSUB); + vpfe_reg_write(ccdc, VPFE_CLAMP_DEFAULT_VAL, VPFE_CLAMP); + return; + } + /* + * Configure gain, Start pixel, No of line to be avg, + * No of pixel/line to be avg, & Enable the Black clamping + */ + val = ((bclamp->sgain & VPFE_BLK_SGAIN_MASK) | + ((bclamp->start_pixel & VPFE_BLK_ST_PXL_MASK) << + VPFE_BLK_ST_PXL_SHIFT) | + ((bclamp->sample_ln & VPFE_BLK_SAMPLE_LINE_MASK) << + VPFE_BLK_SAMPLE_LINE_SHIFT) | + ((bclamp->sample_pixel & VPFE_BLK_SAMPLE_LN_MASK) << + VPFE_BLK_SAMPLE_LN_SHIFT) | VPFE_BLK_CLAMP_ENABLE); + vpfe_reg_write(ccdc, val, VPFE_CLAMP); + /* If Black clamping is enable then make dcsub 0 */ + vpfe_reg_write(ccdc, VPFE_DCSUB_DEFAULT_VAL, VPFE_DCSUB); +} + +static void +vpfe_ccdc_config_black_compense(struct vpfe_ccdc *ccdc, + struct vpfe_ccdc_black_compensation *bcomp) +{ + u32 val; + + val = ((bcomp->b & VPFE_BLK_COMP_MASK) | + ((bcomp->gb & VPFE_BLK_COMP_MASK) << + VPFE_BLK_COMP_GB_COMP_SHIFT) | + ((bcomp->gr & VPFE_BLK_COMP_MASK) << + VPFE_BLK_COMP_GR_COMP_SHIFT) | + ((bcomp->r & VPFE_BLK_COMP_MASK) << + VPFE_BLK_COMP_R_COMP_SHIFT)); + vpfe_reg_write(ccdc, val, VPFE_BLKCMP); +} + +/* + * vpfe_ccdc_config_raw() + * This function will configure CCDC for Raw capture mode + */ +static void vpfe_ccdc_config_raw(struct vpfe_ccdc *ccdc) +{ + struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); + struct vpfe_ccdc_config_params_raw *config_params = + &ccdc->ccdc_cfg.bayer.config_params; + struct ccdc_params_raw *params = &ccdc->ccdc_cfg.bayer; + unsigned int syn_mode; + unsigned int val; + + vpfe_dbg(3, vpfe, "vpfe_ccdc_config_raw:\n"); + + /* Reset CCDC */ + vpfe_ccdc_restore_defaults(ccdc); + + /* Disable latching function registers on VSYNC */ + vpfe_reg_write(ccdc, VPFE_LATCH_ON_VSYNC_DISABLE, VPFE_CCDCFG); + + /* + * Configure the vertical sync polarity(SYN_MODE.VDPOL), + * horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity + * (SYN_MODE.FLDPOL), frame format(progressive or interlace), + * data size(SYNMODE.DATSIZ), &pixel format (Input mode), output + * SDRAM, enable internal timing generator + */ + syn_mode = (((params->vd_pol & VPFE_VD_POL_MASK) << VPFE_VD_POL_SHIFT) | + ((params->hd_pol & VPFE_HD_POL_MASK) << VPFE_HD_POL_SHIFT) | + ((params->fid_pol & VPFE_FID_POL_MASK) << + VPFE_FID_POL_SHIFT) | ((params->frm_fmt & + VPFE_FRM_FMT_MASK) << VPFE_FRM_FMT_SHIFT) | + ((config_params->data_sz & VPFE_DATA_SZ_MASK) << + VPFE_DATA_SZ_SHIFT) | ((params->pix_fmt & + VPFE_PIX_FMT_MASK) << VPFE_PIX_FMT_SHIFT) | + VPFE_WEN_ENABLE | VPFE_VDHDEN_ENABLE); + + /* Enable and configure aLaw register if needed */ + if (config_params->alaw.enable) { + val = ((config_params->alaw.gamma_wd & + VPFE_ALAW_GAMMA_WD_MASK) | VPFE_ALAW_ENABLE); + vpfe_reg_write(ccdc, val, VPFE_ALAW); + vpfe_dbg(3, vpfe, "\nWriting 0x%x to ALAW...\n", val); + } + + /* Configure video window */ + vpfe_ccdc_setwin(ccdc, ¶ms->win, params->frm_fmt, + params->bytesperpixel); + + /* Configure Black Clamp */ + vpfe_ccdc_config_black_clamp(ccdc, &config_params->blk_clamp); + + /* Configure Black level compensation */ + vpfe_ccdc_config_black_compense(ccdc, &config_params->blk_comp); + + /* If data size is 8 bit then pack the data */ + if ((config_params->data_sz == VPFE_CCDC_DATA_8BITS) || + config_params->alaw.enable) + syn_mode |= VPFE_DATA_PACK_ENABLE; + + /* + * Configure Horizontal offset register. If pack 8 is enabled then + * 1 pixel will take 1 byte + */ + vpfe_reg_write(ccdc, params->bytesperline, VPFE_HSIZE_OFF); + + vpfe_dbg(3, vpfe, "Writing %d (%x) to HSIZE_OFF\n", + params->bytesperline, params->bytesperline); + + /* Set value for SDOFST */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_enable) { + /* For interlace inverse mode */ + vpfe_reg_write(ccdc, VPFE_INTERLACED_IMAGE_INVERT, + VPFE_SDOFST); + } else { + /* For interlace non inverse mode */ + vpfe_reg_write(ccdc, VPFE_INTERLACED_NO_IMAGE_INVERT, + VPFE_SDOFST); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + vpfe_reg_write(ccdc, VPFE_PROGRESSIVE_NO_IMAGE_INVERT, + VPFE_SDOFST); + } + + vpfe_reg_write(ccdc, syn_mode, VPFE_SYNMODE); + + vpfe_reg_dump(ccdc); +} + +static inline int +vpfe_ccdc_set_buftype(struct vpfe_ccdc *ccdc, + enum ccdc_buftype buf_type) +{ + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc->ccdc_cfg.bayer.buf_type = buf_type; + else + ccdc->ccdc_cfg.ycbcr.buf_type = buf_type; + + return 0; +} + +static inline enum ccdc_buftype vpfe_ccdc_get_buftype(struct vpfe_ccdc *ccdc) +{ + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc->ccdc_cfg.bayer.buf_type; + + return ccdc->ccdc_cfg.ycbcr.buf_type; +} + +static int vpfe_ccdc_set_pixel_format(struct vpfe_ccdc *ccdc, u32 pixfmt) +{ + struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); + + vpfe_dbg(1, vpfe, "vpfe_ccdc_set_pixel_format: if_type: %d, pixfmt:%s\n", + ccdc->ccdc_cfg.if_type, print_fourcc(pixfmt)); + + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) { + ccdc->ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + /* + * Need to clear it in case it was left on + * after the last capture. + */ + ccdc->ccdc_cfg.bayer.config_params.alaw.enable = 0; + + switch (pixfmt) { + case V4L2_PIX_FMT_SBGGR8: + ccdc->ccdc_cfg.bayer.config_params.alaw.enable = 1; + break; + + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_RGB565X: + break; + + case V4L2_PIX_FMT_SBGGR16: + default: + return -EINVAL; + } + } else { + switch (pixfmt) { + case V4L2_PIX_FMT_YUYV: + ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + break; + + case V4L2_PIX_FMT_UYVY: + ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + break; + + default: + return -EINVAL; + } + } + + return 0; +} + +static u32 vpfe_ccdc_get_pixel_format(struct vpfe_ccdc *ccdc) +{ + u32 pixfmt; + + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) { + pixfmt = V4L2_PIX_FMT_YUYV; + } else { + if (ccdc->ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + + return pixfmt; +} + +static int +vpfe_ccdc_set_image_window(struct vpfe_ccdc *ccdc, + struct v4l2_rect *win, unsigned int bpp) +{ + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) { + ccdc->ccdc_cfg.bayer.win = *win; + ccdc->ccdc_cfg.bayer.bytesperpixel = bpp; + ccdc->ccdc_cfg.bayer.bytesperline = ALIGN(win->width * bpp, 32); + } else { + ccdc->ccdc_cfg.ycbcr.win = *win; + ccdc->ccdc_cfg.ycbcr.bytesperpixel = bpp; + ccdc->ccdc_cfg.ycbcr.bytesperline = ALIGN(win->width * bpp, 32); + } + + return 0; +} + +static inline void +vpfe_ccdc_get_image_window(struct vpfe_ccdc *ccdc, + struct v4l2_rect *win) +{ + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) + *win = ccdc->ccdc_cfg.bayer.win; + else + *win = ccdc->ccdc_cfg.ycbcr.win; +} + +static inline unsigned int vpfe_ccdc_get_line_length(struct vpfe_ccdc *ccdc) +{ + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc->ccdc_cfg.bayer.bytesperline; + + return ccdc->ccdc_cfg.ycbcr.bytesperline; +} + +static inline int +vpfe_ccdc_set_frame_format(struct vpfe_ccdc *ccdc, + enum ccdc_frmfmt frm_fmt) +{ + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc->ccdc_cfg.bayer.frm_fmt = frm_fmt; + else + ccdc->ccdc_cfg.ycbcr.frm_fmt = frm_fmt; + + return 0; +} + +static inline enum ccdc_frmfmt +vpfe_ccdc_get_frame_format(struct vpfe_ccdc *ccdc) +{ + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc->ccdc_cfg.bayer.frm_fmt; + + return ccdc->ccdc_cfg.ycbcr.frm_fmt; +} + +static inline int vpfe_ccdc_getfid(struct vpfe_ccdc *ccdc) +{ + return (vpfe_reg_read(ccdc, VPFE_SYNMODE) >> 15) & 1; +} + +static inline void vpfe_set_sdr_addr(struct vpfe_ccdc *ccdc, unsigned long addr) +{ + vpfe_reg_write(ccdc, addr & 0xffffffe0, VPFE_SDR_ADDR); +} + +static int vpfe_ccdc_set_hw_if_params(struct vpfe_ccdc *ccdc, + struct vpfe_hw_if_param *params) +{ + struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); + + ccdc->ccdc_cfg.if_type = params->if_type; + + switch (params->if_type) { + case VPFE_BT656: + case VPFE_YCBCR_SYNC_16: + case VPFE_YCBCR_SYNC_8: + case VPFE_BT656_10BIT: + ccdc->ccdc_cfg.ycbcr.vd_pol = params->vdpol; + ccdc->ccdc_cfg.ycbcr.hd_pol = params->hdpol; + break; + + case VPFE_RAW_BAYER: + ccdc->ccdc_cfg.bayer.vd_pol = params->vdpol; + ccdc->ccdc_cfg.bayer.hd_pol = params->hdpol; + if (params->bus_width == 10) + ccdc->ccdc_cfg.bayer.config_params.data_sz = + VPFE_CCDC_DATA_10BITS; + else + ccdc->ccdc_cfg.bayer.config_params.data_sz = + VPFE_CCDC_DATA_8BITS; + vpfe_dbg(1, vpfe, "params.bus_width: %d\n", + params->bus_width); + vpfe_dbg(1, vpfe, "config_params.data_sz: %d\n", + ccdc->ccdc_cfg.bayer.config_params.data_sz); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static void vpfe_clear_intr(struct vpfe_ccdc *ccdc, int vdint) +{ + unsigned int vpfe_int_status; + + vpfe_int_status = vpfe_reg_read(ccdc, VPFE_IRQ_STS); + + switch (vdint) { + /* VD0 interrupt */ + case VPFE_VDINT0: + vpfe_int_status &= ~VPFE_VDINT0; + vpfe_int_status |= VPFE_VDINT0; + break; + + /* VD1 interrupt */ + case VPFE_VDINT1: + vpfe_int_status &= ~VPFE_VDINT1; + vpfe_int_status |= VPFE_VDINT1; + break; + + /* VD2 interrupt */ + case VPFE_VDINT2: + vpfe_int_status &= ~VPFE_VDINT2; + vpfe_int_status |= VPFE_VDINT2; + break; + + /* Clear all interrupts */ + default: + vpfe_int_status &= ~(VPFE_VDINT0 | + VPFE_VDINT1 | + VPFE_VDINT2); + vpfe_int_status |= (VPFE_VDINT0 | + VPFE_VDINT1 | + VPFE_VDINT2); + break; + } + /* Clear specific VDINT from the status register */ + vpfe_reg_write(ccdc, vpfe_int_status, VPFE_IRQ_STS); + + vpfe_int_status = vpfe_reg_read(ccdc, VPFE_IRQ_STS); + + /* Acknowledge that we are done with all interrupts */ + vpfe_reg_write(ccdc, 1, VPFE_IRQ_EOI); +} + +static void vpfe_ccdc_config_defaults(struct vpfe_ccdc *ccdc) +{ + ccdc->ccdc_cfg.if_type = VPFE_RAW_BAYER; + + ccdc->ccdc_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT; + ccdc->ccdc_cfg.ycbcr.frm_fmt = CCDC_FRMFMT_INTERLACED; + ccdc->ccdc_cfg.ycbcr.fid_pol = VPFE_PINPOL_POSITIVE; + ccdc->ccdc_cfg.ycbcr.vd_pol = VPFE_PINPOL_POSITIVE; + ccdc->ccdc_cfg.ycbcr.hd_pol = VPFE_PINPOL_POSITIVE; + ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + ccdc->ccdc_cfg.ycbcr.buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED; + + ccdc->ccdc_cfg.ycbcr.win.left = 0; + ccdc->ccdc_cfg.ycbcr.win.top = 0; + ccdc->ccdc_cfg.ycbcr.win.width = 720; + ccdc->ccdc_cfg.ycbcr.win.height = 576; + ccdc->ccdc_cfg.ycbcr.bt656_enable = 1; + + ccdc->ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + ccdc->ccdc_cfg.bayer.frm_fmt = CCDC_FRMFMT_PROGRESSIVE; + ccdc->ccdc_cfg.bayer.fid_pol = VPFE_PINPOL_POSITIVE; + ccdc->ccdc_cfg.bayer.vd_pol = VPFE_PINPOL_POSITIVE; + ccdc->ccdc_cfg.bayer.hd_pol = VPFE_PINPOL_POSITIVE; + + ccdc->ccdc_cfg.bayer.win.left = 0; + ccdc->ccdc_cfg.bayer.win.top = 0; + ccdc->ccdc_cfg.bayer.win.width = 800; + ccdc->ccdc_cfg.bayer.win.height = 600; + ccdc->ccdc_cfg.bayer.config_params.data_sz = VPFE_CCDC_DATA_8BITS; + ccdc->ccdc_cfg.bayer.config_params.alaw.gamma_wd = + VPFE_CCDC_GAMMA_BITS_09_0; +} + +/* + * vpfe_get_ccdc_image_format - Get image parameters based on CCDC settings + */ +static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe, + struct v4l2_format *f) +{ + struct v4l2_rect image_win; + enum ccdc_buftype buf_type; + enum ccdc_frmfmt frm_fmt; + + memset(f, 0, sizeof(*f)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vpfe_ccdc_get_image_window(&vpfe->ccdc, &image_win); + f->fmt.pix.width = image_win.width; + f->fmt.pix.height = image_win.height; + f->fmt.pix.bytesperline = vpfe_ccdc_get_line_length(&vpfe->ccdc); + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height; + buf_type = vpfe_ccdc_get_buftype(&vpfe->ccdc); + f->fmt.pix.pixelformat = vpfe_ccdc_get_pixel_format(&vpfe->ccdc); + frm_fmt = vpfe_ccdc_get_frame_format(&vpfe->ccdc); + + if (frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + f->fmt.pix.field = V4L2_FIELD_NONE; + } else if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) { + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + } else if (buf_type == CCDC_BUFTYPE_FLD_SEPARATED) { + f->fmt.pix.field = V4L2_FIELD_SEQ_TB; + } else { + vpfe_err(vpfe, "Invalid buf_type\n"); + return -EINVAL; + } + } else { + vpfe_err(vpfe, "Invalid frm_fmt\n"); + return -EINVAL; + } + return 0; +} + +static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe) +{ + enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED; + int ret; + + vpfe_dbg(2, vpfe, "vpfe_config_ccdc_image_format\n"); + + vpfe_dbg(1, vpfe, "pixelformat: %s\n", + print_fourcc(vpfe->fmt.fmt.pix.pixelformat)); + + if (vpfe_ccdc_set_pixel_format(&vpfe->ccdc, + vpfe->fmt.fmt.pix.pixelformat) < 0) { + vpfe_err(vpfe, "couldn't set pix format in ccdc\n"); + return -EINVAL; + } + + /* configure the image window */ + vpfe_ccdc_set_image_window(&vpfe->ccdc, &vpfe->crop, vpfe->bpp); + + switch (vpfe->fmt.fmt.pix.field) { + case V4L2_FIELD_INTERLACED: + /* do nothing, since it is default */ + ret = vpfe_ccdc_set_buftype( + &vpfe->ccdc, + CCDC_BUFTYPE_FLD_INTERLEAVED); + break; + + case V4L2_FIELD_NONE: + frm_fmt = CCDC_FRMFMT_PROGRESSIVE; + /* buffer type only applicable for interlaced scan */ + break; + + case V4L2_FIELD_SEQ_TB: + ret = vpfe_ccdc_set_buftype( + &vpfe->ccdc, + CCDC_BUFTYPE_FLD_SEPARATED); + break; + + default: + return -EINVAL; + } + + if (ret) + return ret; + + return vpfe_ccdc_set_frame_format(&vpfe->ccdc, frm_fmt); +} + +/* + * vpfe_config_image_format() + * For a given standard, this functions sets up the default + * pix format & crop values in the vpfe device and ccdc. It first + * starts with defaults based values from the standard table. + * It then checks if sub device support g_mbus_fmt and then override the + * values based on that.Sets crop values to match with scan resolution + * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the + * values in ccdc + */ +static int vpfe_config_image_format(struct vpfe_device *vpfe, + v4l2_std_id std_id) +{ + struct v4l2_pix_format *pix = &vpfe->fmt.fmt.pix; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) { + if (vpfe_standards[i].std_id & std_id) { + vpfe->std_info.active_pixels = + vpfe_standards[i].width; + vpfe->std_info.active_lines = + vpfe_standards[i].height; + vpfe->std_info.frame_format = + vpfe_standards[i].frame_format; + vpfe->std_index = i; + + break; + } + } + + if (i == ARRAY_SIZE(vpfe_standards)) { + vpfe_err(vpfe, "standard not supported\n"); + return -EINVAL; + } + + vpfe->crop.top = vpfe->crop.left = 0; + vpfe->crop.width = vpfe->std_info.active_pixels; + vpfe->crop.height = vpfe->std_info.active_lines; + pix->width = vpfe->crop.width; + pix->height = vpfe->crop.height; + pix->pixelformat = V4L2_PIX_FMT_YUYV; + + /* first field and frame format based on standard frame format */ + if (vpfe->std_info.frame_format) + pix->field = V4L2_FIELD_INTERLACED; + else + pix->field = V4L2_FIELD_NONE; + + ret = __vpfe_get_format(vpfe, &vpfe->fmt, &vpfe->bpp); + if (ret) + return ret; + + /* Update the crop window based on found values */ + vpfe->crop.width = pix->width; + vpfe->crop.height = pix->height; + + return vpfe_config_ccdc_image_format(vpfe); +} + +static int vpfe_initialize_device(struct vpfe_device *vpfe) +{ + struct vpfe_subdev_info *sdinfo; + int ret; + + sdinfo = &vpfe->cfg->sub_devs[0]; + sdinfo->sd = vpfe->sd[0]; + vpfe->current_input = 0; + vpfe->std_index = 0; + /* Configure the default format information */ + ret = vpfe_config_image_format(vpfe, + vpfe_standards[vpfe->std_index].std_id); + if (ret) + return ret; + + pm_runtime_get_sync(vpfe->pdev); + + vpfe_config_enable(&vpfe->ccdc, 1); + + vpfe_ccdc_restore_defaults(&vpfe->ccdc); + + /* Clear all VPFE interrupts */ + vpfe_clear_intr(&vpfe->ccdc, -1); + + return ret; +} + +/* + * vpfe_release : This function is based on the vb2_fop_release + * helper function. + * It has been augmented to handle module power management, + * by disabling/enabling h/w module fcntl clock when necessary. + */ +static int vpfe_release(struct file *file) +{ + struct vpfe_device *vpfe = video_drvdata(file); + int ret; + + mutex_lock(&vpfe->lock); + + if (v4l2_fh_is_singular_file(file)) + vpfe_ccdc_close(&vpfe->ccdc, vpfe->pdev); + ret = _vb2_fop_release(file, NULL); + + mutex_unlock(&vpfe->lock); + + return ret; +} + +/* + * vpfe_open : This function is based on the v4l2_fh_open helper function. + * It has been augmented to handle module power management, + * by disabling/enabling h/w module fcntl clock when necessary. + */ +static int vpfe_open(struct file *file) +{ + struct vpfe_device *vpfe = video_drvdata(file); + int ret; + + mutex_lock(&vpfe->lock); + + ret = v4l2_fh_open(file); + if (ret) { + vpfe_err(vpfe, "v4l2_fh_open failed\n"); + goto unlock; + } + + if (!v4l2_fh_is_singular_file(file)) + goto unlock; + + if (vpfe_initialize_device(vpfe)) { + v4l2_fh_release(file); + ret = -ENODEV; + } + +unlock: + mutex_unlock(&vpfe->lock); + return ret; +} + +/** + * vpfe_schedule_next_buffer: set next buffer address for capture + * @vpfe : ptr to vpfe device + * + * This function will get next buffer from the dma queue and + * set the buffer address in the vpfe register for capture. + * the buffer is marked active + * + * Assumes caller is holding vpfe->dma_queue_lock already + */ +static inline void vpfe_schedule_next_buffer(struct vpfe_device *vpfe) +{ + vpfe->next_frm = list_entry(vpfe->dma_queue.next, + struct vpfe_cap_buffer, list); + list_del(&vpfe->next_frm->list); + + vpfe_set_sdr_addr(&vpfe->ccdc, + vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb, 0)); +} + +static inline void vpfe_schedule_bottom_field(struct vpfe_device *vpfe) +{ + unsigned long addr; + + addr = vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb, 0) + + vpfe->field_off; + + vpfe_set_sdr_addr(&vpfe->ccdc, addr); +} + +/* + * vpfe_process_buffer_complete: process a completed buffer + * @vpfe : ptr to vpfe device + * + * This function time stamp the buffer and mark it as DONE. It also + * wake up any process waiting on the QUEUE and set the next buffer + * as current + */ +static inline void vpfe_process_buffer_complete(struct vpfe_device *vpfe) +{ + v4l2_get_timestamp(&vpfe->cur_frm->vb.v4l2_buf.timestamp); + vpfe->cur_frm->vb.v4l2_buf.field = vpfe->fmt.fmt.pix.field; + vpfe->cur_frm->vb.v4l2_buf.sequence = vpfe->sequence++; + vb2_buffer_done(&vpfe->cur_frm->vb, VB2_BUF_STATE_DONE); + vpfe->cur_frm = vpfe->next_frm; +} + +/* + * vpfe_isr : ISR handler for vpfe capture (VINT0) + * @irq: irq number + * @dev_id: dev_id ptr + * + * It changes status of the captured buffer, takes next buffer from the queue + * and sets its address in VPFE registers + */ +static irqreturn_t vpfe_isr(int irq, void *dev) +{ + struct vpfe_device *vpfe = (struct vpfe_device *)dev; + enum v4l2_field field; + int intr_status; + int fid; + + intr_status = vpfe_reg_read(&vpfe->ccdc, VPFE_IRQ_STS); + + if (intr_status & VPFE_VDINT0) { + field = vpfe->fmt.fmt.pix.field; + + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + if (vpfe->cur_frm != vpfe->next_frm) + vpfe_process_buffer_complete(vpfe); + goto next_intr; + } + + /* interlaced or TB capture check which field + we are in hardware */ + fid = vpfe_ccdc_getfid(&vpfe->ccdc); + + /* switch the software maintained field id */ + vpfe->field ^= 1; + if (fid == vpfe->field) { + /* we are in-sync here,continue */ + if (fid == 0) { + /* + * One frame is just being captured. If the + * next frame is available, release the + * current frame and move on + */ + if (vpfe->cur_frm != vpfe->next_frm) + vpfe_process_buffer_complete(vpfe); + /* + * based on whether the two fields are stored + * interleave or separately in memory, + * reconfigure the CCDC memory address + */ + if (field == V4L2_FIELD_SEQ_TB) + vpfe_schedule_bottom_field(vpfe); + + goto next_intr; + } + /* + * if one field is just being captured configure + * the next frame get the next frame from the empty + * queue if no frame is available hold on to the + * current buffer + */ + spin_lock(&vpfe->dma_queue_lock); + if (!list_empty(&vpfe->dma_queue) && + vpfe->cur_frm == vpfe->next_frm) + vpfe_schedule_next_buffer(vpfe); + spin_unlock(&vpfe->dma_queue_lock); + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + vpfe->field = fid; + } + } + +next_intr: + if (intr_status & VPFE_VDINT1) { + spin_lock(&vpfe->dma_queue_lock); + if (vpfe->fmt.fmt.pix.field == V4L2_FIELD_NONE && + !list_empty(&vpfe->dma_queue) && + vpfe->cur_frm == vpfe->next_frm) + vpfe_schedule_next_buffer(vpfe); + spin_unlock(&vpfe->dma_queue_lock); + } + + vpfe_clear_intr(&vpfe->ccdc, intr_status); + + return IRQ_HANDLED; +} + +static inline void vpfe_detach_irq(struct vpfe_device *vpfe) +{ + unsigned int intr = VPFE_VDINT0; + enum ccdc_frmfmt frame_format; + + frame_format = vpfe_ccdc_get_frame_format(&vpfe->ccdc); + if (frame_format == CCDC_FRMFMT_PROGRESSIVE) + intr |= VPFE_VDINT1; + + vpfe_reg_write(&vpfe->ccdc, intr, VPFE_IRQ_EN_CLR); +} + +static inline void vpfe_attach_irq(struct vpfe_device *vpfe) +{ + unsigned int intr = VPFE_VDINT0; + enum ccdc_frmfmt frame_format; + + frame_format = vpfe_ccdc_get_frame_format(&vpfe->ccdc); + if (frame_format == CCDC_FRMFMT_PROGRESSIVE) + intr |= VPFE_VDINT1; + + vpfe_reg_write(&vpfe->ccdc, intr, VPFE_IRQ_EN_SET); +} + +static int vpfe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpfe_device *vpfe = video_drvdata(file); + + vpfe_dbg(2, vpfe, "vpfe_querycap\n"); + + strlcpy(cap->driver, VPFE_MODULE_NAME, sizeof(cap->driver)); + strlcpy(cap->card, "TI AM437x VPFE", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", vpfe->v4l2_dev.name); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +/* get the format set at output pad of the adjacent subdev */ +static int __vpfe_get_format(struct vpfe_device *vpfe, + struct v4l2_format *format, unsigned int *bpp) +{ + struct v4l2_mbus_framefmt mbus_fmt; + struct vpfe_subdev_info *sdinfo; + struct v4l2_subdev_format fmt; + int ret; + + sdinfo = vpfe->current_subdev; + if (!sdinfo->sd) + return -EINVAL; + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.pad = 0; + + ret = v4l2_subdev_call(sdinfo->sd, pad, get_fmt, NULL, &fmt); + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + if (!ret) { + v4l2_fill_pix_format(&format->fmt.pix, &fmt.format); + mbus_to_pix(vpfe, &fmt.format, &format->fmt.pix, bpp); + } else { + ret = v4l2_device_call_until_err(&vpfe->v4l2_dev, + sdinfo->grp_id, + video, g_mbus_fmt, + &mbus_fmt); + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + v4l2_fill_pix_format(&format->fmt.pix, &mbus_fmt); + mbus_to_pix(vpfe, &mbus_fmt, &format->fmt.pix, bpp); + } + + format->type = vpfe->fmt.type; + + vpfe_dbg(1, vpfe, + "%s size %dx%d (%s) bytesperline = %d, size = %d, bpp = %d\n", + __func__, format->fmt.pix.width, format->fmt.pix.height, + print_fourcc(format->fmt.pix.pixelformat), + format->fmt.pix.bytesperline, format->fmt.pix.sizeimage, *bpp); + + return 0; +} + +/* set the format at output pad of the adjacent subdev */ +static int __vpfe_set_format(struct vpfe_device *vpfe, + struct v4l2_format *format, unsigned int *bpp) +{ + struct v4l2_mbus_framefmt mbus_fmt; + struct vpfe_subdev_info *sdinfo; + struct v4l2_subdev_format fmt; + int ret; + + vpfe_dbg(2, vpfe, "__vpfe_set_format\n"); + + sdinfo = vpfe->current_subdev; + if (!sdinfo->sd) + return -EINVAL; + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.pad = 0; + + pix_to_mbus(vpfe, &format->fmt.pix, &fmt.format); + + ret = v4l2_subdev_call(sdinfo->sd, pad, set_fmt, NULL, &fmt); + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + if (!ret) { + v4l2_fill_pix_format(&format->fmt.pix, &fmt.format); + mbus_to_pix(vpfe, &fmt.format, &format->fmt.pix, bpp); + } else { + ret = v4l2_device_call_until_err(&vpfe->v4l2_dev, + sdinfo->grp_id, + video, s_mbus_fmt, + &mbus_fmt); + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + v4l2_fill_pix_format(&format->fmt.pix, &mbus_fmt); + mbus_to_pix(vpfe, &mbus_fmt, &format->fmt.pix, bpp); + } + + format->type = vpfe->fmt.type; + + vpfe_dbg(1, vpfe, + "%s size %dx%d (%s) bytesperline = %d, size = %d, bpp = %d\n", + __func__, format->fmt.pix.width, format->fmt.pix.height, + print_fourcc(format->fmt.pix.pixelformat), + format->fmt.pix.bytesperline, format->fmt.pix.sizeimage, *bpp); + + return 0; +} + +static int vpfe_g_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe = video_drvdata(file); + + vpfe_dbg(2, vpfe, "vpfe_g_fmt\n"); + + *fmt = vpfe->fmt; + + return 0; +} + +static int vpfe_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct vpfe_device *vpfe = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + struct vpfe_fmt *fmt = NULL; + unsigned int k; + + vpfe_dbg(2, vpfe, "vpfe_enum_format index:%d\n", + f->index); + + sdinfo = vpfe->current_subdev; + if (!sdinfo->sd) + return -EINVAL; + + if (f->index > ARRAY_SIZE(formats)) + return -EINVAL; + + for (k = 0; k < ARRAY_SIZE(formats); k++) { + if (formats[k].index == f->index) { + fmt = &formats[k]; + break; + } + } + if (!fmt) + return -EINVAL; + + strncpy(f->description, fmt->name, sizeof(f->description) - 1); + f->pixelformat = fmt->fourcc; + f->type = vpfe->fmt.type; + + vpfe_dbg(1, vpfe, "vpfe_enum_format: mbus index: %d code: %x pixelformat: %s [%s]\n", + f->index, fmt->code, print_fourcc(fmt->fourcc), fmt->name); + + return 0; +} + +static int vpfe_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe = video_drvdata(file); + unsigned int bpp; + + vpfe_dbg(2, vpfe, "vpfe_try_fmt\n"); + + return __vpfe_get_format(vpfe, fmt, &bpp); +} + +static int vpfe_s_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe = video_drvdata(file); + struct v4l2_format format; + unsigned int bpp; + int ret; + + vpfe_dbg(2, vpfe, "vpfe_s_fmt\n"); + + /* If streaming is started, return error */ + if (vb2_is_busy(&vpfe->buffer_queue)) { + vpfe_err(vpfe, "%s device busy\n", __func__); + return -EBUSY; + } + + ret = vpfe_try_fmt(file, priv, fmt); + if (ret) + return ret; + + + if (!cmp_v4l2_format(fmt, &format)) { + /* Sensor format is different from the requested format + * so we need to change it + */ + ret = __vpfe_set_format(vpfe, fmt, &bpp); + if (ret) + return ret; + } else /* Just make sure all of the fields are consistent */ + *fmt = format; + + /* First detach any IRQ if currently attached */ + vpfe_detach_irq(vpfe); + vpfe->fmt = *fmt; + vpfe->bpp = bpp; + + /* Update the crop window based on found values */ + vpfe->crop.width = fmt->fmt.pix.width; + vpfe->crop.height = fmt->fmt.pix.height; + + /* set image capture parameters in the ccdc */ + return vpfe_config_ccdc_image_format(vpfe); +} + +static int vpfe_enum_size(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct vpfe_device *vpfe = video_drvdata(file); + struct v4l2_subdev_frame_size_enum fse; + struct vpfe_subdev_info *sdinfo; + struct v4l2_mbus_framefmt mbus; + struct v4l2_pix_format pix; + struct vpfe_fmt *fmt; + int ret; + + vpfe_dbg(2, vpfe, "vpfe_enum_size\n"); + + /* check for valid format */ + fmt = find_format_by_pix(fsize->pixel_format); + if (!fmt) { + vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n", + fsize->pixel_format); + return -EINVAL; + } + + memset(fsize->reserved, 0x0, sizeof(fsize->reserved)); + + sdinfo = vpfe->current_subdev; + if (!sdinfo->sd) + return -EINVAL; + + memset(&pix, 0x0, sizeof(pix)); + /* Construct pix from parameter and use default for the rest */ + pix.pixelformat = fsize->pixel_format; + pix.width = 640; + pix.height = 480; + pix.colorspace = V4L2_COLORSPACE_SRGB; + pix.field = V4L2_FIELD_NONE; + pix_to_mbus(vpfe, &pix, &mbus); + + memset(&fse, 0x0, sizeof(fse)); + fse.index = fsize->index; + fse.pad = 0; + fse.code = mbus.code; + ret = v4l2_subdev_call(sdinfo->sd, pad, enum_frame_size, NULL, &fse); + if (ret) + return -EINVAL; + + vpfe_dbg(1, vpfe, "vpfe_enum_size: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", + fse.index, fse.code, fse.min_width, fse.max_width, + fse.min_height, fse.max_height); + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.max_width; + fsize->discrete.height = fse.max_height; + + vpfe_dbg(1, vpfe, "vpfe_enum_size: index: %d pixformat: %s size: %dx%d\n", + fsize->index, print_fourcc(fsize->pixel_format), + fsize->discrete.width, fsize->discrete.height); + + return 0; +} + +/* + * vpfe_get_subdev_input_index - Get subdev index and subdev input index for a + * given app input index + */ +static int +vpfe_get_subdev_input_index(struct vpfe_device *vpfe, + int *subdev_index, + int *subdev_input_index, + int app_input_index) +{ + struct vpfe_config *cfg = vpfe->cfg; + struct vpfe_subdev_info *sdinfo; + int i, j = 0; + + for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) { + sdinfo = &cfg->sub_devs[i]; + if (app_input_index < (j + 1)) { + *subdev_index = i; + *subdev_input_index = app_input_index - j; + return 0; + } + j++; + } + return -EINVAL; +} + +/* + * vpfe_get_app_input - Get app input index for a given subdev input index + * driver stores the input index of the current sub device and translate it + * when application request the current input + */ +static int vpfe_get_app_input_index(struct vpfe_device *vpfe, + int *app_input_index) +{ + struct vpfe_config *cfg = vpfe->cfg; + struct vpfe_subdev_info *sdinfo; + int i, j = 0; + + for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) { + sdinfo = &cfg->sub_devs[i]; + if (!strcmp(sdinfo->name, vpfe->current_subdev->name)) { + if (vpfe->current_input >= 1) + return -1; + *app_input_index = j + vpfe->current_input; + return 0; + } + j++; + } + return -EINVAL; +} + +static int vpfe_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct vpfe_device *vpfe = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int subdev, index; + + vpfe_dbg(2, vpfe, "vpfe_enum_input\n"); + + if (vpfe_get_subdev_input_index(vpfe, &subdev, &index, + inp->index) < 0) { + vpfe_dbg(1, vpfe, + "input information not found for the subdev\n"); + return -EINVAL; + } + sdinfo = &vpfe->cfg->sub_devs[subdev]; + *inp = sdinfo->inputs[index]; + + return 0; +} + +static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct vpfe_device *vpfe = video_drvdata(file); + + vpfe_dbg(2, vpfe, "vpfe_g_input\n"); + + return vpfe_get_app_input_index(vpfe, index); +} + +/* Assumes caller is holding vpfe_dev->lock */ +static int vpfe_set_input(struct vpfe_device *vpfe, unsigned int index) +{ + int subdev_index = 0, inp_index = 0; + struct vpfe_subdev_info *sdinfo; + struct vpfe_route *route; + u32 input, output; + int ret; + + vpfe_dbg(2, vpfe, "vpfe_set_input: index: %d\n", index); + + /* If streaming is started, return error */ + if (vb2_is_busy(&vpfe->buffer_queue)) { + vpfe_err(vpfe, "%s device busy\n", __func__); + return -EBUSY; + } + ret = vpfe_get_subdev_input_index(vpfe, + &subdev_index, + &inp_index, + index); + if (ret < 0) { + vpfe_err(vpfe, "invalid input index: %d\n", index); + goto get_out; + } + + sdinfo = &vpfe->cfg->sub_devs[subdev_index]; + sdinfo->sd = vpfe->sd[subdev_index]; + route = &sdinfo->routes[inp_index]; + if (route && sdinfo->can_route) { + input = route->input; + output = route->output; + if (sdinfo->sd) { + ret = v4l2_subdev_call(sdinfo->sd, video, + s_routing, input, output, 0); + if (ret) { + vpfe_err(vpfe, "s_routing failed\n"); + ret = -EINVAL; + goto get_out; + } + } + + } + + vpfe->current_subdev = sdinfo; + if (sdinfo->sd) + vpfe->v4l2_dev.ctrl_handler = sdinfo->sd->ctrl_handler; + vpfe->current_input = index; + vpfe->std_index = 0; + + /* set the bus/interface parameter for the sub device in ccdc */ + ret = vpfe_ccdc_set_hw_if_params(&vpfe->ccdc, &sdinfo->vpfe_param); + if (ret) + return ret; + + /* set the default image parameters in the device */ + return vpfe_config_image_format(vpfe, + vpfe_standards[vpfe->std_index].std_id); + +get_out: + return ret; +} + +static int vpfe_s_input(struct file *file, void *priv, unsigned int index) +{ + struct vpfe_device *vpfe = video_drvdata(file); + + vpfe_dbg(2, vpfe, + "vpfe_s_input: index: %d\n", index); + + return vpfe_set_input(vpfe, index); +} + +static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + + vpfe_dbg(2, vpfe, "vpfe_querystd\n"); + + sdinfo = vpfe->current_subdev; + if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD)) + return -ENODATA; + + /* Call querystd function of decoder device */ + return v4l2_device_call_until_err(&vpfe->v4l2_dev, sdinfo->grp_id, + video, querystd, std_id); +} + +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id) +{ + struct vpfe_device *vpfe = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int ret; + + vpfe_dbg(2, vpfe, "vpfe_s_std\n"); + + sdinfo = vpfe->current_subdev; + if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD)) + return -ENODATA; + + /* If streaming is started, return error */ + if (vb2_is_busy(&vpfe->buffer_queue)) { + vpfe_err(vpfe, "%s device busy\n", __func__); + ret = -EBUSY; + return ret; + } + + ret = v4l2_device_call_until_err(&vpfe->v4l2_dev, sdinfo->grp_id, + video, s_std, std_id); + if (ret < 0) { + vpfe_err(vpfe, "Failed to set standard\n"); + return ret; + } + ret = vpfe_config_image_format(vpfe, std_id); + + return ret; +} + +static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + + vpfe_dbg(2, vpfe, "vpfe_g_std\n"); + + sdinfo = vpfe->current_subdev; + if (sdinfo->inputs[0].capabilities != V4L2_IN_CAP_STD) + return -ENODATA; + + *std_id = vpfe_standards[vpfe->std_index].std_id; + + return 0; +} + +/* + * vpfe_calculate_offsets : This function calculates buffers offset + * for top and bottom field + */ +static void vpfe_calculate_offsets(struct vpfe_device *vpfe) +{ + struct v4l2_rect image_win; + + vpfe_dbg(2, vpfe, "vpfe_calculate_offsets\n"); + + vpfe_ccdc_get_image_window(&vpfe->ccdc, &image_win); + vpfe->field_off = image_win.height * image_win.width; +} + +/* + * vpfe_queue_setup - Callback function for buffer setup. + * @vq: vb2_queue ptr + * @fmt: v4l2 format + * @nbuffers: ptr to number of buffers requested by application + * @nplanes:: contains number of distinct video planes needed to hold a frame + * @sizes[]: contains the size (in bytes) of each plane. + * @alloc_ctxs: ptr to allocation context + * + * This callback function is called when reqbuf() is called to adjust + * the buffer count and buffer size + */ +static int vpfe_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct vpfe_device *vpfe = vb2_get_drv_priv(vq); + + if (fmt && fmt->fmt.pix.sizeimage < vpfe->fmt.fmt.pix.sizeimage) + return -EINVAL; + + if (vq->num_buffers + *nbuffers < 3) + *nbuffers = 3 - vq->num_buffers; + + *nplanes = 1; + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : vpfe->fmt.fmt.pix.sizeimage; + alloc_ctxs[0] = vpfe->alloc_ctx; + + vpfe_dbg(1, vpfe, + "nbuffers=%d, size=%u\n", *nbuffers, sizes[0]); + + /* Calculate field offset */ + vpfe_calculate_offsets(vpfe); + + return 0; +} + +/* + * vpfe_buffer_prepare : callback function for buffer prepare + * @vb: ptr to vb2_buffer + * + * This is the callback function for buffer prepare when vb2_qbuf() + * function is called. The buffer is prepared and user space virtual address + * or user address is converted into physical address + */ +static int vpfe_buffer_prepare(struct vb2_buffer *vb) +{ + struct vpfe_device *vpfe = vb2_get_drv_priv(vb->vb2_queue); + + vb2_set_plane_payload(vb, 0, vpfe->fmt.fmt.pix.sizeimage); + + if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) + return -EINVAL; + + vb->v4l2_buf.field = vpfe->fmt.fmt.pix.field; + + return 0; +} + +/* + * vpfe_buffer_queue : Callback function to add buffer to DMA queue + * @vb: ptr to vb2_buffer + */ +static void vpfe_buffer_queue(struct vb2_buffer *vb) +{ + struct vpfe_device *vpfe = vb2_get_drv_priv(vb->vb2_queue); + struct vpfe_cap_buffer *buf = to_vpfe_buffer(vb); + unsigned long flags = 0; + + /* add the buffer to the DMA queue */ + spin_lock_irqsave(&vpfe->dma_queue_lock, flags); + list_add_tail(&buf->list, &vpfe->dma_queue); + spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags); +} + +/* + * vpfe_start_streaming : Starts the DMA engine for streaming + * @vb: ptr to vb2_buffer + * @count: number of buffers + */ +static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct vpfe_device *vpfe = vb2_get_drv_priv(vq); + struct vpfe_cap_buffer *buf, *tmp; + struct vpfe_subdev_info *sdinfo; + unsigned long flags; + unsigned long addr; + int ret; + + spin_lock_irqsave(&vpfe->dma_queue_lock, flags); + + vpfe->field = 0; + vpfe->sequence = 0; + + sdinfo = vpfe->current_subdev; + + vpfe_attach_irq(vpfe); + + if (vpfe->ccdc.ccdc_cfg.if_type == VPFE_RAW_BAYER) + vpfe_ccdc_config_raw(&vpfe->ccdc); + else + vpfe_ccdc_config_ycbcr(&vpfe->ccdc); + + /* Get the next frame from the buffer queue */ + vpfe->next_frm = list_entry(vpfe->dma_queue.next, + struct vpfe_cap_buffer, list); + vpfe->cur_frm = vpfe->next_frm; + /* Remove buffer from the buffer queue */ + list_del(&vpfe->cur_frm->list); + spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags); + + addr = vb2_dma_contig_plane_dma_addr(&vpfe->cur_frm->vb, 0); + + vpfe_set_sdr_addr(&vpfe->ccdc, (unsigned long)(addr)); + + vpfe_pcr_enable(&vpfe->ccdc, 1); + + ret = v4l2_subdev_call(sdinfo->sd, video, s_stream, 1); + if (ret < 0) { + vpfe_err(vpfe, "Error in attaching interrupt handle\n"); + goto err; + } + + return 0; + +err: + list_for_each_entry_safe(buf, tmp, &vpfe->dma_queue, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + } + spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags); + + return ret; +} + +/* + * vpfe_stop_streaming : Stop the DMA engine + * @vq: ptr to vb2_queue + * + * This callback stops the DMA engine and any remaining buffers + * in the DMA queue are released. + */ +static void vpfe_stop_streaming(struct vb2_queue *vq) +{ + struct vpfe_device *vpfe = vb2_get_drv_priv(vq); + struct vpfe_subdev_info *sdinfo; + unsigned long flags; + int ret; + + vpfe_pcr_enable(&vpfe->ccdc, 0); + + vpfe_detach_irq(vpfe); + + sdinfo = vpfe->current_subdev; + ret = v4l2_subdev_call(sdinfo->sd, video, s_stream, 0); + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) + vpfe_dbg(1, vpfe, "stream off failed in subdev\n"); + + /* release all active buffers */ + spin_lock_irqsave(&vpfe->dma_queue_lock, flags); + if (vpfe->cur_frm == vpfe->next_frm) { + vb2_buffer_done(&vpfe->cur_frm->vb, VB2_BUF_STATE_ERROR); + } else { + if (vpfe->cur_frm != NULL) + vb2_buffer_done(&vpfe->cur_frm->vb, + VB2_BUF_STATE_ERROR); + if (vpfe->next_frm != NULL) + vb2_buffer_done(&vpfe->next_frm->vb, + VB2_BUF_STATE_ERROR); + } + + while (!list_empty(&vpfe->dma_queue)) { + vpfe->next_frm = list_entry(vpfe->dma_queue.next, + struct vpfe_cap_buffer, list); + list_del(&vpfe->next_frm->list); + vb2_buffer_done(&vpfe->next_frm->vb, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags); +} + +static int vpfe_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *crop) +{ + struct vpfe_device *vpfe = video_drvdata(file); + + vpfe_dbg(2, vpfe, "vpfe_cropcap\n"); + + if (vpfe->std_index >= ARRAY_SIZE(vpfe_standards)) + return -EINVAL; + + memset(crop, 0, sizeof(struct v4l2_cropcap)); + + crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + crop->defrect.width = vpfe_standards[vpfe->std_index].width; + crop->bounds.width = crop->defrect.width; + crop->defrect.height = vpfe_standards[vpfe->std_index].height; + crop->bounds.height = crop->defrect.height; + crop->pixelaspect = vpfe_standards[vpfe->std_index].pixelaspect; + + return 0; +} + +static int +vpfe_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpfe_device *vpfe = video_drvdata(file); + + switch (s->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + s->r.left = s->r.top = 0; + s->r.width = vpfe->crop.width; + s->r.height = vpfe->crop.height; + break; + + case V4L2_SEL_TGT_CROP: + s->r = vpfe->crop; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) +{ + if (a->left < b->left || a->top < b->top) + return 0; + + if (a->left + a->width > b->left + b->width) + return 0; + + if (a->top + a->height > b->top + b->height) + return 0; + + return 1; +} + +static int +vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpfe_device *vpfe = video_drvdata(file); + struct v4l2_rect cr = vpfe->crop; + struct v4l2_rect r = s->r; + + /* If streaming is started, return error */ + if (vb2_is_busy(&vpfe->buffer_queue)) { + vpfe_err(vpfe, "%s device busy\n", __func__); + return -EBUSY; + } + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + s->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + v4l_bound_align_image(&r.width, 0, cr.width, 0, + &r.height, 0, cr.height, 0, 0); + + r.left = clamp_t(unsigned int, r.left, 0, cr.width - r.width); + r.top = clamp_t(unsigned int, r.top, 0, cr.height - r.height); + + if (s->flags & V4L2_SEL_FLAG_LE && !enclosed_rectangle(&r, &s->r)) + return -ERANGE; + + if (s->flags & V4L2_SEL_FLAG_GE && !enclosed_rectangle(&s->r, &r)) + return -ERANGE; + + s->r = vpfe->crop = r; + + vpfe_ccdc_set_image_window(&vpfe->ccdc, &r, vpfe->bpp); + vpfe->fmt.fmt.pix.width = r.width; + vpfe->fmt.fmt.pix.height = r.height; + vpfe->fmt.fmt.pix.bytesperline = vpfe_ccdc_get_line_length(&vpfe->ccdc); + vpfe->fmt.fmt.pix.sizeimage = vpfe->fmt.fmt.pix.bytesperline * + vpfe->fmt.fmt.pix.height; + + vpfe_dbg(1, vpfe, "cropped (%d,%d)/%dx%d of %dx%d\n", + r.left, r.top, r.width, r.height, cr.width, cr.height); + + return 0; +} + +static long vpfe_ioctl_default(struct file *file, void *priv, + bool valid_prio, unsigned int cmd, void *param) +{ + struct vpfe_device *vpfe = video_drvdata(file); + int ret; + + vpfe_dbg(2, vpfe, "vpfe_ioctl_default\n"); + + if (!valid_prio) { + vpfe_err(vpfe, "%s device busy\n", __func__); + return -EBUSY; + } + + /* If streaming is started, return error */ + if (vb2_is_busy(&vpfe->buffer_queue)) { + vpfe_err(vpfe, "%s device busy\n", __func__); + return -EBUSY; + } + + switch (cmd) { + case VIDIOC_AM437X_CCDC_CFG: + ret = vpfe_ccdc_set_params(&vpfe->ccdc, param); + if (ret) { + vpfe_dbg(2, vpfe, + "Error setting parameters in CCDC\n"); + return ret; + } + ret = vpfe_get_ccdc_image_format(vpfe, + &vpfe->fmt); + if (ret < 0) { + vpfe_dbg(2, vpfe, + "Invalid image format at CCDC\n"); + return ret; + } + break; + + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +static const struct vb2_ops vpfe_video_qops = { + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .queue_setup = vpfe_queue_setup, + .buf_prepare = vpfe_buffer_prepare, + .buf_queue = vpfe_buffer_queue, + .start_streaming = vpfe_start_streaming, + .stop_streaming = vpfe_stop_streaming, +}; + +/* vpfe capture driver file operations */ +static const struct v4l2_file_operations vpfe_fops = { + .owner = THIS_MODULE, + .open = vpfe_open, + .release = vpfe_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +/* vpfe capture ioctl operations */ +static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { + .vidioc_querycap = vpfe_querycap, + .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt, + .vidioc_g_fmt_vid_cap = vpfe_g_fmt, + .vidioc_s_fmt_vid_cap = vpfe_s_fmt, + .vidioc_try_fmt_vid_cap = vpfe_try_fmt, + + .vidioc_enum_framesizes = vpfe_enum_size, + + .vidioc_enum_input = vpfe_enum_input, + .vidioc_g_input = vpfe_g_input, + .vidioc_s_input = vpfe_s_input, + + .vidioc_querystd = vpfe_querystd, + .vidioc_s_std = vpfe_s_std, + .vidioc_g_std = vpfe_g_std, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + + .vidioc_cropcap = vpfe_cropcap, + .vidioc_g_selection = vpfe_g_selection, + .vidioc_s_selection = vpfe_s_selection, + + .vidioc_default = vpfe_ioctl_default, +}; + +static int +vpfe_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct vpfe_device *vpfe = container_of(notifier->v4l2_dev, + struct vpfe_device, v4l2_dev); + struct v4l2_subdev_mbus_code_enum mbus_code; + struct vpfe_subdev_info *sdinfo; + bool found = false; + int i, j; + + vpfe_dbg(1, vpfe, "vpfe_async_bound\n"); + + for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) { + sdinfo = &vpfe->cfg->sub_devs[i]; + + if (!strcmp(sdinfo->name, subdev->name)) { + vpfe->sd[i] = subdev; + vpfe_info(vpfe, + "v4l2 sub device %s registered\n", + subdev->name); + vpfe->sd[i]->grp_id = + sdinfo->grp_id; + /* update tvnorms from the sub devices */ + for (j = 0; j < 1; j++) + vpfe->video_dev->tvnorms |= + sdinfo->inputs[j].std; + + found = true; + break; + } + } + + if (!found) { + vpfe_info(vpfe, "sub device (%s) not matched\n", subdev->name); + return -EINVAL; + } + + /* setup the supported formats & indexes */ + for (j = 0, i = 0; ; ++j) { + struct vpfe_fmt *fmt; + int ret; + + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index = j; + ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, + NULL, &mbus_code); + if (ret) + break; + + fmt = find_format_by_code(mbus_code.code); + if (!fmt) + continue; + + fmt->supported = true; + fmt->index = i++; + } + + return 0; +} + +static int vpfe_probe_complete(struct vpfe_device *vpfe) +{ + struct video_device *vdev; + struct vb2_queue *q; + int err; + + spin_lock_init(&vpfe->dma_queue_lock); + mutex_init(&vpfe->lock); + + vpfe->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + /* set first sub device as current one */ + vpfe->current_subdev = &vpfe->cfg->sub_devs[0]; + vpfe->v4l2_dev.ctrl_handler = vpfe->sd[0]->ctrl_handler; + + err = vpfe_set_input(vpfe, 0); + if (err) + goto probe_out; + + /* Initialize videobuf2 queue as per the buffer type */ + vpfe->alloc_ctx = vb2_dma_contig_init_ctx(vpfe->pdev); + if (IS_ERR(vpfe->alloc_ctx)) { + vpfe_err(vpfe, "Failed to get the context\n"); + err = PTR_ERR(vpfe->alloc_ctx); + goto probe_out; + } + + q = &vpfe->buffer_queue; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + q->drv_priv = vpfe; + q->ops = &vpfe_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct vpfe_cap_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &vpfe->lock; + q->min_buffers_needed = 1; + + err = vb2_queue_init(q); + if (err) { + vpfe_err(vpfe, "vb2_queue_init() failed\n"); + vb2_dma_contig_cleanup_ctx(vpfe->alloc_ctx); + goto probe_out; + } + + INIT_LIST_HEAD(&vpfe->dma_queue); + + vdev = vpfe->video_dev; + strlcpy(vdev->name, VPFE_MODULE_NAME, sizeof(vdev->name)); + vdev->release = video_device_release; + vdev->fops = &vpfe_fops; + vdev->ioctl_ops = &vpfe_ioctl_ops; + vdev->v4l2_dev = &vpfe->v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + vdev->queue = q; + vdev->lock = &vpfe->lock; + video_set_drvdata(vdev, vpfe); + err = video_register_device(vpfe->video_dev, VFL_TYPE_GRABBER, -1); + if (err) { + vpfe_err(vpfe, + "Unable to register video device.\n"); + goto probe_out; + } + + return 0; + +probe_out: + v4l2_device_unregister(&vpfe->v4l2_dev); + return err; +} + +static int vpfe_async_complete(struct v4l2_async_notifier *notifier) +{ + struct vpfe_device *vpfe = container_of(notifier->v4l2_dev, + struct vpfe_device, v4l2_dev); + + return vpfe_probe_complete(vpfe); +} + +static struct vpfe_config * +vpfe_get_pdata(struct platform_device *pdev) +{ + struct device_node *endpoint = NULL, *rem = NULL; + struct v4l2_of_endpoint bus_cfg; + struct vpfe_subdev_info *sdinfo; + struct vpfe_config *pdata; + unsigned int flags; + unsigned int i; + int err; + + dev_dbg(&pdev->dev, "vpfe_get_pdata\n"); + + if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node) + return pdev->dev.platform_data; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + for (i = 0; ; i++) { + endpoint = of_graph_get_next_endpoint(pdev->dev.of_node, + endpoint); + if (!endpoint) + break; + + sdinfo = &pdata->sub_devs[i]; + sdinfo->grp_id = 0; + + /* we only support camera */ + sdinfo->inputs[0].index = i; + strcpy(sdinfo->inputs[0].name, "Camera"); + sdinfo->inputs[0].type = V4L2_INPUT_TYPE_CAMERA; + sdinfo->inputs[0].std = V4L2_STD_ALL; + sdinfo->inputs[0].capabilities = V4L2_IN_CAP_STD; + + sdinfo->can_route = 0; + sdinfo->routes = NULL; + + of_property_read_u32(endpoint, "ti,am437x-vpfe-interface", + &sdinfo->vpfe_param.if_type); + if (sdinfo->vpfe_param.if_type < 0 || + sdinfo->vpfe_param.if_type > 4) { + sdinfo->vpfe_param.if_type = VPFE_RAW_BAYER; + } + + err = v4l2_of_parse_endpoint(endpoint, &bus_cfg); + if (err) { + dev_err(&pdev->dev, "Could not parse the endpoint\n"); + goto done; + } + + sdinfo->vpfe_param.bus_width = bus_cfg.bus.parallel.bus_width; + + if (sdinfo->vpfe_param.bus_width < 8 || + sdinfo->vpfe_param.bus_width > 16) { + dev_err(&pdev->dev, "Invalid bus width.\n"); + goto done; + } + + flags = bus_cfg.bus.parallel.flags; + + if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + sdinfo->vpfe_param.hdpol = 1; + + if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) + sdinfo->vpfe_param.vdpol = 1; + + rem = of_graph_get_remote_port_parent(endpoint); + if (!rem) { + dev_err(&pdev->dev, "Remote device at %s not found\n", + endpoint->full_name); + goto done; + } + + strncpy(sdinfo->name, rem->name, sizeof(sdinfo->name)); + + pdata->asd[i] = devm_kzalloc(&pdev->dev, + sizeof(struct v4l2_async_subdev), + GFP_KERNEL); + pdata->asd[i]->match_type = V4L2_ASYNC_MATCH_OF; + pdata->asd[i]->match.of.node = rem; + of_node_put(endpoint); + of_node_put(rem); + } + + of_node_put(endpoint); + return pdata; + +done: + of_node_put(endpoint); + of_node_put(rem); + return NULL; +} + +/* + * vpfe_probe : This function creates device entries by register + * itself to the V4L2 driver and initializes fields of each + * device objects + */ +static int vpfe_probe(struct platform_device *pdev) +{ + struct vpfe_config *vpfe_cfg = vpfe_get_pdata(pdev); + struct vpfe_device *vpfe; + struct vpfe_ccdc *ccdc; + struct resource *res; + int ret; + + if (!vpfe_cfg) { + dev_err(&pdev->dev, "No platform data\n"); + return -EINVAL; + } + + vpfe = devm_kzalloc(&pdev->dev, sizeof(*vpfe), GFP_KERNEL); + if (!vpfe) + return -ENOMEM; + + vpfe->pdev = &pdev->dev; + vpfe->cfg = vpfe_cfg; + ccdc = &vpfe->ccdc; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ccdc->ccdc_cfg.base_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ccdc->ccdc_cfg.base_addr)) + return PTR_ERR(ccdc->ccdc_cfg.base_addr); + + vpfe->irq = platform_get_irq(pdev, 0); + if (vpfe->irq <= 0) { + dev_err(&pdev->dev, "No IRQ resource\n"); + return -ENODEV; + } + + ret = devm_request_irq(vpfe->pdev, vpfe->irq, vpfe_isr, 0, + "vpfe_capture0", vpfe); + if (ret) { + dev_err(&pdev->dev, "Unable to request interrupt\n"); + return -EINVAL; + } + + vpfe->video_dev = video_device_alloc(); + if (!vpfe->video_dev) { + dev_err(&pdev->dev, "Unable to allocate video device\n"); + return -ENOMEM; + } + + ret = v4l2_device_register(&pdev->dev, &vpfe->v4l2_dev); + if (ret) { + vpfe_err(vpfe, + "Unable to register v4l2 device.\n"); + goto probe_out_video_release; + } + + /* set the driver data in platform device */ + platform_set_drvdata(pdev, vpfe); + /* Enabling module functional clock */ + pm_runtime_enable(&pdev->dev); + + /* for now just enable it here instead of waiting for the open */ + pm_runtime_get_sync(&pdev->dev); + + vpfe_ccdc_config_defaults(ccdc); + + pm_runtime_put_sync(&pdev->dev); + + vpfe->sd = devm_kzalloc(&pdev->dev, sizeof(struct v4l2_subdev *) * + ARRAY_SIZE(vpfe->cfg->asd), GFP_KERNEL); + if (!vpfe->sd) { + ret = -ENOMEM; + goto probe_out_v4l2_unregister; + } + + vpfe->notifier.subdevs = vpfe->cfg->asd; + vpfe->notifier.num_subdevs = ARRAY_SIZE(vpfe->cfg->asd); + vpfe->notifier.bound = vpfe_async_bound; + vpfe->notifier.complete = vpfe_async_complete; + ret = v4l2_async_notifier_register(&vpfe->v4l2_dev, + &vpfe->notifier); + if (ret) { + vpfe_err(vpfe, "Error registering async notifier\n"); + ret = -EINVAL; + goto probe_out_v4l2_unregister; + } + + return 0; + +probe_out_v4l2_unregister: + v4l2_device_unregister(&vpfe->v4l2_dev); +probe_out_video_release: + if (!video_is_registered(vpfe->video_dev)) + video_device_release(vpfe->video_dev); + return ret; +} + +/* + * vpfe_remove : It un-register device from V4L2 driver + */ +static int vpfe_remove(struct platform_device *pdev) +{ + struct vpfe_device *vpfe = platform_get_drvdata(pdev); + + vpfe_dbg(2, vpfe, "vpfe_remove\n"); + + pm_runtime_disable(&pdev->dev); + + v4l2_async_notifier_unregister(&vpfe->notifier); + v4l2_device_unregister(&vpfe->v4l2_dev); + video_unregister_device(vpfe->video_dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP + +static void vpfe_save_context(struct vpfe_ccdc *ccdc) +{ + ccdc->ccdc_ctx[VPFE_PCR >> 2] = vpfe_reg_read(ccdc, VPFE_PCR); + ccdc->ccdc_ctx[VPFE_SYNMODE >> 2] = vpfe_reg_read(ccdc, VPFE_SYNMODE); + ccdc->ccdc_ctx[VPFE_SDOFST >> 2] = vpfe_reg_read(ccdc, VPFE_SDOFST); + ccdc->ccdc_ctx[VPFE_SDR_ADDR >> 2] = vpfe_reg_read(ccdc, VPFE_SDR_ADDR); + ccdc->ccdc_ctx[VPFE_CLAMP >> 2] = vpfe_reg_read(ccdc, VPFE_CLAMP); + ccdc->ccdc_ctx[VPFE_DCSUB >> 2] = vpfe_reg_read(ccdc, VPFE_DCSUB); + ccdc->ccdc_ctx[VPFE_COLPTN >> 2] = vpfe_reg_read(ccdc, VPFE_COLPTN); + ccdc->ccdc_ctx[VPFE_BLKCMP >> 2] = vpfe_reg_read(ccdc, VPFE_BLKCMP); + ccdc->ccdc_ctx[VPFE_VDINT >> 2] = vpfe_reg_read(ccdc, VPFE_VDINT); + ccdc->ccdc_ctx[VPFE_ALAW >> 2] = vpfe_reg_read(ccdc, VPFE_ALAW); + ccdc->ccdc_ctx[VPFE_REC656IF >> 2] = vpfe_reg_read(ccdc, VPFE_REC656IF); + ccdc->ccdc_ctx[VPFE_CCDCFG >> 2] = vpfe_reg_read(ccdc, VPFE_CCDCFG); + ccdc->ccdc_ctx[VPFE_CULLING >> 2] = vpfe_reg_read(ccdc, VPFE_CULLING); + ccdc->ccdc_ctx[VPFE_HD_VD_WID >> 2] = vpfe_reg_read(ccdc, + VPFE_HD_VD_WID); + ccdc->ccdc_ctx[VPFE_PIX_LINES >> 2] = vpfe_reg_read(ccdc, + VPFE_PIX_LINES); + ccdc->ccdc_ctx[VPFE_HORZ_INFO >> 2] = vpfe_reg_read(ccdc, + VPFE_HORZ_INFO); + ccdc->ccdc_ctx[VPFE_VERT_START >> 2] = vpfe_reg_read(ccdc, + VPFE_VERT_START); + ccdc->ccdc_ctx[VPFE_VERT_LINES >> 2] = vpfe_reg_read(ccdc, + VPFE_VERT_LINES); + ccdc->ccdc_ctx[VPFE_HSIZE_OFF >> 2] = vpfe_reg_read(ccdc, + VPFE_HSIZE_OFF); +} + +static int vpfe_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct vpfe_device *vpfe = platform_get_drvdata(pdev); + struct vpfe_ccdc *ccdc = &vpfe->ccdc; + + /* if streaming has not started we don't care */ + if (!vb2_start_streaming_called(&vpfe->buffer_queue)) + return 0; + + pm_runtime_get_sync(dev); + vpfe_config_enable(ccdc, 1); + + /* Save VPFE context */ + vpfe_save_context(ccdc); + + /* Disable CCDC */ + vpfe_pcr_enable(ccdc, 0); + vpfe_config_enable(ccdc, 0); + + /* Disable both master and slave clock */ + pm_runtime_put_sync(dev); + + /* Select sleep pin state */ + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static void vpfe_restore_context(struct vpfe_ccdc *ccdc) +{ + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_SYNMODE >> 2], VPFE_SYNMODE); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_CULLING >> 2], VPFE_CULLING); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_SDOFST >> 2], VPFE_SDOFST); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_SDR_ADDR >> 2], VPFE_SDR_ADDR); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_CLAMP >> 2], VPFE_CLAMP); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_DCSUB >> 2], VPFE_DCSUB); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_COLPTN >> 2], VPFE_COLPTN); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_BLKCMP >> 2], VPFE_BLKCMP); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_VDINT >> 2], VPFE_VDINT); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_ALAW >> 2], VPFE_ALAW); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_REC656IF >> 2], VPFE_REC656IF); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_CCDCFG >> 2], VPFE_CCDCFG); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_PCR >> 2], VPFE_PCR); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_HD_VD_WID >> 2], + VPFE_HD_VD_WID); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_PIX_LINES >> 2], + VPFE_PIX_LINES); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_HORZ_INFO >> 2], + VPFE_HORZ_INFO); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_VERT_START >> 2], + VPFE_VERT_START); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_VERT_LINES >> 2], + VPFE_VERT_LINES); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_HSIZE_OFF >> 2], + VPFE_HSIZE_OFF); +} + +static int vpfe_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct vpfe_device *vpfe = platform_get_drvdata(pdev); + struct vpfe_ccdc *ccdc = &vpfe->ccdc; + + /* if streaming has not started we don't care */ + if (!vb2_start_streaming_called(&vpfe->buffer_queue)) + return 0; + + /* Enable both master and slave clock */ + pm_runtime_get_sync(dev); + vpfe_config_enable(ccdc, 1); + + /* Restore VPFE context */ + vpfe_restore_context(ccdc); + + vpfe_config_enable(ccdc, 0); + pm_runtime_put_sync(dev); + + /* Select default pin state */ + pinctrl_pm_select_default_state(dev); + + return 0; +} + +#endif + +static SIMPLE_DEV_PM_OPS(vpfe_pm_ops, vpfe_suspend, vpfe_resume); + +static const struct of_device_id vpfe_of_match[] = { + { .compatible = "ti,am437x-vpfe", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, vpfe_of_match); + +static struct platform_driver vpfe_driver = { + .probe = vpfe_probe, + .remove = vpfe_remove, + .driver = { + .name = VPFE_MODULE_NAME, + .owner = THIS_MODULE, + .pm = &vpfe_pm_ops, + .of_match_table = of_match_ptr(vpfe_of_match), + }, +}; + +module_platform_driver(vpfe_driver); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("TI AM437x VPFE driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(VPFE_VERSION); diff --git a/drivers/media/platform/am437x/am437x-vpfe.h b/drivers/media/platform/am437x/am437x-vpfe.h new file mode 100644 index 0000000..0f55735 --- /dev/null +++ b/drivers/media/platform/am437x/am437x-vpfe.h @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2013 - 2014 Texas Instruments, Inc. + * + * Benoit Parrot + * Lad, Prabhakar + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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. + */ + +#ifndef AM437X_VPFE_H +#define AM437X_VPFE_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "am437x-vpfe_regs.h" + +enum vpfe_pin_pol { + VPFE_PINPOL_POSITIVE = 0, + VPFE_PINPOL_NEGATIVE, +}; + +enum vpfe_hw_if_type { + /* Raw Bayer */ + VPFE_RAW_BAYER = 0, + /* BT656 - 8 bit */ + VPFE_BT656, + /* BT656 - 10 bit */ + VPFE_BT656_10BIT, + /* YCbCr - 8 bit with external sync */ + VPFE_YCBCR_SYNC_8, + /* YCbCr - 16 bit with external sync */ + VPFE_YCBCR_SYNC_16, +}; + +/* interface description */ +struct vpfe_hw_if_param { + enum vpfe_hw_if_type if_type; + enum vpfe_pin_pol hdpol; + enum vpfe_pin_pol vdpol; + unsigned int bus_width; +}; + +#define VPFE_MAX_SUBDEV 1 +#define VPFE_MAX_INPUTS 1 + +struct vpfe_pixel_format { + struct v4l2_fmtdesc fmtdesc; + /* bytes per pixel */ + int bpp; +}; + +struct vpfe_std_info { + int active_pixels; + int active_lines; + /* current frame format */ + int frame_format; +}; + +struct vpfe_route { + u32 input; + u32 output; +}; + +struct vpfe_subdev_info { + char name[32]; + /* Sub device group id */ + int grp_id; + /* inputs available at the sub device */ + struct v4l2_input inputs[VPFE_MAX_INPUTS]; + /* Sub dev routing information for each input */ + struct vpfe_route *routes; + /* check if sub dev supports routing */ + int can_route; + /* ccdc bus/interface configuration */ + struct vpfe_hw_if_param vpfe_param; + struct v4l2_subdev *sd; +}; + +struct vpfe_config { + /* information about each subdev */ + struct vpfe_subdev_info sub_devs[VPFE_MAX_SUBDEV]; + /* Flat array, arranged in groups */ + struct v4l2_async_subdev *asd[VPFE_MAX_SUBDEV]; +}; + +struct vpfe_cap_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + +enum ccdc_pixfmt { + CCDC_PIXFMT_RAW = 0, + CCDC_PIXFMT_YCBCR_16BIT, + CCDC_PIXFMT_YCBCR_8BIT, +}; + +enum ccdc_frmfmt { + CCDC_FRMFMT_PROGRESSIVE = 0, + CCDC_FRMFMT_INTERLACED, +}; + +/* PIXEL ORDER IN MEMORY from LSB to MSB */ +/* only applicable for 8-bit input mode */ +enum ccdc_pixorder { + CCDC_PIXORDER_YCBYCR, + CCDC_PIXORDER_CBYCRY, +}; + +enum ccdc_buftype { + CCDC_BUFTYPE_FLD_INTERLEAVED, + CCDC_BUFTYPE_FLD_SEPARATED +}; + + +/* returns the highest bit used for the gamma */ +static inline u8 ccdc_gamma_width_max_bit(enum vpfe_ccdc_gamma_width width) +{ + return 15 - width; +} + +/* returns the highest bit used for this data size */ +static inline u8 ccdc_data_size_max_bit(enum vpfe_ccdc_data_size sz) +{ + return sz == VPFE_CCDC_DATA_8BITS ? 7 : 15 - sz; +} + +/* Structure for CCDC configuration parameters for raw capture mode */ +struct ccdc_params_raw { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + struct v4l2_rect win; + /* Current Format Bytes Per Pixels */ + unsigned int bytesperpixel; + /* Current Format Bytes per Lines + * (Aligned to 32 bytes) used for HORZ_INFO + */ + unsigned int bytesperline; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; + /* + * enable to store the image in inverse + * order in memory(bottom to top) + */ + unsigned char image_invert_enable; + /* configurable parameters */ + struct vpfe_ccdc_config_params_raw config_params; +}; + +struct ccdc_params_ycbcr { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + struct v4l2_rect win; + /* Current Format Bytes Per Pixels */ + unsigned int bytesperpixel; + /* Current Format Bytes per Lines + * (Aligned to 32 bytes) used for HORZ_INFO + */ + unsigned int bytesperline; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* enable BT.656 embedded sync mode */ + int bt656_enable; + /* cb:y:cr:y or y:cb:y:cr in memory */ + enum ccdc_pixorder pix_order; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; +}; + +/* + * CCDC operational configuration + */ +struct ccdc_config { + /* CCDC interface type */ + enum vpfe_hw_if_type if_type; + /* Raw Bayer configuration */ + struct ccdc_params_raw bayer; + /* YCbCr configuration */ + struct ccdc_params_ycbcr ycbcr; + /* ccdc base address */ + void __iomem *base_addr; +}; + +struct vpfe_ccdc { + struct ccdc_config ccdc_cfg; + u32 ccdc_ctx[VPFE_REG_END / sizeof(u32)]; +}; + +struct vpfe_device { + /* V4l2 specific parameters */ + /* Identifies video device for this channel */ + struct video_device *video_dev; + /* sub devices */ + struct v4l2_subdev **sd; + /* vpfe cfg */ + struct vpfe_config *cfg; + /* V4l2 device */ + struct v4l2_device v4l2_dev; + /* parent device */ + struct device *pdev; + /* subdevice async Notifier */ + struct v4l2_async_notifier notifier; + /* Indicates id of the field which is being displayed */ + unsigned field; + unsigned sequence; + /* current interface type */ + struct vpfe_hw_if_param vpfe_if_params; + /* ptr to currently selected sub device */ + struct vpfe_subdev_info *current_subdev; + /* current input at the sub device */ + int current_input; + /* Keeps track of the information about the standard */ + struct vpfe_std_info std_info; + /* std index into std table */ + int std_index; + /* IRQs used when CCDC output to SDRAM */ + unsigned int irq; + /* Pointer pointing to current v4l2_buffer */ + struct vpfe_cap_buffer *cur_frm; + /* Pointer pointing to next v4l2_buffer */ + struct vpfe_cap_buffer *next_frm; + /* Used to store pixel format */ + struct v4l2_format fmt; + /* Used to store current bytes per pixel based on current format */ + unsigned int bpp; + /* + * used when IMP is chained to store the crop window which + * is different from the image window + */ + struct v4l2_rect crop; + /* Buffer queue used in video-buf */ + struct vb2_queue buffer_queue; + /* Allocator-specific contexts for each plane */ + struct vb2_alloc_ctx *alloc_ctx; + /* Queue of filled frames */ + struct list_head dma_queue; + /* IRQ lock for DMA queue */ + spinlock_t dma_queue_lock; + /* lock used to access this structure */ + struct mutex lock; + /* + * offset where second field starts from the starting of the + * buffer for field separated YCbCr formats + */ + u32 field_off; + struct vpfe_ccdc ccdc; +}; + +#endif /* AM437X_VPFE_H */ diff --git a/drivers/media/platform/am437x/am437x-vpfe_regs.h b/drivers/media/platform/am437x/am437x-vpfe_regs.h new file mode 100644 index 0000000..4a0ed29 --- /dev/null +++ b/drivers/media/platform/am437x/am437x-vpfe_regs.h @@ -0,0 +1,140 @@ +/* + * TI AM437x Image Sensor Interface Registers + * + * Copyright (C) 2013 - 2014 Texas Instruments, Inc. + * + * Benoit Parrot + * Lad, Prabhakar + * + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef AM437X_VPFE_REGS_H +#define AM437X_VPFE_REGS_H + +/* VPFE module register offset */ +#define VPFE_REVISION 0x0 +#define VPFE_PCR 0x4 +#define VPFE_SYNMODE 0x8 +#define VPFE_HD_VD_WID 0xc +#define VPFE_PIX_LINES 0x10 +#define VPFE_HORZ_INFO 0x14 +#define VPFE_VERT_START 0x18 +#define VPFE_VERT_LINES 0x1c +#define VPFE_CULLING 0x20 +#define VPFE_HSIZE_OFF 0x24 +#define VPFE_SDOFST 0x28 +#define VPFE_SDR_ADDR 0x2c +#define VPFE_CLAMP 0x30 +#define VPFE_DCSUB 0x34 +#define VPFE_COLPTN 0x38 +#define VPFE_BLKCMP 0x3c +#define VPFE_VDINT 0x48 +#define VPFE_ALAW 0x4c +#define VPFE_REC656IF 0x50 +#define VPFE_CCDCFG 0x54 +#define VPFE_DMA_CNTL 0x98 +#define VPFE_SYSCONFIG 0x104 +#define VPFE_CONFIG 0x108 +#define VPFE_IRQ_EOI 0x110 +#define VPFE_IRQ_STS_RAW 0x114 +#define VPFE_IRQ_STS 0x118 +#define VPFE_IRQ_EN_SET 0x11c +#define VPFE_IRQ_EN_CLR 0x120 +#define VPFE_REG_END 0x124 + +/* Define bit fields within selected registers */ +#define VPFE_FID_POL_MASK 1 +#define VPFE_FID_POL_SHIFT 4 +#define VPFE_HD_POL_MASK 1 +#define VPFE_HD_POL_SHIFT 3 +#define VPFE_VD_POL_MASK 1 +#define VPFE_VD_POL_SHIFT 2 +#define VPFE_HSIZE_OFF_MASK 0xffffffe0 +#define VPFE_32BYTE_ALIGN_VAL 31 +#define VPFE_FRM_FMT_MASK 0x1 +#define VPFE_FRM_FMT_SHIFT 7 +#define VPFE_DATA_SZ_MASK 7 +#define VPFE_DATA_SZ_SHIFT 8 +#define VPFE_PIX_FMT_MASK 3 +#define VPFE_PIX_FMT_SHIFT 12 +#define VPFE_VP2SDR_DISABLE 0xfffbffff +#define VPFE_WEN_ENABLE (1 << 17) +#define VPFE_SDR2RSZ_DISABLE 0xfff7ffff +#define VPFE_VDHDEN_ENABLE (1 << 16) +#define VPFE_LPF_ENABLE (1 << 14) +#define VPFE_ALAW_ENABLE (1 << 3) +#define VPFE_ALAW_GAMMA_WD_MASK 7 +#define VPFE_BLK_CLAMP_ENABLE (1 << 31) +#define VPFE_BLK_SGAIN_MASK 0x1f +#define VPFE_BLK_ST_PXL_MASK 0x7fff +#define VPFE_BLK_ST_PXL_SHIFT 10 +#define VPFE_BLK_SAMPLE_LN_MASK 7 +#define VPFE_BLK_SAMPLE_LN_SHIFT 28 +#define VPFE_BLK_SAMPLE_LINE_MASK 7 +#define VPFE_BLK_SAMPLE_LINE_SHIFT 25 +#define VPFE_BLK_DC_SUB_MASK 0x03fff +#define VPFE_BLK_COMP_MASK 0xff +#define VPFE_BLK_COMP_GB_COMP_SHIFT 8 +#define VPFE_BLK_COMP_GR_COMP_SHIFT 16 +#define VPFE_BLK_COMP_R_COMP_SHIFT 24 +#define VPFE_LATCH_ON_VSYNC_DISABLE (1 << 15) +#define VPFE_DATA_PACK_ENABLE (1 << 11) +#define VPFE_HORZ_INFO_SPH_SHIFT 16 +#define VPFE_VERT_START_SLV0_SHIFT 16 +#define VPFE_VDINT_VDINT0_SHIFT 16 +#define VPFE_VDINT_VDINT1_MASK 0xffff +#define VPFE_PPC_RAW 1 +#define VPFE_DCSUB_DEFAULT_VAL 0 +#define VPFE_CLAMP_DEFAULT_VAL 0 +#define VPFE_COLPTN_VAL 0xbb11bb11 +#define VPFE_TWO_BYTES_PER_PIXEL 2 +#define VPFE_INTERLACED_IMAGE_INVERT 0x4b6d +#define VPFE_INTERLACED_NO_IMAGE_INVERT 0x0249 +#define VPFE_PROGRESSIVE_IMAGE_INVERT 0x4000 +#define VPFE_PROGRESSIVE_NO_IMAGE_INVERT 0 +#define VPFE_INTERLACED_HEIGHT_SHIFT 1 +#define VPFE_SYN_MODE_INPMOD_SHIFT 12 +#define VPFE_SYN_MODE_INPMOD_MASK 3 +#define VPFE_SYN_MODE_8BITS (7 << 8) +#define VPFE_SYN_MODE_10BITS (6 << 8) +#define VPFE_SYN_MODE_11BITS (5 << 8) +#define VPFE_SYN_MODE_12BITS (4 << 8) +#define VPFE_SYN_MODE_13BITS (3 << 8) +#define VPFE_SYN_MODE_14BITS (2 << 8) +#define VPFE_SYN_MODE_15BITS (1 << 8) +#define VPFE_SYN_MODE_16BITS (0 << 8) +#define VPFE_SYN_FLDMODE_MASK 1 +#define VPFE_SYN_FLDMODE_SHIFT 7 +#define VPFE_REC656IF_BT656_EN 3 +#define VPFE_SYN_MODE_VD_POL_NEGATIVE (1 << 2) +#define VPFE_CCDCFG_Y8POS_SHIFT 11 +#define VPFE_CCDCFG_BW656_10BIT (1 << 5) +#define VPFE_SDOFST_FIELD_INTERLEAVED 0x249 +#define VPFE_NO_CULLING 0xffff00ff +#define VPFE_VDINT0 (1 << 0) +#define VPFE_VDINT1 (1 << 1) +#define VPFE_VDINT2 (1 << 2) +#define VPFE_DMA_CNTL_OVERFLOW (1 << 31) + +#define VPFE_CONFIG_PCLK_INV_SHIFT 0 +#define VPFE_CONFIG_PCLK_INV_MASK 1 +#define VPFE_CONFIG_PCLK_INV_NOT_INV 0 +#define VPFE_CONFIG_PCLK_INV_INV 1 +#define VPFE_CONFIG_EN_SHIFT 1 +#define VPFE_CONFIG_EN_MASK 2 +#define VPFE_CONFIG_EN_DISABLE 0 +#define VPFE_CONFIG_EN_ENABLE 1 +#define VPFE_CONFIG_ST_SHIFT 2 +#define VPFE_CONFIG_ST_MASK 4 +#define VPFE_CONFIG_ST_OCP_ACTIVE 0 +#define VPFE_CONFIG_ST_OCP_STANDBY 1 + +#endif /* AM437X_VPFE_REGS_H */ diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 00b10002..9312d58 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -35,6 +35,7 @@ header-y += adfs_fs.h header-y += affs_hardblocks.h header-y += agpgart.h header-y += aio_abi.h +header-y += am437x-vpfe.h header-y += apm_bios.h header-y += arcfb.h header-y += atalk.h diff --git a/include/uapi/linux/am437x-vpfe.h b/include/uapi/linux/am437x-vpfe.h new file mode 100644 index 0000000..9b03033f --- /dev/null +++ b/include/uapi/linux/am437x-vpfe.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2013 - 2014 Texas Instruments, Inc. + * + * Benoit Parrot + * Lad, Prabhakar + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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. + */ + +#ifndef AM437X_VPFE_USER_H +#define AM437X_VPFE_USER_H + +enum vpfe_ccdc_data_size { + VPFE_CCDC_DATA_16BITS = 0, + VPFE_CCDC_DATA_15BITS, + VPFE_CCDC_DATA_14BITS, + VPFE_CCDC_DATA_13BITS, + VPFE_CCDC_DATA_12BITS, + VPFE_CCDC_DATA_11BITS, + VPFE_CCDC_DATA_10BITS, + VPFE_CCDC_DATA_8BITS, +}; + +/* enum for No of pixel per line to be avg. in Black Clamping*/ +enum vpfe_ccdc_sample_length { + VPFE_CCDC_SAMPLE_1PIXELS = 0, + VPFE_CCDC_SAMPLE_2PIXELS, + VPFE_CCDC_SAMPLE_4PIXELS, + VPFE_CCDC_SAMPLE_8PIXELS, + VPFE_CCDC_SAMPLE_16PIXELS, +}; + +/* enum for No of lines in Black Clamping */ +enum vpfe_ccdc_sample_line { + VPFE_CCDC_SAMPLE_1LINES = 0, + VPFE_CCDC_SAMPLE_2LINES, + VPFE_CCDC_SAMPLE_4LINES, + VPFE_CCDC_SAMPLE_8LINES, + VPFE_CCDC_SAMPLE_16LINES, +}; + +/* enum for Alaw gamma width */ +enum vpfe_ccdc_gamma_width { + VPFE_CCDC_GAMMA_BITS_15_6 = 0, /* use bits 15-6 for gamma */ + VPFE_CCDC_GAMMA_BITS_14_5, + VPFE_CCDC_GAMMA_BITS_13_4, + VPFE_CCDC_GAMMA_BITS_12_3, + VPFE_CCDC_GAMMA_BITS_11_2, + VPFE_CCDC_GAMMA_BITS_10_1, + VPFE_CCDC_GAMMA_BITS_09_0, /* use bits 9-0 for gamma */ +}; + +/* structure for ALaw */ +struct vpfe_ccdc_a_law { + /* Enable/disable A-Law */ + unsigned char enable; + /* Gamma Width Input */ + enum vpfe_ccdc_gamma_width gamma_wd; +}; + +/* structure for Black Clamping */ +struct vpfe_ccdc_black_clamp { + unsigned char enable; + /* only if bClampEnable is TRUE */ + enum vpfe_ccdc_sample_length sample_pixel; + /* only if bClampEnable is TRUE */ + enum vpfe_ccdc_sample_line sample_ln; + /* only if bClampEnable is TRUE */ + unsigned short start_pixel; + /* only if bClampEnable is TRUE */ + unsigned short sgain; + /* only if bClampEnable is FALSE */ + unsigned short dc_sub; +}; + +/* structure for Black Level Compensation */ +struct vpfe_ccdc_black_compensation { + /* Constant value to subtract from Red component */ + char r; + /* Constant value to subtract from Gr component */ + char gr; + /* Constant value to subtract from Blue component */ + char b; + /* Constant value to subtract from Gb component */ + char gb; +}; + +/* Structure for CCDC configuration parameters for raw capture mode passed + * by application + */ +struct vpfe_ccdc_config_params_raw { + /* data size value from 8 to 16 bits */ + enum vpfe_ccdc_data_size data_sz; + /* Structure for Optional A-Law */ + struct vpfe_ccdc_a_law alaw; + /* Structure for Optical Black Clamp */ + struct vpfe_ccdc_black_clamp blk_clamp; + /* Structure for Black Compensation */ + struct vpfe_ccdc_black_compensation blk_comp; +}; + +/* + * Private IOCTL + * VIDIOC_AM437X_CCDC_CFG - Set CCDC configuration for raw capture + * This is an experimental ioctl that will change in future kernels. So use + * this ioctl with care ! + **/ +#define VIDIOC_AM437X_CCDC_CFG \ + _IOW('V', BASE_VIDIOC_PRIVATE + 1, void *) + +#endif /* AM437X_VPFE_USER_H */ -- cgit v0.10.2 From 30c78167bc6536d9074aa79385a575596343bf69 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 17 Dec 2014 12:20:45 +0200 Subject: ath10k: set max_num_vdevs based on wmi op version To make it easier to manage firmware differences, we should not use ATH10K_FW_FEATURE_WMI_10X outside ath10k_core_init_firmware_features(). To achieve that create new field ar->max_num_vdevs and set it based on wmi op version. Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index c83f1e7..bf4a46a 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -904,12 +904,14 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) case ATH10K_FW_WMI_OP_VERSION_MAIN: ar->max_num_peers = TARGET_NUM_PEERS; ar->max_num_stations = TARGET_NUM_STATIONS; + ar->max_num_vdevs = TARGET_NUM_VDEVS; ar->htt.max_num_pending_tx = TARGET_NUM_MSDU_DESC; break; case ATH10K_FW_WMI_OP_VERSION_10_1: case ATH10K_FW_WMI_OP_VERSION_10_2: ar->max_num_peers = TARGET_10X_NUM_PEERS; ar->max_num_stations = TARGET_10X_NUM_STATIONS; + ar->max_num_vdevs = TARGET_10X_NUM_VDEVS; ar->htt.max_num_pending_tx = TARGET_10X_NUM_MSDU_DESC; break; case ATH10K_FW_WMI_OP_VERSION_TLV: @@ -1061,10 +1063,7 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode) if (status) goto err_hif_stop; - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) - ar->free_vdev_map = (1LL << TARGET_10X_NUM_VDEVS) - 1; - else - ar->free_vdev_map = (1LL << TARGET_NUM_VDEVS) - 1; + ar->free_vdev_map = (1LL << ar->max_num_vdevs) - 1; INIT_LIST_HEAD(&ar->arvifs); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 613355c..3998f1e33 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -581,6 +581,7 @@ struct ath10k { int max_num_peers; int max_num_stations; + int max_num_vdevs; struct work_struct offchan_tx_work; struct sk_buff_head offchan_tx_queue; -- cgit v0.10.2 From 5cc7caf46cb8823f0a86b25b96ebbc531bd1a21c Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 17 Dec 2014 12:20:54 +0200 Subject: ath10k: use wmi op version to check which iface combination to use ATH10K_FW_FEATURE_WMI_10X should not be used for anymore as that's now deprecated, instead use wmi op version. Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 42f6a4d..780c63b 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -5129,16 +5129,25 @@ int ath10k_mac_register(struct ath10k *ar) */ ar->hw->queues = 4; - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb; - ar->hw->wiphy->n_iface_combinations = - ARRAY_SIZE(ath10k_10x_if_comb); - } else { + switch (ar->wmi.op_version) { + case ATH10K_FW_WMI_OP_VERSION_MAIN: + case ATH10K_FW_WMI_OP_VERSION_TLV: ar->hw->wiphy->iface_combinations = ath10k_if_comb; ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ath10k_if_comb); - ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC); + break; + case ATH10K_FW_WMI_OP_VERSION_10_1: + case ATH10K_FW_WMI_OP_VERSION_10_2: + ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb; + ar->hw->wiphy->n_iface_combinations = + ARRAY_SIZE(ath10k_10x_if_comb); + break; + case ATH10K_FW_WMI_OP_VERSION_UNSET: + case ATH10K_FW_WMI_OP_VERSION_MAX: + WARN_ON(1); + ret = -EINVAL; + goto err_free; } ar->hw->netdev_features = NETIF_F_HW_CSUM; -- cgit v0.10.2 From ffdd07576fb6722f2bdeb67783d7a85af4b6dd44 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 17 Dec 2014 12:21:03 +0200 Subject: ath10k: print ath10k wmi op version The internal firmware version doesn't tell much to the user, it's more informative to use that field to print the wmi op version. Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 70a9599..6ca2442 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -124,7 +124,7 @@ EXPORT_SYMBOL(ath10k_info); void ath10k_print_driver_info(struct ath10k *ar) { - ath10k_info(ar, "%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d wmi %d.%d.%d.%d cal %s max_sta %d\n", + ath10k_info(ar, "%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d wmi %d cal %s max_sta %d\n", ar->hw_params.name, ar->target_version, ar->chip_id, @@ -132,10 +132,7 @@ void ath10k_print_driver_info(struct ath10k *ar) ar->fw_api, ar->htt.target_version_major, ar->htt.target_version_minor, - ar->fw_version_major, - ar->fw_version_minor, - ar->fw_version_release, - ar->fw_version_build, + ar->wmi.op_version, ath10k_cal_mode_str(ar->cal_mode), ar->max_num_stations); ath10k_info(ar, "debug %d debugfs %d tracing %d dfs %d testmode %d\n", -- cgit v0.10.2 From 4a16fbec1cd0a760d17f3d2b587d504a6eae4da6 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 17 Dec 2014 12:21:12 +0200 Subject: ath10k: add 10.2.4 firmware support 10.2.4 firmware uses bitmask in wmi_resource_config to configure 10.2 firmware features like airtime fairness and rx batch mode instead of maintaining separete bool entry. This allows new features that can be configure during init time without breaking backward compatibility. kvalo: use WMI op version, bump up FW API to 4 to not break older versions of ath10k Signed-off-by: Rajkumar Manoharan Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index bf4a46a..7e377f8 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -687,6 +687,13 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar) /* calibration file is optional, don't check for any errors */ ath10k_fetch_cal_file(ar); + ar->fw_api = 4; + ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api); + + ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API4_FILE); + if (ret == 0) + goto success; + ar->fw_api = 3; ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api); @@ -891,7 +898,8 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) */ if (ar->wmi.op_version == ATH10K_FW_WMI_OP_VERSION_UNSET) { if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) + if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, + ar->fw_features)) ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_10_2; else ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_10_1; @@ -909,6 +917,7 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) break; case ATH10K_FW_WMI_OP_VERSION_10_1: case ATH10K_FW_WMI_OP_VERSION_10_2: + case ATH10K_FW_WMI_OP_VERSION_10_2_4: ar->max_num_peers = TARGET_10X_NUM_PEERS; ar->max_num_stations = TARGET_10X_NUM_STATIONS; ar->max_num_vdevs = TARGET_10X_NUM_VDEVS; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 809c252..5729901 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -37,6 +37,9 @@ #define ATH10K_FW_API2_FILE "firmware-2.bin" #define ATH10K_FW_API3_FILE "firmware-3.bin" +/* added support for ATH10K_FW_IE_WMI_OP_VERSION */ +#define ATH10K_FW_API4_FILE "firmware-4.bin" + #define ATH10K_FW_UTF_FILE "utf.bin" /* includes also the null byte */ @@ -72,6 +75,7 @@ enum ath10k_fw_wmi_op_version { ATH10K_FW_WMI_OP_VERSION_10_1 = 2, ATH10K_FW_WMI_OP_VERSION_10_2 = 3, ATH10K_FW_WMI_OP_VERSION_TLV = 4, + ATH10K_FW_WMI_OP_VERSION_10_2_4 = 5, /* keep last */ ATH10K_FW_WMI_OP_VERSION_MAX, diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 780c63b..31354db 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -5139,6 +5139,7 @@ int ath10k_mac_register(struct ath10k *ar) break; case ATH10K_FW_WMI_OP_VERSION_10_1: case ATH10K_FW_WMI_OP_VERSION_10_2: + case ATH10K_FW_WMI_OP_VERSION_10_2_4: ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb; ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ath10k_10x_if_comb); diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index fa486f6..cf9c727 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -269,6 +269,127 @@ static struct wmi_cmd_map wmi_10x_cmd_map = { .gpio_output_cmdid = WMI_10X_GPIO_OUTPUT_CMDID, }; +/* 10.2.4 WMI cmd track */ +static struct wmi_cmd_map wmi_10_2_4_cmd_map = { + .init_cmdid = WMI_10_2_INIT_CMDID, + .start_scan_cmdid = WMI_10_2_START_SCAN_CMDID, + .stop_scan_cmdid = WMI_10_2_STOP_SCAN_CMDID, + .scan_chan_list_cmdid = WMI_10_2_SCAN_CHAN_LIST_CMDID, + .scan_sch_prio_tbl_cmdid = WMI_CMD_UNSUPPORTED, + .pdev_set_regdomain_cmdid = WMI_10_2_PDEV_SET_REGDOMAIN_CMDID, + .pdev_set_channel_cmdid = WMI_10_2_PDEV_SET_CHANNEL_CMDID, + .pdev_set_param_cmdid = WMI_10_2_PDEV_SET_PARAM_CMDID, + .pdev_pktlog_enable_cmdid = WMI_10_2_PDEV_PKTLOG_ENABLE_CMDID, + .pdev_pktlog_disable_cmdid = WMI_10_2_PDEV_PKTLOG_DISABLE_CMDID, + .pdev_set_wmm_params_cmdid = WMI_10_2_PDEV_SET_WMM_PARAMS_CMDID, + .pdev_set_ht_cap_ie_cmdid = WMI_10_2_PDEV_SET_HT_CAP_IE_CMDID, + .pdev_set_vht_cap_ie_cmdid = WMI_10_2_PDEV_SET_VHT_CAP_IE_CMDID, + .pdev_set_quiet_mode_cmdid = WMI_10_2_PDEV_SET_QUIET_MODE_CMDID, + .pdev_green_ap_ps_enable_cmdid = WMI_10_2_PDEV_GREEN_AP_PS_ENABLE_CMDID, + .pdev_get_tpc_config_cmdid = WMI_10_2_PDEV_GET_TPC_CONFIG_CMDID, + .pdev_set_base_macaddr_cmdid = WMI_10_2_PDEV_SET_BASE_MACADDR_CMDID, + .vdev_create_cmdid = WMI_10_2_VDEV_CREATE_CMDID, + .vdev_delete_cmdid = WMI_10_2_VDEV_DELETE_CMDID, + .vdev_start_request_cmdid = WMI_10_2_VDEV_START_REQUEST_CMDID, + .vdev_restart_request_cmdid = WMI_10_2_VDEV_RESTART_REQUEST_CMDID, + .vdev_up_cmdid = WMI_10_2_VDEV_UP_CMDID, + .vdev_stop_cmdid = WMI_10_2_VDEV_STOP_CMDID, + .vdev_down_cmdid = WMI_10_2_VDEV_DOWN_CMDID, + .vdev_set_param_cmdid = WMI_10_2_VDEV_SET_PARAM_CMDID, + .vdev_install_key_cmdid = WMI_10_2_VDEV_INSTALL_KEY_CMDID, + .peer_create_cmdid = WMI_10_2_PEER_CREATE_CMDID, + .peer_delete_cmdid = WMI_10_2_PEER_DELETE_CMDID, + .peer_flush_tids_cmdid = WMI_10_2_PEER_FLUSH_TIDS_CMDID, + .peer_set_param_cmdid = WMI_10_2_PEER_SET_PARAM_CMDID, + .peer_assoc_cmdid = WMI_10_2_PEER_ASSOC_CMDID, + .peer_add_wds_entry_cmdid = WMI_10_2_PEER_ADD_WDS_ENTRY_CMDID, + .peer_remove_wds_entry_cmdid = WMI_10_2_PEER_REMOVE_WDS_ENTRY_CMDID, + .peer_mcast_group_cmdid = WMI_10_2_PEER_MCAST_GROUP_CMDID, + .bcn_tx_cmdid = WMI_10_2_BCN_TX_CMDID, + .pdev_send_bcn_cmdid = WMI_10_2_PDEV_SEND_BCN_CMDID, + .bcn_tmpl_cmdid = WMI_CMD_UNSUPPORTED, + .bcn_filter_rx_cmdid = WMI_10_2_BCN_FILTER_RX_CMDID, + .prb_req_filter_rx_cmdid = WMI_10_2_PRB_REQ_FILTER_RX_CMDID, + .mgmt_tx_cmdid = WMI_10_2_MGMT_TX_CMDID, + .prb_tmpl_cmdid = WMI_CMD_UNSUPPORTED, + .addba_clear_resp_cmdid = WMI_10_2_ADDBA_CLEAR_RESP_CMDID, + .addba_send_cmdid = WMI_10_2_ADDBA_SEND_CMDID, + .addba_status_cmdid = WMI_10_2_ADDBA_STATUS_CMDID, + .delba_send_cmdid = WMI_10_2_DELBA_SEND_CMDID, + .addba_set_resp_cmdid = WMI_10_2_ADDBA_SET_RESP_CMDID, + .send_singleamsdu_cmdid = WMI_10_2_SEND_SINGLEAMSDU_CMDID, + .sta_powersave_mode_cmdid = WMI_10_2_STA_POWERSAVE_MODE_CMDID, + .sta_powersave_param_cmdid = WMI_10_2_STA_POWERSAVE_PARAM_CMDID, + .sta_mimo_ps_mode_cmdid = WMI_10_2_STA_MIMO_PS_MODE_CMDID, + .pdev_dfs_enable_cmdid = WMI_10_2_PDEV_DFS_ENABLE_CMDID, + .pdev_dfs_disable_cmdid = WMI_10_2_PDEV_DFS_DISABLE_CMDID, + .roam_scan_mode = WMI_10_2_ROAM_SCAN_MODE, + .roam_scan_rssi_threshold = WMI_10_2_ROAM_SCAN_RSSI_THRESHOLD, + .roam_scan_period = WMI_10_2_ROAM_SCAN_PERIOD, + .roam_scan_rssi_change_threshold = + WMI_10_2_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + .roam_ap_profile = WMI_10_2_ROAM_AP_PROFILE, + .ofl_scan_add_ap_profile = WMI_10_2_OFL_SCAN_ADD_AP_PROFILE, + .ofl_scan_remove_ap_profile = WMI_10_2_OFL_SCAN_REMOVE_AP_PROFILE, + .ofl_scan_period = WMI_10_2_OFL_SCAN_PERIOD, + .p2p_dev_set_device_info = WMI_10_2_P2P_DEV_SET_DEVICE_INFO, + .p2p_dev_set_discoverability = WMI_10_2_P2P_DEV_SET_DISCOVERABILITY, + .p2p_go_set_beacon_ie = WMI_10_2_P2P_GO_SET_BEACON_IE, + .p2p_go_set_probe_resp_ie = WMI_10_2_P2P_GO_SET_PROBE_RESP_IE, + .p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNSUPPORTED, + .ap_ps_peer_param_cmdid = WMI_10_2_AP_PS_PEER_PARAM_CMDID, + .ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNSUPPORTED, + .peer_rate_retry_sched_cmdid = WMI_10_2_PEER_RATE_RETRY_SCHED_CMDID, + .wlan_profile_trigger_cmdid = WMI_10_2_WLAN_PROFILE_TRIGGER_CMDID, + .wlan_profile_set_hist_intvl_cmdid = + WMI_10_2_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + .wlan_profile_get_profile_data_cmdid = + WMI_10_2_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + .wlan_profile_enable_profile_id_cmdid = + WMI_10_2_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + .wlan_profile_list_profile_id_cmdid = + WMI_10_2_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + .pdev_suspend_cmdid = WMI_10_2_PDEV_SUSPEND_CMDID, + .pdev_resume_cmdid = WMI_10_2_PDEV_RESUME_CMDID, + .add_bcn_filter_cmdid = WMI_10_2_ADD_BCN_FILTER_CMDID, + .rmv_bcn_filter_cmdid = WMI_10_2_RMV_BCN_FILTER_CMDID, + .wow_add_wake_pattern_cmdid = WMI_10_2_WOW_ADD_WAKE_PATTERN_CMDID, + .wow_del_wake_pattern_cmdid = WMI_10_2_WOW_DEL_WAKE_PATTERN_CMDID, + .wow_enable_disable_wake_event_cmdid = + WMI_10_2_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + .wow_enable_cmdid = WMI_10_2_WOW_ENABLE_CMDID, + .wow_hostwakeup_from_sleep_cmdid = + WMI_10_2_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + .rtt_measreq_cmdid = WMI_10_2_RTT_MEASREQ_CMDID, + .rtt_tsf_cmdid = WMI_10_2_RTT_TSF_CMDID, + .vdev_spectral_scan_configure_cmdid = + WMI_10_2_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID, + .vdev_spectral_scan_enable_cmdid = + WMI_10_2_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + .request_stats_cmdid = WMI_10_2_REQUEST_STATS_CMDID, + .set_arp_ns_offload_cmdid = WMI_CMD_UNSUPPORTED, + .network_list_offload_config_cmdid = WMI_CMD_UNSUPPORTED, + .gtk_offload_cmdid = WMI_CMD_UNSUPPORTED, + .csa_offload_enable_cmdid = WMI_CMD_UNSUPPORTED, + .csa_offload_chanswitch_cmdid = WMI_CMD_UNSUPPORTED, + .chatter_set_mode_cmdid = WMI_CMD_UNSUPPORTED, + .peer_tid_addba_cmdid = WMI_CMD_UNSUPPORTED, + .peer_tid_delba_cmdid = WMI_CMD_UNSUPPORTED, + .sta_dtim_ps_method_cmdid = WMI_CMD_UNSUPPORTED, + .sta_uapsd_auto_trig_cmdid = WMI_CMD_UNSUPPORTED, + .sta_keepalive_cmd = WMI_CMD_UNSUPPORTED, + .echo_cmdid = WMI_10_2_ECHO_CMDID, + .pdev_utf_cmdid = WMI_10_2_PDEV_UTF_CMDID, + .dbglog_cfg_cmdid = WMI_10_2_DBGLOG_CFG_CMDID, + .pdev_qvit_cmdid = WMI_10_2_PDEV_QVIT_CMDID, + .pdev_ftm_intg_cmdid = WMI_CMD_UNSUPPORTED, + .vdev_set_keepalive_cmdid = WMI_CMD_UNSUPPORTED, + .vdev_get_keepalive_cmdid = WMI_CMD_UNSUPPORTED, + .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED, + .gpio_config_cmdid = WMI_10_2_GPIO_CONFIG_CMDID, + .gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID, +}; + /* MAIN WMI VDEV param map */ static struct wmi_vdev_param_map wmi_vdev_param_map = { .rts_threshold = WMI_VDEV_PARAM_RTS_THRESHOLD, @@ -387,6 +508,64 @@ static struct wmi_vdev_param_map wmi_10x_vdev_param_map = { WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS, }; +static struct wmi_vdev_param_map wmi_10_2_4_vdev_param_map = { + .rts_threshold = WMI_10X_VDEV_PARAM_RTS_THRESHOLD, + .fragmentation_threshold = WMI_10X_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + .beacon_interval = WMI_10X_VDEV_PARAM_BEACON_INTERVAL, + .listen_interval = WMI_10X_VDEV_PARAM_LISTEN_INTERVAL, + .multicast_rate = WMI_10X_VDEV_PARAM_MULTICAST_RATE, + .mgmt_tx_rate = WMI_10X_VDEV_PARAM_MGMT_TX_RATE, + .slot_time = WMI_10X_VDEV_PARAM_SLOT_TIME, + .preamble = WMI_10X_VDEV_PARAM_PREAMBLE, + .swba_time = WMI_10X_VDEV_PARAM_SWBA_TIME, + .wmi_vdev_stats_update_period = WMI_10X_VDEV_STATS_UPDATE_PERIOD, + .wmi_vdev_pwrsave_ageout_time = WMI_10X_VDEV_PWRSAVE_AGEOUT_TIME, + .wmi_vdev_host_swba_interval = WMI_10X_VDEV_HOST_SWBA_INTERVAL, + .dtim_period = WMI_10X_VDEV_PARAM_DTIM_PERIOD, + .wmi_vdev_oc_scheduler_air_time_limit = + WMI_10X_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + .wds = WMI_10X_VDEV_PARAM_WDS, + .atim_window = WMI_10X_VDEV_PARAM_ATIM_WINDOW, + .bmiss_count_max = WMI_10X_VDEV_PARAM_BMISS_COUNT_MAX, + .bmiss_first_bcnt = WMI_VDEV_PARAM_UNSUPPORTED, + .bmiss_final_bcnt = WMI_VDEV_PARAM_UNSUPPORTED, + .feature_wmm = WMI_10X_VDEV_PARAM_FEATURE_WMM, + .chwidth = WMI_10X_VDEV_PARAM_CHWIDTH, + .chextoffset = WMI_10X_VDEV_PARAM_CHEXTOFFSET, + .disable_htprotection = WMI_10X_VDEV_PARAM_DISABLE_HTPROTECTION, + .sta_quickkickout = WMI_10X_VDEV_PARAM_STA_QUICKKICKOUT, + .mgmt_rate = WMI_10X_VDEV_PARAM_MGMT_RATE, + .protection_mode = WMI_10X_VDEV_PARAM_PROTECTION_MODE, + .fixed_rate = WMI_10X_VDEV_PARAM_FIXED_RATE, + .sgi = WMI_10X_VDEV_PARAM_SGI, + .ldpc = WMI_10X_VDEV_PARAM_LDPC, + .tx_stbc = WMI_10X_VDEV_PARAM_TX_STBC, + .rx_stbc = WMI_10X_VDEV_PARAM_RX_STBC, + .intra_bss_fwd = WMI_10X_VDEV_PARAM_INTRA_BSS_FWD, + .def_keyid = WMI_10X_VDEV_PARAM_DEF_KEYID, + .nss = WMI_10X_VDEV_PARAM_NSS, + .bcast_data_rate = WMI_10X_VDEV_PARAM_BCAST_DATA_RATE, + .mcast_data_rate = WMI_10X_VDEV_PARAM_MCAST_DATA_RATE, + .mcast_indicate = WMI_10X_VDEV_PARAM_MCAST_INDICATE, + .dhcp_indicate = WMI_10X_VDEV_PARAM_DHCP_INDICATE, + .unknown_dest_indicate = WMI_10X_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + .ap_keepalive_min_idle_inactive_time_secs = + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_idle_inactive_time_secs = + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_unresponsive_time_secs = + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + .ap_enable_nawds = WMI_10X_VDEV_PARAM_AP_ENABLE_NAWDS, + .mcast2ucast_set = WMI_10X_VDEV_PARAM_MCAST2UCAST_SET, + .enable_rtscts = WMI_10X_VDEV_PARAM_ENABLE_RTSCTS, + .txbf = WMI_VDEV_PARAM_UNSUPPORTED, + .packet_powersave = WMI_VDEV_PARAM_UNSUPPORTED, + .drop_unencry = WMI_VDEV_PARAM_UNSUPPORTED, + .tx_encap_type = WMI_VDEV_PARAM_UNSUPPORTED, + .ap_detect_out_of_sync_sleeping_sta_time_secs = + WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS, +}; + static struct wmi_pdev_param_map wmi_pdev_param_map = { .tx_chain_mask = WMI_PDEV_PARAM_TX_CHAIN_MASK, .rx_chain_mask = WMI_PDEV_PARAM_RX_CHAIN_MASK, @@ -492,6 +671,59 @@ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = { .cal_period = WMI_10X_PDEV_PARAM_CAL_PERIOD, }; +static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = { + .tx_chain_mask = WMI_10X_PDEV_PARAM_TX_CHAIN_MASK, + .rx_chain_mask = WMI_10X_PDEV_PARAM_RX_CHAIN_MASK, + .txpower_limit2g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT2G, + .txpower_limit5g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT5G, + .txpower_scale = WMI_10X_PDEV_PARAM_TXPOWER_SCALE, + .beacon_gen_mode = WMI_10X_PDEV_PARAM_BEACON_GEN_MODE, + .beacon_tx_mode = WMI_10X_PDEV_PARAM_BEACON_TX_MODE, + .resmgr_offchan_mode = WMI_10X_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + .protection_mode = WMI_10X_PDEV_PARAM_PROTECTION_MODE, + .dynamic_bw = WMI_10X_PDEV_PARAM_DYNAMIC_BW, + .non_agg_sw_retry_th = WMI_10X_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + .agg_sw_retry_th = WMI_10X_PDEV_PARAM_AGG_SW_RETRY_TH, + .sta_kickout_th = WMI_10X_PDEV_PARAM_STA_KICKOUT_TH, + .ac_aggrsize_scaling = WMI_10X_PDEV_PARAM_AC_AGGRSIZE_SCALING, + .ltr_enable = WMI_10X_PDEV_PARAM_LTR_ENABLE, + .ltr_ac_latency_be = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BE, + .ltr_ac_latency_bk = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BK, + .ltr_ac_latency_vi = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VI, + .ltr_ac_latency_vo = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VO, + .ltr_ac_latency_timeout = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + .ltr_sleep_override = WMI_10X_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + .ltr_rx_override = WMI_10X_PDEV_PARAM_LTR_RX_OVERRIDE, + .ltr_tx_activity_timeout = WMI_10X_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + .l1ss_enable = WMI_10X_PDEV_PARAM_L1SS_ENABLE, + .dsleep_enable = WMI_10X_PDEV_PARAM_DSLEEP_ENABLE, + .pcielp_txbuf_flush = WMI_PDEV_PARAM_UNSUPPORTED, + .pcielp_txbuf_watermark = WMI_PDEV_PARAM_UNSUPPORTED, + .pcielp_txbuf_tmo_en = WMI_PDEV_PARAM_UNSUPPORTED, + .pcielp_txbuf_tmo_value = WMI_PDEV_PARAM_UNSUPPORTED, + .pdev_stats_update_period = WMI_10X_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + .vdev_stats_update_period = WMI_10X_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + .peer_stats_update_period = WMI_10X_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + .bcnflt_stats_update_period = + WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + .pmf_qos = WMI_10X_PDEV_PARAM_PMF_QOS, + .arp_ac_override = WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE, + .dcs = WMI_10X_PDEV_PARAM_DCS, + .ani_enable = WMI_10X_PDEV_PARAM_ANI_ENABLE, + .ani_poll_period = WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD, + .ani_listen_period = WMI_10X_PDEV_PARAM_ANI_LISTEN_PERIOD, + .ani_ofdm_level = WMI_10X_PDEV_PARAM_ANI_OFDM_LEVEL, + .ani_cck_level = WMI_10X_PDEV_PARAM_ANI_CCK_LEVEL, + .dyntxchain = WMI_10X_PDEV_PARAM_DYNTXCHAIN, + .proxy_sta = WMI_PDEV_PARAM_UNSUPPORTED, + .idle_ps_config = WMI_PDEV_PARAM_UNSUPPORTED, + .power_gating_sleep = WMI_PDEV_PARAM_UNSUPPORTED, + .fast_channel_reset = WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET, + .burst_dur = WMI_10X_PDEV_PARAM_BURST_DUR, + .burst_enable = WMI_10X_PDEV_PARAM_BURST_ENABLE, + .cal_period = WMI_10X_PDEV_PARAM_CAL_PERIOD, +}; + /* firmware 10.2 specific mappings */ static struct wmi_cmd_map wmi_10_2_cmd_map = { .init_cmdid = WMI_10_2_INIT_CMDID, @@ -4745,9 +4977,68 @@ static const struct wmi_ops wmi_10_2_ops = { .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable, }; +static const struct wmi_ops wmi_10_2_4_ops = { + .rx = ath10k_wmi_10_2_op_rx, + .gen_init = ath10k_wmi_10_2_op_gen_init, + .gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc, + + /* shared with 10.1 */ + .map_svc = wmi_10x_svc_map, + .pull_svc_rdy = ath10k_wmi_10x_op_pull_svc_rdy_ev, + .pull_fw_stats = ath10k_wmi_10x_op_pull_fw_stats, + .gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd, + .gen_start_scan = ath10k_wmi_10x_op_gen_start_scan, + + .pull_scan = ath10k_wmi_op_pull_scan_ev, + .pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev, + .pull_ch_info = ath10k_wmi_op_pull_ch_info_ev, + .pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev, + .pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev, + .pull_swba = ath10k_wmi_op_pull_swba_ev, + .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev, + .pull_rdy = ath10k_wmi_op_pull_rdy_ev, + + .gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend, + .gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume, + .gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param, + .gen_stop_scan = ath10k_wmi_op_gen_stop_scan, + .gen_vdev_create = ath10k_wmi_op_gen_vdev_create, + .gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete, + .gen_vdev_start = ath10k_wmi_op_gen_vdev_start, + .gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop, + .gen_vdev_up = ath10k_wmi_op_gen_vdev_up, + .gen_vdev_down = ath10k_wmi_op_gen_vdev_down, + .gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param, + .gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key, + .gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf, + .gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable, + .gen_peer_create = ath10k_wmi_op_gen_peer_create, + .gen_peer_delete = ath10k_wmi_op_gen_peer_delete, + .gen_peer_flush = ath10k_wmi_op_gen_peer_flush, + .gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param, + .gen_set_psmode = ath10k_wmi_op_gen_set_psmode, + .gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps, + .gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps, + .gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list, + .gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma, + .gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm, + .gen_request_stats = ath10k_wmi_op_gen_request_stats, + .gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang, + .gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx, + .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg, + .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable, + .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable, +}; + int ath10k_wmi_attach(struct ath10k *ar) { switch (ar->wmi.op_version) { + case ATH10K_FW_WMI_OP_VERSION_10_2_4: + ar->wmi.cmd = &wmi_10_2_4_cmd_map; + ar->wmi.ops = &wmi_10_2_4_ops; + ar->wmi.vdev_param = &wmi_10_2_4_vdev_param_map; + ar->wmi.pdev_param = &wmi_10_2_4_pdev_param_map; + break; case ATH10K_FW_WMI_OP_VERSION_10_2: ar->wmi.cmd = &wmi_10_2_cmd_map; ar->wmi.ops = &wmi_10_2_ops; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index ff2d076..7f34093 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -1940,6 +1940,11 @@ struct wmi_resource_config_10x { __le32 max_frag_entries; } __packed; +enum wmi_10_2_feature_mask { + WMI_10_2_RX_BATCH_MODE = BIT(0), + WMI_10_2_ATF_CONFIG = BIT(1), +}; + struct wmi_resource_config_10_2 { struct wmi_resource_config_10x common; __le32 max_peer_ext_stats; @@ -1948,7 +1953,7 @@ struct wmi_resource_config_10_2 { __le32 be_min_free; __le32 vi_min_free; __le32 vo_min_free; - __le32 rx_batchmode; /* 0-disable, 1-enable */ + __le32 feature_mask; } __packed; #define NUM_UNITS_IS_NUM_VDEVS 0x1 -- cgit v0.10.2 From ffdd738d90f0d9d609e3d790059ab4d351a75963 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 17 Dec 2014 12:21:40 +0200 Subject: ath10k: add wmi support for pdev_set_quiet_mode Add WMI support to send pdev_set_quiet_mode command to target. This will be used for thermal mitigation purpose. Signed-off-by: Rajkumar Manoharan Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h index 1fbc520..feed0fe 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-ops.h +++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h @@ -114,6 +114,10 @@ struct wmi_ops { struct sk_buff *(*gen_dbglog_cfg)(struct ath10k *ar, u32 module_enable); struct sk_buff *(*gen_pktlog_enable)(struct ath10k *ar, u32 filter); struct sk_buff *(*gen_pktlog_disable)(struct ath10k *ar); + struct sk_buff *(*gen_pdev_set_quiet_mode)(struct ath10k *ar, + u32 period, u32 duration, + u32 next_offset, + u32 enabled); }; int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id); @@ -818,4 +822,22 @@ ath10k_wmi_pdev_pktlog_disable(struct ath10k *ar) ar->wmi.cmd->pdev_pktlog_disable_cmdid); } +static inline int +ath10k_wmi_pdev_set_quiet_mode(struct ath10k *ar, u32 period, u32 duration, + u32 next_offset, u32 enabled) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pdev_set_quiet_mode) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pdev_set_quiet_mode(ar, period, duration, + next_offset, enabled); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_set_quiet_mode_cmdid); +} + #endif diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 1627ec5..e203dad 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -2204,6 +2204,7 @@ static const struct wmi_ops wmi_tlv_ops = { .gen_dbglog_cfg = ath10k_wmi_tlv_op_gen_dbglog_cfg, .gen_pktlog_enable = ath10k_wmi_tlv_op_gen_pktlog_enable, .gen_pktlog_disable = ath10k_wmi_tlv_op_gen_pktlog_disable, + /* .gen_pdev_set_quiet_mode not implemented */ }; /************/ diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index cf9c727..77bb331 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -4821,6 +4821,30 @@ ath10k_wmi_op_gen_pktlog_disable(struct ath10k *ar) return skb; } +static struct sk_buff * +ath10k_wmi_op_gen_pdev_set_quiet_mode(struct ath10k *ar, u32 period, + u32 duration, u32 next_offset, + u32 enabled) +{ + struct wmi_pdev_set_quiet_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + cmd = (struct wmi_pdev_set_quiet_cmd *)skb->data; + cmd->period = __cpu_to_le32(period); + cmd->duration = __cpu_to_le32(duration); + cmd->next_start = __cpu_to_le32(next_offset); + cmd->enabled = __cpu_to_le32(enabled); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi quiet param: period %u duration %u enabled %d\n", + period, duration, enabled); + return skb; +} + static const struct wmi_ops wmi_ops = { .rx = ath10k_wmi_op_rx, .map_svc = wmi_main_svc_map, @@ -4870,6 +4894,7 @@ static const struct wmi_ops wmi_ops = { .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg, .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable, .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable, + .gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode, }; static const struct wmi_ops wmi_10_1_ops = { @@ -4922,6 +4947,7 @@ static const struct wmi_ops wmi_10_1_ops = { .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg, .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable, .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable, + .gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode, }; static const struct wmi_ops wmi_10_2_ops = { @@ -4975,6 +5001,7 @@ static const struct wmi_ops wmi_10_2_ops = { .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg, .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable, .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable, + .gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode, }; static const struct wmi_ops wmi_10_2_4_ops = { @@ -5028,6 +5055,7 @@ static const struct wmi_ops wmi_10_2_4_ops = { .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg, .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable, .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable, + .gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode, }; int ath10k_wmi_attach(struct ath10k *ar) -- cgit v0.10.2 From fe6f36d62152ba85a8928e46fc2cbb919538c51d Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 17 Dec 2014 12:22:07 +0200 Subject: ath10k: add thermal cooling device support Thermal cooling device support is added to control the temperature by throttling the data transmission for the given duration. Throttling is done using hw MAC quiet time setting. Period, duration and offset from TBTT can be set up to quiet the MAC transmits for the required duty cycle (% of quiet duration). The thermal device allows user to configure duty cycle. The quiet params are derived as follows. period = max(25TU, beacon interval / number of bss) duration = period * duty cycle / 100 Quiet mode can be disabled by setting the duty cycle to 0. The cooling device can be found under /sys/class/thermal/cooling_deviceX/. Corresponding soft link to this device can be found under phy folder. /sys/class/ieee80211/phy*/device/cooling_device. To set duty cycle as 40%, echo 40 >/sys/class/ieee80211/phy*/device/cooling_device/cur_state Signed-off-by: Rajkumar Manoharan Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile index 8abb66c..ffa3b1a 100644 --- a/drivers/net/wireless/ath/ath10k/Makefile +++ b/drivers/net/wireless/ath/ath10k/Makefile @@ -14,6 +14,7 @@ ath10k_core-y += mac.o \ ath10k_core-$(CONFIG_ATH10K_DEBUGFS) += spectral.o ath10k_core-$(CONFIG_NL80211_TESTMODE) += testmode.o ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o +ath10k_core-$(CONFIG_THERMAL) += thermal.o obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o ath10k_pci-y += pci.o \ diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 7e377f8..70d53e6 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1231,9 +1231,18 @@ static void ath10k_core_register_work(struct work_struct *work) goto err_debug_destroy; } + status = ath10k_thermal_register(ar); + if (status) { + ath10k_err(ar, "could not register thermal device: %d\n", + status); + goto err_spectral_destroy; + } + set_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags); return; +err_spectral_destroy: + ath10k_spectral_destroy(ar); err_debug_destroy: ath10k_debug_destroy(ar); err_unregister_mac: @@ -1263,6 +1272,7 @@ void ath10k_core_unregister(struct ath10k *ar) if (!test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) return; + ath10k_thermal_unregister(ar); /* Stop spectral before unregistering from mac80211 to remove the * relayfs debugfs file cleanly. Otherwise the parent debugfs tree * would be already be free'd recursively, leading to a double free. diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 3998f1e33..7b6d9e4 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -34,6 +34,7 @@ #include "../regd.h" #include "../dfs_pattern_detector.h" #include "spectral.h" +#include "thermal.h" #define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB) #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) @@ -634,6 +635,8 @@ struct ath10k { u32 fw_cold_reset_counter; } stats; + struct ath10k_thermal thermal; + /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); }; diff --git a/drivers/net/wireless/ath/ath10k/thermal.c b/drivers/net/wireless/ath/ath10k/thermal.c new file mode 100644 index 0000000..e98ce8c --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/thermal.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2014 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include "core.h" +#include "debug.h" +#include "wmi-ops.h" + +static int ath10k_thermal_get_active_vifs(struct ath10k *ar, + enum wmi_vdev_type type) +{ + struct ath10k_vif *arvif; + int count = 0; + + lockdep_assert_held(&ar->conf_mutex); + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (!arvif->is_started) + continue; + + if (!arvif->is_up) + continue; + + if (arvif->vdev_type != type) + continue; + + count++; + } + return count; +} + +static int ath10k_thermal_get_max_dutycycle(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = ATH10K_QUIET_DUTY_CYCLE_MAX; + + return 0; +} + +static int ath10k_thermal_get_cur_dutycycle(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct ath10k *ar = cdev->devdata; + + mutex_lock(&ar->conf_mutex); + *state = ar->thermal.duty_cycle; + mutex_unlock(&ar->conf_mutex); + + return 0; +} + +static int ath10k_thermal_set_cur_dutycycle(struct thermal_cooling_device *cdev, + unsigned long duty_cycle) +{ + struct ath10k *ar = cdev->devdata; + u32 period, duration, enabled; + int num_bss, ret = 0; + + mutex_lock(&ar->conf_mutex); + if (ar->state != ATH10K_STATE_ON) { + ret = -ENETDOWN; + goto out; + } + + if (duty_cycle > ATH10K_QUIET_DUTY_CYCLE_MAX) { + ath10k_warn(ar, "duty cycle %ld is exceeding the limit %d\n", + duty_cycle, ATH10K_QUIET_DUTY_CYCLE_MAX); + ret = -EINVAL; + goto out; + } + /* TODO: Right now, thermal mitigation is handled only for single/multi + * vif AP mode. Since quiet param is not validated in STA mode, it needs + * to be investigated further to handle multi STA and multi-vif (AP+STA) + * mode properly. + */ + num_bss = ath10k_thermal_get_active_vifs(ar, WMI_VDEV_TYPE_AP); + if (!num_bss) { + ath10k_warn(ar, "no active AP interfaces\n"); + ret = -ENETDOWN; + goto out; + } + period = max(ATH10K_QUIET_PERIOD_MIN, + (ATH10K_QUIET_PERIOD_DEFAULT / num_bss)); + duration = period * (duty_cycle / 100); + enabled = duration ? 1 : 0; + + ret = ath10k_wmi_pdev_set_quiet_mode(ar, period, duration, + ATH10K_QUIET_START_OFFSET, + enabled); + if (ret) { + ath10k_warn(ar, "failed to set quiet mode period %u duarion %u enabled %u ret %d\n", + period, duration, enabled, ret); + goto out; + } + ar->thermal.duty_cycle = duty_cycle; +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static struct thermal_cooling_device_ops ath10k_thermal_ops = { + .get_max_state = ath10k_thermal_get_max_dutycycle, + .get_cur_state = ath10k_thermal_get_cur_dutycycle, + .set_cur_state = ath10k_thermal_set_cur_dutycycle, +}; + +int ath10k_thermal_register(struct ath10k *ar) +{ + struct thermal_cooling_device *cdev; + int ret; + + cdev = thermal_cooling_device_register("ath10k_thermal", ar, + &ath10k_thermal_ops); + + if (IS_ERR(cdev)) { + ath10k_err(ar, "failed to setup thermal device result: %ld\n", + PTR_ERR(cdev)); + return -EINVAL; + } + + ret = sysfs_create_link(&ar->dev->kobj, &cdev->device.kobj, + "cooling_device"); + if (ret) { + ath10k_err(ar, "failed to create thermal symlink\n"); + goto err_cooling_destroy; + } + + ar->thermal.cdev = cdev; + return 0; + +err_cooling_destroy: + thermal_cooling_device_unregister(cdev); + return ret; +} + +void ath10k_thermal_unregister(struct ath10k *ar) +{ + thermal_cooling_device_unregister(ar->thermal.cdev); + sysfs_remove_link(&ar->dev->kobj, "cooling_device"); +} diff --git a/drivers/net/wireless/ath/ath10k/thermal.h b/drivers/net/wireless/ath/ath10k/thermal.h new file mode 100644 index 0000000..e20bb87 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/thermal.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef _THERMAL_ +#define _THERMAL_ + +#define ATH10K_QUIET_PERIOD_DEFAULT 100 +#define ATH10K_QUIET_PERIOD_MIN 25 +#define ATH10K_QUIET_START_OFFSET 10 +#define ATH10K_QUIET_DUTY_CYCLE_MAX 70 + +struct ath10k_thermal { + struct thermal_cooling_device *cdev; + + /* protected by conf_mutex */ + u32 duty_cycle; +}; + +#ifdef CONFIG_THERMAL +int ath10k_thermal_register(struct ath10k *ar); +void ath10k_thermal_unregister(struct ath10k *ar); +#else +static inline int ath10k_thermal_register(struct ath10k *ar) +{ + return 0; +} + +static inline void ath10k_thermal_unregister(struct ath10k *ar) +{ +} +#endif +#endif /* _THERMAL_ */ -- cgit v0.10.2 From a57a6a2753ac4a85cb083dd32dc0be414d52e001 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 17 Dec 2014 12:22:17 +0200 Subject: ath10k: add wmi interface for pdev_get_temperature Add WMI command support for reading temperature from the target and corresponding WMI temperature event handler. The pdev_get_temperature command is currently supported in 10.2 firmware alone. Signed-off-by: Rajkumar Manoharan Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h index feed0fe..20e2c30 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-ops.h +++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h @@ -118,6 +118,7 @@ struct wmi_ops { u32 period, u32 duration, u32 next_offset, u32 enabled); + struct sk_buff *(*gen_pdev_get_temperature)(struct ath10k *ar); }; int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id); @@ -840,4 +841,20 @@ ath10k_wmi_pdev_set_quiet_mode(struct ath10k *ar, u32 period, u32 duration, ar->wmi.cmd->pdev_set_quiet_mode_cmdid); } +static inline int +ath10k_wmi_pdev_get_temperature(struct ath10k *ar) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pdev_get_temperature) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pdev_get_temperature(ar); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_get_temperature_cmdid); +} + #endif diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index e203dad..4c050ce 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -2044,6 +2044,7 @@ static struct wmi_cmd_map wmi_tlv_cmd_map = { .force_fw_hang_cmdid = WMI_TLV_FORCE_FW_HANG_CMDID, .gpio_config_cmdid = WMI_TLV_GPIO_CONFIG_CMDID, .gpio_output_cmdid = WMI_TLV_GPIO_OUTPUT_CMDID, + .pdev_get_temperature_cmdid = WMI_TLV_CMD_UNSUPPORTED, }; static struct wmi_pdev_param_map wmi_tlv_pdev_param_map = { @@ -2205,6 +2206,7 @@ static const struct wmi_ops wmi_tlv_ops = { .gen_pktlog_enable = ath10k_wmi_tlv_op_gen_pktlog_enable, .gen_pktlog_disable = ath10k_wmi_tlv_op_gen_pktlog_disable, /* .gen_pdev_set_quiet_mode not implemented */ + /* .gen_pdev_get_temperature not implemented */ }; /************/ diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 77bb331..c2d7ac3 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -145,6 +145,7 @@ static struct wmi_cmd_map wmi_cmd_map = { .force_fw_hang_cmdid = WMI_FORCE_FW_HANG_CMDID, .gpio_config_cmdid = WMI_GPIO_CONFIG_CMDID, .gpio_output_cmdid = WMI_GPIO_OUTPUT_CMDID, + .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED, }; /* 10.X WMI cmd track */ @@ -267,6 +268,7 @@ static struct wmi_cmd_map wmi_10x_cmd_map = { .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED, .gpio_config_cmdid = WMI_10X_GPIO_CONFIG_CMDID, .gpio_output_cmdid = WMI_10X_GPIO_OUTPUT_CMDID, + .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED, }; /* 10.2.4 WMI cmd track */ @@ -388,6 +390,7 @@ static struct wmi_cmd_map wmi_10_2_4_cmd_map = { .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED, .gpio_config_cmdid = WMI_10_2_GPIO_CONFIG_CMDID, .gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID, + .pdev_get_temperature_cmdid = WMI_10_2_PDEV_GET_TEMPERATURE_CMDID, }; /* MAIN WMI VDEV param map */ @@ -843,6 +846,7 @@ static struct wmi_cmd_map wmi_10_2_cmd_map = { .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED, .gpio_config_cmdid = WMI_10_2_GPIO_CONFIG_CMDID, .gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID, + .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED, }; void ath10k_wmi_put_wmi_channel(struct wmi_channel *ch, @@ -3026,6 +3030,17 @@ int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb) return 0; } +static int ath10k_wmi_event_temperature(struct ath10k *ar, struct sk_buff *skb) +{ + const struct wmi_pdev_temperature_event *ev; + + ev = (struct wmi_pdev_temperature_event *)skb->data; + if (WARN_ON(skb->len < sizeof(*ev))) + return -EPROTO; + + return 0; +} + static void ath10k_wmi_op_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; @@ -3365,6 +3380,9 @@ static void ath10k_wmi_10_2_op_rx(struct ath10k *ar, struct sk_buff *skb) case WMI_10_2_READY_EVENTID: ath10k_wmi_event_ready(ar, skb); break; + case WMI_10_2_PDEV_TEMPERATURE_EVENTID: + ath10k_wmi_event_temperature(ar, skb); + break; case WMI_10_2_RTT_KEEPALIVE_EVENTID: case WMI_10_2_GPIO_INPUT_EVENTID: case WMI_10_2_PEER_RATECODE_LIST_EVENTID: @@ -4646,6 +4664,19 @@ ath10k_wmi_10_2_op_gen_peer_assoc(struct ath10k *ar, return skb; } +static struct sk_buff * +ath10k_wmi_10_2_op_gen_pdev_get_temperature(struct ath10k *ar) +{ + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, 0); + if (!skb) + return ERR_PTR(-ENOMEM); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev get temperature\n"); + return skb; +} + /* This function assumes the beacon is already DMA mapped */ static struct sk_buff * ath10k_wmi_op_gen_beacon_dma(struct ath10k_vif *arvif) @@ -4895,6 +4926,7 @@ static const struct wmi_ops wmi_ops = { .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable, .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable, .gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode, + /* .gen_pdev_get_temperature not implemented */ }; static const struct wmi_ops wmi_10_1_ops = { @@ -4906,6 +4938,7 @@ static const struct wmi_ops wmi_10_1_ops = { .gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd, .gen_start_scan = ath10k_wmi_10x_op_gen_start_scan, .gen_peer_assoc = ath10k_wmi_10_1_op_gen_peer_assoc, + /* .gen_pdev_get_temperature not implemented */ /* shared with main branch */ .pull_scan = ath10k_wmi_op_pull_scan_ev, @@ -4954,6 +4987,7 @@ static const struct wmi_ops wmi_10_2_ops = { .rx = ath10k_wmi_10_2_op_rx, .gen_init = ath10k_wmi_10_2_op_gen_init, .gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc, + /* .gen_pdev_get_temperature not implemented */ /* shared with 10.1 */ .map_svc = wmi_10x_svc_map, @@ -5008,6 +5042,7 @@ static const struct wmi_ops wmi_10_2_4_ops = { .rx = ath10k_wmi_10_2_op_rx, .gen_init = ath10k_wmi_10_2_op_gen_init, .gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc, + .gen_pdev_get_temperature = ath10k_wmi_10_2_op_gen_pdev_get_temperature, /* shared with 10.1 */ .map_svc = wmi_10x_svc_map, diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 7f34093..bd7f29a 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -550,6 +550,7 @@ struct wmi_cmd_map { u32 force_fw_hang_cmdid; u32 gpio_config_cmdid; u32 gpio_output_cmdid; + u32 pdev_get_temperature_cmdid; }; /* @@ -1154,6 +1155,11 @@ enum wmi_10_2_cmd_id { WMI_10_2_PDEV_SET_MIMOGAIN_TABLE_CMDID, WMI_10_2_PDEV_RATEPWR_TABLE_CMDID, WMI_10_2_PDEV_RATEPWR_CHAINMSK_TABLE_CMDID, + WMI_10_2_PDEV_GET_INFO, + WMI_10_2_VDEV_GET_INFO, + WMI_10_2_VDEV_ATF_REQUEST_CMDID, + WMI_10_2_PEER_ATF_REQUEST_CMDID, + WMI_10_2_PDEV_GET_TEMPERATURE_CMDID, WMI_10_2_PDEV_UTF_CMDID = WMI_10_2_END_CMDID - 1, }; @@ -1195,6 +1201,8 @@ enum wmi_10_2_event_id { WMI_10_2_MCAST_BUF_RELEASE_EVENTID, WMI_10_2_MCAST_LIST_AGEOUT_EVENTID, WMI_10_2_WDS_PEER_EVENTID, + WMI_10_2_PEER_STA_PS_STATECHG_EVENTID, + WMI_10_2_PDEV_TEMPERATURE_EVENTID, WMI_10_2_PDEV_UTF_EVENTID = WMI_10_2_END_EVENTID - 1, }; @@ -4740,6 +4748,11 @@ struct wmi_rdy_ev_arg { const u8 *mac_addr; }; +struct wmi_pdev_temperature_event { + /* temperature value in Celcius degree */ + __le32 temperature; +} __packed; + struct ath10k; struct ath10k_vif; struct ath10k_fw_stats_pdev; -- cgit v0.10.2 From ac2953fcc35871181bfafb11238b757ae1a4ce9f Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 17 Dec 2014 12:22:26 +0200 Subject: ath10k: add thermal sensor device support Temperature sensor generates electrical analog voltage from temperature of each chain. The analog voltage is converted to digital value through ADC. For reading temperature values fom user space, hw monitoring device is used. Whenever the user requests for current temperature, the driver sends WMI command and wait for response. For reading temperature, cat /sys/class/ieee80211/phy*/device/hwmon/hwmon2/temp1_input Signed-off-by: Rajkumar Manoharan Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 70d53e6..2d0671e 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -844,6 +844,7 @@ static void ath10k_core_restart(struct work_struct *work) complete_all(&ar->offchan_tx_completed); complete_all(&ar->install_key_done); complete_all(&ar->vdev_setup_done); + complete_all(&ar->thermal.wmi_sync); wake_up(&ar->htt.empty_tx_wq); wake_up(&ar->wmi.tx_credits_wq); wake_up(&ar->peer_mapping_wq); @@ -1316,6 +1317,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, init_completion(&ar->install_key_done); init_completion(&ar->vdev_setup_done); + init_completion(&ar->thermal.wmi_sync); INIT_DELAYED_WORK(&ar->scan.timeout, ath10k_scan_timeout_work); diff --git a/drivers/net/wireless/ath/ath10k/thermal.c b/drivers/net/wireless/ath/ath10k/thermal.c index e98ce8c..d939135 100644 --- a/drivers/net/wireless/ath/ath10k/thermal.c +++ b/drivers/net/wireless/ath/ath10k/thermal.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include "core.h" #include "debug.h" #include "wmi-ops.h" @@ -119,9 +121,72 @@ static struct thermal_cooling_device_ops ath10k_thermal_ops = { .set_cur_state = ath10k_thermal_set_cur_dutycycle, }; +static ssize_t ath10k_thermal_show_temp(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ath10k *ar = dev_get_drvdata(dev); + int ret, temperature; + + mutex_lock(&ar->conf_mutex); + + /* Can't get temperature when the card is off */ + if (ar->state != ATH10K_STATE_ON) { + ret = -ENETDOWN; + goto out; + } + + reinit_completion(&ar->thermal.wmi_sync); + ret = ath10k_wmi_pdev_get_temperature(ar); + if (ret) { + ath10k_warn(ar, "failed to read temperature %d\n", ret); + goto out; + } + + if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) { + ret = -ESHUTDOWN; + goto out; + } + + ret = wait_for_completion_timeout(&ar->thermal.wmi_sync, + ATH10K_THERMAL_SYNC_TIMEOUT_HZ); + if (ret == 0) { + ath10k_warn(ar, "failed to synchronize thermal read\n"); + ret = -ETIMEDOUT; + goto out; + } + + spin_lock_bh(&ar->data_lock); + temperature = ar->thermal.temperature; + spin_unlock_bh(&ar->data_lock); + + ret = snprintf(buf, PAGE_SIZE, "%d", temperature); +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature) +{ + spin_lock_bh(&ar->data_lock); + ar->thermal.temperature = temperature; + spin_unlock_bh(&ar->data_lock); + complete(&ar->thermal.wmi_sync); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ath10k_thermal_show_temp, + NULL, 0); + +static struct attribute *ath10k_hwmon_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(ath10k_hwmon); + int ath10k_thermal_register(struct ath10k *ar) { struct thermal_cooling_device *cdev; + struct device *hwmon_dev; int ret; cdev = thermal_cooling_device_register("ath10k_thermal", ar, @@ -141,8 +206,26 @@ int ath10k_thermal_register(struct ath10k *ar) } ar->thermal.cdev = cdev; + + /* Do not register hwmon device when temperature reading is not + * supported by firmware + */ + if (ar->wmi.op_version != ATH10K_FW_WMI_OP_VERSION_10_2_4) + return 0; + + hwmon_dev = devm_hwmon_device_register_with_groups(ar->dev, + "ath10k_hwmon", ar, + ath10k_hwmon_groups); + if (IS_ERR(hwmon_dev)) { + ath10k_err(ar, "failed to register hwmon device: %ld\n", + PTR_ERR(hwmon_dev)); + ret = -EINVAL; + goto err_remove_link; + } return 0; +err_remove_link: + sysfs_remove_link(&ar->dev->kobj, "thermal_sensor"); err_cooling_destroy: thermal_cooling_device_unregister(cdev); return ret; diff --git a/drivers/net/wireless/ath/ath10k/thermal.h b/drivers/net/wireless/ath/ath10k/thermal.h index e20bb87..bccc17a 100644 --- a/drivers/net/wireless/ath/ath10k/thermal.h +++ b/drivers/net/wireless/ath/ath10k/thermal.h @@ -20,17 +20,25 @@ #define ATH10K_QUIET_PERIOD_MIN 25 #define ATH10K_QUIET_START_OFFSET 10 #define ATH10K_QUIET_DUTY_CYCLE_MAX 70 +#define ATH10K_HWMON_NAME_LEN 15 +#define ATH10K_THERMAL_SYNC_TIMEOUT_HZ (5*HZ) struct ath10k_thermal { struct thermal_cooling_device *cdev; + struct completion wmi_sync; /* protected by conf_mutex */ u32 duty_cycle; + /* temperature value in Celcius degree + * protected by data_lock + */ + int temperature; }; #ifdef CONFIG_THERMAL int ath10k_thermal_register(struct ath10k *ar); void ath10k_thermal_unregister(struct ath10k *ar); +void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature); #else static inline int ath10k_thermal_register(struct ath10k *ar) { @@ -40,5 +48,11 @@ static inline int ath10k_thermal_register(struct ath10k *ar) static inline void ath10k_thermal_unregister(struct ath10k *ar) { } + +static inline void ath10k_thermal_event_temperature(struct ath10k *ar, + int temperature) +{ +} + #endif #endif /* _THERMAL_ */ diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index c2d7ac3..ac74290 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -3038,6 +3038,7 @@ static int ath10k_wmi_event_temperature(struct ath10k *ar, struct sk_buff *skb) if (WARN_ON(skb->len < sizeof(*ev))) return -EPROTO; + ath10k_thermal_event_temperature(ar, __le32_to_cpu(ev->temperature)); return 0; } -- cgit v0.10.2 From 627d9841eaa623e34657af7af0e6293805e07888 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 17 Dec 2014 12:29:54 +0200 Subject: ath10k: fix low TX rates when IBSS and HT This fix TX problem when IBSS used in HT mode. Before we used 6Mbps all the time for TX direction. Reported-by: Yeoh Chun-Yeow Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 31354db..719e599 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1468,9 +1468,16 @@ static void ath10k_peer_assoc_h_qos(struct ath10k *ar, if (vif->bss_conf.qos) arg->peer_flags |= WMI_PEER_QOS; break; + case WMI_VDEV_TYPE_IBSS: + if (sta->wme) + arg->peer_flags |= WMI_PEER_QOS; + break; default: break; } + + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac peer %pM qos %d\n", + sta->addr, !!(arg->peer_flags & WMI_PEER_QOS)); } static bool ath10k_mac_sta_has_11g_rates(struct ieee80211_sta *sta) -- cgit v0.10.2 From 55884c045d31a29cf69db8332d1064a1b61dd159 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 17 Dec 2014 12:30:02 +0200 Subject: ath10k: send (re)assoc peer command when NSS changed Assoc peer command contain information about NSS. When we will get IEEE80211_RC_NSS_CHANGED we should also send (re) assoc peer command to be sure firmware will know about it and RC will work correctly. This was found during code review. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 719e599..827faf07 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3659,8 +3659,9 @@ static void ath10k_sta_rc_update_wk(struct work_struct *wk) sta->addr, smps, err); } - if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) { - ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates\n", + if (changed & IEEE80211_RC_SUPP_RATES_CHANGED || + changed & IEEE80211_RC_NSS_CHANGED) { + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates/nss\n", sta->addr); err = ath10k_station_assoc(ar, arvif->vif, sta, true); -- cgit v0.10.2 From 6faab1273f79788a7d90f8c3c99b9b8f3b404c98 Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Thu, 18 Dec 2014 10:13:00 -0800 Subject: ath10k: set phymode to 11b when NO_OFDM flag set phymode should use 11b only if NO_OFDM flags is set. Hence check up channel flag for NO_OFDM and set to 11b. Signed-off-by: Peter Oh Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 827faf07..5085f55 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -269,7 +269,10 @@ chan_to_phymode(const struct cfg80211_chan_def *chandef) case IEEE80211_BAND_2GHZ: switch (chandef->width) { case NL80211_CHAN_WIDTH_20_NOHT: - phymode = MODE_11G; + if (chandef->chan->flags & IEEE80211_CHAN_NO_OFDM) + phymode = MODE_11B; + else + phymode = MODE_11G; break; case NL80211_CHAN_WIDTH_20: phymode = MODE_11NG_HT20; -- cgit v0.10.2 From 6de16eba62b3b4d01b2b232ea7724d5450a19e30 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Tue, 23 Dec 2014 08:38:24 -0700 Subject: Docs: Remove "tips and tricks" from SubmittingPatches This section was just a weird collection of stuff that is better found elsewhere. The "coding style" section somewhat duplicated the previous coding style section; the useful information there has been collected into a single place. Signed-off-by: Jonathan Corbet diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 1fa1caa..8f416a2 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -193,17 +193,33 @@ then only post say 15 or so at a time and wait for review and integration. -4) Style check your changes. +4) Style-check your changes. +---------------------------- Check your patch for basic style violations, details of which can be found in Documentation/CodingStyle. Failure to do so simply wastes the reviewers time and will get your patch rejected, probably without even being read. -At a minimum you should check your patches with the patch style -checker prior to submission (scripts/checkpatch.pl). You should -be able to justify all violations that remain in your patch. +One significant exception is when moving code from one file to +another -- in this case you should not modify the moved code at all in +the same patch which moves it. This clearly delineates the act of +moving the code and your changes. This greatly aids review of the +actual differences and allows tools to better track the history of +the code itself. + +Check your patches with the patch style checker prior to submission +(scripts/checkpatch.pl). Note, though, that the style checker should be +viewed as a guide, not as a replacement for human judgment. If your code +looks better with a violation then its probably best left alone. +The checker reports at three levels: + - ERROR: things that are very likely to be wrong + - WARNING: things requiring careful review + - CHECK: things requiring thought + +You should be able to justify all violations that remain in your +patch. 5) Select e-mail destination. @@ -684,100 +700,9 @@ new/deleted or renamed files. With rename detection, the statistics are rather different [...] because git will notice that a fair number of the changes are renames. ------------------------------------ -SECTION 2 - HINTS, TIPS, AND TRICKS ------------------------------------ - -This section lists many of the common "rules" associated with code -submitted to the kernel. There are always exceptions... but you must -have a really good reason for doing so. You could probably call this -section Linus Computer Science 101. - - - -1) Read Documentation/CodingStyle - -Nuff said. If your code deviates too much from this, it is likely -to be rejected without further review, and without comment. - -One significant exception is when moving code from one file to -another -- in this case you should not modify the moved code at all in -the same patch which moves it. This clearly delineates the act of -moving the code and your changes. This greatly aids review of the -actual differences and allows tools to better track the history of -the code itself. - -Check your patches with the patch style checker prior to submission -(scripts/checkpatch.pl). The style checker should be viewed as -a guide not as the final word. If your code looks better with -a violation then its probably best left alone. - -The checker reports at three levels: - - ERROR: things that are very likely to be wrong - - WARNING: things requiring careful review - - CHECK: things requiring thought - -You should be able to justify all violations that remain in your -patch. - - - -2) #ifdefs are ugly - -Code cluttered with ifdefs is difficult to read and maintain. Don't do -it. Instead, put your ifdefs in a header, and conditionally define -'static inline' functions, or macros, which are used in the code. -Let the compiler optimize away the "no-op" case. - -Simple example, of poor code: - - dev = alloc_etherdev (sizeof(struct funky_private)); - if (!dev) - return -ENODEV; - #ifdef CONFIG_NET_FUNKINESS - init_funky_net(dev); - #endif - -Cleaned-up example: - -(in header) - #ifndef CONFIG_NET_FUNKINESS - static inline void init_funky_net (struct net_device *d) {} - #endif - -(in the code itself) - dev = alloc_etherdev (sizeof(struct funky_private)); - if (!dev) - return -ENODEV; - init_funky_net(dev); - - - -3) 'static inline' is better than a macro - -Static inline functions are greatly preferred over macros. -They provide type safety, have no length limitations, no formatting -limitations, and under gcc they are as cheap as macros. - -Macros should only be used for cases where a static inline is clearly -suboptimal [there are a few, isolated cases of this in fast paths], -or where it is impossible to use a static inline function [such as -string-izing]. - -'static inline' is preferred over 'static __inline__', 'extern inline', -and 'extern __inline__'. - - - -4) Don't over-design. - -Don't try to anticipate nebulous future cases which may or may not -be useful: "Make it as simple as you can, and no simpler." - - ---------------------- -SECTION 3 - REFERENCES +SECTION 2 - REFERENCES ---------------------- Andrew Morton, "The perfect patch" (tpp). -- cgit v0.10.2 From 7994cc15d83c6188a77516f4c8400d3a4965b0a5 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Tue, 23 Dec 2014 08:43:41 -0700 Subject: Docs: Bring SubmittingPatches more into the git era Much of the information in SubmittingPatches shows its pre-git history. Clean that up a bit and rephrase things with the assumption that developers will be using git. Also rewrite the "pull requests" section and include information on using signed tags. Signed-off-by: Jonathan Corbet diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 8f416a2..230a3b8 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -24,13 +24,30 @@ SECTION 1 - CREATING AND SENDING YOUR CHANGE -------------------------------------------- +0) Obtain a current source tree +------------------------------- + +If you do not have a repository with the current kernel source handy, use +git to obtain one. You'll want to start with the mainline repository, +which can be grabbed with: + + git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git + +Note, however, that you may not want to develop against the mainline tree +directly. Most subsystem maintainers run their own trees and want to see +patches prepared against those trees. See the "T:" entry for the subsystem +in the MAINTAINERS file to find that tree, or simply ask the maintainer if +the tree is not listed there. + +It is still possible to download kernel releases via tarballs (as described +in the next section), but that is the hard way to do kernel development. 1) "diff -up" ------------ -Use "diff -up" or "diff -uprN" to create patches. git generates patches -in this form by default; if you're using git, you can skip this section -entirely. +If you must generate your patches by hand, use "diff -up" or "diff -uprN" +to create patches. Git generates patches in this form by default; if +you're using git, you can skip this section entirely. All changes to the Linux kernel occur in the form of patches, as generated by diff(1). When creating your patch, make sure to create it @@ -156,10 +173,15 @@ Example: platform_set_drvdata(), but left the variable "dev" unused, delete it. +You should also be sure to use at least the first twelve characters of the +SHA-1 ID. The kernel repository holds a *lot* of objects, making +collisions with shorter IDs a real possibility. Bear in mind that, even if +there is no collision with your six-character ID now, that condition may +change five years from now. + If your patch fixes a bug in a specific commit, e.g. you found an issue using git-bisect, please use the 'Fixes:' tag with the first 12 characters of the -SHA-1 ID, and the one line summary. -Example: +SHA-1 ID, and the one line summary. For example: Fixes: e21d2170f366 ("video: remove unnecessary platform_set_drvdata()") @@ -188,6 +210,12 @@ If one patch depends on another patch in order for a change to be complete, that is OK. Simply note "this patch depends on patch X" in your patch description. +When dividing your change into a series of patches, take special care to +ensure that the kernel builds and runs properly after each patch in the +series. Developers using "git bisect" to track down a problem can end up +splitting your patch series at any point; they will not thank you if you +introduce bugs in the middle. + If you cannot condense your patch set into a smaller set of patches, then only post say 15 or so at a time and wait for review and integration. @@ -445,15 +473,15 @@ which appears in the changelog. Special note to back-porters: It seems to be a common and useful practice to insert an indication of the origin of a patch at the top of the commit message (just after the subject line) to facilitate tracking. For instance, -here's what we see in 2.6-stable : +here's what we see in a 3.x-stable release: - Date: Tue May 13 19:10:30 2008 +0000 +Date: Tue Oct 7 07:26:38 2014 -0400 - SCSI: libiscsi regression in 2.6.25: fix nop timer handling + libata: Un-break ATA blacklist - commit 4cf1043593db6a337f10e006c23c69e5fc93e722 upstream + commit 1c40279960bcd7d52dbdf1d466b20d24b99176c8 upstream. -And here's what appears in 2.4 : +And here's what might appear in an older kernel once a patch is backported: Date: Tue May 13 22:12:27 2008 +0200 @@ -462,7 +490,7 @@ And here's what appears in 2.4 : [backport of 2.6 commit b7acbdfbd1f277c1eb23f344f899cfa4cd0bf36a] Whatever the format, this information provides a valuable help to people -tracking your trees, and to people trying to trouble-shoot bugs in your +tracking your trees, and to people trying to troubleshoot bugs in your tree. @@ -558,6 +586,12 @@ method for indicating a bug fixed by the patch. See #2 above for more details. 15) The canonical patch format +------------------------------ + +This section describes how the patch itself should be formatted. Note +that, if you have your patches stored in a git repository, proper patch +formatting can be had with "git format-patch". The tools cannot create +the necessary text, though, so read the instructions below anyway. The canonical patch subject line is: @@ -672,33 +706,57 @@ See more details on the proper patch format in the following references. -16) Sending "git pull" requests (from Linus emails) +16) Sending "git pull" requests +------------------------------- + +If you have a series of patches, it may be most convenient to have the +maintainer pull them directly into the subsystem repository with a +"git pull" operation. Note, however, that pulling patches from a developer +requires a higher degree of trust than taking patches from a mailing list. +As a result, many subsystem maintainers are reluctant to take pull +requests, especially from new, unknown developers. + +A pull request should have [GIT] or [PULL] in the subject line. The +request itself should include the repository name and the branch of +interest on a single line; it should look something like: + + Please pull from -Please write the git repo address and branch name alone on the same line -so that I can't even by mistake pull from the wrong branch, and so -that a triple-click just selects the whole thing. + git://jdelvare.pck.nerim.net/jdelvare-2.6 i2c-for-linus -So the proper format is something along the lines of: + to get these changes:" - "Please pull from +A pull request should also include an overall message saying what will be +included in the request, a "git shortlog" listing of the patches +themselves, and a diffstat showing the overall effect of the patch series. +The easiest way to get all this information together is, of course, to let +git do it for you with the "git request-pull" command. - git://jdelvare.pck.nerim.net/jdelvare-2.6 i2c-for-linus +Some maintainers (including Linus) want to see pull requests from signed +commits; that increases their confidence that the request actually came +from you. Linus, in particular, will not pull from public hosting sites +like GitHub in the absence of a signed tag. - to get these changes:" +The first step toward creating such tags is to make a GNUPG key and get it +signed by one or more core kernel developers. This step can be hard for +new developers, but there is no way around it. Attending conferences can +be a good way to find developers who can sign your key. -so that I don't have to hunt-and-peck for the address and inevitably -get it wrong (actually, I've only gotten it wrong a few times, and -checking against the diffstat tells me when I get it wrong, but I'm -just a lot more comfortable when I don't have to "look for" the right -thing to pull, and double-check that I have the right branch-name). +Once you have prepared a patch series in git that you wish to have somebody +pull, create a signed tag with "git tag -s". This will create a new tag +identifying the last commit in the series and containing a signature +created with your private key. You will also have the opportunity to add a +changelog-style message to the tag; this is an ideal place to describe the +effects of the pull request as a whole. +If the tree the maintainer will be pulling from is not the repository you +are working from, don't forget to push the signed tag explicitly to the +public tree. -Please use "git diff -M --stat --summary" to generate the diffstat: -the -M enables rename detection, and the summary enables a summary of -new/deleted or renamed files. +When generating your pull request, use the signed tag as the target. A +command like this will do the trick: -With rename detection, the statistics are rather different [...] -because git will notice that a fair number of the changes are renames. + git request-pull master git://my.public.tree/linux.git my-signed-tag ---------------------- -- cgit v0.10.2 From ccae8616ecfb9506e7060f77c6cff2b782772fa0 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Tue, 23 Dec 2014 08:49:18 -0700 Subject: Docs: Update recipient information in SubmittingPatches SubmittingPatches had two sections on selecting recipients; both were showing their age. Unify them into a single section that more closely reflects how we do things now. Signed-off-by: Jonathan Corbet diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 230a3b8..e169c6c 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -250,68 +250,68 @@ You should be able to justify all violations that remain in your patch. -5) Select e-mail destination. - -Look through the MAINTAINERS file and the source code, and determine -if your change applies to a specific subsystem of the kernel, with -an assigned maintainer. If so, e-mail that person. The script -scripts/get_maintainer.pl can be very useful at this step. - -If no maintainer is listed, or the maintainer does not respond, send -your patch to the primary Linux kernel developer's mailing list, -linux-kernel@vger.kernel.org. Most kernel developers monitor this -e-mail list, and can comment on your changes. - +5) Select the recipients for your patch. +---------------------------------------- + +You should always copy the appropriate subsystem maintainer(s) on any patch +to code that they maintain; look through the MAINTAINERS file and the +source code revision history to see who those maintainers are. The +script scripts/get_maintainer.pl can be very useful at this step. If you +cannot find a maintainer for the subsystem your are working on, Andrew +Morton (akpm@linux-foundation.org) serves as a maintainer of last resort. + +You should also normally choose at least one mailing list to receive a copy +of your patch set. linux-kernel@vger.kernel.org functions as a list of +last resort, but the volume on that list has caused a number of developers +to tune it out. Look in the MAINTAINERS file for a subsystem-specific +list; your patch will probably get more attention there. Please do not +spam unrelated lists, though. + +Many kernel-related lists are hosted on vger.kernel.org; you can find a +list of them at http://vger.kernel.org/vger-lists.html. There are +kernel-related lists hosted elsewhere as well, though. Do not send more than 15 patches at once to the vger mailing lists!!! - Linus Torvalds is the final arbiter of all changes accepted into the Linux kernel. His e-mail address is . -He gets a lot of e-mail, so typically you should do your best to -avoid- -sending him e-mail. - -Patches which are bug fixes, are "obvious" changes, or similarly -require little discussion should be sent or CC'd to Linus. Patches -which require discussion or do not have a clear advantage should -usually be sent first to linux-kernel. Only after the patch is -discussed should the patch then be submitted to Linus. - - +He gets a lot of e-mail, and, at this point, very few patches go through +Linus directly, so typically you should do your best to -avoid- +sending him e-mail. -6) Select your CC (e-mail carbon copy) list. +If you have a patch that fixes an exploitable security bug, send that patch +to security@kernel.org. For severe bugs, a short embargo may be considered +to allow distrbutors to get the patch out to users; in such cases, +obviously, the patch should not be sent to any public lists. -Unless you have a reason NOT to do so, CC linux-kernel@vger.kernel.org. +Patches that fix a severe bug in a released kernel should be directed +toward the stable maintainers by putting a line like this: -Other kernel developers besides Linus need to be aware of your change, -so that they may comment on it and offer code review and suggestions. -linux-kernel is the primary Linux kernel developer mailing list. -Other mailing lists are available for specific subsystems, such as -USB, framebuffer devices, the VFS, the SCSI subsystem, etc. See the -MAINTAINERS file for a mailing list that relates specifically to -your change. + Cc: stable@vger.kernel.org -Majordomo lists of VGER.KERNEL.ORG at: - +into your patch. -If changes affect userland-kernel interfaces, please send -the MAN-PAGES maintainer (as listed in the MAINTAINERS file) -a man-pages patch, or at least a notification of the change, -so that some information makes its way into the manual pages. +Note, however, that some subsystem maintainers want to come to their own +conclusions on which patches should go to the stable trees. The networking +maintainer, in particular, would rather not see individual developers +adding lines like the above to their patches. -Even if the maintainer did not respond in step #5, make sure to ALWAYS -copy the maintainer when you change their code. +If changes affect userland-kernel interfaces, please send the MAN-PAGES +maintainer (as listed in the MAINTAINERS file) a man-pages patch, or at +least a notification of the change, so that some information makes its way +into the manual pages. User-space API changes should also be copied to +linux-api@vger.kernel.org. For small patches you may want to CC the Trivial Patch Monkey trivial@kernel.org which collects "trivial" patches. Have a look into the MAINTAINERS file for its current manager. Trivial patches must qualify for one of the following rules: Spelling fixes in documentation - Spelling fixes which could break grep(1) + Spelling fixes for errors which could break grep(1) Warning fixes (cluttering with useless warnings is bad) Compilation fixes (only if they are actually correct) Runtime fixes (only if they actually fix things) - Removing use of deprecated functions/macros (eg. check_region) + Removing use of deprecated functions/macros Contact detail and documentation fixes Non-portable code replaced by portable code (even in arch-specific, since people copy, as long as it's trivial) @@ -320,7 +320,7 @@ Trivial patches must qualify for one of the following rules: -7) No MIME, no links, no compression, no attachments. Just plain text. +6) No MIME, no links, no compression, no attachments. Just plain text. Linus and other kernel developers need to be able to read and comment on the changes you are submitting. It is important for a kernel @@ -343,7 +343,7 @@ you to re-send them using MIME. See Documentation/email-clients.txt for hints about configuring your e-mail client so that it sends your patches untouched. -8) E-mail size. +7) E-mail size. When sending patches to Linus, always follow step #7. @@ -354,7 +354,7 @@ server, and provide instead a URL (link) pointing to your patch. -9) Name your kernel version. +8) Name your kernel version. It is important to note, either in the subject line or in the patch description, the kernel version to which this patch applies. @@ -364,7 +364,7 @@ Linus will not apply it. -10) Don't get discouraged. Re-submit. +9) Don't get discouraged. Re-submit. After you have submitted your change, be patient and wait. If Linus likes your change and applies it, it will appear in the next version @@ -390,7 +390,7 @@ When in doubt, solicit comments on linux-kernel mailing list. -11) Include PATCH in the subject +10) Include PATCH in the subject Due to high e-mail traffic to Linus, and to linux-kernel, it is common convention to prefix your subject line with [PATCH]. This lets Linus @@ -399,7 +399,7 @@ e-mail discussions. -12) Sign your work +11) Sign your work To improve tracking of who did what, especially with patches that can percolate to their final resting place in the kernel through several @@ -494,7 +494,7 @@ tracking your trees, and to people trying to troubleshoot bugs in your tree. -13) When to use Acked-by: and Cc: +12) When to use Acked-by: and Cc: The Signed-off-by: tag indicates that the signer was involved in the development of the patch, or that he/she was in the patch's delivery path. @@ -525,7 +525,7 @@ person it names. This tag documents that potentially interested parties have been included in the discussion -14) Using Reported-by:, Tested-by:, Reviewed-by:, Suggested-by: and Fixes: +13) Using Reported-by:, Tested-by:, Reviewed-by:, Suggested-by: and Fixes: The Reported-by tag gives credit to people who find bugs and report them and it hopefully inspires them to help us again in the future. Please note that if @@ -585,7 +585,7 @@ which stable kernel versions should receive your fix. This is the preferred method for indicating a bug fixed by the patch. See #2 above for more details. -15) The canonical patch format +14) The canonical patch format ------------------------------ This section describes how the patch itself should be formatted. Note @@ -599,7 +599,8 @@ The canonical patch subject line is: The canonical patch message body contains the following: - - A "from" line specifying the patch author. + - A "from" line specifying the patch author (only needed if the person + sending the patch is not the author). - An empty line. @@ -706,7 +707,7 @@ See more details on the proper patch format in the following references. -16) Sending "git pull" requests +15) Sending "git pull" requests ------------------------------- If you have a series of patches, it may be most convenient to have the -- cgit v0.10.2 From 0eea2314377146767273eadfc5b34b4f017777b2 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Tue, 23 Dec 2014 08:52:01 -0700 Subject: Docs: SubmittingPatches: update follow-through instructions SubmittingPatches was written in the "keep sending to Linus until something shows up in a release" era. Given that we don't do things that way anymore and the system is far less lossy, update this information and add some hints on responding to reviewer comments. Signed-off-by: Jonathan Corbet diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index e169c6c..a830840 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -354,40 +354,34 @@ server, and provide instead a URL (link) pointing to your patch. -8) Name your kernel version. - -It is important to note, either in the subject line or in the patch -description, the kernel version to which this patch applies. - -If the patch does not apply cleanly to the latest kernel version, -Linus will not apply it. - - +8) Respond to review comments. +------------------------------ -9) Don't get discouraged. Re-submit. +Your patch will almost certainly get comments from reviewers on ways in +which the patch can be improved. You must respond to those comments; +ignoring reviewers is a good way to get ignored in return. Review comments +or questions that do not lead to a code change should almost certainly +bring about a comment or changelog entry so that the next reviewer better +understands what is going on. -After you have submitted your change, be patient and wait. If Linus -likes your change and applies it, it will appear in the next version -of the kernel that he releases. +Be sure to tell the reviewers what changes you are making and to thank them +for their time. Code review is a tiring and time-consuming process, and +reviewers sometimes get grumpy. Even in that case, though, respond +politely and address the problems they have pointed out. -However, if your change doesn't appear in the next version of the -kernel, there could be any number of reasons. It's YOUR job to -narrow down those reasons, correct what was wrong, and submit your -updated change. -It is quite common for Linus to "drop" your patch without comment. -That's the nature of the system. If he drops your patch, it could be -due to -* Your patch did not apply cleanly to the latest kernel version. -* Your patch was not sufficiently discussed on linux-kernel. -* A style issue (see section 2). -* An e-mail formatting issue (re-read this section). -* A technical problem with your change. -* He gets tons of e-mail, and yours got lost in the shuffle. -* You are being annoying. +9) Don't get discouraged - or impatient. +---------------------------------------- -When in doubt, solicit comments on linux-kernel mailing list. +After you have submitted your change, be patient and wait. Reviewers are +busy people and may not get to your patch right away. +Once upon a time, patches used to disappear into the void without comment, +but the development process works more smoothly than that now. You should +receive comments within a week or so; if that does not happen, make sure +that you have sent your patches to the right place. Wait for a minimum of +one week before resubmitting or pinging reviewers - possibly longer during +busy times like merge windows. 10) Include PATCH in the subject -- cgit v0.10.2 From d00c455964002a8e7f126b16051e846a9f9877c6 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Tue, 23 Dec 2014 08:54:36 -0700 Subject: Docs: SubmittingPatches: miscellaneous cleanups Changes to make the formatting a bit more consistent and fix up wording in various places. Signed-off-by: Jonathan Corbet diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index a830840..e6cbe59 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -10,14 +10,18 @@ kernel, the process can sometimes be daunting if you're not familiar with "the system." This text is a collection of suggestions which can greatly increase the chances of your change being accepted. -Read Documentation/SubmitChecklist for a list of items to check -before submitting code. If you are submitting a driver, also read +This document contains a large number of suggestions in a relatively terse +format. For detailed information on how the kernel development process +works, see Documentation/development-process. Also, read +Documentation/SubmitChecklist for a list of items to check before +submitting code. If you are submitting a driver, also read Documentation/SubmittingDrivers. Many of these steps describe the default behavior of the git version control system; if you use git to prepare your patches, you'll find much of the mechanical work done for you, though you'll still need to prepare -and document a sensible set of patches. +and document a sensible set of patches. In general, use of git will make +your life as a kernel developer easier. -------------------------------------------- SECTION 1 - CREATING AND SENDING YOUR CHANGE @@ -59,7 +63,7 @@ not in any lower subdirectory. To create a patch for a single file, it is often sufficient to do: - SRCTREE= linux-2.6 + SRCTREE= linux MYFILE= drivers/net/mydriver.c cd $SRCTREE @@ -72,17 +76,16 @@ To create a patch for multiple files, you should unpack a "vanilla", or unmodified kernel source tree, and generate a diff against your own source tree. For example: - MYSRC= /devel/linux-2.6 + MYSRC= /devel/linux - tar xvfz linux-2.6.12.tar.gz - mv linux-2.6.12 linux-2.6.12-vanilla - diff -uprN -X linux-2.6.12-vanilla/Documentation/dontdiff \ - linux-2.6.12-vanilla $MYSRC > /tmp/patch + tar xvfz linux-3.19.tar.gz + mv linux-3.19 linux-3.19-vanilla + diff -uprN -X linux-3.19-vanilla/Documentation/dontdiff \ + linux-3.19-vanilla $MYSRC > /tmp/patch "dontdiff" is a list of files which are generated by the kernel during the build process, and should be ignored in any diff(1)-generated -patch. The "dontdiff" file is included in the kernel tree in -2.6.12 and later. +patch. Make sure your patch does not include any extra files which do not belong in a patch submission. Make sure to review your patch -after- @@ -100,6 +103,7 @@ is another popular alternative. 2) Describe your changes. +------------------------- Describe your problem. Whether your patch is a one-line bug fix or 5000 lines of a new feature, there must be an underlying problem that @@ -141,10 +145,10 @@ See #3, next. When you submit or resubmit a patch or patch series, include the complete patch description and justification for it. Don't just say that this is version N of the patch (series). Don't expect the -patch merger to refer back to earlier patch versions or referenced +subsystem maintainer to refer back to earlier patch versions or referenced URLs to find the patch description and put that into the patch. I.e., the patch (series) and its description should be self-contained. -This benefits both the patch merger(s) and reviewers. Some reviewers +This benefits both the maintainers and reviewers. Some reviewers probably didn't even receive earlier versions of the patch. Describe your changes in imperative mood, e.g. "make xyzzy do frotz" @@ -194,8 +198,9 @@ outputting the above style in the git log or git show commands fixes = Fixes: %h (\"%s\") 3) Separate your changes. +------------------------- -Separate _logical changes_ into a single patch file. +Separate each _logical change_ into a separate patch. For example, if your changes include both bug fixes and performance enhancements for a single driver, separate those changes into two @@ -206,6 +211,10 @@ On the other hand, if you make a single change to numerous files, group those changes into a single patch. Thus a single logical change is contained within a single patch. +The point to remember is that each patch should make an easily understood +change that can be verified by reviewers. Each patch should be justifiable +on its own merits. + If one patch depends on another patch in order for a change to be complete, that is OK. Simply note "this patch depends on patch X" in your patch description. @@ -321,6 +330,7 @@ Trivial patches must qualify for one of the following rules: 6) No MIME, no links, no compression, no attachments. Just plain text. +----------------------------------------------------------------------- Linus and other kernel developers need to be able to read and comment on the changes you are submitting. It is important for a kernel @@ -344,15 +354,14 @@ See Documentation/email-clients.txt for hints about configuring your e-mail client so that it sends your patches untouched. 7) E-mail size. - -When sending patches to Linus, always follow step #7. +--------------- Large changes are not appropriate for mailing lists, and some maintainers. If your patch, uncompressed, exceeds 300 kB in size, it is preferred that you store your patch on an Internet-accessible -server, and provide instead a URL (link) pointing to your patch. - - +server, and provide instead a URL (link) pointing to your patch. But note +that if your patch exceeds 300 kB, it almost certainly needs to be broken up +anyway. 8) Respond to review comments. ------------------------------ @@ -385,6 +394,7 @@ busy times like merge windows. 10) Include PATCH in the subject +-------------------------------- Due to high e-mail traffic to Linus, and to linux-kernel, it is common convention to prefix your subject line with [PATCH]. This lets Linus @@ -394,6 +404,7 @@ e-mail discussions. 11) Sign your work +------------------ To improve tracking of who did what, especially with patches that can percolate to their final resting place in the kernel through several @@ -489,13 +500,14 @@ tree. 12) When to use Acked-by: and Cc: +--------------------------------- The Signed-off-by: tag indicates that the signer was involved in the development of the patch, or that he/she was in the patch's delivery path. If a person was not directly involved in the preparation or handling of a patch but wishes to signify and record their approval of it then they can -arrange to have an Acked-by: line added to the patch's changelog. +ask to have an Acked-by: line added to the patch's changelog. Acked-by: is often used by the maintainer of the affected code when that maintainer neither contributed to nor forwarded the patch. @@ -503,7 +515,8 @@ maintainer neither contributed to nor forwarded the patch. Acked-by: is not as formal as Signed-off-by:. It is a record that the acker has at least reviewed the patch and has indicated acceptance. Hence patch mergers will sometimes manually convert an acker's "yep, looks good to me" -into an Acked-by:. +into an Acked-by: (but note that it is usually better to ask for an +explicit ack). Acked-by: does not necessarily indicate acknowledgement of the entire patch. For example, if a patch affects multiple subsystems and has an Acked-by: from @@ -515,11 +528,13 @@ list archives. If a person has had the opportunity to comment on a patch, but has not provided such comments, you may optionally add a "Cc:" tag to the patch. This is the only tag which might be added without an explicit action by the -person it names. This tag documents that potentially interested parties -have been included in the discussion +person it names - but it should indicate that this person was copied on the +patch. This tag documents that potentially interested parties +have been included in the discussion. 13) Using Reported-by:, Tested-by:, Reviewed-by:, Suggested-by: and Fixes: +-------------------------------------------------------------------------- The Reported-by tag gives credit to people who find bugs and report them and it hopefully inspires them to help us again in the future. Please note that if -- cgit v0.10.2 From 082bd1ca9a7eaf28695d8bab9adcff54f77c040c Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Tue, 23 Dec 2014 09:27:04 -0700 Subject: Docs: Mention device tree binding info Suggested-by: Frank Rowand Signed-off-by: Jonathan Corbet diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index e6cbe59..1f4e8c8 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -15,7 +15,8 @@ format. For detailed information on how the kernel development process works, see Documentation/development-process. Also, read Documentation/SubmitChecklist for a list of items to check before submitting code. If you are submitting a driver, also read -Documentation/SubmittingDrivers. +Documentation/SubmittingDrivers; for device tree binding patches, read +Documentation/devicetree/bindings/submitting-patches.txt. Many of these steps describe the default behavior of the git version control system; if you use git to prepare your patches, you'll find much -- cgit v0.10.2 From b792ffe464f64c84c48d51e01c0fecabc4b39579 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Tue, 23 Dec 2014 09:28:40 -0700 Subject: Docs: SubmittingPatches: mention using pull requests as a cover letter Suggested-by: Mark Brown Signed-off-by: Jonathan Corbet diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 1f4e8c8..40b619e 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -725,7 +725,9 @@ maintainer pull them directly into the subsystem repository with a "git pull" operation. Note, however, that pulling patches from a developer requires a higher degree of trust than taking patches from a mailing list. As a result, many subsystem maintainers are reluctant to take pull -requests, especially from new, unknown developers. +requests, especially from new, unknown developers. If in doubt you can use +the pull request as the cover letter for a normal posting of the patch +series, giving the maintainer the option of using either. A pull request should have [GIT] or [PULL] in the subject line. The request itself should include the repository name and the branch of -- cgit v0.10.2 From d2fb785227c03443b5df4ea8e4b6f6d8bd2b2d13 Mon Sep 17 00:00:00 2001 From: Benjamin Larsson Date: Wed, 26 Nov 2014 18:51:46 -0300 Subject: [media] mn88472: calculate the IF register values Add xtal as a configuration parameter so it can be used in the IF register value calculation. Signed-off-by: Benjamin Larsson Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/mn88472.h b/drivers/media/dvb-frontends/mn88472.h index da4558b..e4e0b80 100644 --- a/drivers/media/dvb-frontends/mn88472.h +++ b/drivers/media/dvb-frontends/mn88472.h @@ -33,6 +33,12 @@ struct mn88472_config { * DVB frontend. */ struct dvb_frontend **fe; + + /* + * Xtal frequency. + * Hz + */ + u32 xtal; }; #endif diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 896a225..73580f8 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -852,6 +852,7 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) mn88472_config.fe = &adap->fe[1]; mn88472_config.i2c_wr_max = 22, strlcpy(info.type, "mn88472", I2C_NAME_SIZE); + mn88472_config.xtal = 20500000; info.addr = 0x18; info.platform_data = &mn88472_config; request_module(info.type); diff --git a/drivers/staging/media/mn88472/mn88472.c b/drivers/staging/media/mn88472/mn88472.c index 52de8f8..f648b58 100644 --- a/drivers/staging/media/mn88472/mn88472.c +++ b/drivers/staging/media/mn88472/mn88472.c @@ -30,6 +30,7 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; u32 if_frequency = 0; + u64 tmp; u8 delivery_system_val, if_val[3], bw_val[7], bw_val2; dev_dbg(&client->dev, @@ -62,17 +63,14 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) case SYS_DVBT2: if (c->bandwidth_hz <= 6000000) { /* IF 3570000 Hz, BW 6000000 Hz */ - memcpy(if_val, "\x2c\x94\xdb", 3); memcpy(bw_val, "\xbf\x55\x55\x15\x6b\x15\x6b", 7); bw_val2 = 0x02; } else if (c->bandwidth_hz <= 7000000) { /* IF 4570000 Hz, BW 7000000 Hz */ - memcpy(if_val, "\x39\x11\xbc", 3); memcpy(bw_val, "\xa4\x00\x00\x0f\x2c\x0f\x2c", 7); bw_val2 = 0x01; } else if (c->bandwidth_hz <= 8000000) { /* IF 4570000 Hz, BW 8000000 Hz */ - memcpy(if_val, "\x39\x11\xbc", 3); memcpy(bw_val, "\x8f\x80\x00\x08\xee\x08\xee", 7); bw_val2 = 0x00; } else { @@ -82,7 +80,6 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) break; case SYS_DVBC_ANNEX_A: /* IF 5070000 Hz, BW 8000000 Hz */ - memcpy(if_val, "\x3f\x50\x2c", 3); memcpy(bw_val, "\x8f\x80\x00\x08\xee\x08\xee", 7); bw_val2 = 0x00; break; @@ -106,17 +103,12 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) dev_dbg(&client->dev, "get_if_frequency=%d\n", if_frequency); } - switch (if_frequency) { - case 3570000: - case 4570000: - case 5070000: - break; - default: - dev_err(&client->dev, "IF frequency %d not supported\n", - if_frequency); - ret = -EINVAL; - goto err; - } + /* Calculate IF registers ( (1<<24)*IF / Xtal ) */ + tmp = div_u64(if_frequency * (u64)(1<<24) + (dev->xtal / 2), + dev->xtal); + if_val[0] = ((tmp >> 16) & 0xff); + if_val[1] = ((tmp >> 8) & 0xff); + if_val[2] = ((tmp >> 0) & 0xff); ret = regmap_write(dev->regmap[2], 0xfb, 0x13); ret = regmap_write(dev->regmap[2], 0xef, 0x13); @@ -411,6 +403,7 @@ static int mn88472_probe(struct i2c_client *client, } dev->i2c_wr_max = config->i2c_wr_max; + dev->xtal = config->xtal; dev->client[0] = client; dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config); if (IS_ERR(dev->regmap[0])) { diff --git a/drivers/staging/media/mn88472/mn88472_priv.h b/drivers/staging/media/mn88472/mn88472_priv.h index 1095949..b12b731 100644 --- a/drivers/staging/media/mn88472/mn88472_priv.h +++ b/drivers/staging/media/mn88472/mn88472_priv.h @@ -31,6 +31,7 @@ struct mn88472_dev { u16 i2c_wr_max; fe_delivery_system_t delivery_system; bool warm; /* FW running */ + u32 xtal; }; #endif -- cgit v0.10.2 From 645c7cea71a9b519cd9f98e45cbb923ef845b1b2 Mon Sep 17 00:00:00 2001 From: Benjamin Larsson Date: Wed, 26 Nov 2014 18:51:47 -0300 Subject: [media] mn88472: document demod reset Signed-off-by: Benjamin Larsson Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/mn88472/mn88472.c b/drivers/staging/media/mn88472/mn88472.c index f648b58..36ef39b 100644 --- a/drivers/staging/media/mn88472/mn88472.c +++ b/drivers/staging/media/mn88472/mn88472.c @@ -190,6 +190,8 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) ret = regmap_write(dev->regmap[0], 0xae, 0x00); ret = regmap_write(dev->regmap[2], 0x08, 0x1d); ret = regmap_write(dev->regmap[0], 0xd9, 0xe3); + + /* Reset demod */ ret = regmap_write(dev->regmap[2], 0xf8, 0x9f); if (ret) goto err; -- cgit v0.10.2 From bbfebeea7640973613c484f0281bdd15d68fd873 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 20 Dec 2014 09:45:15 -0300 Subject: [media] em28xx: fix em28xx-input removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removing the em28xx-rc module results in the following lockdep splat, which is caused by trying to call cancel_delayed_work_sync() on an uninitialised delayed work. Fix this by ensuring we always initialise the work. INFO: trying to register non-static key. the code is fine but needs lockdep annotation. turning off the locking correctness validator. CPU: 0 PID: 2183 Comm: rmmod Not tainted 3.18.0+ #1464 Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) Backtrace: [] (dump_backtrace) from [] (show_stack+0x18/0x1c) r6:c1419d2c r5:00000000 r4:00000000 r3:00000000 [] (show_stack) from [] (dump_stack+0x7c/0x98) [] (dump_stack) from [] (__lock_acquire+0x16d4/0x1bb0) r4:edf19f74 r3:df049380 [] (__lock_acquire) from [] (lock_acquire+0xb0/0x124) r10:00000000 r9:c003ba90 r8:00000000 r7:00000000 r6:00000000 r5:edf19f74 r4:00000000 [] (lock_acquire) from [] (flush_work+0x44/0x264) r10:00000000 r9:eaa86000 r8:edf190b0 r7:edf19f74 r6:00000001 r5:edf19f64 r4:00000000 [] (flush_work) from [] (__cancel_work_timer+0x8c/0x124) r7:00000000 r6:00000001 r5:00000000 r4:edf19f64 [] (__cancel_work_timer) from [] (cancel_delayed_work_sync+0x14/0x18) r7:00000000 r6:eccc3600 r5:00000000 r4:edf19000 [] (cancel_delayed_work_sync) from [] (em28xx_ir_fini+0x48/0xd8 [em28xx_rc]) [] (em28xx_ir_fini [em28xx_rc]) from [] (em28xx_unregister_extension+0x40/0x94 [em28xx]) r8:c000edc4 r7:00000081 r6:bf092bf4 r5:bf0b6a2c r4:edf19000 r3:bf0b5bc8 [] (em28xx_unregister_extension [em28xx]) from [] (em28xx_rc_unregister+0x14/0x1c [em28xx_rc]) r6:00000800 r5:00000000 r4:bf0b6a50 r3:bf0b64c8 [] (em28xx_rc_unregister [em28xx_rc]) from [] (SyS_delete_module+0x11c/0x180) [] (SyS_delete_module) from [] (ret_fast_syscall+0x0/0x48) r6:00000001 r5:beb0f813 r4:b8b17d00 Fixes: f52226099382 ("[media] em28xx: extend the support for device buttons") Cc: Signed-off-by: Russell King Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index d8dc03a..ef36c49 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -654,8 +654,6 @@ next_button: if (dev->num_button_polling_addresses) { memset(dev->button_polling_last_values, 0, EM28XX_NUM_BUTTON_ADDRESSES_MAX); - INIT_DELAYED_WORK(&dev->buttons_query_work, - em28xx_query_buttons); schedule_delayed_work(&dev->buttons_query_work, msecs_to_jiffies(dev->button_polling_interval)); } @@ -689,6 +687,7 @@ static int em28xx_ir_init(struct em28xx *dev) } kref_get(&dev->ref); + INIT_DELAYED_WORK(&dev->buttons_query_work, em28xx_query_buttons); if (dev->board.buttons) em28xx_init_buttons(dev); -- cgit v0.10.2 From 0418ca6073478f54f1da2e4013fa50d36838de75 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 20 Dec 2014 09:45:20 -0300 Subject: [media] em28xx: ensure "closing" messages terminate with a newline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The lockdep splat addressed in a previous commit revealed that at least one message in em28xx-input.c was missing a new line: em28178 #0: Closing input extensionINFO: trying to register non-static key. Further inspection shows several other messages also miss a new line. These will be fixed in a subsequent patch. Fixes: aa929ad783c0 ("[media] em28xx: print a message at disconnect") Cc: Signed-off-by: Russell King Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 44ae1e0..52dc9d7 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -981,7 +981,7 @@ static int em28xx_audio_fini(struct em28xx *dev) return 0; } - em28xx_info("Closing audio extension"); + em28xx_info("Closing audio extension\n"); if (dev->adev.sndcard) { snd_card_disconnect(dev->adev.sndcard); diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 9877b69..80c384c 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -1724,7 +1724,7 @@ static int em28xx_dvb_fini(struct em28xx *dev) if (!dev->dvb) return 0; - em28xx_info("Closing DVB extension"); + em28xx_info("Closing DVB extension\n"); dvb = dev->dvb; client = dvb->i2c_client_tuner; diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index ef36c49..aea22de 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -832,7 +832,7 @@ static int em28xx_ir_fini(struct em28xx *dev) return 0; } - em28xx_info("Closing input extension"); + em28xx_info("Closing input extension\n"); em28xx_shutdown_buttons(dev); diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 3691b39..eeb4ad5 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1958,7 +1958,7 @@ static int em28xx_v4l2_fini(struct em28xx *dev) if (v4l2 == NULL) return 0; - em28xx_info("Closing video extension"); + em28xx_info("Closing video extension\n"); mutex_lock(&dev->lock); -- cgit v0.10.2 From ebfd59cf549899a166d595bf1eab7eec3299ebe7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 20 Dec 2014 09:45:26 -0300 Subject: [media] em28xx-input: fix missing newlines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inspection shows that newlines are missing from several kernel messages in em28xx-input. Fix these. Fixes: 5025076aadfe ("[media] em28xx-input: implement em28xx_ops: suspend/resume hooks") Cc: Signed-off-by: Russell King Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index aea22de..4007356 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -861,7 +861,7 @@ static int em28xx_ir_suspend(struct em28xx *dev) if (dev->is_audio_only) return 0; - em28xx_info("Suspending input extension"); + em28xx_info("Suspending input extension\n"); if (ir) cancel_delayed_work_sync(&ir->work); cancel_delayed_work_sync(&dev->buttons_query_work); @@ -878,7 +878,7 @@ static int em28xx_ir_resume(struct em28xx *dev) if (dev->is_audio_only) return 0; - em28xx_info("Resuming input extension"); + em28xx_info("Resuming input extension\n"); /* if suspend calls ir_raw_event_unregister(), the should call ir_raw_event_register() */ if (ir) -- cgit v0.10.2 From 522adc7c1f70d302155bb07f7fdf5a7fe4ff9094 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 20 Dec 2014 09:45:31 -0300 Subject: [media] em28xx-core: fix missing newlines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inspection shows that newlines are missing from several kernel messages in em28xx-core. Fix these. Fixes: 9c669b731470 ("[media] em28xx: add suspend/resume to em28xx_ops") Cc: Signed-off-by: Russell King Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 86461a7..3745607 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -1125,7 +1125,7 @@ int em28xx_suspend_extension(struct em28xx *dev) { const struct em28xx_ops *ops = NULL; - em28xx_info("Suspending extensions"); + em28xx_info("Suspending extensions\n"); mutex_lock(&em28xx_devlist_mutex); list_for_each_entry(ops, &em28xx_extension_devlist, next) { if (ops->suspend) @@ -1139,7 +1139,7 @@ int em28xx_resume_extension(struct em28xx *dev) { const struct em28xx_ops *ops = NULL; - em28xx_info("Resuming extensions"); + em28xx_info("Resuming extensions\n"); mutex_lock(&em28xx_devlist_mutex); list_for_each_entry(ops, &em28xx_extension_devlist, next) { if (ops->resume) -- cgit v0.10.2 From 7818b0aab87b680fb10f68eccebeeb6cd8283c73 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 20 Dec 2014 09:45:36 -0300 Subject: [media] em28xx-audio: fix missing newlines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inspection shows that newlines are missing from several kernel messages in em28xx-audio. Fix these. Fixes: 1b3fd2d34266 ("[media] em28xx-audio: don't hardcode audio URB calculus") Cc: Signed-off-by: Russell King Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 52dc9d7..82d58eb 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -820,7 +820,7 @@ static int em28xx_audio_urb_init(struct em28xx *dev) if (urb_size > ep_size * npackets) npackets = DIV_ROUND_UP(urb_size, ep_size); - em28xx_info("Number of URBs: %d, with %d packets and %d size", + em28xx_info("Number of URBs: %d, with %d packets and %d size\n", num_urb, npackets, urb_size); /* Estimate the bytes per period */ -- cgit v0.10.2 From fbaa48d1853002c2e7bcf12c1fdc0f6fb16d1525 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 20 Dec 2014 09:45:41 -0300 Subject: [media] em28xx-audio: fix missing newlines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inspection shows that newlines are missing from several kernel messages in em28xx-audio. Fix these. Fixes: 6d746f91f230 ("[media] em28xx-audio: implement em28xx_ops: suspend/resume hooks") Cc: Signed-off-by: Russell King Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 82d58eb..49a5f95 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -1005,7 +1005,7 @@ static int em28xx_audio_suspend(struct em28xx *dev) if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) return 0; - em28xx_info("Suspending audio extension"); + em28xx_info("Suspending audio extension\n"); em28xx_deinit_isoc_audio(dev); atomic_set(&dev->adev.stream_started, 0); return 0; @@ -1019,7 +1019,7 @@ static int em28xx_audio_resume(struct em28xx *dev) if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) return 0; - em28xx_info("Resuming audio extension"); + em28xx_info("Resuming audio extension\n"); /* Nothing to do other than schedule_work() ?? */ schedule_work(&dev->adev.wq_trigger); return 0; -- cgit v0.10.2 From a084c57fc1ccd24ef8e6ca41e75afa745d5dbb98 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 20 Dec 2014 09:45:46 -0300 Subject: [media] em28xx-dvb: fix missing newlines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inspection shows that newlines are missing from several kernel messages in em28xx-dvb. Fix these. Fixes: ca2b46dacbf5 ("[media] em28xx-dvb: implement em28xx_ops: suspend/resume hooks") Cc: Signed-off-by: Russell King Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 80c384c..aee70d4 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -1775,17 +1775,17 @@ static int em28xx_dvb_suspend(struct em28xx *dev) if (!dev->board.has_dvb) return 0; - em28xx_info("Suspending DVB extension"); + em28xx_info("Suspending DVB extension\n"); if (dev->dvb) { struct em28xx_dvb *dvb = dev->dvb; if (dvb->fe[0]) { ret = dvb_frontend_suspend(dvb->fe[0]); - em28xx_info("fe0 suspend %d", ret); + em28xx_info("fe0 suspend %d\n", ret); } if (dvb->fe[1]) { dvb_frontend_suspend(dvb->fe[1]); - em28xx_info("fe1 suspend %d", ret); + em28xx_info("fe1 suspend %d\n", ret); } } @@ -1802,18 +1802,18 @@ static int em28xx_dvb_resume(struct em28xx *dev) if (!dev->board.has_dvb) return 0; - em28xx_info("Resuming DVB extension"); + em28xx_info("Resuming DVB extension\n"); if (dev->dvb) { struct em28xx_dvb *dvb = dev->dvb; if (dvb->fe[0]) { ret = dvb_frontend_resume(dvb->fe[0]); - em28xx_info("fe0 resume %d", ret); + em28xx_info("fe0 resume %d\n", ret); } if (dvb->fe[1]) { ret = dvb_frontend_resume(dvb->fe[1]); - em28xx_info("fe1 resume %d", ret); + em28xx_info("fe1 resume %d\n", ret); } } -- cgit v0.10.2 From 32e63f0368ed16e5ac417dc0bc2a5f8acbfb1511 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 20 Dec 2014 09:45:51 -0300 Subject: [media] em28xx-video: fix missing newlines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inspection shows that newlines are missing from several kernel messages in em28xx-video. Fix these. Fixes: a61f68119af3 ("[media] em28xx-video: implement em28xx_ops: suspend/resume hooks") Cc: Signed-off-by: Russell King Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index eeb4ad5..9ecf656 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2007,7 +2007,7 @@ static int em28xx_v4l2_suspend(struct em28xx *dev) if (!dev->has_video) return 0; - em28xx_info("Suspending video extension"); + em28xx_info("Suspending video extension\n"); em28xx_stop_urbs(dev); return 0; } @@ -2020,7 +2020,7 @@ static int em28xx_v4l2_resume(struct em28xx *dev) if (!dev->has_video) return 0; - em28xx_info("Resuming video extension"); + em28xx_info("Resuming video extension\n"); /* what do we do here */ return 0; } -- cgit v0.10.2 From ab93ce06eef556cc85a016e12ff7c7a763f04c91 Mon Sep 17 00:00:00 2001 From: Sifan Naeem Date: Thu, 11 Dec 2014 17:06:22 -0300 Subject: [media] rc: img-ir: add scancode requests to a struct The information being requested of hardware decode callbacks through the img-ir-hw scancode API is mounting up, so combine it into a struct which can be passed in with a single pointer rather than multiple pointer arguments. This allows it to be extended more easily without touching all the hardware decode callbacks. Signed-off-by: Sifan Naeem Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index 2fd47c9..88fada5 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -806,20 +806,22 @@ static void img_ir_handle_data(struct img_ir_priv *priv, u32 len, u64 raw) struct img_ir_priv_hw *hw = &priv->hw; const struct img_ir_decoder *dec = hw->decoder; int ret = IMG_IR_SCANCODE; - u32 scancode; - enum rc_type protocol = RC_TYPE_UNKNOWN; + struct img_ir_scancode_req request; + + request.protocol = RC_TYPE_UNKNOWN; if (dec->scancode) - ret = dec->scancode(len, raw, &protocol, &scancode, hw->enabled_protocols); + ret = dec->scancode(len, raw, hw->enabled_protocols, &request); else if (len >= 32) - scancode = (u32)raw; + request.scancode = (u32)raw; else if (len < 32) - scancode = (u32)raw & ((1 << len)-1); + request.scancode = (u32)raw & ((1 << len)-1); dev_dbg(priv->dev, "data (%u bits) = %#llx\n", len, (unsigned long long)raw); if (ret == IMG_IR_SCANCODE) { - dev_dbg(priv->dev, "decoded scan code %#x\n", scancode); - rc_keydown(hw->rdev, protocol, scancode, 0); + dev_dbg(priv->dev, "decoded scan code %#x\n", + request.scancode); + rc_keydown(hw->rdev, request.protocol, request.scancode, 0); img_ir_end_repeat(priv); } else if (ret == IMG_IR_REPEATCODE) { if (hw->mode == IMG_IR_M_REPEATING) { diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h index 5c2b216..aeef3d1 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.h +++ b/drivers/media/rc/img-ir/img-ir-hw.h @@ -133,6 +133,18 @@ struct img_ir_timing_regvals { #define IMG_IR_REPEATCODE 1 /* repeat the previous code */ /** + * struct img_ir_scancode_req - Scancode request data. + * @protocol: Protocol code of received message (defaults to + * RC_TYPE_UNKNOWN). + * @scancode: Scan code of received message (must be written by + * handler if IMG_IR_SCANCODE is returned). + */ +struct img_ir_scancode_req { + enum rc_type protocol; + u32 scancode; +}; + +/** * struct img_ir_decoder - Decoder settings for an IR protocol. * @type: Protocol types bitmap. * @tolerance: Timing tolerance as a percentage (default 10%). @@ -162,8 +174,8 @@ struct img_ir_decoder { struct img_ir_control control; /* scancode logic */ - int (*scancode)(int len, u64 raw, enum rc_type *protocol, - u32 *scancode, u64 enabled_protocols); + int (*scancode)(int len, u64 raw, u64 enabled_protocols, + struct img_ir_scancode_req *request); int (*filter)(const struct rc_scancode_filter *in, struct img_ir_filter *out, u64 protocols); }; diff --git a/drivers/media/rc/img-ir/img-ir-jvc.c b/drivers/media/rc/img-ir/img-ir-jvc.c index a60dda8..d3e2fc0 100644 --- a/drivers/media/rc/img-ir/img-ir-jvc.c +++ b/drivers/media/rc/img-ir/img-ir-jvc.c @@ -12,8 +12,8 @@ #include "img-ir-hw.h" /* Convert JVC data to a scancode */ -static int img_ir_jvc_scancode(int len, u64 raw, enum rc_type *protocol, - u32 *scancode, u64 enabled_protocols) +static int img_ir_jvc_scancode(int len, u64 raw, u64 enabled_protocols, + struct img_ir_scancode_req *request) { unsigned int cust, data; @@ -23,8 +23,8 @@ static int img_ir_jvc_scancode(int len, u64 raw, enum rc_type *protocol, cust = (raw >> 0) & 0xff; data = (raw >> 8) & 0xff; - *protocol = RC_TYPE_JVC; - *scancode = cust << 8 | data; + request->protocol = RC_TYPE_JVC; + request->scancode = cust << 8 | data; return IMG_IR_SCANCODE; } diff --git a/drivers/media/rc/img-ir/img-ir-nec.c b/drivers/media/rc/img-ir/img-ir-nec.c index 7398975..27a7ea8 100644 --- a/drivers/media/rc/img-ir/img-ir-nec.c +++ b/drivers/media/rc/img-ir/img-ir-nec.c @@ -13,8 +13,8 @@ #include /* Convert NEC data to a scancode */ -static int img_ir_nec_scancode(int len, u64 raw, enum rc_type *protocol, - u32 *scancode, u64 enabled_protocols) +static int img_ir_nec_scancode(int len, u64 raw, u64 enabled_protocols, + struct img_ir_scancode_req *request) { unsigned int addr, addr_inv, data, data_inv; /* a repeat code has no data */ @@ -30,23 +30,23 @@ static int img_ir_nec_scancode(int len, u64 raw, enum rc_type *protocol, if ((data_inv ^ data) != 0xff) { /* 32-bit NEC (used by Apple and TiVo remotes) */ /* scan encoding: as transmitted, MSBit = first received bit */ - *scancode = bitrev8(addr) << 24 | - bitrev8(addr_inv) << 16 | - bitrev8(data) << 8 | - bitrev8(data_inv); + request->scancode = bitrev8(addr) << 24 | + bitrev8(addr_inv) << 16 | + bitrev8(data) << 8 | + bitrev8(data_inv); } else if ((addr_inv ^ addr) != 0xff) { /* Extended NEC */ /* scan encoding: AAaaDD */ - *scancode = addr << 16 | - addr_inv << 8 | - data; + request->scancode = addr << 16 | + addr_inv << 8 | + data; } else { /* Normal NEC */ /* scan encoding: AADD */ - *scancode = addr << 8 | - data; + request->scancode = addr << 8 | + data; } - *protocol = RC_TYPE_NEC; + request->protocol = RC_TYPE_NEC; return IMG_IR_SCANCODE; } diff --git a/drivers/media/rc/img-ir/img-ir-sanyo.c b/drivers/media/rc/img-ir/img-ir-sanyo.c index 6b0653e..f394994 100644 --- a/drivers/media/rc/img-ir/img-ir-sanyo.c +++ b/drivers/media/rc/img-ir/img-ir-sanyo.c @@ -23,8 +23,8 @@ #include "img-ir-hw.h" /* Convert Sanyo data to a scancode */ -static int img_ir_sanyo_scancode(int len, u64 raw, enum rc_type *protocol, - u32 *scancode, u64 enabled_protocols) +static int img_ir_sanyo_scancode(int len, u64 raw, u64 enabled_protocols, + struct img_ir_scancode_req *request) { unsigned int addr, addr_inv, data, data_inv; /* a repeat code has no data */ @@ -44,8 +44,8 @@ static int img_ir_sanyo_scancode(int len, u64 raw, enum rc_type *protocol, return -EINVAL; /* Normal Sanyo */ - *protocol = RC_TYPE_SANYO; - *scancode = addr << 8 | data; + request->protocol = RC_TYPE_SANYO; + request->scancode = addr << 8 | data; return IMG_IR_SCANCODE; } diff --git a/drivers/media/rc/img-ir/img-ir-sharp.c b/drivers/media/rc/img-ir/img-ir-sharp.c index 3300a38..fe5acc4 100644 --- a/drivers/media/rc/img-ir/img-ir-sharp.c +++ b/drivers/media/rc/img-ir/img-ir-sharp.c @@ -12,8 +12,8 @@ #include "img-ir-hw.h" /* Convert Sharp data to a scancode */ -static int img_ir_sharp_scancode(int len, u64 raw, enum rc_type *protocol, - u32 *scancode, u64 enabled_protocols) +static int img_ir_sharp_scancode(int len, u64 raw, u64 enabled_protocols, + struct img_ir_scancode_req *request) { unsigned int addr, cmd, exp, chk; @@ -32,8 +32,8 @@ static int img_ir_sharp_scancode(int len, u64 raw, enum rc_type *protocol, /* probably the second half of the message */ return -EINVAL; - *protocol = RC_TYPE_SHARP; - *scancode = addr << 8 | cmd; + request->protocol = RC_TYPE_SHARP; + request->scancode = addr << 8 | cmd; return IMG_IR_SCANCODE; } diff --git a/drivers/media/rc/img-ir/img-ir-sony.c b/drivers/media/rc/img-ir/img-ir-sony.c index 3a0f17b..7f7375f 100644 --- a/drivers/media/rc/img-ir/img-ir-sony.c +++ b/drivers/media/rc/img-ir/img-ir-sony.c @@ -12,8 +12,8 @@ #include "img-ir-hw.h" /* Convert Sony data to a scancode */ -static int img_ir_sony_scancode(int len, u64 raw, enum rc_type *protocol, - u32 *scancode, u64 enabled_protocols) +static int img_ir_sony_scancode(int len, u64 raw, u64 enabled_protocols, + struct img_ir_scancode_req *request) { unsigned int dev, subdev, func; @@ -25,7 +25,7 @@ static int img_ir_sony_scancode(int len, u64 raw, enum rc_type *protocol, raw >>= 7; dev = raw & 0x1f; /* next 5 bits */ subdev = 0; - *protocol = RC_TYPE_SONY12; + request->protocol = RC_TYPE_SONY12; break; case 15: if (!(enabled_protocols & RC_BIT_SONY15)) @@ -34,7 +34,7 @@ static int img_ir_sony_scancode(int len, u64 raw, enum rc_type *protocol, raw >>= 7; dev = raw & 0xff; /* next 8 bits */ subdev = 0; - *protocol = RC_TYPE_SONY15; + request->protocol = RC_TYPE_SONY15; break; case 20: if (!(enabled_protocols & RC_BIT_SONY20)) @@ -44,12 +44,12 @@ static int img_ir_sony_scancode(int len, u64 raw, enum rc_type *protocol, dev = raw & 0x1f; /* next 5 bits */ raw >>= 5; subdev = raw & 0xff; /* next 8 bits */ - *protocol = RC_TYPE_SONY20; + request->protocol = RC_TYPE_SONY20; break; default: return -EINVAL; } - *scancode = dev << 16 | subdev << 8 | func; + request->scancode = dev << 16 | subdev << 8 | func; return IMG_IR_SCANCODE; } -- cgit v0.10.2 From 33e01833268d2f006e599b863a21d4e219f96bd7 Mon Sep 17 00:00:00 2001 From: Sifan Naeem Date: Thu, 11 Dec 2014 17:06:23 -0300 Subject: [media] rc: img-ir: pass toggle bit to the rc driver Add toggle bit to struct img_ir_scancode_req so that protocols can provide it to img_ir_handle_data(), and pass that toggle bit up to rc_keydown instead of 0. This is needed for the upcoming rc-5 and rc-6 patches. Signed-off-by: Sifan Naeem Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index 88fada5..9cecda7 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -809,6 +809,7 @@ static void img_ir_handle_data(struct img_ir_priv *priv, u32 len, u64 raw) struct img_ir_scancode_req request; request.protocol = RC_TYPE_UNKNOWN; + request.toggle = 0; if (dec->scancode) ret = dec->scancode(len, raw, hw->enabled_protocols, &request); @@ -819,9 +820,10 @@ static void img_ir_handle_data(struct img_ir_priv *priv, u32 len, u64 raw) dev_dbg(priv->dev, "data (%u bits) = %#llx\n", len, (unsigned long long)raw); if (ret == IMG_IR_SCANCODE) { - dev_dbg(priv->dev, "decoded scan code %#x\n", - request.scancode); - rc_keydown(hw->rdev, request.protocol, request.scancode, 0); + dev_dbg(priv->dev, "decoded scan code %#x, toggle %u\n", + request.scancode, request.toggle); + rc_keydown(hw->rdev, request.protocol, request.scancode, + request.toggle); img_ir_end_repeat(priv); } else if (ret == IMG_IR_REPEATCODE) { if (hw->mode == IMG_IR_M_REPEATING) { diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h index aeef3d1..beac3a6 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.h +++ b/drivers/media/rc/img-ir/img-ir-hw.h @@ -138,10 +138,12 @@ struct img_ir_timing_regvals { * RC_TYPE_UNKNOWN). * @scancode: Scan code of received message (must be written by * handler if IMG_IR_SCANCODE is returned). + * @toggle: Toggle bit (defaults to 0). */ struct img_ir_scancode_req { enum rc_type protocol; u32 scancode; + u8 toggle; }; /** -- cgit v0.10.2 From 02744c8c9a11a64bce740528077cf5223ab60e31 Mon Sep 17 00:00:00 2001 From: Sifan Naeem Date: Thu, 11 Dec 2014 17:06:24 -0300 Subject: [media] rc: img-ir: biphase enabled with workaround Biphase decoding in the current img-ir has got a quirk, where multiple Interrupts are generated when an incomplete IR code is received by the decoder. Patch adds a work around for the quirk and enables biphase decoding. Signed-off-by: Sifan Naeem Acked-by: James Hogan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index 9cecda7..5c32f05 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -52,6 +52,11 @@ static struct img_ir_decoder *img_ir_decoders[] = { #define IMG_IR_QUIRK_CODE_BROKEN 0x1 /* Decode is broken */ #define IMG_IR_QUIRK_CODE_LEN_INCR 0x2 /* Bit length needs increment */ +/* + * The decoder generates rapid interrupts without actually having + * received any new data after an incomplete IR code is decoded. + */ +#define IMG_IR_QUIRK_CODE_IRQ 0x4 /* functions for preprocessing timings, ensuring max is set */ @@ -542,6 +547,7 @@ static void img_ir_set_decoder(struct img_ir_priv *priv, */ spin_unlock_irq(&priv->lock); del_timer_sync(&hw->end_timer); + del_timer_sync(&hw->suspend_timer); spin_lock_irq(&priv->lock); hw->stopping = false; @@ -861,6 +867,29 @@ static void img_ir_end_timer(unsigned long arg) spin_unlock_irq(&priv->lock); } +/* + * Timer function to re-enable the current protocol after it had been + * cleared when invalid interrupts were generated due to a quirk in the + * img-ir decoder. + */ +static void img_ir_suspend_timer(unsigned long arg) +{ + struct img_ir_priv *priv = (struct img_ir_priv *)arg; + + spin_lock_irq(&priv->lock); + /* + * Don't overwrite enabled valid/match IRQs if they have already been + * changed by e.g. a filter change. + */ + if ((priv->hw.quirk_suspend_irq & IMG_IR_IRQ_EDGE) == + img_ir_read(priv, IMG_IR_IRQ_ENABLE)) + img_ir_write(priv, IMG_IR_IRQ_ENABLE, + priv->hw.quirk_suspend_irq); + /* enable */ + img_ir_write(priv, IMG_IR_CONTROL, priv->hw.reg_timings.ctrl); + spin_unlock_irq(&priv->lock); +} + #ifdef CONFIG_COMMON_CLK static void img_ir_change_frequency(struct img_ir_priv *priv, struct clk_notifier_data *change) @@ -926,15 +955,38 @@ void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status) if (!hw->decoder) return; + ct = hw->decoder->control.code_type; + ir_status = img_ir_read(priv, IMG_IR_STATUS); - if (!(ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2))) + if (!(ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2))) { + if (!(priv->hw.ct_quirks[ct] & IMG_IR_QUIRK_CODE_IRQ) || + hw->stopping) + return; + /* + * The below functionality is added as a work around to stop + * multiple Interrupts generated when an incomplete IR code is + * received by the decoder. + * The decoder generates rapid interrupts without actually + * having received any new data. After a single interrupt it's + * expected to clear up, but instead multiple interrupts are + * rapidly generated. only way to get out of this loop is to + * reset the control register after a short delay. + */ + img_ir_write(priv, IMG_IR_CONTROL, 0); + hw->quirk_suspend_irq = img_ir_read(priv, IMG_IR_IRQ_ENABLE); + img_ir_write(priv, IMG_IR_IRQ_ENABLE, + hw->quirk_suspend_irq & IMG_IR_IRQ_EDGE); + + /* Timer activated to re-enable the protocol. */ + mod_timer(&hw->suspend_timer, + jiffies + msecs_to_jiffies(5)); return; + } ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2); img_ir_write(priv, IMG_IR_STATUS, ir_status); len = (ir_status & IMG_IR_RXDLEN) >> IMG_IR_RXDLEN_SHIFT; /* some versions report wrong length for certain code types */ - ct = hw->decoder->control.code_type; if (hw->ct_quirks[ct] & IMG_IR_QUIRK_CODE_LEN_INCR) ++len; @@ -976,7 +1028,7 @@ static void img_ir_probe_hw_caps(struct img_ir_priv *priv) hw->ct_quirks[IMG_IR_CODETYPE_PULSELEN] |= IMG_IR_QUIRK_CODE_LEN_INCR; hw->ct_quirks[IMG_IR_CODETYPE_BIPHASE] - |= IMG_IR_QUIRK_CODE_BROKEN; + |= IMG_IR_QUIRK_CODE_IRQ; hw->ct_quirks[IMG_IR_CODETYPE_2BITPULSEPOS] |= IMG_IR_QUIRK_CODE_BROKEN; } @@ -995,6 +1047,8 @@ int img_ir_probe_hw(struct img_ir_priv *priv) /* Set up the end timer */ setup_timer(&hw->end_timer, img_ir_end_timer, (unsigned long)priv); + setup_timer(&hw->suspend_timer, img_ir_suspend_timer, + (unsigned long)priv); /* Register a clock notifier */ if (!IS_ERR(priv->clk)) { diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h index beac3a6..b31ffc9 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.h +++ b/drivers/media/rc/img-ir/img-ir-hw.h @@ -218,6 +218,7 @@ enum img_ir_mode { * @rdev: Remote control device * @clk_nb: Notifier block for clock notify events. * @end_timer: Timer until repeat timeout. + * @suspend_timer: Timer to re-enable protocol. * @decoder: Current decoder settings. * @enabled_protocols: Currently enabled protocols. * @clk_hz: Current core clock rate in Hz. @@ -228,12 +229,14 @@ enum img_ir_mode { * @stopping: Indicates that decoder is being taken down and timers * should not be restarted. * @suspend_irqen: Saved IRQ enable mask over suspend. + * @quirk_suspend_irq: Saved IRQ enable mask over quirk suspend timer. */ struct img_ir_priv_hw { unsigned int ct_quirks[4]; struct rc_dev *rdev; struct notifier_block clk_nb; struct timer_list end_timer; + struct timer_list suspend_timer; const struct img_ir_decoder *decoder; u64 enabled_protocols; unsigned long clk_hz; @@ -244,6 +247,7 @@ struct img_ir_priv_hw { enum img_ir_mode mode; bool stopping; u32 suspend_irqen; + u32 quirk_suspend_irq; }; static inline bool img_ir_hw_enabled(struct img_ir_priv_hw *hw) -- cgit v0.10.2 From aa7383db35aec23906868a632a964b5783a5254d Mon Sep 17 00:00:00 2001 From: Sifan Naeem Date: Thu, 11 Dec 2014 17:06:25 -0300 Subject: [media] rc: img-ir: add philips rc5 decoder module Add img-ir module for decoding Philips rc5 protocol. Signed-off-by: Sifan Naeem Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig index 580715c..b20b3e9 100644 --- a/drivers/media/rc/img-ir/Kconfig +++ b/drivers/media/rc/img-ir/Kconfig @@ -60,3 +60,10 @@ config IR_IMG_SANYO help Say Y here to enable support for the Sanyo protocol (used by Sanyo, Aiwa, Chinon remotes) in the ImgTec infrared decoder block. + +config IR_IMG_RC5 + bool "Philips RC5 protocol support" + depends on IR_IMG_HW + help + Say Y here to enable support for the RC5 protocol in the ImgTec + infrared decoder block. diff --git a/drivers/media/rc/img-ir/Makefile b/drivers/media/rc/img-ir/Makefile index 92a459d..898b1b8 100644 --- a/drivers/media/rc/img-ir/Makefile +++ b/drivers/media/rc/img-ir/Makefile @@ -6,6 +6,7 @@ img-ir-$(CONFIG_IR_IMG_JVC) += img-ir-jvc.o img-ir-$(CONFIG_IR_IMG_SONY) += img-ir-sony.o img-ir-$(CONFIG_IR_IMG_SHARP) += img-ir-sharp.o img-ir-$(CONFIG_IR_IMG_SANYO) += img-ir-sanyo.o +img-ir-$(CONFIG_IR_IMG_RC5) += img-ir-rc5.o img-ir-objs := $(img-ir-y) obj-$(CONFIG_IR_IMG) += img-ir.o diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index 5c32f05..13f0b1e 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -42,6 +42,9 @@ static struct img_ir_decoder *img_ir_decoders[] = { #ifdef CONFIG_IR_IMG_SANYO &img_ir_sanyo, #endif +#ifdef CONFIG_IR_IMG_RC5 + &img_ir_rc5, +#endif NULL }; diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h index b31ffc9..b9e799d5 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.h +++ b/drivers/media/rc/img-ir/img-ir-hw.h @@ -187,6 +187,7 @@ extern struct img_ir_decoder img_ir_jvc; extern struct img_ir_decoder img_ir_sony; extern struct img_ir_decoder img_ir_sharp; extern struct img_ir_decoder img_ir_sanyo; +extern struct img_ir_decoder img_ir_rc5; /** * struct img_ir_reg_timings - Reg values for decoder timings at clock rate. diff --git a/drivers/media/rc/img-ir/img-ir-rc5.c b/drivers/media/rc/img-ir/img-ir-rc5.c new file mode 100644 index 0000000..a8a28a3 --- /dev/null +++ b/drivers/media/rc/img-ir/img-ir-rc5.c @@ -0,0 +1,88 @@ +/* + * ImgTec IR Decoder setup for Philips RC-5 protocol. + * + * Copyright 2012-2014 Imagination Technologies Ltd. + * + * 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 "img-ir-hw.h" + +/* Convert RC5 data to a scancode */ +static int img_ir_rc5_scancode(int len, u64 raw, u64 enabled_protocols, + struct img_ir_scancode_req *request) +{ + unsigned int addr, cmd, tgl, start; + + /* Quirk in the decoder shifts everything by 2 to the left. */ + raw >>= 2; + + start = (raw >> 13) & 0x01; + tgl = (raw >> 11) & 0x01; + addr = (raw >> 6) & 0x1f; + cmd = raw & 0x3f; + /* + * 12th bit is used to extend the command in extended RC5 and has + * no effect on standard RC5. + */ + cmd += ((raw >> 12) & 0x01) ? 0 : 0x40; + + if (!start) + return -EINVAL; + + request->protocol = RC_TYPE_RC5; + request->scancode = addr << 8 | cmd; + request->toggle = tgl; + return IMG_IR_SCANCODE; +} + +/* Convert RC5 scancode to RC5 data filter */ +static int img_ir_rc5_filter(const struct rc_scancode_filter *in, + struct img_ir_filter *out, u64 protocols) +{ + /* Not supported by the hw. */ + return -EINVAL; +} + +/* + * RC-5 decoder + * see http://www.sbprojects.com/knowledge/ir/rc5.php + */ +struct img_ir_decoder img_ir_rc5 = { + .type = RC_BIT_RC5, + .control = { + .bitoriend2 = 1, + .code_type = IMG_IR_CODETYPE_BIPHASE, + .decodend2 = 1, + }, + /* main timings */ + .tolerance = 16, + .unit = 888888, /* 1/36k*32=888.888microseconds */ + .timings = { + /* 10 symbol */ + .s10 = { + .pulse = { 1 }, + .space = { 1 }, + }, + + /* 11 symbol */ + .s11 = { + .pulse = { 1 }, + .space = { 1 }, + }, + + /* free time */ + .ft = { + .minlen = 14, + .maxlen = 14, + .ft_min = 5, + }, + }, + + /* scancode logic */ + .scancode = img_ir_rc5_scancode, + .filter = img_ir_rc5_filter, +}; -- cgit v0.10.2 From cb9564e133f4f790920d715714790512085bb2e3 Mon Sep 17 00:00:00 2001 From: Sifan Naeem Date: Thu, 11 Dec 2014 17:06:26 -0300 Subject: [media] rc: img-ir: add philips rc6 decoder module Add img-ir module for decoding Philips rc6 protocol. Signed-off-by: Sifan Naeem Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig index b20b3e9..a896d3c 100644 --- a/drivers/media/rc/img-ir/Kconfig +++ b/drivers/media/rc/img-ir/Kconfig @@ -67,3 +67,11 @@ config IR_IMG_RC5 help Say Y here to enable support for the RC5 protocol in the ImgTec infrared decoder block. + +config IR_IMG_RC6 + bool "Philips RC6 protocol support" + depends on IR_IMG_HW + help + Say Y here to enable support for the RC6 protocol in the ImgTec + infrared decoder block. + Note: This version only supports mode 0. diff --git a/drivers/media/rc/img-ir/Makefile b/drivers/media/rc/img-ir/Makefile index 898b1b8..8e6d458 100644 --- a/drivers/media/rc/img-ir/Makefile +++ b/drivers/media/rc/img-ir/Makefile @@ -7,6 +7,7 @@ img-ir-$(CONFIG_IR_IMG_SONY) += img-ir-sony.o img-ir-$(CONFIG_IR_IMG_SHARP) += img-ir-sharp.o img-ir-$(CONFIG_IR_IMG_SANYO) += img-ir-sanyo.o img-ir-$(CONFIG_IR_IMG_RC5) += img-ir-rc5.o +img-ir-$(CONFIG_IR_IMG_RC6) += img-ir-rc6.o img-ir-objs := $(img-ir-y) obj-$(CONFIG_IR_IMG) += img-ir.o diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index 13f0b1e..7bb71bc 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -45,6 +45,9 @@ static struct img_ir_decoder *img_ir_decoders[] = { #ifdef CONFIG_IR_IMG_RC5 &img_ir_rc5, #endif +#ifdef CONFIG_IR_IMG_RC6 + &img_ir_rc6, +#endif NULL }; diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h index b9e799d5..91a2977 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.h +++ b/drivers/media/rc/img-ir/img-ir-hw.h @@ -188,6 +188,7 @@ extern struct img_ir_decoder img_ir_sony; extern struct img_ir_decoder img_ir_sharp; extern struct img_ir_decoder img_ir_sanyo; extern struct img_ir_decoder img_ir_rc5; +extern struct img_ir_decoder img_ir_rc6; /** * struct img_ir_reg_timings - Reg values for decoder timings at clock rate. diff --git a/drivers/media/rc/img-ir/img-ir-rc6.c b/drivers/media/rc/img-ir/img-ir-rc6.c new file mode 100644 index 0000000..de1e275 --- /dev/null +++ b/drivers/media/rc/img-ir/img-ir-rc6.c @@ -0,0 +1,117 @@ +/* + * ImgTec IR Decoder setup for Philips RC-6 protocol. + * + * Copyright 2012-2014 Imagination Technologies Ltd. + * + * 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 "img-ir-hw.h" + +/* Convert RC6 data to a scancode */ +static int img_ir_rc6_scancode(int len, u64 raw, u64 enabled_protocols, + struct img_ir_scancode_req *request) +{ + unsigned int addr, cmd, mode, trl1, trl2; + + /* + * Due to a side effect of the decoder handling the double length + * Trailer bit, the header information is a bit scrambled, and the + * raw data is shifted incorrectly. + * This workaround effectively recovers the header bits. + * + * The Header field should look like this: + * + * StartBit ModeBit2 ModeBit1 ModeBit0 TrailerBit + * + * But what we get is: + * + * ModeBit2 ModeBit1 ModeBit0 TrailerBit1 TrailerBit2 + * + * The start bit is not important to recover the scancode. + */ + + raw >>= 27; + + trl1 = (raw >> 17) & 0x01; + trl2 = (raw >> 16) & 0x01; + + mode = (raw >> 18) & 0x07; + addr = (raw >> 8) & 0xff; + cmd = raw & 0xff; + + /* + * Due to the above explained irregularity the trailer bits cannot + * have the same value. + */ + if (trl1 == trl2) + return -EINVAL; + + /* Only mode 0 supported for now */ + if (mode) + return -EINVAL; + + request->protocol = RC_TYPE_RC6_0; + request->scancode = addr << 8 | cmd; + request->toggle = trl2; + return IMG_IR_SCANCODE; +} + +/* Convert RC6 scancode to RC6 data filter */ +static int img_ir_rc6_filter(const struct rc_scancode_filter *in, + struct img_ir_filter *out, u64 protocols) +{ + /* Not supported by the hw. */ + return -EINVAL; +} + +/* + * RC-6 decoder + * see http://www.sbprojects.com/knowledge/ir/rc6.php + */ +struct img_ir_decoder img_ir_rc6 = { + .type = RC_BIT_RC6_0, + .control = { + .bitorien = 1, + .code_type = IMG_IR_CODETYPE_BIPHASE, + .decoden = 1, + .decodinpol = 1, + }, + /* main timings */ + .tolerance = 20, + /* + * Due to a quirk in the img-ir decoder, default header values do + * not work, the values described below were extracted from + * successful RTL test cases. + */ + .timings = { + /* leader symbol */ + .ldr = { + .pulse = { 650 }, + .space = { 660 }, + }, + /* 0 symbol */ + .s00 = { + .pulse = { 370 }, + .space = { 370 }, + }, + /* 01 symbol */ + .s01 = { + .pulse = { 370 }, + .space = { 370 }, + }, + /* free time */ + .ft = { + .minlen = 21, + .maxlen = 21, + .ft_min = 2666, /* 2.666 ms */ + }, + }, + + /* scancode logic */ + .scancode = img_ir_rc6_scancode, + .filter = img_ir_rc6_filter, +}; -- cgit v0.10.2 From 221ca91220a9a9efe27cde2ae4ef16e7dd44db69 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Wed, 10 Dec 2014 21:22:01 -0300 Subject: [media] staging: media: lirc: lirc_zilog.c: fix quoted strings split across lines checkpatch makes an exception to the 80-colum rule for quotes strings, and Documentation/CodingStyle recommends not splitting quotes strings across lines because it breaks the ability to grep for the string. Fixing these. WARNING: quoted string split across lines Signed-off-by: Luis de Bethencourt Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index cc872fb..0d29c8e 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -372,14 +372,14 @@ static int add_to_buf(struct IR *ir) ret); if (failures >= 3) { mutex_unlock(&ir->ir_lock); - dev_err(ir->l.dev, "unable to read from the IR chip " - "after 3 resets, giving up\n"); + dev_err(ir->l.dev, + "unable to read from the IR chip after 3 resets, giving up\n"); break; } /* Looks like the chip crashed, reset it */ - dev_err(ir->l.dev, "polling the IR receiver chip failed, " - "trying reset\n"); + dev_err(ir->l.dev, + "polling the IR receiver chip failed, trying reset\n"); set_current_state(TASK_UNINTERRUPTIBLE); if (kthread_should_stop()) { @@ -405,8 +405,9 @@ static int add_to_buf(struct IR *ir) ret = i2c_master_recv(rx->c, keybuf, sizeof(keybuf)); mutex_unlock(&ir->ir_lock); if (ret != sizeof(keybuf)) { - dev_err(ir->l.dev, "i2c_master_recv failed with %d -- " - "keeping last read buffer\n", ret); + dev_err(ir->l.dev, + "i2c_master_recv failed with %d -- keeping last read buffer\n", + ret); } else { rx->b[0] = keybuf[3]; rx->b[1] = keybuf[4]; @@ -713,8 +714,9 @@ static int send_boot_data(struct IR_tx *tx) buf[0]); return 0; } - dev_notice(tx->ir->l.dev, "Zilog/Hauppauge IR blaster firmware version " - "%d.%d.%d loaded\n", buf[1], buf[2], buf[3]); + dev_notice(tx->ir->l.dev, + "Zilog/Hauppauge IR blaster firmware version %d.%d.%d loaded\n", + buf[1], buf[2], buf[3]); return 0; } @@ -792,9 +794,9 @@ static int fw_load(struct IR_tx *tx) if (!read_uint8(&data, tx_data->endp, &version)) goto corrupt; if (version != 1) { - dev_err(tx->ir->l.dev, "unsupported code set file version (%u, expected" - "1) -- please upgrade to a newer driver", - version); + dev_err(tx->ir->l.dev, + "unsupported code set file version (%u, expected 1) -- please upgrade to a newer driver", + version); fw_unload_locked(); ret = -EFAULT; goto out; @@ -981,8 +983,9 @@ static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key) ret = get_key_data(data_block, code, key); if (ret == -EPROTO) { - dev_err(tx->ir->l.dev, "failed to get data for code %u, key %u -- check " - "lircd.conf entries\n", code, key); + dev_err(tx->ir->l.dev, + "failed to get data for code %u, key %u -- check lircd.conf entries\n", + code, key); return ret; } else if (ret != 0) return ret; @@ -1057,12 +1060,14 @@ static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key) ret = i2c_master_send(tx->c, buf, 1); if (ret == 1) break; - dev_dbg(tx->ir->l.dev, "NAK expected: i2c_master_send " - "failed with %d (try %d)\n", ret, i+1); + dev_dbg(tx->ir->l.dev, + "NAK expected: i2c_master_send failed with %d (try %d)\n", + ret, i+1); } if (ret != 1) { - dev_err(tx->ir->l.dev, "IR TX chip never got ready: last i2c_master_send " - "failed with %d\n", ret); + dev_err(tx->ir->l.dev, + "IR TX chip never got ready: last i2c_master_send failed with %d\n", + ret); return ret < 0 ? ret : -EFAULT; } @@ -1165,12 +1170,12 @@ static ssize_t write(struct file *filep, const char __user *buf, size_t n, */ if (ret != 0) { /* Looks like the chip crashed, reset it */ - dev_err(tx->ir->l.dev, "sending to the IR transmitter chip " - "failed, trying reset\n"); + dev_err(tx->ir->l.dev, + "sending to the IR transmitter chip failed, trying reset\n"); if (failures >= 3) { - dev_err(tx->ir->l.dev, "unable to send to the IR chip " - "after 3 resets, giving up\n"); + dev_err(tx->ir->l.dev, + "unable to send to the IR chip after 3 resets, giving up\n"); mutex_unlock(&ir->ir_lock); mutex_unlock(&tx->client_lock); put_ir_tx(tx, false); @@ -1540,8 +1545,9 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Proceed only if the Rx client is also ready or not needed */ if (rx == NULL && !tx_only) { - dev_info(tx->ir->l.dev, "probe of IR Tx on %s (i2c-%d) done. Waiting" - " on IR Rx.\n", adap->name, adap->nr); + dev_info(tx->ir->l.dev, + "probe of IR Tx on %s (i2c-%d) done. Waiting on IR Rx.\n", + adap->name, adap->nr); goto out_ok; } } else { @@ -1579,8 +1585,9 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) "zilog-rx-i2c-%d", adap->nr); if (IS_ERR(rx->task)) { ret = PTR_ERR(rx->task); - dev_err(tx->ir->l.dev, "%s: could not start IR Rx polling thread" - "\n", __func__); + dev_err(tx->ir->l.dev, + "%s: could not start IR Rx polling thread\n", + __func__); /* Failed kthread, so put back the ir ref */ put_ir_device(ir, true); /* Failure exit, so put back rx ref from i2c_client */ @@ -1592,8 +1599,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Proceed only if the Tx client is also ready */ if (tx == NULL) { - pr_info("probe of IR Rx on %s (i2c-%d) done. Waiting" - " on IR Tx.\n", adap->name, adap->nr); + pr_info("probe of IR Rx on %s (i2c-%d) done. Waiting on IR Tx.\n", + adap->name, adap->nr); goto out_ok; } } -- cgit v0.10.2 From 2ecd424006d0e17c526a85e7d5ac4ace476b0dbb Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Wed, 10 Dec 2014 21:22:52 -0300 Subject: [media] staging: media: lirc: lirc_zilog.c: keep consistency in dev functions The previous patch switched some dev functions to move the string to a second line. Doing this for all similar functions because it makes the driver easier to read if all similar lines use the same criteria. Signed-off-by: Luis de Bethencourt Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index 0d29c8e..20fa401 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -369,7 +369,7 @@ static int add_to_buf(struct IR *ir) ret = i2c_master_send(rx->c, sendbuf, 1); if (ret != 1) { dev_err(ir->l.dev, "i2c_master_send failed with %d\n", - ret); + ret); if (failures >= 3) { mutex_unlock(&ir->ir_lock); dev_err(ir->l.dev, @@ -412,8 +412,9 @@ static int add_to_buf(struct IR *ir) rx->b[0] = keybuf[3]; rx->b[1] = keybuf[4]; rx->b[2] = keybuf[5]; - dev_dbg(ir->l.dev, "key (0x%02x/0x%02x)\n", - rx->b[0], rx->b[1]); + dev_dbg(ir->l.dev, + "key (0x%02x/0x%02x)\n", + rx->b[0], rx->b[1]); } /* key pressed ? */ @@ -657,8 +658,8 @@ static int send_data_block(struct IR_tx *tx, unsigned char *data_block) dev_dbg(tx->ir->l.dev, "%*ph", 5, buf); ret = i2c_master_send(tx->c, buf, tosend + 1); if (ret != tosend + 1) { - dev_err(tx->ir->l.dev, "i2c_master_send failed with %d\n", - ret); + dev_err(tx->ir->l.dev, + "i2c_master_send failed with %d\n", ret); return ret < 0 ? ret : -EFAULT; } i += tosend; @@ -711,7 +712,7 @@ static int send_boot_data(struct IR_tx *tx) } if ((buf[0] != 0x80) && (buf[0] != 0xa0)) { dev_err(tx->ir->l.dev, "unexpected IR TX init response: %02x\n", - buf[0]); + buf[0]); return 0; } dev_notice(tx->ir->l.dev, @@ -761,8 +762,9 @@ static int fw_load(struct IR_tx *tx) /* Request codeset data file */ ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", tx->ir->l.dev); if (ret != 0) { - dev_err(tx->ir->l.dev, "firmware haup-ir-blaster.bin not available (%d)\n", - ret); + dev_err(tx->ir->l.dev, + "firmware haup-ir-blaster.bin not available (%d)\n", + ret); ret = ret < 0 ? ret : -EFAULT; goto out; } @@ -812,7 +814,7 @@ static int fw_load(struct IR_tx *tx) goto corrupt; dev_dbg(tx->ir->l.dev, "%u IR blaster codesets loaded\n", - tx_data->num_code_sets); + tx_data->num_code_sets); tx_data->code_sets = vmalloc( tx_data->num_code_sets * sizeof(char *)); @@ -942,8 +944,9 @@ static ssize_t read(struct file *filep, char __user *outbuf, size_t n, unsigned char buf[MAX_XFER_SIZE]; if (rbuf->chunk_size > sizeof(buf)) { - dev_err(ir->l.dev, "chunk_size is too big (%d)!\n", - rbuf->chunk_size); + dev_err(ir->l.dev, + "chunk_size is too big (%d)!\n", + rbuf->chunk_size); ret = -EINVAL; break; } @@ -966,8 +969,8 @@ static ssize_t read(struct file *filep, char __user *outbuf, size_t n, put_ir_rx(rx, false); set_current_state(TASK_RUNNING); - dev_dbg(ir->l.dev, "read result = %d (%s)\n", - ret, ret ? "Error" : "OK"); + dev_dbg(ir->l.dev, "read result = %d (%s)\n", ret, + ret ? "Error" : "OK"); return ret ? ret : written; } @@ -1079,7 +1082,7 @@ static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key) } if (buf[0] != 0x80) { dev_err(tx->ir->l.dev, "unexpected IR TX response #2: %02x\n", - buf[0]); + buf[0]); return -EFAULT; } @@ -1231,7 +1234,7 @@ static unsigned int poll(struct file *filep, poll_table *wait) ret = lirc_buffer_empty(rbuf) ? 0 : (POLLIN|POLLRDNORM); dev_dbg(ir->l.dev, "poll result = %s\n", - ret ? "POLLIN|POLLRDNORM" : "none"); + ret ? "POLLIN|POLLRDNORM" : "none"); return ret; } @@ -1338,7 +1341,8 @@ static int close(struct inode *node, struct file *filep) struct IR *ir = filep->private_data; if (ir == NULL) { - dev_err(ir->l.dev, "close: no private_data attached to the file!\n"); + dev_err(ir->l.dev, + "close: no private_data attached to the file!\n"); return -ENODEV; } @@ -1609,13 +1613,15 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->l.minor = minor; /* module option: user requested minor number */ ir->l.minor = lirc_register_driver(&ir->l); if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) { - dev_err(tx->ir->l.dev, "%s: \"minor\" must be between 0 and %d (%d)!\n", - __func__, MAX_IRCTL_DEVICES-1, ir->l.minor); + dev_err(tx->ir->l.dev, + "%s: \"minor\" must be between 0 and %d (%d)!\n", + __func__, MAX_IRCTL_DEVICES-1, ir->l.minor); ret = -EBADRQC; goto out_put_xx; } - dev_info(ir->l.dev, "IR unit on %s (i2c-%d) registered as lirc%d and ready\n", - adap->name, adap->nr, ir->l.minor); + dev_info(ir->l.dev, + "IR unit on %s (i2c-%d) registered as lirc%d and ready\n", + adap->name, adap->nr, ir->l.minor); out_ok: if (rx != NULL) @@ -1623,8 +1629,9 @@ out_ok: if (tx != NULL) put_ir_tx(tx, true); put_ir_device(ir, true); - dev_info(ir->l.dev, "probe of IR %s on %s (i2c-%d) done\n", - tx_probe ? "Tx" : "Rx", adap->name, adap->nr); + dev_info(ir->l.dev, + "probe of IR %s on %s (i2c-%d) done\n", + tx_probe ? "Tx" : "Rx", adap->name, adap->nr); mutex_unlock(&ir_devices_lock); return 0; @@ -1636,9 +1643,9 @@ out_put_xx: out_put_ir: put_ir_device(ir, true); out_no_ir: - dev_err(&client->dev, "%s: probing IR %s on %s (i2c-%d) failed with %d\n", - __func__, tx_probe ? "Tx" : "Rx", adap->name, adap->nr, - ret); + dev_err(&client->dev, + "%s: probing IR %s on %s (i2c-%d) failed with %d\n", + __func__, tx_probe ? "Tx" : "Rx", adap->name, adap->nr, ret); mutex_unlock(&ir_devices_lock); return ret; } -- cgit v0.10.2 From 40b8a5a65aa3ba51a80f7bd667df8403d54d6e7a Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Wed, 10 Dec 2014 21:23:42 -0300 Subject: [media] staging: media: lirc: lirc_zilog.c: missing newline in dev_err() Missing newline character at the end of string passed to dev_err() Signed-off-by: Luis de Bethencourt Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index 20fa401..e16627c 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -797,7 +797,7 @@ static int fw_load(struct IR_tx *tx) goto corrupt; if (version != 1) { dev_err(tx->ir->l.dev, - "unsupported code set file version (%u, expected 1) -- please upgrade to a newer driver", + "unsupported code set file version (%u, expected 1) -- please upgrade to a newer driver\n", version); fw_unload_locked(); ret = -EFAULT; -- cgit v0.10.2 From 983c5bd26b86ba1c0d79b770e596bb8b77e42f32 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 8 Dec 2014 13:17:07 -0300 Subject: [media] rc-main: Re-apply filter for no-op protocol change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit da6e162d6a46 ("[media] rc-core: simplify sysfs code"), when the IR protocol is set using the sysfs interface to the same set of protocols that are already set, store_protocols() does not refresh the scancode filter with the new protocol, even if it has already called the change_protocol() callback successfully. This results in the filter being disabled in the hardware and not re-enabled until the filter is set again using sysfs. Fix in store_protocols() by still re-applying the filter whenever the change_protocol() driver callback succeeded. The problem can be reproduced with the img-ir driver by setting a filter, and then setting the protocol to the same protocol that is already set: $ echo nec > protocols $ echo 0xffff > filter_mask $ echo nec > protocols After this, messages which don't match the filter were still being received. Fixes: da6e162d6a46 ("[media] rc-core: simplify sysfs code") Reported-by: Sifan Naeem Signed-off-by: James Hogan Cc: David Härdeman Cc: # v3.17+ Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 86ffcd5..f8c5e47 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -1021,16 +1021,16 @@ static ssize_t store_protocols(struct device *device, goto out; } - if (new_protocols == old_protocols) { - rc = len; - goto out; + if (new_protocols != old_protocols) { + *current_protocols = new_protocols; + IR_dprintk(1, "Protocols changed to 0x%llx\n", + (long long)new_protocols); } - *current_protocols = new_protocols; - IR_dprintk(1, "Protocols changed to 0x%llx\n", (long long)new_protocols); - /* - * If the protocol is changed the filter needs updating. + * If a protocol change was attempted the filter may need updating, even + * if the actual protocol mask hasn't changed (since the driver may have + * cleared the filter). * Try setting the same filter with the new protocol (if any). * Fall back to clearing the filter. */ -- cgit v0.10.2 From 99f3cd52aee21091ce62442285a68873e3be833f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 15 Dec 2014 10:40:28 -0300 Subject: [media] vb2-vmalloc: Protect DMA-specific code by #ifdef CONFIG_HAS_DMA If NO_DMA=y: drivers/built-in.o: In function `vb2_vmalloc_dmabuf_ops_detach': videobuf2-vmalloc.c:(.text+0x6f11b0): undefined reference to `dma_unmap_sg' drivers/built-in.o: In function `vb2_vmalloc_dmabuf_ops_map': videobuf2-vmalloc.c:(.text+0x6f1266): undefined reference to `dma_unmap_sg' videobuf2-vmalloc.c:(.text+0x6f1282): undefined reference to `dma_map_sg' As we don't want to make the core VIDEOBUF2_VMALLOC depend on HAS_DMA (it's v4l2 core code, and selected by a lot of drivers), stub out the DMA support if HAS_DMA is not set. Signed-off-by: Geert Uytterhoeven Acked-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c index 7f6d41b..bcde885 100644 --- a/drivers/media/v4l2-core/videobuf2-vmalloc.c +++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c @@ -211,6 +211,7 @@ static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma) return 0; } +#ifdef CONFIG_HAS_DMA /*********************************************/ /* DMABUF ops for exporters */ /*********************************************/ @@ -380,6 +381,8 @@ static struct dma_buf *vb2_vmalloc_get_dmabuf(void *buf_priv, unsigned long flag return dbuf; } +#endif /* CONFIG_HAS_DMA */ + /*********************************************/ /* callbacks for DMABUF buffers */ @@ -437,7 +440,9 @@ const struct vb2_mem_ops vb2_vmalloc_memops = { .put = vb2_vmalloc_put, .get_userptr = vb2_vmalloc_get_userptr, .put_userptr = vb2_vmalloc_put_userptr, +#ifdef CONFIG_HAS_DMA .get_dmabuf = vb2_vmalloc_get_dmabuf, +#endif .map_dmabuf = vb2_vmalloc_map_dmabuf, .unmap_dmabuf = vb2_vmalloc_unmap_dmabuf, .attach_dmabuf = vb2_vmalloc_attach_dmabuf, -- cgit v0.10.2 From dbe1b5ca26396b6c61d711c8ac4de13ebb02e9f6 Mon Sep 17 00:00:00 2001 From: Carlos Maiolino Date: Wed, 24 Dec 2014 08:51:38 +1100 Subject: xfs: Make xfs_vn_rename compliant with renameat2() syscall To be able to support RENAME_EXCHANGE flag from renameat2() system call, XFS must have its inode_operations updated, exporting .rename2 method, instead of .rename. This patch just replaces the (now old) .rename method by .rename2, using the same infra-structure, but checking rename flags. Calls to .rename2 using RENAME_EXCHANGE flag, although now handled inside XFS, still return -EINVAL. RENAME_NOREPLACE is handled via VFS and we don't need to care about it inside xfs_vn_rename. Signed-off-by: Carlos Maiolino Reviewed-by: Brian Foster Signed-off-by: Dave Chinner diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index c50311c..abb838a 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -380,18 +380,23 @@ xfs_vn_rename( struct inode *odir, struct dentry *odentry, struct inode *ndir, - struct dentry *ndentry) + struct dentry *ndentry, + unsigned int flags) { struct inode *new_inode = ndentry->d_inode; struct xfs_name oname; struct xfs_name nname; + /* XFS does not support RENAME_EXCHANGE yet */ + if (flags & ~RENAME_NOREPLACE) + return -EINVAL; + xfs_dentry_to_name(&oname, odentry, 0); xfs_dentry_to_name(&nname, ndentry, odentry->d_inode->i_mode); return xfs_rename(XFS_I(odir), &oname, XFS_I(odentry->d_inode), - XFS_I(ndir), &nname, new_inode ? - XFS_I(new_inode) : NULL); + XFS_I(ndir), &nname, + new_inode ? XFS_I(new_inode) : NULL); } /* @@ -1144,7 +1149,7 @@ static const struct inode_operations xfs_dir_inode_operations = { */ .rmdir = xfs_vn_unlink, .mknod = xfs_vn_mknod, - .rename = xfs_vn_rename, + .rename2 = xfs_vn_rename, .get_acl = xfs_get_acl, .set_acl = xfs_set_acl, .getattr = xfs_vn_getattr, @@ -1172,7 +1177,7 @@ static const struct inode_operations xfs_dir_ci_inode_operations = { */ .rmdir = xfs_vn_unlink, .mknod = xfs_vn_mknod, - .rename = xfs_vn_rename, + .rename2 = xfs_vn_rename, .get_acl = xfs_get_acl, .set_acl = xfs_set_acl, .getattr = xfs_vn_getattr, -- cgit v0.10.2 From d31a1825450062b85282b4afed1c840fd306d012 Mon Sep 17 00:00:00 2001 From: Carlos Maiolino Date: Wed, 24 Dec 2014 08:51:42 +1100 Subject: xfs: Add support to RENAME_EXCHANGE flag Adds a new function named xfs_cross_rename(), responsible for handling requests from sys_renameat2() using RENAME_EXCHANGE flag. Signed-off-by: Carlos Maiolino Reviewed-by: Brian Foster Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 41f804e..9916aef 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -2656,6 +2656,124 @@ xfs_sort_for_rename( } /* + * xfs_cross_rename() + * + * responsible for handling RENAME_EXCHANGE flag in renameat2() sytemcall + */ +STATIC int +xfs_cross_rename( + struct xfs_trans *tp, + struct xfs_inode *dp1, + struct xfs_name *name1, + struct xfs_inode *ip1, + struct xfs_inode *dp2, + struct xfs_name *name2, + struct xfs_inode *ip2, + struct xfs_bmap_free *free_list, + xfs_fsblock_t *first_block, + int spaceres) +{ + int error = 0; + int ip1_flags = 0; + int ip2_flags = 0; + int dp2_flags = 0; + + /* Swap inode number for dirent in first parent */ + error = xfs_dir_replace(tp, dp1, name1, + ip2->i_ino, + first_block, free_list, spaceres); + if (error) + goto out; + + /* Swap inode number for dirent in second parent */ + error = xfs_dir_replace(tp, dp2, name2, + ip1->i_ino, + first_block, free_list, spaceres); + if (error) + goto out; + + /* + * If we're renaming one or more directories across different parents, + * update the respective ".." entries (and link counts) to match the new + * parents. + */ + if (dp1 != dp2) { + dp2_flags = XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG; + + if (S_ISDIR(ip2->i_d.di_mode)) { + error = xfs_dir_replace(tp, ip2, &xfs_name_dotdot, + dp1->i_ino, first_block, + free_list, spaceres); + if (error) + goto out; + + /* transfer ip2 ".." reference to dp1 */ + if (!S_ISDIR(ip1->i_d.di_mode)) { + error = xfs_droplink(tp, dp2); + if (error) + goto out; + error = xfs_bumplink(tp, dp1); + if (error) + goto out; + } + + /* + * Although ip1 isn't changed here, userspace needs + * to be warned about the change, so that applications + * relying on it (like backup ones), will properly + * notify the change + */ + ip1_flags |= XFS_ICHGTIME_CHG; + ip2_flags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG; + } + + if (S_ISDIR(ip1->i_d.di_mode)) { + error = xfs_dir_replace(tp, ip1, &xfs_name_dotdot, + dp2->i_ino, first_block, + free_list, spaceres); + if (error) + goto out; + + /* transfer ip1 ".." reference to dp2 */ + if (!S_ISDIR(ip2->i_d.di_mode)) { + error = xfs_droplink(tp, dp1); + if (error) + goto out; + error = xfs_bumplink(tp, dp2); + if (error) + goto out; + } + + /* + * Although ip2 isn't changed here, userspace needs + * to be warned about the change, so that applications + * relying on it (like backup ones), will properly + * notify the change + */ + ip1_flags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG; + ip2_flags |= XFS_ICHGTIME_CHG; + } + } + + if (ip1_flags) { + xfs_trans_ichgtime(tp, ip1, ip1_flags); + xfs_trans_log_inode(tp, ip1, XFS_ILOG_CORE); + } + if (ip2_flags) { + xfs_trans_ichgtime(tp, ip2, ip2_flags); + xfs_trans_log_inode(tp, ip2, XFS_ILOG_CORE); + } + if (dp2_flags) { + xfs_trans_ichgtime(tp, dp2, dp2_flags); + xfs_trans_log_inode(tp, dp2, XFS_ILOG_CORE); + } + xfs_trans_ichgtime(tp, dp1, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); + xfs_trans_log_inode(tp, dp1, XFS_ILOG_CORE); +out: + return error; +} + +/* * xfs_rename */ int @@ -2665,7 +2783,8 @@ xfs_rename( xfs_inode_t *src_ip, xfs_inode_t *target_dp, struct xfs_name *target_name, - xfs_inode_t *target_ip) + xfs_inode_t *target_ip, + unsigned int flags) { xfs_trans_t *tp = NULL; xfs_mount_t *mp = src_dp->i_mount; @@ -2743,6 +2862,18 @@ xfs_rename( } /* + * Handle RENAME_EXCHANGE flags + */ + if (flags & RENAME_EXCHANGE) { + error = xfs_cross_rename(tp, src_dp, src_name, src_ip, + target_dp, target_name, target_ip, + &free_list, &first_block, spaceres); + if (error) + goto abort_return; + goto finish_rename; + } + + /* * Set up the target. */ if (target_ip == NULL) { @@ -2881,6 +3012,7 @@ xfs_rename( if (new_parent) xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE); +finish_rename: /* * If this is a synchronous mount, make sure that the * rename transaction goes to disk before returning to diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 4ed2ba9..f772296 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -338,7 +338,7 @@ int xfs_link(struct xfs_inode *tdp, struct xfs_inode *sip, int xfs_rename(struct xfs_inode *src_dp, struct xfs_name *src_name, struct xfs_inode *src_ip, struct xfs_inode *target_dp, struct xfs_name *target_name, - struct xfs_inode *target_ip); + struct xfs_inode *target_ip, unsigned int flags); void xfs_ilock(xfs_inode_t *, uint); int xfs_ilock_nowait(xfs_inode_t *, uint); diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index abb838a..ce80eeb 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -384,19 +384,23 @@ xfs_vn_rename( unsigned int flags) { struct inode *new_inode = ndentry->d_inode; + int omode = 0; struct xfs_name oname; struct xfs_name nname; - /* XFS does not support RENAME_EXCHANGE yet */ - if (flags & ~RENAME_NOREPLACE) + if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE)) return -EINVAL; - xfs_dentry_to_name(&oname, odentry, 0); + /* if we are exchanging files, we need to set i_mode of both files */ + if (flags & RENAME_EXCHANGE) + omode = ndentry->d_inode->i_mode; + + xfs_dentry_to_name(&oname, odentry, omode); xfs_dentry_to_name(&nname, ndentry, odentry->d_inode->i_mode); return xfs_rename(XFS_I(odir), &oname, XFS_I(odentry->d_inode), XFS_I(ndir), &nname, - new_inode ? XFS_I(new_inode) : NULL); + new_inode ? XFS_I(new_inode) : NULL, flags); } /* -- cgit v0.10.2 From 96ab7954bca0eeedfb17094719db1351fba361d3 Mon Sep 17 00:00:00 2001 From: Brian Foster Date: Wed, 24 Dec 2014 09:46:23 +1100 Subject: xfs: initialize log buf I/O completion wq on log alloc Log buffer I/O completion passes through the high priority m_log_workqueue rather than the default metadata buffer workqueue. The log buffer wq is initialized at I/O submission time. The log buffers are reused once initialized, however, so this is not necessary. Initialize the log buffer I/O completion workqueue pointers once when the log is allocated and log buffers initialized rather than on every log buffer I/O submission. Signed-off-by: Brian Foster Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index e408bf5..4f09e0f 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -1395,6 +1395,8 @@ xlog_alloc_log( ASSERT(xfs_buf_islocked(bp)); xfs_buf_unlock(bp); + /* use high priority wq for log I/O completion */ + bp->b_ioend_wq = mp->m_log_workqueue; bp->b_iodone = xlog_iodone; log->l_xbuf = bp; @@ -1427,6 +1429,8 @@ xlog_alloc_log( ASSERT(xfs_buf_islocked(bp)); xfs_buf_unlock(bp); + /* use high priority wq for log I/O completion */ + bp->b_ioend_wq = mp->m_log_workqueue; bp->b_iodone = xlog_iodone; iclog->ic_bp = bp; iclog->ic_data = bp->b_addr; @@ -1806,8 +1810,6 @@ xlog_sync( XFS_BUF_ZEROFLAGS(bp); XFS_BUF_ASYNC(bp); bp->b_flags |= XBF_SYNCIO; - /* use high priority completion wq */ - bp->b_ioend_wq = log->l_mp->m_log_workqueue; if (log->l_mp->m_flags & XFS_MOUNT_BARRIER) { bp->b_flags |= XBF_FUA; @@ -1856,8 +1858,6 @@ xlog_sync( bp->b_flags |= XBF_SYNCIO; if (log->l_mp->m_flags & XFS_MOUNT_BARRIER) bp->b_flags |= XBF_FUA; - /* use high priority completion wq */ - bp->b_ioend_wq = log->l_mp->m_log_workqueue; ASSERT(XFS_BUF_ADDR(bp) <= log->l_logBBsize-1); ASSERT(XFS_BUF_ADDR(bp) + BTOBB(count) <= log->l_logBBsize); -- cgit v0.10.2 From 77af574eef78c404ea630f7955a5ed0c926a63fe Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Wed, 24 Dec 2014 09:47:27 +1100 Subject: xfs: remove extra newlines from xfs messages xfs_warn() and friends add a newline by default, but some messages add another one. Particularly for the failing write message below, this can waste a lot of console real estate! Signed-off-by: Eric Sandeen Reviewed-by: Christoph Hellwig Signed-off-by: Dave Chinner diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 3f9bd58..925ead2 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -535,7 +535,7 @@ xfs_buf_item_push( if ((bp->b_flags & XBF_WRITE_FAIL) && ___ratelimit(&xfs_buf_write_fail_rl_state, "XFS:")) { xfs_warn(bp->b_target->bt_mount, -"Detected failing async write on buffer block 0x%llx. Retrying async write.\n", +"Detected failing async write on buffer block 0x%llx. Retrying async write.", (long long)bp->b_bn); } diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index 4f09e0f..8fbbfb2 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -2027,7 +2027,7 @@ xlog_print_tic_res( " total reg = %u bytes (o/flow = %u bytes)\n" " ophdrs = %u (ophdr space = %u bytes)\n" " ophdr + reg = %u bytes\n" - " num regions = %u\n", + " num regions = %u", ((ticket->t_trans_type <= 0 || ticket->t_trans_type > XFS_TRANS_TYPE_MAX) ? "bad-trans-type" : trans_type_str[ticket->t_trans_type-1]), diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 19cbda1..22e6aca 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -685,7 +685,7 @@ xfs_blkdev_get( mp); if (IS_ERR(*bdevp)) { error = PTR_ERR(*bdevp); - xfs_warn(mp, "Invalid device [%s], error=%d\n", name, error); + xfs_warn(mp, "Invalid device [%s], error=%d", name, error); } return error; -- cgit v0.10.2 From 1a43ec03ddd40793db00dbc7340685f5accf6fc3 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 24 Dec 2014 09:48:35 +1100 Subject: xfs: Keep sb_bad_features2 consistent with sb_features2 Currently when we modify sb_features2, we store the same value also in sb_bad_features2. However in most places we forget to mark field sb_bad_features2 for logging and thus it can happen that a change to it is lost. This results in an inconsistent sb_features2 and sb_bad_features2 fields e.g. after xfstests test xfs/187. Fix the problem by changing XFS_SB_FEATURES2 to actually mean both sb_features2 and sb_bad_features2 fields since this is always what we want to log. This isn't ideal because the fact that XFS_SB_FEATURES2 means two fields could cause some problem in future however the code is hopefully less error prone that it is now. Signed-off-by: Jan Kara Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h index fbd6da2..4762732 100644 --- a/fs/xfs/libxfs/xfs_format.h +++ b/fs/xfs/libxfs/xfs_format.h @@ -304,8 +304,8 @@ typedef enum { #define XFS_SB_ICOUNT XFS_SB_MVAL(ICOUNT) #define XFS_SB_IFREE XFS_SB_MVAL(IFREE) #define XFS_SB_FDBLOCKS XFS_SB_MVAL(FDBLOCKS) -#define XFS_SB_FEATURES2 XFS_SB_MVAL(FEATURES2) -#define XFS_SB_BAD_FEATURES2 XFS_SB_MVAL(BAD_FEATURES2) +#define XFS_SB_FEATURES2 (XFS_SB_MVAL(FEATURES2) | \ + XFS_SB_MVAL(BAD_FEATURES2)) #define XFS_SB_FEATURES_COMPAT XFS_SB_MVAL(FEATURES_COMPAT) #define XFS_SB_FEATURES_RO_COMPAT XFS_SB_MVAL(FEATURES_RO_COMPAT) #define XFS_SB_FEATURES_INCOMPAT XFS_SB_MVAL(FEATURES_INCOMPAT) @@ -319,9 +319,9 @@ typedef enum { XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | \ XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH | \ XFS_SB_ICOUNT | XFS_SB_IFREE | XFS_SB_FDBLOCKS | XFS_SB_FEATURES2 | \ - XFS_SB_BAD_FEATURES2 | XFS_SB_FEATURES_COMPAT | \ - XFS_SB_FEATURES_RO_COMPAT | XFS_SB_FEATURES_INCOMPAT | \ - XFS_SB_FEATURES_LOG_INCOMPAT | XFS_SB_PQUOTINO) + XFS_SB_FEATURES_COMPAT | XFS_SB_FEATURES_RO_COMPAT | \ + XFS_SB_FEATURES_INCOMPAT | XFS_SB_FEATURES_LOG_INCOMPAT | \ + XFS_SB_PQUOTINO) /* diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index d3d3883..71d2c97 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -678,7 +678,7 @@ xfs_mountfs( xfs_warn(mp, "correcting sb_features alignment problem"); sbp->sb_features2 |= sbp->sb_bad_features2; sbp->sb_bad_features2 = sbp->sb_features2; - mp->m_update_flags |= XFS_SB_FEATURES2 | XFS_SB_BAD_FEATURES2; + mp->m_update_flags |= XFS_SB_FEATURES2; /* * Re-check for ATTR2 in case it was found in bad_features2 @@ -1436,8 +1436,7 @@ xfs_mount_log_sb( int error; ASSERT(fields & (XFS_SB_UNIT | XFS_SB_WIDTH | XFS_SB_UUID | - XFS_SB_FEATURES2 | XFS_SB_BAD_FEATURES2 | - XFS_SB_VERSIONNUM)); + XFS_SB_FEATURES2 | XFS_SB_VERSIONNUM)); tp = xfs_trans_alloc(mp, XFS_TRANS_SB_UNIT); error = xfs_trans_reserve(tp, &M_RES(mp)->tr_sb, 0, 0); -- cgit v0.10.2 From e1f1fe798d2f2eab904624125ba924d08205f960 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 19 Dec 2014 06:51:26 +0000 Subject: jfs: get rid of homegrown endianness helpers Get rid of le24 stuff, along with the bitfields use - all that stuff can be done with standard stuff, in sparse-verifiable manner. Moreover, that way (shift-and-mask) often generates better code - gcc optimizer sucks on bitfields... Signed-off-by: Al Viro Signed-off-by: Dave Kleikamp ---- diff --git a/fs/jfs/endian24.h b/fs/jfs/endian24.h deleted file mode 100644 index fa92f7f..0000000 --- a/fs/jfs/endian24.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) International Business Machines Corp., 2001 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef _H_ENDIAN24 -#define _H_ENDIAN24 - -/* - * endian24.h: - * - * Endian conversion for 24-byte data - * - */ -#define __swab24(x) \ -({ \ - __u32 __x = (x); \ - ((__u32)( \ - ((__x & (__u32)0x000000ffUL) << 16) | \ - (__x & (__u32)0x0000ff00UL) | \ - ((__x & (__u32)0x00ff0000UL) >> 16) )); \ -}) - -#if (defined(__KERNEL__) && defined(__LITTLE_ENDIAN)) || (defined(__BYTE_ORDER) && (__BYTE_ORDER == __LITTLE_ENDIAN)) - #define __cpu_to_le24(x) ((__u32)(x)) - #define __le24_to_cpu(x) ((__u32)(x)) -#else - #define __cpu_to_le24(x) __swab24(x) - #define __le24_to_cpu(x) __swab24(x) -#endif - -#ifdef __KERNEL__ - #define cpu_to_le24 __cpu_to_le24 - #define le24_to_cpu __le24_to_cpu -#endif - -#endif /* !_H_ENDIAN24 */ diff --git a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c index 984c2bb..d88576e 100644 --- a/fs/jfs/jfs_dtree.c +++ b/fs/jfs/jfs_dtree.c @@ -1040,8 +1040,8 @@ static int dtSplitUp(tid_t tid, pxdlist.maxnpxd = 1; pxdlist.npxd = 0; pxd = &pxdlist.pxd[0]; - PXDaddress(pxd, nxaddr) - PXDlength(pxd, xlen + n); + PXDaddress(pxd, nxaddr); + PXDlength(pxd, xlen + n); split->pxdlist = &pxdlist; if ((rc = dtExtendPage(tid, ip, split, btstack))) { nxaddr = addressPXD(pxd); diff --git a/fs/jfs/jfs_types.h b/fs/jfs/jfs_types.h index 43ea371..8f602dc 100644 --- a/fs/jfs/jfs_types.h +++ b/fs/jfs/jfs_types.h @@ -30,8 +30,6 @@ #include #include -#include "endian24.h" - /* * transaction and lock id's * @@ -59,26 +57,42 @@ struct timestruc_t { /* * physical xd (pxd) + * + * The leftmost 24 bits of len_addr are the extent length. + * The rightmost 8 bits of len_addr are the most signficant bits of + * the extent address */ typedef struct { - unsigned len:24; - unsigned addr1:8; + __le32 len_addr; __le32 addr2; } pxd_t; /* xd_t field construction */ -#define PXDlength(pxd, length32) ((pxd)->len = __cpu_to_le24(length32)) -#define PXDaddress(pxd, address64)\ -{\ - (pxd)->addr1 = ((s64)address64) >> 32;\ - (pxd)->addr2 = __cpu_to_le32((address64) & 0xffffffff);\ +static inline void PXDlength(pxd_t *pxd, __u32 len) +{ + pxd->len_addr = (pxd->len_addr & cpu_to_le32(~0xffffff)) | + cpu_to_le32(len & 0xffffff); +} + +static inline void PXDaddress(pxd_t *pxd, __u64 addr) +{ + pxd->len_addr = (pxd->len_addr & cpu_to_le32(0xffffff)) | + cpu_to_le32((addr >> 32)<<24); + pxd->addr2 = cpu_to_le32(addr & 0xffffffff); } /* xd_t field extraction */ -#define lengthPXD(pxd) __le24_to_cpu((pxd)->len) -#define addressPXD(pxd)\ - ( ((s64)((pxd)->addr1)) << 32 | __le32_to_cpu((pxd)->addr2)) +static inline __u32 lengthPXD(pxd_t *pxd) +{ + return le32_to_cpu((pxd)->len_addr) & 0xffffff; +} + +static inline __u64 addressPXD(pxd_t *pxd) +{ + __u64 n = le32_to_cpu(pxd->len_addr) & ~0xffffff; + return (n << 8) + le32_to_cpu(pxd->addr2); +} #define MAXTREEHEIGHT 8 /* pxd list */ @@ -93,12 +107,10 @@ struct pxdlist { * data extent descriptor (dxd) */ typedef struct { - unsigned flag:8; /* 1: flags */ - unsigned rsrvd:24; + __u8 flag; /* 1: flags */ + __u8 rsrvd[3]; __le32 size; /* 4: size in byte */ - unsigned len:24; /* 3: length in unit of fsblksize */ - unsigned addr1:8; /* 1: address in unit of fsblksize */ - __le32 addr2; /* 4: address in unit of fsblksize */ + pxd_t loc; /* 8: address and length in unit of fsblksize */ } dxd_t; /* - 16 - */ /* dxd_t flags */ @@ -109,12 +121,11 @@ typedef struct { #define DXD_CORRUPT 0x08 /* Inconsistency detected */ /* dxd_t field construction - * Conveniently, the PXD macros work for DXD */ -#define DXDlength PXDlength -#define DXDaddress PXDaddress -#define lengthDXD lengthPXD -#define addressDXD addressPXD +#define DXDlength(dxd, len) PXDlength(&(dxd)->loc, len) +#define DXDaddress(dxd, addr) PXDaddress(&(dxd)->loc, addr) +#define lengthDXD(dxd) lengthPXD(&(dxd)->loc) +#define addressDXD(dxd) addressPXD(&(dxd)->loc) #define DXDsize(dxd, size32) ((dxd)->size = cpu_to_le32(size32)) #define sizeDXD(dxd) le32_to_cpu((dxd)->size) diff --git a/fs/jfs/jfs_xtree.h b/fs/jfs/jfs_xtree.h index 08c0c74..1e09879 100644 --- a/fs/jfs/jfs_xtree.h +++ b/fs/jfs/jfs_xtree.h @@ -29,13 +29,11 @@ * extent allocation descriptor (xad) */ typedef struct xad { - unsigned flag:8; /* 1: flag */ - unsigned rsvrd:16; /* 2: reserved */ - unsigned off1:8; /* 1: offset in unit of fsblksize */ - __le32 off2; /* 4: offset in unit of fsblksize */ - unsigned len:24; /* 3: length in unit of fsblksize */ - unsigned addr1:8; /* 1: address in unit of fsblksize */ - __le32 addr2; /* 4: address in unit of fsblksize */ + __u8 flag; /* 1: flag */ + __u8 rsvrd[2]; /* 2: reserved */ + __u8 off1; /* 1: offset in unit of fsblksize */ + __le32 off2; /* 4: offset in unit of fsblksize */ + pxd_t loc; /* 8: length and address in unit of fsblksize */ } xad_t; /* (16) */ #define MAXXLEN ((1 << 24) - 1) @@ -49,19 +47,14 @@ typedef struct xad { (xad)->off1 = ((u64)offset64) >> 32;\ (xad)->off2 = __cpu_to_le32((offset64) & 0xffffffff);\ } -#define XADaddress(xad, address64)\ -{\ - (xad)->addr1 = ((u64)address64) >> 32;\ - (xad)->addr2 = __cpu_to_le32((address64) & 0xffffffff);\ -} -#define XADlength(xad, length32) (xad)->len = __cpu_to_le24(length32) +#define XADaddress(xad, address64) PXDaddress(&(xad)->loc, address64) +#define XADlength(xad, length32) PXDlength(&(xad)->loc, length32) /* xad_t field extraction */ #define offsetXAD(xad)\ ( ((s64)((xad)->off1)) << 32 | __le32_to_cpu((xad)->off2)) -#define addressXAD(xad)\ - ( ((s64)((xad)->addr1)) << 32 | __le32_to_cpu((xad)->addr2)) -#define lengthXAD(xad) __le24_to_cpu((xad)->len) +#define addressXAD(xad) addressPXD(&(xad)->loc) +#define lengthXAD(xad) lengthPXD(&(xad)->loc) /* xad list */ struct xadlist { -- cgit v0.10.2 From 700a3048aaa394b2a6d1468430f892151ce04a01 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 23 Dec 2014 17:06:43 -0600 Subject: livepatch: samples: fix usage example comments Fix a few typos in the livepatch-sample.c usage example comments and add some whitespace to make the comments a little more legible. Reported-by: Udo Seidel Signed-off-by: Josh Poimboeuf Signed-off-by: Jiri Kosina diff --git a/samples/livepatch/livepatch-sample.c b/samples/livepatch/livepatch-sample.c index 21f159d..fb8c861 100644 --- a/samples/livepatch/livepatch-sample.c +++ b/samples/livepatch/livepatch-sample.c @@ -26,12 +26,16 @@ * kernel boot cmdline when /proc/cmdline is read. * * Example: + * * $ cat /proc/cmdline * + * * $ insmod livepatch-sample.ko * $ cat /proc/cmdline * this has been live patched - * $ echo 0 > /sys/kernel/livepatch/klp_sample/enabled + * + * $ echo 0 > /sys/kernel/livepatch/livepatch_sample/enabled + * $ cat /proc/cmdline * */ -- cgit v0.10.2 From 1004b9f146940d280a0f62ca33d241469022bce7 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Tue, 23 Dec 2014 18:41:33 +0100 Subject: spi/fsl: unnecessary double init_completion removed The double call to init_completion(&mpc8xxx_spi->done); is not needed presumably this is a editing mistake only. Signed-off-by: Nicholas Mc Guire Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-fsl-lib.c b/drivers/spi/spi-fsl-lib.c index 446b737..9d3f9e0 100644 --- a/drivers/spi/spi-fsl-lib.c +++ b/drivers/spi/spi-fsl-lib.c @@ -102,8 +102,6 @@ void mpc8xxx_spi_probe(struct device *dev, struct resource *mem, mpc8xxx_spi->rx_shift = 0; mpc8xxx_spi->tx_shift = 0; - init_completion(&mpc8xxx_spi->done); - master->bus_num = pdata->bus_num; master->num_chipselect = pdata->max_chipselect; -- cgit v0.10.2 From 90cc7f1cbbde49c0919953928c225c036ece0d9c Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Tue, 23 Dec 2014 11:04:41 +0200 Subject: ASoC: pcm: Fix vague codec and cpu DAI prepare error messages Both codec and cpu DAI prepare print the same error message making it a bit more difficult to grep quickly from sources. Fix this by telling it explicitly. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index eb87d96e2..0ae0e2a 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -746,7 +746,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) codec_dai); if (ret < 0) { dev_err(codec_dai->dev, - "ASoC: DAI prepare error: %d\n", ret); + "ASoC: codec DAI prepare error: %d\n", + ret); goto out; } } @@ -755,8 +756,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) if (cpu_dai->driver->ops && cpu_dai->driver->ops->prepare) { ret = cpu_dai->driver->ops->prepare(substream, cpu_dai); if (ret < 0) { - dev_err(cpu_dai->dev, "ASoC: DAI prepare error: %d\n", - ret); + dev_err(cpu_dai->dev, + "ASoC: cpu DAI prepare error: %d\n", ret); goto out; } } -- cgit v0.10.2 From 85b88a8dd0c758464f31a9758deb27b31c0d5de7 Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Tue, 23 Dec 2014 09:24:50 +0800 Subject: ASoC: Intel: Store the entry_point read from FW file To enable some modules from other than base FW, according to FW interface spec, we need pass the correct entry point param to FW, so here store the entry_point read from FW file for later usage. Signed-off-by: Jie Yang Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c index 4a5bde9..cad6ea1 100644 --- a/sound/soc/intel/sst-firmware.c +++ b/sound/soc/intel/sst-firmware.c @@ -497,6 +497,7 @@ struct sst_module *sst_module_new(struct sst_fw *sst_fw, sst_module->sst_fw = sst_fw; sst_module->scratch_size = template->scratch_size; sst_module->persistent_size = template->persistent_size; + sst_module->entry = template->entry; INIT_LIST_HEAD(&sst_module->block_list); INIT_LIST_HEAD(&sst_module->runtime_list); -- cgit v0.10.2 From 6800b5ba154df5666f123b7d78161e1a9e6ad8ef Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Tue, 23 Dec 2014 10:27:54 +0800 Subject: ASoC: rt5677: Revise the filter powers Add the filter powers of the dac mono3 and mono4, and remove the connection of dac stereo1 filter that connect to DAC1 MIX. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 81fe146..a15e39d 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -2645,10 +2645,18 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { /* DAC Mixer */ SND_SOC_DAPM_SUPPLY("dac stereo1 filter", RT5677_PWR_DIG2, RT5677_PWR_DAC_S1F_BIT, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("dac mono left filter", RT5677_PWR_DIG2, + SND_SOC_DAPM_SUPPLY("dac mono2 left filter", RT5677_PWR_DIG2, RT5677_PWR_DAC_M2F_L_BIT, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("dac mono right filter", RT5677_PWR_DIG2, + SND_SOC_DAPM_SUPPLY("dac mono2 right filter", RT5677_PWR_DIG2, RT5677_PWR_DAC_M2F_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("dac mono3 left filter", RT5677_PWR_DIG2, + RT5677_PWR_DAC_M3F_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("dac mono3 right filter", RT5677_PWR_DIG2, + RT5677_PWR_DAC_M3F_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("dac mono4 left filter", RT5677_PWR_DIG2, + RT5677_PWR_DAC_M4F_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("dac mono4 right filter", RT5677_PWR_DIG2, + RT5677_PWR_DAC_M4F_R_BIT, 0, NULL, 0), SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, rt5677_sto1_dac_l_mix, ARRAY_SIZE(rt5677_sto1_dac_l_mix)), @@ -3455,10 +3463,8 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "DAC1 MIXL", "Stereo ADC Switch", "ADDA1 Mux" }, { "DAC1 MIXL", "DAC1 Switch", "DAC1 Mux" }, - { "DAC1 MIXL", NULL, "dac stereo1 filter" }, { "DAC1 MIXR", "Stereo ADC Switch", "ADDA1 Mux" }, { "DAC1 MIXR", "DAC1 Switch", "DAC1 Mux" }, - { "DAC1 MIXR", NULL, "dac stereo1 filter" }, { "DAC1 FS", NULL, "DAC1 MIXL" }, { "DAC1 FS", NULL, "DAC1 MIXR" }, @@ -3530,30 +3536,34 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "Mono DAC MIXL", "DAC1 L Switch", "DAC1 MIXL" }, { "Mono DAC MIXL", "DAC2 L Switch", "DAC2 L Mux" }, { "Mono DAC MIXL", "DAC2 R Switch", "DAC2 R Mux" }, - { "Mono DAC MIXL", NULL, "dac mono left filter" }, + { "Mono DAC MIXL", NULL, "dac mono2 left filter" }, { "Mono DAC MIXR", "ST R Switch", "Sidetone Mux" }, { "Mono DAC MIXR", "DAC1 R Switch", "DAC1 MIXR" }, { "Mono DAC MIXR", "DAC2 R Switch", "DAC2 R Mux" }, { "Mono DAC MIXR", "DAC2 L Switch", "DAC2 L Mux" }, - { "Mono DAC MIXR", NULL, "dac mono right filter" }, + { "Mono DAC MIXR", NULL, "dac mono2 right filter" }, { "DD1 MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" }, { "DD1 MIXL", "Mono DAC Mix L Switch", "Mono DAC MIXL" }, { "DD1 MIXL", "DAC3 L Switch", "DAC3 L Mux" }, { "DD1 MIXL", "DAC3 R Switch", "DAC3 R Mux" }, + { "DD1 MIXL", NULL, "dac mono3 left filter" }, { "DD1 MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" }, { "DD1 MIXR", "Mono DAC Mix R Switch", "Mono DAC MIXR" }, { "DD1 MIXR", "DAC3 L Switch", "DAC3 L Mux" }, { "DD1 MIXR", "DAC3 R Switch", "DAC3 R Mux" }, + { "DD1 MIXR", NULL, "dac mono3 right filter" }, { "DD2 MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" }, { "DD2 MIXL", "Mono DAC Mix L Switch", "Mono DAC MIXL" }, { "DD2 MIXL", "DAC4 L Switch", "DAC4 L Mux" }, { "DD2 MIXL", "DAC4 R Switch", "DAC4 R Mux" }, + { "DD2 MIXL", NULL, "dac mono4 left filter" }, { "DD2 MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" }, { "DD2 MIXR", "Mono DAC Mix R Switch", "Mono DAC MIXR" }, { "DD2 MIXR", "DAC4 L Switch", "DAC4 L Mux" }, { "DD2 MIXR", "DAC4 R Switch", "DAC4 R Mux" }, + { "DD2 MIXR", NULL, "dac mono4 right filter" }, { "Stereo DAC MIX", NULL, "Stereo DAC MIXL" }, { "Stereo DAC MIX", NULL, "Stereo DAC MIXR" }, -- cgit v0.10.2 From 5a8c7c2628f8d12e9bca6ab24f6718fc57728991 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Tue, 23 Dec 2014 10:27:55 +0800 Subject: ASoC: rt5677: Add the ASRC support Add the ASRC support Signed-off-by: Oder Chiou Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index a15e39d..66abfa3 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -921,6 +921,97 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, return 0; } +static int is_using_asrc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + unsigned int reg, shift, val; + + if (source->reg == RT5677_ASRC_1) { + switch (source->shift) { + case 12: + reg = RT5677_ASRC_4; + shift = 0; + break; + case 13: + reg = RT5677_ASRC_4; + shift = 4; + break; + case 14: + reg = RT5677_ASRC_4; + shift = 8; + break; + case 15: + reg = RT5677_ASRC_4; + shift = 12; + break; + default: + return 0; + } + } else { + switch (source->shift) { + case 0: + reg = RT5677_ASRC_6; + shift = 8; + break; + case 1: + reg = RT5677_ASRC_6; + shift = 12; + break; + case 2: + reg = RT5677_ASRC_5; + shift = 0; + break; + case 3: + reg = RT5677_ASRC_5; + shift = 4; + break; + case 4: + reg = RT5677_ASRC_5; + shift = 8; + break; + case 5: + reg = RT5677_ASRC_5; + shift = 12; + break; + case 12: + reg = RT5677_ASRC_3; + shift = 0; + break; + case 13: + reg = RT5677_ASRC_3; + shift = 4; + break; + case 14: + reg = RT5677_ASRC_3; + shift = 12; + break; + default: + return 0; + } + } + + val = (snd_soc_read(source->codec, reg) >> shift) & 0xf; + switch (val) { + case 1 ... 6: + return 1; + default: + return 0; + } + +} + +static int can_use_asrc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + + if (rt5677->sysclk > rt5677->lrck[RT5677_AIF1] * 384) + return 1; + + return 0; +} + /* Digital Mixer */ static const struct snd_kcontrol_new rt5677_sto1_adc_l_mix[] = { SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO1_ADC_MIXER, @@ -2215,6 +2306,45 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("PLL2", RT5677_PWR_ANLG2, RT5677_PWR_PLL2_BIT, 0, rt5677_set_pll2_event, SND_SOC_DAPM_POST_PMU), + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5677_ASRC_1, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5677_ASRC_1, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S3 ASRC", 1, RT5677_ASRC_1, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S4 ASRC", 1, RT5677_ASRC_1, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5677_ASRC_2, 14, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO2 L ASRC", 1, RT5677_ASRC_2, 13, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO2 R ASRC", 1, RT5677_ASRC_2, 12, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO3 L ASRC", 1, RT5677_ASRC_1, 15, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO3 R ASRC", 1, RT5677_ASRC_1, 14, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO4 L ASRC", 1, RT5677_ASRC_1, 13, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO4 R ASRC", 1, RT5677_ASRC_1, 12, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5677_ASRC_2, 11, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO2 ASRC", 1, RT5677_ASRC_2, 10, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO3 ASRC", 1, RT5677_ASRC_2, 9, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO4 ASRC", 1, RT5677_ASRC_2, 8, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5677_ASRC_2, 7, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5677_ASRC_2, 6, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5677_ASRC_2, 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO2 ASRC", 1, RT5677_ASRC_2, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO3 ASRC", 1, RT5677_ASRC_2, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO4 ASRC", 1, RT5677_ASRC_2, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC MONO L ASRC", 1, RT5677_ASRC_2, 1, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("ADC MONO R ASRC", 1, RT5677_ASRC_2, 0, 0, NULL, + 0), + /* Input Side */ /* micbias */ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5677_PWR_ANLG2, RT5677_PWR_MB1_BIT, @@ -2729,6 +2859,31 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { }; static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { + { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", can_use_asrc }, + { "Stereo2 DMIC Mux", NULL, "DMIC STO2 ASRC", can_use_asrc }, + { "Stereo3 DMIC Mux", NULL, "DMIC STO3 ASRC", can_use_asrc }, + { "Stereo4 DMIC Mux", NULL, "DMIC STO4 ASRC", can_use_asrc }, + { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC", can_use_asrc }, + { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC", can_use_asrc }, + { "I2S1", NULL, "I2S1 ASRC", can_use_asrc}, + { "I2S2", NULL, "I2S2 ASRC", can_use_asrc}, + { "I2S3", NULL, "I2S3 ASRC", can_use_asrc}, + { "I2S4", NULL, "I2S4 ASRC", can_use_asrc}, + + { "dac stereo1 filter", NULL, "DAC STO ASRC", is_using_asrc }, + { "dac mono2 left filter", NULL, "DAC MONO2 L ASRC", is_using_asrc }, + { "dac mono2 right filter", NULL, "DAC MONO2 R ASRC", is_using_asrc }, + { "dac mono3 left filter", NULL, "DAC MONO3 L ASRC", is_using_asrc }, + { "dac mono3 right filter", NULL, "DAC MONO3 R ASRC", is_using_asrc }, + { "dac mono4 left filter", NULL, "DAC MONO4 L ASRC", is_using_asrc }, + { "dac mono4 right filter", NULL, "DAC MONO4 R ASRC", is_using_asrc }, + { "adc stereo1 filter", NULL, "ADC STO1 ASRC", is_using_asrc }, + { "adc stereo2 filter", NULL, "ADC STO2 ASRC", is_using_asrc }, + { "adc stereo3 filter", NULL, "ADC STO3 ASRC", is_using_asrc }, + { "adc stereo4 filter", NULL, "ADC STO4 ASRC", is_using_asrc }, + { "adc mono left filter", NULL, "ADC MONO L ASRC", is_using_asrc }, + { "adc mono right filter", NULL, "ADC MONO R ASRC", is_using_asrc }, + { "DMIC1", NULL, "DMIC L1" }, { "DMIC1", NULL, "DMIC R1" }, { "DMIC2", NULL, "DMIC L2" }, -- cgit v0.10.2 From 38d595e2e09ede17712b2d4909339e691fff3f85 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Tue, 23 Dec 2014 10:27:56 +0800 Subject: ASoC: rt5677: Adjust the routing of "PLL1" Remove the duplicated PLL1 connections of the adc stereo filters, and remove the PLL1 connections of the DACs because the PLL1 should be connected to dac filters. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 66abfa3..cf39fe6 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -3014,8 +3014,6 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" }, { "Stereo1 ADC MIXL", NULL, "adc stereo1 filter" }, - { "adc stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll }, - { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" }, { "Stereo1 ADC MIXR", NULL, "adc stereo1 filter" }, { "adc stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll }, @@ -3036,8 +3034,6 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "Stereo2 ADC MIXL", NULL, "Stereo2 ADC LR Mux" }, { "Stereo2 ADC MIXL", NULL, "adc stereo2 filter" }, - { "adc stereo2 filter", NULL, "PLL1", is_sys_clk_from_pll }, - { "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" }, { "Stereo2 ADC MIXR", NULL, "adc stereo2 filter" }, { "adc stereo2 filter", NULL, "PLL1", is_sys_clk_from_pll }, @@ -3052,8 +3048,6 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "Stereo3 ADC MIXL", NULL, "Sto3 ADC MIXL" }, { "Stereo3 ADC MIXL", NULL, "adc stereo3 filter" }, - { "adc stereo3 filter", NULL, "PLL1", is_sys_clk_from_pll }, - { "Stereo3 ADC MIXR", NULL, "Sto3 ADC MIXR" }, { "Stereo3 ADC MIXR", NULL, "adc stereo3 filter" }, { "adc stereo3 filter", NULL, "PLL1", is_sys_clk_from_pll }, @@ -3068,8 +3062,6 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "Stereo4 ADC MIXL", NULL, "Sto4 ADC MIXL" }, { "Stereo4 ADC MIXL", NULL, "adc stereo4 filter" }, - { "adc stereo4 filter", NULL, "PLL1", is_sys_clk_from_pll }, - { "Stereo4 ADC MIXR", NULL, "Sto4 ADC MIXR" }, { "Stereo4 ADC MIXR", NULL, "adc stereo4 filter" }, { "adc stereo4 filter", NULL, "PLL1", is_sys_clk_from_pll }, @@ -3686,39 +3678,46 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "Stereo DAC MIXR", "DAC2 R Switch", "DAC2 R Mux" }, { "Stereo DAC MIXR", "DAC1 L Switch", "DAC1 MIXL" }, { "Stereo DAC MIXR", NULL, "dac stereo1 filter" }, + { "dac stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll }, { "Mono DAC MIXL", "ST L Switch", "Sidetone Mux" }, { "Mono DAC MIXL", "DAC1 L Switch", "DAC1 MIXL" }, { "Mono DAC MIXL", "DAC2 L Switch", "DAC2 L Mux" }, { "Mono DAC MIXL", "DAC2 R Switch", "DAC2 R Mux" }, { "Mono DAC MIXL", NULL, "dac mono2 left filter" }, + { "dac mono2 left filter", NULL, "PLL1", is_sys_clk_from_pll }, { "Mono DAC MIXR", "ST R Switch", "Sidetone Mux" }, { "Mono DAC MIXR", "DAC1 R Switch", "DAC1 MIXR" }, { "Mono DAC MIXR", "DAC2 R Switch", "DAC2 R Mux" }, { "Mono DAC MIXR", "DAC2 L Switch", "DAC2 L Mux" }, { "Mono DAC MIXR", NULL, "dac mono2 right filter" }, + { "dac mono2 right filter", NULL, "PLL1", is_sys_clk_from_pll }, { "DD1 MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" }, { "DD1 MIXL", "Mono DAC Mix L Switch", "Mono DAC MIXL" }, { "DD1 MIXL", "DAC3 L Switch", "DAC3 L Mux" }, { "DD1 MIXL", "DAC3 R Switch", "DAC3 R Mux" }, { "DD1 MIXL", NULL, "dac mono3 left filter" }, + { "dac mono3 left filter", NULL, "PLL1", is_sys_clk_from_pll }, { "DD1 MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" }, { "DD1 MIXR", "Mono DAC Mix R Switch", "Mono DAC MIXR" }, { "DD1 MIXR", "DAC3 L Switch", "DAC3 L Mux" }, { "DD1 MIXR", "DAC3 R Switch", "DAC3 R Mux" }, { "DD1 MIXR", NULL, "dac mono3 right filter" }, + { "dac mono3 right filter", NULL, "PLL1", is_sys_clk_from_pll }, { "DD2 MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" }, { "DD2 MIXL", "Mono DAC Mix L Switch", "Mono DAC MIXL" }, { "DD2 MIXL", "DAC4 L Switch", "DAC4 L Mux" }, { "DD2 MIXL", "DAC4 R Switch", "DAC4 R Mux" }, { "DD2 MIXL", NULL, "dac mono4 left filter" }, + { "dac mono4 left filter", NULL, "PLL1", is_sys_clk_from_pll }, { "DD2 MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" }, { "DD2 MIXR", "Mono DAC Mix R Switch", "Mono DAC MIXR" }, { "DD2 MIXR", "DAC4 L Switch", "DAC4 L Mux" }, { "DD2 MIXR", "DAC4 R Switch", "DAC4 R Mux" }, { "DD2 MIXR", NULL, "dac mono4 right filter" }, + { "dac mono4 right filter", NULL, "PLL1", is_sys_clk_from_pll }, { "Stereo DAC MIX", NULL, "Stereo DAC MIXL" }, { "Stereo DAC MIX", NULL, "Stereo DAC MIXR" }, @@ -3740,11 +3739,8 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "DAC3 SRC Mux", "DD MIX2L", "DD2 MIXL" }, { "DAC 1", NULL, "DAC12 SRC Mux" }, - { "DAC 1", NULL, "PLL1", is_sys_clk_from_pll }, { "DAC 2", NULL, "DAC12 SRC Mux" }, - { "DAC 2", NULL, "PLL1", is_sys_clk_from_pll }, { "DAC 3", NULL, "DAC3 SRC Mux" }, - { "DAC 3", NULL, "PLL1", is_sys_clk_from_pll }, { "PDM1 L Mux", "STO1 DAC MIX", "Stereo DAC MIXL" }, { "PDM1 L Mux", "MONO DAC MIX", "Mono DAC MIXL" }, -- cgit v0.10.2 From 549858ce76e31d5e2139e5588df976ee42607a0d Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Mon, 22 Dec 2014 11:35:16 -0800 Subject: spi: img-spfi: Select FIFO based on transfer length Since the 32-bit FIFO is deeper (64 bytes) than the 8-bit FIFO (16 bytes), use the 32-bit FIFO when there are at least 32 bits remaining to be transferred in PIO mode or when the transfer length is 32-bit aligned in DMA mode. Signed-off-by: Andrew Bresticker Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index aad6683..c01567d 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -160,16 +160,16 @@ static unsigned int spfi_pio_write32(struct img_spfi *spfi, const u32 *buf, unsigned int count = 0; u32 status; - while (count < max) { + while (count < max / 4) { spfi_writel(spfi, SPFI_INTERRUPT_SDFUL, SPFI_INTERRUPT_CLEAR); status = spfi_readl(spfi, SPFI_INTERRUPT_STATUS); if (status & SPFI_INTERRUPT_SDFUL) break; - spfi_writel(spfi, buf[count / 4], SPFI_TX_32BIT_VALID_DATA); - count += 4; + spfi_writel(spfi, buf[count], SPFI_TX_32BIT_VALID_DATA); + count++; } - return count; + return count * 4; } static unsigned int spfi_pio_write8(struct img_spfi *spfi, const u8 *buf, @@ -196,17 +196,17 @@ static unsigned int spfi_pio_read32(struct img_spfi *spfi, u32 *buf, unsigned int count = 0; u32 status; - while (count < max) { + while (count < max / 4) { spfi_writel(spfi, SPFI_INTERRUPT_GDEX32BIT, SPFI_INTERRUPT_CLEAR); status = spfi_readl(spfi, SPFI_INTERRUPT_STATUS); if (!(status & SPFI_INTERRUPT_GDEX32BIT)) break; - buf[count / 4] = spfi_readl(spfi, SPFI_RX_32BIT_VALID_DATA); - count += 4; + buf[count] = spfi_readl(spfi, SPFI_RX_32BIT_VALID_DATA); + count++; } - return count; + return count * 4; } static unsigned int spfi_pio_read8(struct img_spfi *spfi, u8 *buf, @@ -251,17 +251,15 @@ static int img_spfi_start_pio(struct spi_master *master, time_before(jiffies, timeout)) { unsigned int tx_count, rx_count; - switch (xfer->bits_per_word) { - case 32: + if (tx_bytes >= 4) tx_count = spfi_pio_write32(spfi, tx_buf, tx_bytes); - rx_count = spfi_pio_read32(spfi, rx_buf, rx_bytes); - break; - case 8: - default: + else tx_count = spfi_pio_write8(spfi, tx_buf, tx_bytes); + + if (rx_bytes >= 4) + rx_count = spfi_pio_read32(spfi, rx_buf, rx_bytes); + else rx_count = spfi_pio_read8(spfi, rx_buf, rx_bytes); - break; - } tx_buf += tx_count; rx_buf += rx_count; @@ -331,14 +329,11 @@ static int img_spfi_start_dma(struct spi_master *master, if (xfer->rx_buf) { rxconf.direction = DMA_DEV_TO_MEM; - switch (xfer->bits_per_word) { - case 32: + if (xfer->len % 4 == 0) { rxconf.src_addr = spfi->phys + SPFI_RX_32BIT_VALID_DATA; rxconf.src_addr_width = 4; rxconf.src_maxburst = 4; - break; - case 8: - default: + } else { rxconf.src_addr = spfi->phys + SPFI_RX_8BIT_VALID_DATA; rxconf.src_addr_width = 1; rxconf.src_maxburst = 4; @@ -358,18 +353,14 @@ static int img_spfi_start_dma(struct spi_master *master, if (xfer->tx_buf) { txconf.direction = DMA_MEM_TO_DEV; - switch (xfer->bits_per_word) { - case 32: + if (xfer->len % 4 == 0) { txconf.dst_addr = spfi->phys + SPFI_TX_32BIT_VALID_DATA; txconf.dst_addr_width = 4; txconf.dst_maxburst = 4; - break; - case 8: - default: + } else { txconf.dst_addr = spfi->phys + SPFI_TX_8BIT_VALID_DATA; txconf.dst_addr_width = 1; txconf.dst_maxburst = 4; - break; } dmaengine_slave_config(spfi->tx_ch, &txconf); @@ -508,9 +499,7 @@ static void img_spfi_set_cs(struct spi_device *spi, bool enable) static bool img_spfi_can_dma(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { - if (xfer->bits_per_word == 8 && xfer->len > SPFI_8BIT_FIFO_SIZE) - return true; - if (xfer->bits_per_word == 32 && xfer->len > SPFI_32BIT_FIFO_SIZE) + if (xfer->len > SPFI_32BIT_FIFO_SIZE) return true; return false; } -- cgit v0.10.2 From 3110628d89f80fbafa085fd62e75afcb39fb764c Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 19 Dec 2014 17:15:53 +0900 Subject: spi: sh-msiof: Configure MSIOF sync signal timing in device tree The MSIOF controller has DTDL and SYNCDL in SITMDR1 register. So, this patch adds new properties like the following commit: d0fb47a5237d8b9576113568bacfd27892308b62 (spi: fsl-espi: Configure FSL eSPI CSBEF and CSAFT) Signed-off-by: Yoshihiro Shimoda Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/sh-msiof.txt b/Documentation/devicetree/bindings/spi/sh-msiof.txt index d11c372..4c388bb 100644 --- a/Documentation/devicetree/bindings/spi/sh-msiof.txt +++ b/Documentation/devicetree/bindings/spi/sh-msiof.txt @@ -30,6 +30,22 @@ Optional properties: specifiers, one for transmission, and one for reception. - dma-names : Must contain a list of two DMA names, "tx" and "rx". +- renesas,dtdl : delay sync signal (setup) in transmit mode. + Must contain one of the following values: + 0 (no bit delay) + 50 (0.5-clock-cycle delay) + 100 (1-clock-cycle delay) + 150 (1.5-clock-cycle delay) + 200 (2-clock-cycle delay) + +- renesas,syncdl : delay sync signal (hold) in transmit mode. + Must contain one of the following values: + 0 (no bit delay) + 50 (0.5-clock-cycle delay) + 100 (1-clock-cycle delay) + 150 (1.5-clock-cycle delay) + 200 (2-clock-cycle delay) + 300 (3-clock-cycle delay) Optional properties, deprecated for soctype-specific bindings: - renesas,tx-fifo-size : Overrides the default tx fifo size given in words diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 239be7c..2a87cb9 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -82,6 +82,8 @@ struct sh_msiof_spi_priv { #define MDR1_SYNCMD_LR 0x30000000 /* L/R mode */ #define MDR1_SYNCAC_SHIFT 25 /* Sync Polarity (1 = Active-low) */ #define MDR1_BITLSB_SHIFT 24 /* MSB/LSB First (1 = LSB first) */ +#define MDR1_DTDL_SHIFT 20 /* Data Pin Bit Delay for MSIOF_SYNC */ +#define MDR1_SYNCDL_SHIFT 16 /* Frame Sync Signal Timing Delay */ #define MDR1_FLD_MASK 0x000000c0 /* Frame Sync Signal Interval (0-3) */ #define MDR1_FLD_SHIFT 2 #define MDR1_XXSTP 0x00000001 /* Transmission/Reception Stop on FIFO */ @@ -279,6 +281,48 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p, sh_msiof_write(p, RSCR, sh_msiof_spi_clk_table[k].scr); } +static u32 sh_msiof_get_delay_bit(u32 dtdl_or_syncdl) +{ + /* + * DTDL/SYNCDL bit : p->info->dtdl or p->info->syncdl + * b'000 : 0 + * b'001 : 100 + * b'010 : 200 + * b'011 (SYNCDL only) : 300 + * b'101 : 50 + * b'110 : 150 + */ + if (dtdl_or_syncdl % 100) + return dtdl_or_syncdl / 100 + 5; + else + return dtdl_or_syncdl / 100; +} + +static u32 sh_msiof_spi_get_dtdl_and_syncdl(struct sh_msiof_spi_priv *p) +{ + u32 val; + + if (!p->info) + return 0; + + /* check if DTDL and SYNCDL is allowed value */ + if (p->info->dtdl > 200 || p->info->syncdl > 300) { + dev_warn(&p->pdev->dev, "DTDL or SYNCDL is too large\n"); + return 0; + } + + /* check if the sum of DTDL and SYNCDL becomes an integer value */ + if ((p->info->dtdl + p->info->syncdl) % 100) { + dev_warn(&p->pdev->dev, "the sum of DTDL/SYNCDL is not good\n"); + return 0; + } + + val = sh_msiof_get_delay_bit(p->info->dtdl) << MDR1_DTDL_SHIFT; + val |= sh_msiof_get_delay_bit(p->info->syncdl) << MDR1_SYNCDL_SHIFT; + + return val; +} + static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, u32 cpol, u32 cpha, u32 tx_hi_z, u32 lsb_first, u32 cs_high) @@ -296,6 +340,7 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, tmp = MDR1_SYNCMD_SPI | 1 << MDR1_FLD_SHIFT | MDR1_XXSTP; tmp |= !cs_high << MDR1_SYNCAC_SHIFT; tmp |= lsb_first << MDR1_BITLSB_SHIFT; + tmp |= sh_msiof_spi_get_dtdl_and_syncdl(p); sh_msiof_write(p, TMDR1, tmp | MDR1_TRMD | TMDR1_PCON); if (p->chipdata->master_flags & SPI_MASTER_MUST_TX) { /* These bits are reserved if RX needs TX */ @@ -952,6 +997,8 @@ static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev) &info->tx_fifo_override); of_property_read_u32(np, "renesas,rx-fifo-size", &info->rx_fifo_override); + of_property_read_u32(np, "renesas,dtdl", &info->dtdl); + of_property_read_u32(np, "renesas,syncdl", &info->syncdl); info->num_chipselect = num_cs; diff --git a/include/linux/spi/sh_msiof.h b/include/linux/spi/sh_msiof.h index 88a14d8..b087a85 100644 --- a/include/linux/spi/sh_msiof.h +++ b/include/linux/spi/sh_msiof.h @@ -7,6 +7,8 @@ struct sh_msiof_spi_info { u16 num_chipselect; unsigned int dma_tx_id; unsigned int dma_rx_id; + u32 dtdl; + u32 syncdl; }; #endif /* __SPI_SH_MSIOF_H__ */ -- cgit v0.10.2 From 0425e2420c0ab1b5da24f6d9fce39241ad85fc46 Mon Sep 17 00:00:00 2001 From: Flora Fu Date: Fri, 5 Dec 2014 12:07:54 +0800 Subject: regulator: mt6397: Add support for MT6397 regulator Add MT6397 regulator driver. Signed-off-by: Flora Fu Signed-off-by: Mark Brown diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index c3a60b5..f622d06 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -433,6 +433,15 @@ config REGULATOR_MC13892 Say y here to support the regulators found on the Freescale MC13892 PMIC. +config REGULATOR_MT6397 + tristate "MediaTek MT6397 PMIC" + depends on MFD_MT6397 + help + Say y here to select this option to enable the power regulator of + MediaTek MT6397 PMIC. + This driver supports the control of different power rails of device + through regulator interface. + config REGULATOR_PALMAS tristate "TI Palmas PMIC Regulators" depends on MFD_PALMAS diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 1f28ebf..1a0fbc3 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_REGULATOR_MAX77802) += max77802.o obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o +obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o diff --git a/drivers/regulator/mt6397-regulator.c b/drivers/regulator/mt6397-regulator.c new file mode 100644 index 0000000..a5b2f47 --- /dev/null +++ b/drivers/regulator/mt6397-regulator.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Flora Fu + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * MT6397 regulators' information + * + * @desc: standard fields of regulator description. + * @qi: Mask for query enable signal status of regulators + * @vselon_reg: Register sections for hardware control mode of bucks + * @vselctrl_reg: Register for controlling the buck control mode. + * @vselctrl_mask: Mask for query buck's voltage control mode. + */ +struct mt6397_regulator_info { + struct regulator_desc desc; + u32 qi; + u32 vselon_reg; + u32 vselctrl_reg; + u32 vselctrl_mask; +}; + +#define MT6397_BUCK(match, vreg, min, max, step, volt_ranges, enreg, \ + vosel, vosel_mask, voselon, vosel_ctrl) \ +[MT6397_ID_##vreg] = { \ + .desc = { \ + .name = #vreg, \ + .of_match = of_match_ptr(match), \ + .ops = &mt6397_volt_range_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = MT6397_ID_##vreg, \ + .owner = THIS_MODULE, \ + .n_voltages = (max - min)/step + 1, \ + .linear_ranges = volt_ranges, \ + .n_linear_ranges = ARRAY_SIZE(volt_ranges), \ + .vsel_reg = vosel, \ + .vsel_mask = vosel_mask, \ + .enable_reg = enreg, \ + .enable_mask = BIT(0), \ + }, \ + .qi = BIT(13), \ + .vselon_reg = voselon, \ + .vselctrl_reg = vosel_ctrl, \ + .vselctrl_mask = BIT(1), \ +} + +#define MT6397_LDO(match, vreg, ldo_volt_table, enreg, enbit, vosel, \ + vosel_mask) \ +[MT6397_ID_##vreg] = { \ + .desc = { \ + .name = #vreg, \ + .of_match = of_match_ptr(match), \ + .ops = &mt6397_volt_table_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = MT6397_ID_##vreg, \ + .owner = THIS_MODULE, \ + .n_voltages = ARRAY_SIZE(ldo_volt_table), \ + .volt_table = ldo_volt_table, \ + .vsel_reg = vosel, \ + .vsel_mask = vosel_mask, \ + .enable_reg = enreg, \ + .enable_mask = BIT(enbit), \ + }, \ + .qi = BIT(15), \ +} + +#define MT6397_REG_FIXED(match, vreg, enreg, enbit, volt) \ +[MT6397_ID_##vreg] = { \ + .desc = { \ + .name = #vreg, \ + .of_match = of_match_ptr(match), \ + .ops = &mt6397_volt_fixed_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = MT6397_ID_##vreg, \ + .owner = THIS_MODULE, \ + .n_voltages = 1, \ + .enable_reg = enreg, \ + .enable_mask = BIT(enbit), \ + .min_uV = volt, \ + }, \ + .qi = BIT(15), \ +} + +static const struct regulator_linear_range buck_volt_range1[] = { + REGULATOR_LINEAR_RANGE(700000, 0, 0x7f, 6250), +}; + +static const struct regulator_linear_range buck_volt_range2[] = { + REGULATOR_LINEAR_RANGE(800000, 0, 0x7f, 6250), +}; + +static const struct regulator_linear_range buck_volt_range3[] = { + REGULATOR_LINEAR_RANGE(1500000, 0, 0x1f, 20000), +}; + +static const u32 ldo_volt_table1[] = { + 1500000, 1800000, 2500000, 2800000, +}; + +static const u32 ldo_volt_table2[] = { + 1800000, 3300000, +}; + +static const u32 ldo_volt_table3[] = { + 3000000, 3300000, +}; + +static const u32 ldo_volt_table4[] = { + 1220000, 1300000, 1500000, 1800000, 2500000, 2800000, 3000000, 3300000, +}; + +static const u32 ldo_volt_table5[] = { + 1200000, 1300000, 1500000, 1800000, 2500000, 2800000, 3000000, 3300000, +}; + +static const u32 ldo_volt_table5_v2[] = { + 1200000, 1000000, 1500000, 1800000, 2500000, 2800000, 3000000, 3300000, +}; + +static const u32 ldo_volt_table6[] = { + 1200000, 1300000, 1500000, 1800000, 2500000, 2800000, 3000000, 2000000, +}; + +static const u32 ldo_volt_table7[] = { + 1300000, 1500000, 1800000, 2000000, 2500000, 2800000, 3000000, 3300000, +}; + +static int mt6397_get_status(struct regulator_dev *rdev) +{ + int ret; + u32 regval; + struct mt6397_regulator_info *info = rdev_get_drvdata(rdev); + + ret = regmap_read(rdev->regmap, info->desc.enable_reg, ®val); + if (ret != 0) { + dev_err(&rdev->dev, "Failed to get enable reg: %d\n", ret); + return ret; + } + + return (regval & info->qi) ? REGULATOR_STATUS_ON : REGULATOR_STATUS_OFF; +} + +static struct regulator_ops mt6397_volt_range_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_status = mt6397_get_status, +}; + +static struct regulator_ops mt6397_volt_table_ops = { + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_iterate, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_status = mt6397_get_status, +}; + +static struct regulator_ops mt6397_volt_fixed_ops = { + .list_voltage = regulator_list_voltage_linear, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_status = mt6397_get_status, +}; + +/* The array is indexed by id(MT6397_ID_XXX) */ +static struct mt6397_regulator_info mt6397_regulators[] = { + MT6397_BUCK("buck_vpca15", VPCA15, 700000, 1493750, 6250, + buck_volt_range1, MT6397_VCA15_CON7, MT6397_VCA15_CON9, 0x7f, + MT6397_VCA15_CON10, MT6397_VCA15_CON5), + MT6397_BUCK("buck_vpca7", VPCA7, 700000, 1493750, 6250, + buck_volt_range1, MT6397_VPCA7_CON7, MT6397_VPCA7_CON9, 0x7f, + MT6397_VPCA7_CON10, MT6397_VPCA7_CON5), + MT6397_BUCK("buck_vsramca15", VSRAMCA15, 700000, 1493750, 6250, + buck_volt_range1, MT6397_VSRMCA15_CON7, MT6397_VSRMCA15_CON9, + 0x7f, MT6397_VSRMCA15_CON10, MT6397_VSRMCA15_CON5), + MT6397_BUCK("buck_vsramca7", VSRAMCA7, 700000, 1493750, 6250, + buck_volt_range1, MT6397_VSRMCA7_CON7, MT6397_VSRMCA7_CON9, + 0x7f, MT6397_VSRMCA7_CON10, MT6397_VSRMCA7_CON5), + MT6397_BUCK("buck_vcore", VCORE, 700000, 1493750, 6250, + buck_volt_range1, MT6397_VCORE_CON7, MT6397_VCORE_CON9, 0x7f, + MT6397_VCORE_CON10, MT6397_VCORE_CON5), + MT6397_BUCK("buck_vgpu", VGPU, 700000, 1493750, 6250, buck_volt_range1, + MT6397_VGPU_CON7, MT6397_VGPU_CON9, 0x7f, + MT6397_VGPU_CON10, MT6397_VGPU_CON5), + MT6397_BUCK("buck_vdrm", VDRM, 800000, 1593750, 6250, buck_volt_range2, + MT6397_VDRM_CON7, MT6397_VDRM_CON9, 0x7f, + MT6397_VDRM_CON10, MT6397_VDRM_CON5), + MT6397_BUCK("buck_vio18", VIO18, 1500000, 2120000, 20000, + buck_volt_range3, MT6397_VIO18_CON7, MT6397_VIO18_CON9, 0x1f, + MT6397_VIO18_CON10, MT6397_VIO18_CON5), + MT6397_REG_FIXED("ldo_vtcxo", VTCXO, MT6397_ANALDO_CON0, 10, 2800000), + MT6397_REG_FIXED("ldo_va28", VA28, MT6397_ANALDO_CON1, 14, 2800000), + MT6397_LDO("ldo_vcama", VCAMA, ldo_volt_table1, + MT6397_ANALDO_CON2, 15, MT6397_ANALDO_CON6, 0xC0), + MT6397_REG_FIXED("ldo_vio28", VIO28, MT6397_DIGLDO_CON0, 14, 2800000), + MT6397_REG_FIXED("ldo_vusb", VUSB, MT6397_DIGLDO_CON1, 14, 3300000), + MT6397_LDO("ldo_vmc", VMC, ldo_volt_table2, + MT6397_DIGLDO_CON2, 12, MT6397_DIGLDO_CON29, 0x10), + MT6397_LDO("ldo_vmch", VMCH, ldo_volt_table3, + MT6397_DIGLDO_CON3, 14, MT6397_DIGLDO_CON17, 0x80), + MT6397_LDO("ldo_vemc3v3", VEMC3V3, ldo_volt_table3, + MT6397_DIGLDO_CON4, 14, MT6397_DIGLDO_CON18, 0x10), + MT6397_LDO("ldo_vgp1", VGP1, ldo_volt_table4, + MT6397_DIGLDO_CON5, 15, MT6397_DIGLDO_CON19, 0xE0), + MT6397_LDO("ldo_vgp2", VGP2, ldo_volt_table5, + MT6397_DIGLDO_CON6, 15, MT6397_DIGLDO_CON20, 0xE0), + MT6397_LDO("ldo_vgp3", VGP3, ldo_volt_table5, + MT6397_DIGLDO_CON7, 15, MT6397_DIGLDO_CON21, 0xE0), + MT6397_LDO("ldo_vgp4", VGP4, ldo_volt_table5, + MT6397_DIGLDO_CON8, 15, MT6397_DIGLDO_CON22, 0xE0), + MT6397_LDO("ldo_vgp5", VGP5, ldo_volt_table6, + MT6397_DIGLDO_CON9, 15, MT6397_DIGLDO_CON23, 0xE0), + MT6397_LDO("ldo_vgp6", VGP6, ldo_volt_table5, + MT6397_DIGLDO_CON10, 15, MT6397_DIGLDO_CON33, 0xE0), + MT6397_LDO("ldo_vibr", VIBR, ldo_volt_table7, + MT6397_DIGLDO_CON24, 15, MT6397_DIGLDO_CON25, 0xE00), +}; + +static int mt6397_set_buck_vosel_reg(struct platform_device *pdev) +{ + struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent); + int i; + u32 regval; + + for (i = 0; i < MT6397_MAX_REGULATOR; i++) { + if (mt6397_regulators[i].vselctrl_reg) { + if (regmap_read(mt6397->regmap, + mt6397_regulators[i].vselctrl_reg, + ®val) < 0) { + dev_err(&pdev->dev, + "Failed to read buck ctrl\n"); + return -EIO; + } + + if (regval & mt6397_regulators[i].vselctrl_mask) { + mt6397_regulators[i].desc.vsel_reg = + mt6397_regulators[i].vselon_reg; + } + } + } + + return 0; +} + +static int mt6397_regulator_probe(struct platform_device *pdev) +{ + struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = {}; + struct regulator_dev *rdev; + int i; + u32 reg_value, version; + + /* Query buck controller to select activated voltage register part */ + if (mt6397_set_buck_vosel_reg(pdev)) + return -EIO; + + /* Read PMIC chip revision to update constraints and voltage table */ + if (regmap_read(mt6397->regmap, MT6397_CID, ®_value) < 0) { + dev_err(&pdev->dev, "Failed to read Chip ID\n"); + return -EIO; + } + dev_info(&pdev->dev, "Chip ID = 0x%x\n", reg_value); + + version = (reg_value & 0xFF); + switch (version) { + case MT6397_REGULATOR_ID91: + mt6397_regulators[MT6397_ID_VGP2].desc.volt_table = + ldo_volt_table5_v2; + break; + default: + break; + } + + for (i = 0; i < MT6397_MAX_REGULATOR; i++) { + config.dev = &pdev->dev; + config.driver_data = &mt6397_regulators[i]; + config.regmap = mt6397->regmap; + rdev = devm_regulator_register(&pdev->dev, + &mt6397_regulators[i].desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register %s\n", + mt6397_regulators[i].desc.name); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static struct platform_driver mt6397_regulator_driver = { + .driver = { + .name = "mt6397-regulator", + }, + .probe = mt6397_regulator_probe, +}; + +module_platform_driver(mt6397_regulator_driver); + +MODULE_AUTHOR("Flora Fu "); +MODULE_DESCRIPTION("Regulator Driver for MediaTek MT6397 PMIC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mt6397-regulator"); diff --git a/include/linux/regulator/mt6397-regulator.h b/include/linux/regulator/mt6397-regulator.h new file mode 100644 index 0000000..30cc596 --- /dev/null +++ b/include/linux/regulator/mt6397-regulator.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Flora Fu + * + * 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. + */ + +#ifndef __LINUX_REGULATOR_MT6397_H +#define __LINUX_REGULATOR_MT6397_H + +enum { + MT6397_ID_VPCA15 = 0, + MT6397_ID_VPCA7, + MT6397_ID_VSRAMCA15, + MT6397_ID_VSRAMCA7, + MT6397_ID_VCORE, + MT6397_ID_VGPU, + MT6397_ID_VDRM, + MT6397_ID_VIO18 = 7, + MT6397_ID_VTCXO, + MT6397_ID_VA28, + MT6397_ID_VCAMA, + MT6397_ID_VIO28, + MT6397_ID_VUSB, + MT6397_ID_VMC, + MT6397_ID_VMCH, + MT6397_ID_VEMC3V3, + MT6397_ID_VGP1, + MT6397_ID_VGP2, + MT6397_ID_VGP3, + MT6397_ID_VGP4, + MT6397_ID_VGP5, + MT6397_ID_VGP6, + MT6397_ID_VIBR, + MT6397_ID_RG_MAX, +}; + +#define MT6397_MAX_REGULATOR MT6397_ID_RG_MAX +#define MT6397_REGULATOR_ID97 0x97 +#define MT6397_REGULATOR_ID91 0x91 + +#endif /* __LINUX_REGULATOR_MT6397_H */ -- cgit v0.10.2 From 2696757579010d5480018e8466973531aa22e56e Mon Sep 17 00:00:00 2001 From: Flora Fu Date: Fri, 5 Dec 2014 12:07:57 +0800 Subject: regulator: Add document for MT6397 regulator Signed-off-by: Flora Fu Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/regulator/mt6397-regulator.txt b/Documentation/devicetree/bindings/regulator/mt6397-regulator.txt new file mode 100644 index 0000000..a42b1d6 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/mt6397-regulator.txt @@ -0,0 +1,217 @@ +Mediatek MT6397 Regulator Driver + +Required properties: +- compatible: "mediatek,mt6397-regulator" +- mt6397regulator: List of regulators provided by this controller. It is named + according to its regulator type, buck_ and ldo_. + The definition for each of these nodes is defined using the standard binding + for regulators at Documentation/devicetree/bindings/regulator/regulator.txt. + +The valid names for regulators are:: +BUCK: + buck_vpca15, buck_vpca7, buck_vsramca15, buck_vsramca7, buck_vcore, buck_vgpu, + buck_vdrm, buck_vio18 +LDO: + ldo_vtcxo, ldo_va28, ldo_vcama, ldo_vio28, ldo_vusb, ldo_vmc, ldo_vmch, + ldo_vemc3v3, ldo_vgp1, ldo_vgp2, ldo_vgp3, ldo_vgp4, ldo_vgp5, ldo_vgp6, + ldo_vibr + +Example: + pmic { + compatible = "mediatek,mt6397"; + + mt6397regulator: mt6397regulator { + compatible = "mediatek,mt6397-regulator"; + + mt6397_vpca15_reg: buck_vpca15 { + regulator-compatible = "buck_vpca15"; + regulator-name = "vpca15"; + regulator-min-microvolt = < 850000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <12500>; + regulator-enable-ramp-delay = <200>; + }; + + mt6397_vpca7_reg: buck_vpca7 { + regulator-compatible = "buck_vpca7"; + regulator-name = "vpca7"; + regulator-min-microvolt = < 850000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <12500>; + regulator-enable-ramp-delay = <115>; + }; + + mt6397_vsramca15_reg: buck_vsramca15 { + regulator-compatible = "buck_vsramca15"; + regulator-name = "vsramca15"; + regulator-min-microvolt = < 850000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <12500>; + regulator-enable-ramp-delay = <115>; + + }; + + mt6397_vsramca7_reg: buck_vsramca7 { + regulator-compatible = "buck_vsramca7"; + regulator-name = "vsramca7"; + regulator-min-microvolt = < 850000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <12500>; + regulator-enable-ramp-delay = <115>; + + }; + + mt6397_vcore_reg: buck_vcore { + regulator-compatible = "buck_vcore"; + regulator-name = "vcore"; + regulator-min-microvolt = < 850000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <12500>; + regulator-enable-ramp-delay = <115>; + }; + + mt6397_vgpu_reg: buck_vgpu { + regulator-compatible = "buck_vgpu"; + regulator-name = "vgpu"; + regulator-min-microvolt = < 700000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <12500>; + regulator-enable-ramp-delay = <115>; + }; + + mt6397_vdrm_reg: buck_vdrm { + regulator-compatible = "buck_vdrm"; + regulator-name = "vdrm"; + regulator-min-microvolt = < 800000>; + regulator-max-microvolt = <1400000>; + regulator-ramp-delay = <12500>; + regulator-enable-ramp-delay = <500>; + }; + + mt6397_vio18_reg: buck_vio18 { + regulator-compatible = "buck_vio18"; + regulator-name = "vio18"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <2120000>; + regulator-ramp-delay = <12500>; + regulator-enable-ramp-delay = <500>; + }; + + mt6397_vtcxo_reg: ldo_vtcxo { + regulator-compatible = "ldo_vtcxo"; + regulator-name = "vtcxo"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-enable-ramp-delay = <90>; + }; + + mt6397_va28_reg: ldo_va28 { + regulator-compatible = "ldo_va28"; + regulator-name = "va28"; + /* fixed output 2.8 V */ + regulator-enable-ramp-delay = <218>; + }; + + mt6397_vcama_reg: ldo_vcama { + regulator-compatible = "ldo_vcama"; + regulator-name = "vcama"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <2800000>; + regulator-enable-ramp-delay = <218>; + }; + + mt6397_vio28_reg: ldo_vio28 { + regulator-compatible = "ldo_vio28"; + regulator-name = "vio28"; + /* fixed output 2.8 V */ + regulator-enable-ramp-delay = <240>; + }; + + mt6397_usb_reg: ldo_vusb { + regulator-compatible = "ldo_vusb"; + regulator-name = "vusb"; + /* fixed output 3.3 V */ + regulator-enable-ramp-delay = <218>; + }; + + mt6397_vmc_reg: ldo_vmc { + regulator-compatible = "ldo_vmc"; + regulator-name = "vmc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-enable-ramp-delay = <218>; + }; + + mt6397_vmch_reg: ldo_vmch { + regulator-compatible = "ldo_vmch"; + regulator-name = "vmch"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3300000>; + regulator-enable-ramp-delay = <218>; + }; + + mt6397_vemc_3v3_reg: ldo_vemc3v3 { + regulator-compatible = "ldo_vemc3v3"; + regulator-name = "vemc_3v3"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3300000>; + regulator-enable-ramp-delay = <218>; + }; + + mt6397_vgp1_reg: ldo_vgp1 { + regulator-compatible = "ldo_vgp1"; + regulator-name = "vcamd"; + regulator-min-microvolt = <1220000>; + regulator-max-microvolt = <3300000>; + regulator-enable-ramp-delay = <240>; + }; + + mt6397_vgp2_reg: ldo_vgp2 { + egulator-compatible = "ldo_vgp2"; + regulator-name = "vcamio"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3300000>; + regulator-enable-ramp-delay = <218>; + }; + + mt6397_vgp3_reg: ldo_vgp3 { + regulator-compatible = "ldo_vgp3"; + regulator-name = "vcamaf"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + regulator-enable-ramp-delay = <218>; + }; + + mt6397_vgp4_reg: ldo_vgp4 { + regulator-compatible = "ldo_vgp4"; + regulator-name = "vgp4"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + regulator-enable-ramp-delay = <218>; + }; + + mt6397_vgp5_reg: ldo_vgp5 { + regulator-compatible = "ldo_vgp5"; + regulator-name = "vgp5"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3000000>; + regulator-enable-ramp-delay = <218>; + }; + + mt6397_vgp6_reg: ldo_vgp6 { + regulator-compatible = "ldo_vgp6"; + regulator-name = "vgp6"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + regulator-enable-ramp-delay = <218>; + }; + + mt6397_vibr_reg: ldo_vibr { + regulator-compatible = "ldo_vibr"; + regulator-name = "vibr"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + regulator-enable-ramp-delay = <218>; + }; + }; + }; -- cgit v0.10.2 From bba1431211e6fdc56680d98d2bdabaab1fd13b79 Mon Sep 17 00:00:00 2001 From: Jianqun Xu Date: Wed, 24 Dec 2014 17:37:01 +0800 Subject: ASoC: rockchip: i2s: set TDL and RDL to 16 samples Set Transmit Data Level(TDL) and Receive Data Level(RDL) to 16 samples. Without this setting, the TDL is default to be 0x00 (means 0 sample), and the RDL is default to be 0x1f (means 32 samples). Signed-off-by: Jianqun Xu Signed-off-by: Mark Brown diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 26ec511..dac0002 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -247,6 +247,10 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(i2s->regmap, I2S_TXCR, I2S_TXCR_VDW_MASK, val); regmap_update_bits(i2s->regmap, I2S_RXCR, I2S_RXCR_VDW_MASK, val); + regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK, + I2S_DMACR_TDL(16)); + regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK, + I2S_DMACR_RDL(16)); return 0; } -- cgit v0.10.2 From 53aebb7f19b71df6cf5aace87609b88cfc1c87ec Mon Sep 17 00:00:00 2001 From: Beomho Seo Date: Thu, 18 Dec 2014 20:13:36 +0900 Subject: regulator: rt5033-regulator: Use regulator_nodes/of_match in the descriptor This patch is add regulator_nodes/of_match in the regulator descriptor for using information from DT instead of sppecific codes. Cc: Liam Girdwood Cc: Mark Brown Signed-off-by: Beomho Seo Signed-off-by: Mark Brown diff --git a/drivers/regulator/rt5033-regulator.c b/drivers/regulator/rt5033-regulator.c index 870cc49..96d2c18 100644 --- a/drivers/regulator/rt5033-regulator.c +++ b/drivers/regulator/rt5033-regulator.c @@ -36,6 +36,8 @@ static struct regulator_ops rt5033_buck_ops = { static const struct regulator_desc rt5033_supported_regulators[] = { [RT5033_BUCK] = { .name = "BUCK", + .of_match = of_match_ptr("BUCK"), + .regulators_node = of_match_ptr("regulators"), .id = RT5033_BUCK, .ops = &rt5033_buck_ops, .type = REGULATOR_VOLTAGE, @@ -50,6 +52,8 @@ static const struct regulator_desc rt5033_supported_regulators[] = { }, [RT5033_LDO] = { .name = "LDO", + .of_match = of_match_ptr("LDO"), + .regulators_node = of_match_ptr("regulators"), .id = RT5033_LDO, .ops = &rt5033_buck_ops, .type = REGULATOR_VOLTAGE, @@ -64,6 +68,8 @@ static const struct regulator_desc rt5033_supported_regulators[] = { }, [RT5033_SAFE_LDO] = { .name = "SAFE_LDO", + .of_match = of_match_ptr("SAFE_LDO"), + .regulators_node = of_match_ptr("regulators"), .id = RT5033_SAFE_LDO, .ops = &rt5033_safe_ldo_ops, .type = REGULATOR_VOLTAGE, @@ -81,7 +87,7 @@ static int rt5033_regulator_probe(struct platform_device *pdev) int ret, i; struct regulator_config config = {}; - config.dev = &pdev->dev; + config.dev = rt5033->dev; config.driver_data = rt5033; for (i = 0; i < ARRAY_SIZE(rt5033_supported_regulators); i++) { -- cgit v0.10.2 From 18e0c0bf3a5ea0f54384149570274d535341dc06 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 7 Dec 2014 23:00:13 +0100 Subject: rtlwifi: rtl8192de: fw.c: Remove unused function Remove the function rtl92d_set_fw_pwrmode_cmd() that is not used anywhere. This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/fw.c b/drivers/net/wireless/rtlwifi/rtl8192de/fw.c index 2317707..62ef820 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/fw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/fw.c @@ -540,23 +540,6 @@ void rtl92d_fill_h2c_cmd(struct ieee80211_hw *hw, return; } -void rtl92d_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - u8 u1_h2c_set_pwrmode[3] = { 0 }; - struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); - - RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode); - SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, mode); - SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode, 1); - SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(u1_h2c_set_pwrmode, - ppsc->reg_max_lps_awakeintvl); - RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, - "rtl92d_set_fw_rsvdpagepkt(): u1_h2c_set_pwrmode", - u1_h2c_set_pwrmode, 3); - rtl92d_fill_h2c_cmd(hw, H2C_SETPWRMODE, 3, u1_h2c_set_pwrmode); -} - static bool _rtl92d_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb) { diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/fw.h b/drivers/net/wireless/rtlwifi/rtl8192de/fw.h index a55a803..1646e7c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/fw.h +++ b/drivers/net/wireless/rtlwifi/rtl8192de/fw.h @@ -136,7 +136,6 @@ int rtl92d_download_fw(struct ieee80211_hw *hw); void rtl92d_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, u32 cmd_len, u8 *p_cmdbuffer); void rtl92d_firmware_selfreset(struct ieee80211_hw *hw); -void rtl92d_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode); void rtl92d_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished); void rtl92d_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus); -- cgit v0.10.2 From eae79b4f3e82ca63a53478a161b190a0d38fe526 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 13 Dec 2014 00:43:55 +0300 Subject: rsi: fix memory leak in rsi_load_ta_instructions() Memory allocated by kmemdup() in rsi_load_ta_instructions() is leaked. But duplication of firmware data here is useless, so the patch removes kmemdup() at all. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c index 4834a9a..b6cc9ff 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c @@ -172,7 +172,6 @@ static int rsi_load_ta_instructions(struct rsi_common *common) (struct rsi_91x_sdiodev *)adapter->rsi_dev; u32 len; u32 num_blocks; - const u8 *fw; const struct firmware *fw_entry = NULL; u32 block_size = dev->tx_blk_size; int status = 0; @@ -201,7 +200,6 @@ static int rsi_load_ta_instructions(struct rsi_common *common) return status; } - fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); len = fw_entry->size; if (len % 4) @@ -212,7 +210,7 @@ static int rsi_load_ta_instructions(struct rsi_common *common) rsi_dbg(INIT_ZONE, "%s: Instruction size:%d\n", __func__, len); rsi_dbg(INIT_ZONE, "%s: num blocks: %d\n", __func__, num_blocks); - status = rsi_copy_to_card(common, fw, len, num_blocks); + status = rsi_copy_to_card(common, fw_entry->data, len, num_blocks); release_firmware(fw_entry); return status; } -- cgit v0.10.2 From a844bae38bf8c07ce18944d8b3d484911d75c8dd Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Mon, 15 Dec 2014 10:55:34 -0800 Subject: ath: fix incorrect PPB on FCC radar type 5 The minimum number of pulses per burst on FCC radar type 5 is 1. Use this number for correct radar detection. Signed-off-by: Peter Oh Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c index cfd0554..3d57f87 100644 --- a/drivers/net/wireless/ath/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c @@ -86,7 +86,7 @@ static const struct radar_detector_specs fcc_radar_ref_types[] = { FCC_PATTERN(1, 0, 5, 150, 230, 1, 23), FCC_PATTERN(2, 6, 10, 200, 500, 1, 16), FCC_PATTERN(3, 11, 20, 200, 500, 1, 12), - FCC_PATTERN(4, 50, 100, 1000, 2000, 1, 20), + FCC_PATTERN(4, 50, 100, 1000, 2000, 1, 1), FCC_PATTERN(5, 0, 1, 333, 333, 1, 9), }; -- cgit v0.10.2 From fe89707f0afa17edbb1bf8381fea1d2457aeebaa Mon Sep 17 00:00:00 2001 From: Troy Tan Date: Thu, 18 Dec 2014 03:05:25 -0600 Subject: rtlwifi: rtl8821ae: Simplify loading of WOWLAN firmware The existing method for loading both normal and WOWLAN firmware for the device duplicates a lot of code. This solution is much cleaner. Signed-off-by: Troy Tan Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index 5fc6f52..deab852 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -95,7 +95,8 @@ void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data) } EXPORT_SYMBOL(rtl_bb_delay); -void rtl_fw_cb(const struct firmware *firmware, void *context) +static void rtl_fw_do_work(const struct firmware *firmware, void *context, + bool is_wow) { struct ieee80211_hw *hw = context; struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -125,12 +126,31 @@ found_alt: release_firmware(firmware); return; } - memcpy(rtlpriv->rtlhal.pfirmware, firmware->data, firmware->size); + if (!is_wow) { + memcpy(rtlpriv->rtlhal.pfirmware, firmware->data, + firmware->size); + rtlpriv->rtlhal.fwsize = firmware->size; + } else { + memcpy(rtlpriv->rtlhal.wowlan_firmware, firmware->data, + firmware->size); + rtlpriv->rtlhal.wowlan_fwsize = firmware->size; + } rtlpriv->rtlhal.fwsize = firmware->size; release_firmware(firmware); } + +void rtl_fw_cb(const struct firmware *firmware, void *context) +{ + rtl_fw_do_work(firmware, context, false); +} EXPORT_SYMBOL(rtl_fw_cb); +void rtl_wowlan_fw_cb(const struct firmware *firmware, void *context) +{ + rtl_fw_do_work(firmware, context, true); +} +EXPORT_SYMBOL(rtl_wowlan_fw_cb); + /*mutex for start & stop is must here. */ static int rtl_op_start(struct ieee80211_hw *hw) { diff --git a/drivers/net/wireless/rtlwifi/core.h b/drivers/net/wireless/rtlwifi/core.h index 624e1dc..8c87eb5 100644 --- a/drivers/net/wireless/rtlwifi/core.h +++ b/drivers/net/wireless/rtlwifi/core.h @@ -37,6 +37,7 @@ extern const struct ieee80211_ops rtl_ops; void rtl_fw_cb(const struct firmware *firmware, void *context); +void rtl_wowlan_fw_cb(const struct firmware *firmware, void *context); void rtl_addr_delay(u32 addr); void rtl_rfreg_delay(struct ieee80211_hw *hw, enum radio_path rfpath, u32 addr, u32 mask, u32 data); diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c b/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c index fc92dd6..a4988121 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c @@ -85,52 +85,6 @@ static void rtl8821ae_init_aspm_vars(struct ieee80211_hw *hw) rtlpci->const_support_pciaspm = 1; } -static void load_wowlan_fw(struct rtl_priv *rtlpriv) -{ - /* callback routine to load wowlan firmware after main fw has - * been loaded - */ - const struct firmware *wowlan_firmware; - char *fw_name = NULL; - int err; - - /* for wowlan firmware buf */ - rtlpriv->rtlhal.wowlan_firmware = vzalloc(0x8000); - if (!rtlpriv->rtlhal.wowlan_firmware) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Can't alloc buffer for wowlan fw.\n"); - return; - } - - if (rtlpriv->rtlhal.hw_type == HARDWARE_TYPE_RTL8821AE) - fw_name = "rtlwifi/rtl8821aefw_wowlan.bin"; - else - fw_name = "rtlwifi/rtl8812aefw_wowlan.bin"; - err = request_firmware(&wowlan_firmware, fw_name, rtlpriv->io.dev); - if (err) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Failed to request wowlan firmware!\n"); - goto error; - } - - if (wowlan_firmware->size > 0x8000) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Wowlan Firmware is too big!\n"); - goto error; - } - - memcpy(rtlpriv->rtlhal.wowlan_firmware, wowlan_firmware->data, - wowlan_firmware->size); - rtlpriv->rtlhal.wowlan_fwsize = wowlan_firmware->size; - release_firmware(wowlan_firmware); - - RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "WOWLAN FirmwareDownload OK\n"); - return; -error: - release_firmware(wowlan_firmware); - vfree(rtlpriv->rtlhal.wowlan_firmware); -} - /*InitializeVariables8812E*/ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) { @@ -231,7 +185,6 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) else if (rtlpriv->psc.reg_fwctrl_lps == 3) rtlpriv->psc.fwctrl_psmode = FW_PS_DTIM_MODE; - rtlpriv->rtl_fw_second_cb = load_wowlan_fw; /* for firmware buf */ rtlpriv->rtlhal.pfirmware = vzalloc(0x8000); if (!rtlpriv->rtlhal.pfirmware) { @@ -239,20 +192,41 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) "Can't alloc buffer for fw.\n"); return 1; } + rtlpriv->rtlhal.wowlan_firmware = vzalloc(0x8000); + if (!rtlpriv->rtlhal.wowlan_firmware) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Can't alloc buffer for wowlan fw.\n"); + return 1; + } - if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) { rtlpriv->cfg->fw_name = "rtlwifi/rtl8812aefw.bin"; - else + rtlpriv->cfg->wowlan_fw_name = "rtlwifi/rtl8812aefw_wowlan.bin"; + } else { rtlpriv->cfg->fw_name = "rtlwifi/rtl8821aefw.bin"; + rtlpriv->cfg->wowlan_fw_name = "rtlwifi/rtl8821aefw_wowlan.bin"; + } rtlpriv->max_fw_size = 0x8000; + /*load normal firmware*/ pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name); err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name, rtlpriv->io.dev, GFP_KERNEL, hw, rtl_fw_cb); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Failed to request firmware!\n"); + "Failed to request normal firmware!\n"); + return 1; + } + /*load wowlan firmware*/ + pr_info("Using firmware %s\n", rtlpriv->cfg->wowlan_fw_name); + err = request_firmware_nowait(THIS_MODULE, 1, + rtlpriv->cfg->wowlan_fw_name, + rtlpriv->io.dev, GFP_KERNEL, hw, + rtl_wowlan_fw_cb); + if (err) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Failed to request wowlan firmware!\n"); return 1; } return 0; diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index 6866dcf..a233b26 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -2242,6 +2242,7 @@ struct rtl_hal_cfg { char *name; char *fw_name; char *alt_fw_name; + char *wowlan_fw_name; struct rtl_hal_ops *ops; struct rtl_mod_params *mod_params; struct rtl_hal_usbint_cfg *usb_interface_cfg; @@ -2518,8 +2519,6 @@ struct proxim { struct rtl_priv { struct ieee80211_hw *hw; - /* Used to load a second firmware */ - void (*rtl_fw_second_cb)(struct rtl_priv *rtlpriv); struct completion firmware_loading_complete; struct list_head list; struct rtl_priv *buddy_priv; -- cgit v0.10.2 From f091282fd367faa014ebc2aa0a2fd178a48b476d Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 18 Dec 2014 03:05:26 -0600 Subject: rtlwifi: rtl8821ae: Fix typos in power-sequence macro Two of the macros that control power sequencing have values to be set that contain bits that are not covered by the associated mask. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/pwrseq.h b/drivers/net/wireless/rtlwifi/rtl8821ae/pwrseq.h index bf0b0ce..36b3e91 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/pwrseq.h +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/pwrseq.h @@ -93,9 +93,9 @@ #define RTL8812_TRANS_CARDEMU_TO_SUS \ {0x0042, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK,\ - PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xF0, 0xcc}, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xF0, 0xc0}, \ {0x0042, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK,\ - PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xF0, 0xEC}, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xF0, 0xE0}, \ {0x0043, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,\ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x07 \ /* gpio11 input mode, gpio10~8 output mode */}, \ -- cgit v0.10.2 From 1ed03272b58f1ed6545b99dde39c42358156af69 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 18 Dec 2014 03:05:27 -0600 Subject: rtlwifi: rtl8192ce: Add code to set the keep-alive operation This change helps the device maintain a connection. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h index b64ae45..e9f4281 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h +++ b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h @@ -37,6 +37,7 @@ #define FW_8192C_POLLING_DELAY 5 #define FW_8192C_POLLING_TIMEOUT_COUNT 100 #define NORMAL_CHIP BIT(4) +#define H2C_92C_KEEP_ALIVE_CTRL 48 #define IS_FW_HEADER_EXIST(_pfwhdr) \ ((le16_to_cpu(_pfwhdr->signature)&0xFFF0) == 0x92C0 ||\ diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c index 5c646d5..b63b78b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c @@ -544,8 +544,13 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) (u8 *)(&fw_current_inps)); } break; } - case HW_VAR_KEEP_ALIVE: - break; + case HW_VAR_KEEP_ALIVE: { + u8 array[2]; + + array[0] = 0xff; + array[1] = *((u8 *)val); + rtl92c_fill_h2c_cmd(hw, H2C_92C_KEEP_ALIVE_CTRL, 2, array); + break; } default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case %d not processed\n", variable); -- cgit v0.10.2 From 9d62c5531b515ef427cee13cca87777dba617c94 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 18 Dec 2014 03:05:28 -0600 Subject: rtlwifi: rtl8192ce: Update setting of the media status This patch applies changes found in the latest vendor driver. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c index b63b78b..2ee778a 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c @@ -1161,36 +1161,24 @@ static int _rtl92ce_set_media_status(struct ieee80211_hw *hw, struct rtl_priv *rtlpriv = rtl_priv(hw); u8 bt_msr = rtl_read_byte(rtlpriv, MSR); enum led_ctl_mode ledaction = LED_CTL_NO_LINK; - bt_msr &= 0xfc; + u8 mode = MSR_NOLINK; - if (type == NL80211_IFTYPE_UNSPECIFIED || - type == NL80211_IFTYPE_STATION) { - _rtl92ce_stop_tx_beacon(hw); - _rtl92ce_enable_bcn_sub_func(hw); - } else if (type == NL80211_IFTYPE_ADHOC || type == NL80211_IFTYPE_AP || - type == NL80211_IFTYPE_MESH_POINT) { - _rtl92ce_resume_tx_beacon(hw); - _rtl92ce_disable_bcn_sub_func(hw); - } else { - RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, - "Set HW_VAR_MEDIA_STATUS: No such media status(%x)\n", - type); - } + bt_msr &= 0xfc; switch (type) { case NL80211_IFTYPE_UNSPECIFIED: - bt_msr |= MSR_NOLINK; + mode = MSR_NOLINK; ledaction = LED_CTL_LINK; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to NO LINK!\n"); break; case NL80211_IFTYPE_ADHOC: - bt_msr |= MSR_ADHOC; + mode = MSR_ADHOC; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to Ad Hoc!\n"); break; case NL80211_IFTYPE_STATION: - bt_msr |= MSR_INFRA; + mode = MSR_INFRA; ledaction = LED_CTL_LINK; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to STA!\n"); @@ -1201,7 +1189,7 @@ static int _rtl92ce_set_media_status(struct ieee80211_hw *hw, "Set Network type to AP!\n"); break; case NL80211_IFTYPE_MESH_POINT: - bt_msr |= MSR_ADHOC; + mode = MSR_ADHOC; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to Mesh Point!\n"); break; @@ -1212,9 +1200,32 @@ static int _rtl92ce_set_media_status(struct ieee80211_hw *hw, } - rtl_write_byte(rtlpriv, (MSR), bt_msr); + /* MSR_INFRA == Link in infrastructure network; + * MSR_ADHOC == Link in ad hoc network; + * Therefore, check link state is necessary. + * + * MSR_AP == AP mode; link state does not matter here. + */ + if (mode != MSR_AP && + rtlpriv->mac80211.link_state < MAC80211_LINKED) { + mode = MSR_NOLINK; + ledaction = LED_CTL_NO_LINK; + } + if (mode == MSR_NOLINK || mode == MSR_INFRA) { + _rtl92ce_stop_tx_beacon(hw); + _rtl92ce_enable_bcn_sub_func(hw); + } else if (mode == MSR_ADHOC || mode == MSR_AP) { + _rtl92ce_resume_tx_beacon(hw); + _rtl92ce_disable_bcn_sub_func(hw); + } else { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "Set HW_VAR_MEDIA_STATUS: No such media status(%x).\n", + mode); + } + rtl_write_byte(rtlpriv, (MSR), bt_msr | mode); + rtlpriv->cfg->ops->led_control(hw, ledaction); - if ((bt_msr & MSR_MASK) == MSR_AP) + if (mode == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); else rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66); -- cgit v0.10.2 From ff6ee6b0ce1f62936a916ef1c33015a2ec48925e Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 18 Dec 2014 03:05:29 -0600 Subject: rtlwifi: rtl8192ce: Update rate setting routines These changes were found in the latest vendor driver. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c index 2ee778a..303b299 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c @@ -1168,7 +1168,6 @@ static int _rtl92ce_set_media_status(struct ieee80211_hw *hw, switch (type) { case NL80211_IFTYPE_UNSPECIFIED: mode = MSR_NOLINK; - ledaction = LED_CTL_LINK; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to NO LINK!\n"); break; @@ -1184,7 +1183,8 @@ static int _rtl92ce_set_media_status(struct ieee80211_hw *hw, "Set Network type to STA!\n"); break; case NL80211_IFTYPE_AP: - bt_msr |= MSR_AP; + mode = MSR_AP; + ledaction = LED_CTL_LINK; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to AP!\n"); break; @@ -1222,7 +1222,7 @@ static int _rtl92ce_set_media_status(struct ieee80211_hw *hw, "Set HW_VAR_MEDIA_STATUS: No such media status(%x).\n", mode); } - rtl_write_byte(rtlpriv, (MSR), bt_msr | mode); + rtl_write_byte(rtlpriv, MSR, bt_msr | mode); rtlpriv->cfg->ops->led_control(hw, ledaction); if (mode == MSR_AP) @@ -1849,7 +1849,6 @@ static void rtl92ce_update_hal_rate_table(struct ieee80211_hw *hw, u32 ratr_value; u8 ratr_index = 0; u8 nmode = mac->ht_enable; - u8 mimo_ps = IEEE80211_SMPS_OFF; u16 shortgi_rate; u32 tmp_ratr_value; u8 curtxbw_40mhz = mac->bw_40; @@ -1858,6 +1857,7 @@ static void rtl92ce_update_hal_rate_table(struct ieee80211_hw *hw, u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? 1 : 0; enum wireless_mode wirelessmode = mac->mode; + u32 ratr_mask; if (rtlhal->current_bandtype == BAND_ON_5G) ratr_value = sta->supp_rates[1] << 4; @@ -1881,19 +1881,13 @@ static void rtl92ce_update_hal_rate_table(struct ieee80211_hw *hw, case WIRELESS_MODE_N_24G: case WIRELESS_MODE_N_5G: nmode = 1; - if (mimo_ps == IEEE80211_SMPS_STATIC) { - ratr_value &= 0x0007F005; - } else { - u32 ratr_mask; - - if (get_rf_type(rtlphy) == RF_1T2R || - get_rf_type(rtlphy) == RF_1T1R) - ratr_mask = 0x000ff005; - else - ratr_mask = 0x0f0ff005; + if (get_rf_type(rtlphy) == RF_1T2R || + get_rf_type(rtlphy) == RF_1T1R) + ratr_mask = 0x000ff005; + else + ratr_mask = 0x0f0ff005; - ratr_value &= ratr_mask; - } + ratr_value &= ratr_mask; break; default: if (rtlphy->rf_type == RF_1T2R) @@ -1946,17 +1940,16 @@ static void rtl92ce_update_hal_rate_mask(struct ieee80211_hw *hw, struct rtl_sta_info *sta_entry = NULL; u32 ratr_bitmap; u8 ratr_index; - u8 curtxbw_40mhz = (sta->bandwidth >= IEEE80211_STA_RX_BW_40) ? 1 : 0; - u8 curshortgi_40mhz = curtxbw_40mhz && - (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? - 1 : 0; + u8 curtxbw_40mhz = (sta->ht_cap.cap & + IEEE80211_HT_CAP_SUP_WIDTH_20_40) ? 1 : 0; + u8 curshortgi_40mhz = (sta->ht_cap.cap & + IEEE80211_HT_CAP_SGI_40) ? 1 : 0; u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? 1 : 0; enum wireless_mode wirelessmode = 0; bool shortgi = false; u8 rate_mask[5]; u8 macid = 0; - u8 mimo_ps = IEEE80211_SMPS_OFF; sta_entry = (struct rtl_sta_info *) sta->drv_priv; wirelessmode = sta_entry->wireless_mode; @@ -2001,47 +1994,38 @@ static void rtl92ce_update_hal_rate_mask(struct ieee80211_hw *hw, case WIRELESS_MODE_N_5G: ratr_index = RATR_INX_WIRELESS_NGB; - if (mimo_ps == IEEE80211_SMPS_STATIC) { - if (rssi_level == 1) - ratr_bitmap &= 0x00070000; - else if (rssi_level == 2) - ratr_bitmap &= 0x0007f000; - else - ratr_bitmap &= 0x0007f005; + if (rtlphy->rf_type == RF_1T2R || + rtlphy->rf_type == RF_1T1R) { + if (curtxbw_40mhz) { + if (rssi_level == 1) + ratr_bitmap &= 0x000f0000; + else if (rssi_level == 2) + ratr_bitmap &= 0x000ff000; + else + ratr_bitmap &= 0x000ff015; + } else { + if (rssi_level == 1) + ratr_bitmap &= 0x000f0000; + else if (rssi_level == 2) + ratr_bitmap &= 0x000ff000; + else + ratr_bitmap &= 0x000ff005; + } } else { - if (rtlphy->rf_type == RF_1T2R || - rtlphy->rf_type == RF_1T1R) { - if (curtxbw_40mhz) { - if (rssi_level == 1) - ratr_bitmap &= 0x000f0000; - else if (rssi_level == 2) - ratr_bitmap &= 0x000ff000; - else - ratr_bitmap &= 0x000ff015; - } else { - if (rssi_level == 1) - ratr_bitmap &= 0x000f0000; - else if (rssi_level == 2) - ratr_bitmap &= 0x000ff000; - else - ratr_bitmap &= 0x000ff005; - } + if (curtxbw_40mhz) { + if (rssi_level == 1) + ratr_bitmap &= 0x0f0f0000; + else if (rssi_level == 2) + ratr_bitmap &= 0x0f0ff000; + else + ratr_bitmap &= 0x0f0ff015; } else { - if (curtxbw_40mhz) { - if (rssi_level == 1) - ratr_bitmap &= 0x0f0f0000; - else if (rssi_level == 2) - ratr_bitmap &= 0x0f0ff000; - else - ratr_bitmap &= 0x0f0ff015; - } else { - if (rssi_level == 1) - ratr_bitmap &= 0x0f0f0000; - else if (rssi_level == 2) - ratr_bitmap &= 0x0f0ff000; - else - ratr_bitmap &= 0x0f0ff005; - } + if (rssi_level == 1) + ratr_bitmap &= 0x0f0f0000; + else if (rssi_level == 2) + ratr_bitmap &= 0x0f0ff000; + else + ratr_bitmap &= 0x0f0ff005; } } @@ -2074,9 +2058,6 @@ static void rtl92ce_update_hal_rate_mask(struct ieee80211_hw *hw, "Rate_index:%x, ratr_val:%x, %5phC\n", ratr_index, ratr_bitmap, rate_mask); rtl92c_fill_h2c_cmd(hw, H2C_RA_MASK, 5, rate_mask); - - if (macid != 0) - sta_entry->ratr_index = ratr_index; } void rtl92ce_update_hal_rate_tbl(struct ieee80211_hw *hw, -- cgit v0.10.2 From 99057920a2f7360977979dc4c2ba60d5f9dc9e23 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 18 Dec 2014 03:05:30 -0600 Subject: rtlwifi: rtl8192ce: Improve RF sleep routine These changes match those of the latest vendor driver. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c index bc5ca98..1ee5a6a 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c @@ -518,11 +518,12 @@ static bool _rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw, } case ERFSLEEP:{ if (ppsc->rfpwr_state == ERFOFF) - return false; + break; for (queue_id = 0, i = 0; queue_id < RTL_PCI_MAX_TX_QUEUE_COUNT;) { ring = &pcipriv->dev.tx_ring[queue_id]; - if (skb_queue_len(&ring->queue) == 0) { + if (queue_id == BEACON_QUEUE || + skb_queue_len(&ring->queue) == 0) { queue_id++; continue; } else { -- cgit v0.10.2 From f1f21770b11ca83d688b2791b163c2b892ca6fa6 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 18 Dec 2014 03:05:31 -0600 Subject: rtlwifi: Remove extraneous argument for rate mapping Four of the drivers (92ce, 92cu, 92de, and 92se) supply an argument to the rate-mapping routine that is never used, thus it can be removed. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index 40b6d1d..d4c8e95 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -883,7 +883,7 @@ static u8 _rtl_get_highest_n_rate(struct ieee80211_hw *hw, * DESC92_RATEMCS0-->DESC92_RATEMCS15 ==> idx is 0-->15 */ int rtlwifi_rate_mapping(struct ieee80211_hw *hw, - bool isht, u8 desc_rate, bool first_ampdu) + bool isht, u8 desc_rate) { int rate_idx; diff --git a/drivers/net/wireless/rtlwifi/base.h b/drivers/net/wireless/rtlwifi/base.h index 982f245..936c2bb 100644 --- a/drivers/net/wireless/rtlwifi/base.h +++ b/drivers/net/wireless/rtlwifi/base.h @@ -124,7 +124,7 @@ void rtl_deinit_deferred_work(struct ieee80211_hw *hw); bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx); int rtlwifi_rate_mapping(struct ieee80211_hw *hw, - bool isht, u8 desc_rate, bool first_ampdu); + bool isht, u8 desc_rate); bool rtl_tx_mgmt_proc(struct ieee80211_hw *hw, struct sk_buff *skb); u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c index e88dcd0..d25aeeb 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c @@ -400,9 +400,8 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw, * are use (RX_FLAG_HT) * Notice: this is diff with windows define */ - rx_status->rate_idx = rtlwifi_rate_mapping(hw, - stats->is_ht, stats->rate, - stats->isfirst_ampdu); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht, + stats->rate); rx_status->mactime = stats->timestamp_low; if (phystatus) { diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c index f383d5f..fa30b26 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c @@ -340,8 +340,7 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_DECRYPTED; rx_status->rate_idx = rtlwifi_rate_mapping(hw, (bool)GET_RX_DESC_RX_HT(pdesc), - (u8)GET_RX_DESC_RX_MCS(pdesc), - (bool)GET_RX_DESC_PAGGR(pdesc)); + (u8)GET_RX_DESC_RX_MCS(pdesc)); rx_status->mactime = GET_RX_DESC_TSFL(pdesc); if (phystatus) { p_drvinfo = (struct rx_fwinfo_92c *)(skb->data + @@ -408,8 +407,7 @@ static void _rtl_rx_process(struct ieee80211_hw *hw, struct sk_buff *skb) /* Data rate */ rx_status->rate_idx = rtlwifi_rate_mapping(hw, (bool)GET_RX_DESC_RX_HT(rxdesc), - (u8)GET_RX_DESC_RX_MCS(rxdesc), - (bool)GET_RX_DESC_PAGGR(rxdesc)); + (u8)GET_RX_DESC_RX_MCS(rxdesc)); /* There is a phy status after this rx descriptor. */ if (GET_RX_DESC_PHY_STATUS(rxdesc)) { p_drvinfo = (struct rx_fwinfo_92c *)(rxdesc + RTL_RX_DESC_SIZE); diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c index 8efbcc7..7fbae49 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c @@ -514,8 +514,7 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, rx_status->flag |= RX_FLAG_DECRYPTED; rx_status->rate_idx = rtlwifi_rate_mapping(hw, (bool)GET_RX_DESC_RXHT(pdesc), - (u8)GET_RX_DESC_RXMCS(pdesc), - (bool)GET_RX_DESC_PAGGR(pdesc)); + (u8)GET_RX_DESC_RXMCS(pdesc)); rx_status->mactime = GET_RX_DESC_TSFL(pdesc); if (phystatus) { p_drvinfo = (struct rx_fwinfo_92d *)(skb->data + diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c index 672fd3b..da053cb 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c @@ -264,7 +264,6 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, struct rx_fwinfo *p_drvinfo; u32 phystatus = (u32)GET_RX_STATUS_DESC_PHY_STATUS(pdesc); struct ieee80211_hdr *hdr; - bool first_ampdu = false; stats->length = (u16)GET_RX_STATUS_DESC_PKT_LEN(pdesc); stats->rx_drvinfo_size = (u8)GET_RX_STATUS_DESC_DRVINFO_SIZE(pdesc) * 8; @@ -319,8 +318,8 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, rx_status->flag |= RX_FLAG_DECRYPTED; } - rx_status->rate_idx = rtlwifi_rate_mapping(hw, - stats->is_ht, stats->rate, first_ampdu); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht, + stats->rate); rx_status->mactime = stats->timestamp_low; if (phystatus) { @@ -398,9 +397,9 @@ void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, if (rtlhal->version == VERSION_8192S_ACUT) { if (ptcb_desc->hw_rate == DESC92_RATE1M || - ptcb_desc->hw_rate == DESC92_RATE2M || - ptcb_desc->hw_rate == DESC92_RATE5_5M || - ptcb_desc->hw_rate == DESC92_RATE11M) { + ptcb_desc->hw_rate == DESC92_RATE2M || + ptcb_desc->hw_rate == DESC92_RATE5_5M || + ptcb_desc->hw_rate == DESC92_RATE11M) { ptcb_desc->hw_rate = DESC92_RATE12M; } } -- cgit v0.10.2 From 7e0dde9248c413485aa2eb2079782d8941f062c8 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 18 Dec 2014 03:05:32 -0600 Subject: rtlwifi: rtl8723be: Switch to use common rate-mapping routine This driver currently has its owm version of this routine that duplicates a routine in rtlwifi. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/trx.c b/drivers/net/wireless/rtlwifi/rtl8723be/trx.c index d6a1c70..cb23bed 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/trx.c @@ -47,164 +47,6 @@ static u8 _rtl8723be_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) return skb->priority; } -/* mac80211's rate_idx is like this: - * - * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ - * - * B/G rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92C_RATE1M-->DESC92C_RATE54M ==> idx is 0-->11, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 - * - * 5G band:rx_status->band == IEEE80211_BAND_5GHZ - * A rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92C_RATE6M-->DESC92C_RATE54M ==> idx is 0-->7, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 - */ -static int _rtl8723be_rate_mapping(struct ieee80211_hw *hw, - bool isht, u8 desc_rate) -{ - int rate_idx; - - if (!isht) { - if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) { - switch (desc_rate) { - case DESC92C_RATE1M: - rate_idx = 0; - break; - case DESC92C_RATE2M: - rate_idx = 1; - break; - case DESC92C_RATE5_5M: - rate_idx = 2; - break; - case DESC92C_RATE11M: - rate_idx = 3; - break; - case DESC92C_RATE6M: - rate_idx = 4; - break; - case DESC92C_RATE9M: - rate_idx = 5; - break; - case DESC92C_RATE12M: - rate_idx = 6; - break; - case DESC92C_RATE18M: - rate_idx = 7; - break; - case DESC92C_RATE24M: - rate_idx = 8; - break; - case DESC92C_RATE36M: - rate_idx = 9; - break; - case DESC92C_RATE48M: - rate_idx = 10; - break; - case DESC92C_RATE54M: - rate_idx = 11; - break; - default: - rate_idx = 0; - break; - } - } else { - switch (desc_rate) { - case DESC92C_RATE6M: - rate_idx = 0; - break; - case DESC92C_RATE9M: - rate_idx = 1; - break; - case DESC92C_RATE12M: - rate_idx = 2; - break; - case DESC92C_RATE18M: - rate_idx = 3; - break; - case DESC92C_RATE24M: - rate_idx = 4; - break; - case DESC92C_RATE36M: - rate_idx = 5; - break; - case DESC92C_RATE48M: - rate_idx = 6; - break; - case DESC92C_RATE54M: - rate_idx = 7; - break; - default: - rate_idx = 0; - break; - } - } - } else { - switch (desc_rate) { - case DESC92C_RATEMCS0: - rate_idx = 0; - break; - case DESC92C_RATEMCS1: - rate_idx = 1; - break; - case DESC92C_RATEMCS2: - rate_idx = 2; - break; - case DESC92C_RATEMCS3: - rate_idx = 3; - break; - case DESC92C_RATEMCS4: - rate_idx = 4; - break; - case DESC92C_RATEMCS5: - rate_idx = 5; - break; - case DESC92C_RATEMCS6: - rate_idx = 6; - break; - case DESC92C_RATEMCS7: - rate_idx = 7; - break; - case DESC92C_RATEMCS8: - rate_idx = 8; - break; - case DESC92C_RATEMCS9: - rate_idx = 9; - break; - case DESC92C_RATEMCS10: - rate_idx = 10; - break; - case DESC92C_RATEMCS11: - rate_idx = 11; - break; - case DESC92C_RATEMCS12: - rate_idx = 12; - break; - case DESC92C_RATEMCS13: - rate_idx = 13; - break; - case DESC92C_RATEMCS14: - rate_idx = 14; - break; - case DESC92C_RATEMCS15: - rate_idx = 15; - break; - default: - rate_idx = 0; - break; - } - } - return rate_idx; -} - static void _rtl8723be_query_rxphystatus(struct ieee80211_hw *hw, struct rtl_stats *pstatus, u8 *pdesc, struct rx_fwinfo_8723be *p_drvinfo, @@ -558,7 +400,7 @@ bool rtl8723be_rx_query_desc(struct ieee80211_hw *hw, * supported rates or MCS index if HT rates * are use (RX_FLAG_HT) */ - rx_status->rate_idx = _rtl8723be_rate_mapping(hw, status->is_ht, + rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht, status->rate); rx_status->mactime = status->timestamp_low; -- cgit v0.10.2 From 5b243feff35ab6050893741bc1a7016fa3ae5a8b Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 18 Dec 2014 03:05:33 -0600 Subject: rtlwifi: rtl8188ee: Switch to use common rate-mapping routine This driver duplicates a routine found in the core. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c index df549c9..d430884 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c @@ -47,164 +47,6 @@ static u8 _rtl88ee_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) return skb->priority; } -/* mac80211's rate_idx is like this: - * - * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ - * - * B/G rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92C_RATE1M-->DESC92C_RATE54M ==> idx is 0-->11, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 - * - * 5G band:rx_status->band == IEEE80211_BAND_5GHZ - * A rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92C_RATE6M-->DESC92C_RATE54M ==> idx is 0-->7, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 - */ -static int _rtl88ee_rate_mapping(struct ieee80211_hw *hw, - bool isht, u8 desc_rate) -{ - int rate_idx; - - if (!isht) { - if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) { - switch (desc_rate) { - case DESC92C_RATE1M: - rate_idx = 0; - break; - case DESC92C_RATE2M: - rate_idx = 1; - break; - case DESC92C_RATE5_5M: - rate_idx = 2; - break; - case DESC92C_RATE11M: - rate_idx = 3; - break; - case DESC92C_RATE6M: - rate_idx = 4; - break; - case DESC92C_RATE9M: - rate_idx = 5; - break; - case DESC92C_RATE12M: - rate_idx = 6; - break; - case DESC92C_RATE18M: - rate_idx = 7; - break; - case DESC92C_RATE24M: - rate_idx = 8; - break; - case DESC92C_RATE36M: - rate_idx = 9; - break; - case DESC92C_RATE48M: - rate_idx = 10; - break; - case DESC92C_RATE54M: - rate_idx = 11; - break; - default: - rate_idx = 0; - break; - } - } else { - switch (desc_rate) { - case DESC92C_RATE6M: - rate_idx = 0; - break; - case DESC92C_RATE9M: - rate_idx = 1; - break; - case DESC92C_RATE12M: - rate_idx = 2; - break; - case DESC92C_RATE18M: - rate_idx = 3; - break; - case DESC92C_RATE24M: - rate_idx = 4; - break; - case DESC92C_RATE36M: - rate_idx = 5; - break; - case DESC92C_RATE48M: - rate_idx = 6; - break; - case DESC92C_RATE54M: - rate_idx = 7; - break; - default: - rate_idx = 0; - break; - } - } - } else { - switch (desc_rate) { - case DESC92C_RATEMCS0: - rate_idx = 0; - break; - case DESC92C_RATEMCS1: - rate_idx = 1; - break; - case DESC92C_RATEMCS2: - rate_idx = 2; - break; - case DESC92C_RATEMCS3: - rate_idx = 3; - break; - case DESC92C_RATEMCS4: - rate_idx = 4; - break; - case DESC92C_RATEMCS5: - rate_idx = 5; - break; - case DESC92C_RATEMCS6: - rate_idx = 6; - break; - case DESC92C_RATEMCS7: - rate_idx = 7; - break; - case DESC92C_RATEMCS8: - rate_idx = 8; - break; - case DESC92C_RATEMCS9: - rate_idx = 9; - break; - case DESC92C_RATEMCS10: - rate_idx = 10; - break; - case DESC92C_RATEMCS11: - rate_idx = 11; - break; - case DESC92C_RATEMCS12: - rate_idx = 12; - break; - case DESC92C_RATEMCS13: - rate_idx = 13; - break; - case DESC92C_RATEMCS14: - rate_idx = 14; - break; - case DESC92C_RATEMCS15: - rate_idx = 15; - break; - default: - rate_idx = 0; - break; - } - } - return rate_idx; -} - static void _rtl88ee_query_rxphystatus(struct ieee80211_hw *hw, struct rtl_stats *pstatus, u8 *pdesc, struct rx_fwinfo_88e *p_drvinfo, @@ -630,8 +472,8 @@ bool rtl88ee_rx_query_desc(struct ieee80211_hw *hw, * are use (RX_FLAG_HT) * Notice: this is diff with windows define */ - rx_status->rate_idx = _rtl88ee_rate_mapping(hw, - status->is_ht, status->rate); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht, + status->rate); rx_status->mactime = status->timestamp_low; if (phystatus == true) { -- cgit v0.10.2 From 8d3fc3a64ba34d859d6bb0de76bd1860b234cd1c Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 18 Dec 2014 03:05:34 -0600 Subject: rtlwifi: rtl8723ae: Modify driver to use rate-mapping routine in core This driver is also converted to use the rate-mapping code in the core. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c index d372cca..662a094 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c @@ -45,164 +45,6 @@ static u8 _rtl8723e_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) return skb->priority; } -/* mac80211's rate_idx is like this: - * - * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ - * - * B/G rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92C_RATE1M-->DESC92C_RATE54M ==> idx is 0-->11, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 - * - * 5G band:rx_status->band == IEEE80211_BAND_5GHZ - * A rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92C_RATE6M-->DESC92C_RATE54M ==> idx is 0-->7, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 - */ -static int _rtl8723e_rate_mapping(struct ieee80211_hw *hw, - bool isht, u8 desc_rate) -{ - int rate_idx; - - if (!isht) { - if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) { - switch (desc_rate) { - case DESC92C_RATE1M: - rate_idx = 0; - break; - case DESC92C_RATE2M: - rate_idx = 1; - break; - case DESC92C_RATE5_5M: - rate_idx = 2; - break; - case DESC92C_RATE11M: - rate_idx = 3; - break; - case DESC92C_RATE6M: - rate_idx = 4; - break; - case DESC92C_RATE9M: - rate_idx = 5; - break; - case DESC92C_RATE12M: - rate_idx = 6; - break; - case DESC92C_RATE18M: - rate_idx = 7; - break; - case DESC92C_RATE24M: - rate_idx = 8; - break; - case DESC92C_RATE36M: - rate_idx = 9; - break; - case DESC92C_RATE48M: - rate_idx = 10; - break; - case DESC92C_RATE54M: - rate_idx = 11; - break; - default: - rate_idx = 0; - break; - } - } else { - switch (desc_rate) { - case DESC92C_RATE6M: - rate_idx = 0; - break; - case DESC92C_RATE9M: - rate_idx = 1; - break; - case DESC92C_RATE12M: - rate_idx = 2; - break; - case DESC92C_RATE18M: - rate_idx = 3; - break; - case DESC92C_RATE24M: - rate_idx = 4; - break; - case DESC92C_RATE36M: - rate_idx = 5; - break; - case DESC92C_RATE48M: - rate_idx = 6; - break; - case DESC92C_RATE54M: - rate_idx = 7; - break; - default: - rate_idx = 0; - break; - } - } - } else { - switch (desc_rate) { - case DESC92C_RATEMCS0: - rate_idx = 0; - break; - case DESC92C_RATEMCS1: - rate_idx = 1; - break; - case DESC92C_RATEMCS2: - rate_idx = 2; - break; - case DESC92C_RATEMCS3: - rate_idx = 3; - break; - case DESC92C_RATEMCS4: - rate_idx = 4; - break; - case DESC92C_RATEMCS5: - rate_idx = 5; - break; - case DESC92C_RATEMCS6: - rate_idx = 6; - break; - case DESC92C_RATEMCS7: - rate_idx = 7; - break; - case DESC92C_RATEMCS8: - rate_idx = 8; - break; - case DESC92C_RATEMCS9: - rate_idx = 9; - break; - case DESC92C_RATEMCS10: - rate_idx = 10; - break; - case DESC92C_RATEMCS11: - rate_idx = 11; - break; - case DESC92C_RATEMCS12: - rate_idx = 12; - break; - case DESC92C_RATEMCS13: - rate_idx = 13; - break; - case DESC92C_RATEMCS14: - rate_idx = 14; - break; - case DESC92C_RATEMCS15: - rate_idx = 15; - break; - default: - rate_idx = 0; - break; - } - } - return rate_idx; -} - static void _rtl8723e_query_rxphystatus(struct ieee80211_hw *hw, struct rtl_stats *pstatus, u8 *pdesc, struct rx_fwinfo_8723e *p_drvinfo, @@ -503,8 +345,8 @@ bool rtl8723e_rx_query_desc(struct ieee80211_hw *hw, * are use (RX_FLAG_HT) * Notice: this is diff with windows define */ - rx_status->rate_idx = _rtl8723e_rate_mapping(hw, - status->is_ht, status->rate); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht, + status->rate); rx_status->mactime = status->timestamp_low; if (phystatus == true) { -- cgit v0.10.2 From 7b7d0d60a558d80165a18c0eceb1cc7b0c7d6439 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 18 Dec 2014 03:05:35 -0600 Subject: rtlwifi: rtl8192ee: Convert driver to use common rate-mapping code Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c index 2fcbef1..fb46fb9 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c @@ -47,164 +47,6 @@ static u8 _rtl92ee_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) return skb->priority; } -/* mac80211's rate_idx is like this: - * - * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ - * - * B/G rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92C_RATE1M-->DESC92C_RATE54M ==> idx is 0-->11, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 - * - * 5G band:rx_status->band == IEEE80211_BAND_5GHZ - * A rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92C_RATE6M-->DESC92C_RATE54M ==> idx is 0-->7, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 - */ -static int _rtl92ee_rate_mapping(struct ieee80211_hw *hw, - bool isht, u8 desc_rate) -{ - int rate_idx; - - if (!isht) { - if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) { - switch (desc_rate) { - case DESC92C_RATE1M: - rate_idx = 0; - break; - case DESC92C_RATE2M: - rate_idx = 1; - break; - case DESC92C_RATE5_5M: - rate_idx = 2; - break; - case DESC92C_RATE11M: - rate_idx = 3; - break; - case DESC92C_RATE6M: - rate_idx = 4; - break; - case DESC92C_RATE9M: - rate_idx = 5; - break; - case DESC92C_RATE12M: - rate_idx = 6; - break; - case DESC92C_RATE18M: - rate_idx = 7; - break; - case DESC92C_RATE24M: - rate_idx = 8; - break; - case DESC92C_RATE36M: - rate_idx = 9; - break; - case DESC92C_RATE48M: - rate_idx = 10; - break; - case DESC92C_RATE54M: - rate_idx = 11; - break; - default: - rate_idx = 0; - break; - } - } else { - switch (desc_rate) { - case DESC92C_RATE6M: - rate_idx = 0; - break; - case DESC92C_RATE9M: - rate_idx = 1; - break; - case DESC92C_RATE12M: - rate_idx = 2; - break; - case DESC92C_RATE18M: - rate_idx = 3; - break; - case DESC92C_RATE24M: - rate_idx = 4; - break; - case DESC92C_RATE36M: - rate_idx = 5; - break; - case DESC92C_RATE48M: - rate_idx = 6; - break; - case DESC92C_RATE54M: - rate_idx = 7; - break; - default: - rate_idx = 0; - break; - } - } - } else { - switch (desc_rate) { - case DESC92C_RATEMCS0: - rate_idx = 0; - break; - case DESC92C_RATEMCS1: - rate_idx = 1; - break; - case DESC92C_RATEMCS2: - rate_idx = 2; - break; - case DESC92C_RATEMCS3: - rate_idx = 3; - break; - case DESC92C_RATEMCS4: - rate_idx = 4; - break; - case DESC92C_RATEMCS5: - rate_idx = 5; - break; - case DESC92C_RATEMCS6: - rate_idx = 6; - break; - case DESC92C_RATEMCS7: - rate_idx = 7; - break; - case DESC92C_RATEMCS8: - rate_idx = 8; - break; - case DESC92C_RATEMCS9: - rate_idx = 9; - break; - case DESC92C_RATEMCS10: - rate_idx = 10; - break; - case DESC92C_RATEMCS11: - rate_idx = 11; - break; - case DESC92C_RATEMCS12: - rate_idx = 12; - break; - case DESC92C_RATEMCS13: - rate_idx = 13; - break; - case DESC92C_RATEMCS14: - rate_idx = 14; - break; - case DESC92C_RATEMCS15: - rate_idx = 15; - break; - default: - rate_idx = 0; - break; - } - } - return rate_idx; -} - static void _rtl92ee_query_rxphystatus(struct ieee80211_hw *hw, struct rtl_stats *pstatus, u8 *pdesc, struct rx_fwinfo *p_drvinfo, @@ -576,9 +418,8 @@ bool rtl92ee_rx_query_desc(struct ieee80211_hw *hw, * are use (RX_FLAG_HT) * Notice: this is diff with windows define */ - rx_status->rate_idx = _rtl92ee_rate_mapping(hw, - status->is_ht, - status->rate); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht, + status->rate); rx_status->mactime = status->timestamp_low; if (phystatus) { -- cgit v0.10.2 From e0e776a3c85c27751ab4399616fc50a474cc8379 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 18 Dec 2014 03:05:36 -0600 Subject: rtlwifi: Convert all drivers to use a common set of rate descriptors This common set of rate descriptors is renamed to be DESC_RATExx. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index d4c8e95..23fc51e 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -867,20 +867,20 @@ static u8 _rtl_get_highest_n_rate(struct ieee80211_hw *hw, * * B/G rate: * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92_RATE1M-->DESC92_RATE54M ==> idx is 0-->11, + * DESC_RATE1M-->DESC_RATE54M ==> idx is 0-->11, * * N rate: * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92_RATEMCS0-->DESC92_RATEMCS15 ==> idx is 0-->15 + * DESC_RATEMCS0-->DESC_RATEMCS15 ==> idx is 0-->15 * * 5G band:rx_status->band == IEEE80211_BAND_5GHZ * A rate: * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92_RATE6M-->DESC92_RATE54M ==> idx is 0-->7, + * DESC_RATE6M-->DESC_RATE54M ==> idx is 0-->7, * * N rate: * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92_RATEMCS0-->DESC92_RATEMCS15 ==> idx is 0-->15 + * DESC_RATEMCS0-->DESC_RATEMCS15 ==> idx is 0-->15 */ int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool isht, u8 desc_rate) @@ -890,40 +890,40 @@ int rtlwifi_rate_mapping(struct ieee80211_hw *hw, if (false == isht) { if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) { switch (desc_rate) { - case DESC92_RATE1M: + case DESC_RATE1M: rate_idx = 0; break; - case DESC92_RATE2M: + case DESC_RATE2M: rate_idx = 1; break; - case DESC92_RATE5_5M: + case DESC_RATE5_5M: rate_idx = 2; break; - case DESC92_RATE11M: + case DESC_RATE11M: rate_idx = 3; break; - case DESC92_RATE6M: + case DESC_RATE6M: rate_idx = 4; break; - case DESC92_RATE9M: + case DESC_RATE9M: rate_idx = 5; break; - case DESC92_RATE12M: + case DESC_RATE12M: rate_idx = 6; break; - case DESC92_RATE18M: + case DESC_RATE18M: rate_idx = 7; break; - case DESC92_RATE24M: + case DESC_RATE24M: rate_idx = 8; break; - case DESC92_RATE36M: + case DESC_RATE36M: rate_idx = 9; break; - case DESC92_RATE48M: + case DESC_RATE48M: rate_idx = 10; break; - case DESC92_RATE54M: + case DESC_RATE54M: rate_idx = 11; break; default: @@ -932,28 +932,28 @@ int rtlwifi_rate_mapping(struct ieee80211_hw *hw, } } else { switch (desc_rate) { - case DESC92_RATE6M: + case DESC_RATE6M: rate_idx = 0; break; - case DESC92_RATE9M: + case DESC_RATE9M: rate_idx = 1; break; - case DESC92_RATE12M: + case DESC_RATE12M: rate_idx = 2; break; - case DESC92_RATE18M: + case DESC_RATE18M: rate_idx = 3; break; - case DESC92_RATE24M: + case DESC_RATE24M: rate_idx = 4; break; - case DESC92_RATE36M: + case DESC_RATE36M: rate_idx = 5; break; - case DESC92_RATE48M: + case DESC_RATE48M: rate_idx = 6; break; - case DESC92_RATE54M: + case DESC_RATE54M: rate_idx = 7; break; default: @@ -963,52 +963,52 @@ int rtlwifi_rate_mapping(struct ieee80211_hw *hw, } } else { switch (desc_rate) { - case DESC92_RATEMCS0: + case DESC_RATEMCS0: rate_idx = 0; break; - case DESC92_RATEMCS1: + case DESC_RATEMCS1: rate_idx = 1; break; - case DESC92_RATEMCS2: + case DESC_RATEMCS2: rate_idx = 2; break; - case DESC92_RATEMCS3: + case DESC_RATEMCS3: rate_idx = 3; break; - case DESC92_RATEMCS4: + case DESC_RATEMCS4: rate_idx = 4; break; - case DESC92_RATEMCS5: + case DESC_RATEMCS5: rate_idx = 5; break; - case DESC92_RATEMCS6: + case DESC_RATEMCS6: rate_idx = 6; break; - case DESC92_RATEMCS7: + case DESC_RATEMCS7: rate_idx = 7; break; - case DESC92_RATEMCS8: + case DESC_RATEMCS8: rate_idx = 8; break; - case DESC92_RATEMCS9: + case DESC_RATEMCS9: rate_idx = 9; break; - case DESC92_RATEMCS10: + case DESC_RATEMCS10: rate_idx = 10; break; - case DESC92_RATEMCS11: + case DESC_RATEMCS11: rate_idx = 11; break; - case DESC92_RATEMCS12: + case DESC_RATEMCS12: rate_idx = 12; break; - case DESC92_RATEMCS13: + case DESC_RATEMCS13: rate_idx = 13; break; - case DESC92_RATEMCS14: + case DESC_RATEMCS14: rate_idx = 14; break; - case DESC92_RATEMCS15: + case DESC_RATEMCS15: rate_idx = 15; break; default: diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c index dd5aa08..de6cb6c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c @@ -334,21 +334,21 @@ static struct rtl_hal_cfg rtl92ce_hal_cfg = { .maps[RTL_IMR_ROK] = IMR_ROK, .maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER), - .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M, - .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M, - .maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M, - .maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M, - .maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M, - .maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M, - .maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M, - .maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M, - .maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M, - .maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M, - .maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M, - .maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M, - - .maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7, - .maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15, + .maps[RTL_RC_CCK_RATE1M] = DESC_RATE1M, + .maps[RTL_RC_CCK_RATE2M] = DESC_RATE2M, + .maps[RTL_RC_CCK_RATE5_5M] = DESC_RATE5_5M, + .maps[RTL_RC_CCK_RATE11M] = DESC_RATE11M, + .maps[RTL_RC_OFDM_RATE6M] = DESC_RATE6M, + .maps[RTL_RC_OFDM_RATE9M] = DESC_RATE9M, + .maps[RTL_RC_OFDM_RATE12M] = DESC_RATE12M, + .maps[RTL_RC_OFDM_RATE18M] = DESC_RATE18M, + .maps[RTL_RC_OFDM_RATE24M] = DESC_RATE24M, + .maps[RTL_RC_OFDM_RATE36M] = DESC_RATE36M, + .maps[RTL_RC_OFDM_RATE48M] = DESC_RATE48M, + .maps[RTL_RC_OFDM_RATE54M] = DESC_RATE54M, + + .maps[RTL_RC_HT_RATEMCS7] = DESC_RATEMCS7, + .maps[RTL_RC_HT_RATEMCS15] = DESC_RATEMCS15, }; static const struct pci_device_id rtl92ce_pci_ids[] = { diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c index d25aeeb..685faea 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c @@ -257,8 +257,8 @@ static void _rtl92ce_query_rxphystatus(struct ieee80211_hw *hw, pstats->recvsignalpower = rx_pwr_all; /* (3)EVM of HT rate */ - if (pstats->is_ht && pstats->rate >= DESC92_RATEMCS8 && - pstats->rate <= DESC92_RATEMCS15) + if (pstats->is_ht && pstats->rate >= DESC_RATEMCS8 && + pstats->rate <= DESC_RATEMCS15) max_spatial_stream = 2; else max_spatial_stream = 1; @@ -500,7 +500,7 @@ void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw, SET_TX_DESC_RTS_BW(pdesc, 0); SET_TX_DESC_RTS_SC(pdesc, tcb_desc->rts_sc); SET_TX_DESC_RTS_SHORT(pdesc, - ((tcb_desc->rts_rate <= DESC92_RATE54M) ? + ((tcb_desc->rts_rate <= DESC_RATE54M) ? (tcb_desc->rts_use_shortpreamble ? 1 : 0) : (tcb_desc->rts_use_shortgi ? 1 : 0))); @@ -623,7 +623,7 @@ void rtl92ce_tx_fill_cmddesc(struct ieee80211_hw *hw, if (firstseg) SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN); - SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE1M); + SET_TX_DESC_TX_RATE(pdesc, DESC_RATE1M); SET_TX_DESC_SEQ(pdesc, 0); diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c index c2d8ec6..133e395 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c @@ -880,8 +880,8 @@ static void _rtl92c_query_rxphystatus(struct ieee80211_hw *hw, pstats->rxpower = rx_pwr_all; pstats->recvsignalpower = rx_pwr_all; if (GET_RX_DESC_RX_MCS(pdesc) && - GET_RX_DESC_RX_MCS(pdesc) >= DESC92_RATEMCS8 && - GET_RX_DESC_RX_MCS(pdesc) <= DESC92_RATEMCS15) + GET_RX_DESC_RX_MCS(pdesc) >= DESC_RATEMCS8 && + GET_RX_DESC_RX_MCS(pdesc) <= DESC_RATEMCS15) max_spatial_stream = 2; else max_spatial_stream = 1; diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c index e06bafe..90a714c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c @@ -257,20 +257,20 @@ static struct rtl_hal_cfg rtl92cu_hal_cfg = { .maps[RTL_IMR_ROK] = IMR_ROK, .maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER), - .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M, - .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M, - .maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M, - .maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M, - .maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M, - .maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M, - .maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M, - .maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M, - .maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M, - .maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M, - .maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M, - .maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M, - .maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7, - .maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15, + .maps[RTL_RC_CCK_RATE1M] = DESC_RATE1M, + .maps[RTL_RC_CCK_RATE2M] = DESC_RATE2M, + .maps[RTL_RC_CCK_RATE5_5M] = DESC_RATE5_5M, + .maps[RTL_RC_CCK_RATE11M] = DESC_RATE11M, + .maps[RTL_RC_OFDM_RATE6M] = DESC_RATE6M, + .maps[RTL_RC_OFDM_RATE9M] = DESC_RATE9M, + .maps[RTL_RC_OFDM_RATE12M] = DESC_RATE12M, + .maps[RTL_RC_OFDM_RATE18M] = DESC_RATE18M, + .maps[RTL_RC_OFDM_RATE24M] = DESC_RATE24M, + .maps[RTL_RC_OFDM_RATE36M] = DESC_RATE36M, + .maps[RTL_RC_OFDM_RATE48M] = DESC_RATE48M, + .maps[RTL_RC_OFDM_RATE54M] = DESC_RATE54M, + .maps[RTL_RC_HT_RATEMCS7] = DESC_RATEMCS7, + .maps[RTL_RC_HT_RATEMCS15] = DESC_RATEMCS15, }; #define USB_VENDER_ID_REALTEK 0x0bda diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c index fa30b26..fe29876 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c @@ -543,7 +543,7 @@ void rtl92cu_tx_fill_desc(struct ieee80211_hw *hw, SET_TX_DESC_RTS_BW(txdesc, 0); SET_TX_DESC_RTS_SC(txdesc, tcb_desc->rts_sc); SET_TX_DESC_RTS_SHORT(txdesc, - ((tcb_desc->rts_rate <= DESC92_RATE54M) ? + ((tcb_desc->rts_rate <= DESC_RATE54M) ? (tcb_desc->rts_use_shortpreamble ? 1 : 0) : (tcb_desc->rts_use_shortgi ? 1 : 0))); if (mac->bw_40) { @@ -642,7 +642,7 @@ void rtl92cu_fill_fake_txdesc(struct ieee80211_hw *hw, u8 * pDesc, } SET_TX_DESC_USE_RATE(pDesc, 1); /* use data rate which is set by Sw */ SET_TX_DESC_OWN(pDesc, 1); - SET_TX_DESC_TX_RATE(pDesc, DESC92_RATE1M); + SET_TX_DESC_TX_RATE(pDesc, DESC_RATE1M); _rtl_tx_desc_checksum(pDesc); } @@ -658,7 +658,7 @@ void rtl92cu_tx_fill_cmddesc(struct ieee80211_hw *hw, memset((void *)pdesc, 0, RTL_TX_HEADER_SIZE); if (firstseg) SET_TX_DESC_OFFSET(pdesc, RTL_TX_HEADER_SIZE); - SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE1M); + SET_TX_DESC_TX_RATE(pdesc, DESC_RATE1M); SET_TX_DESC_SEQ(pdesc, 0); SET_TX_DESC_LINIP(pdesc, 0); SET_TX_DESC_QUEUE_SEL(pdesc, fw_queue); diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c index a0aba08..b19d039 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c @@ -337,21 +337,21 @@ static struct rtl_hal_cfg rtl92de_hal_cfg = { .maps[RTL_IMR_ROK] = IMR_ROK, .maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER), - .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M, - .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M, - .maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M, - .maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M, - .maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M, - .maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M, - .maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M, - .maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M, - .maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M, - .maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M, - .maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M, - .maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M, - - .maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7, - .maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15, + .maps[RTL_RC_CCK_RATE1M] = DESC_RATE1M, + .maps[RTL_RC_CCK_RATE2M] = DESC_RATE2M, + .maps[RTL_RC_CCK_RATE5_5M] = DESC_RATE5_5M, + .maps[RTL_RC_CCK_RATE11M] = DESC_RATE11M, + .maps[RTL_RC_OFDM_RATE6M] = DESC_RATE6M, + .maps[RTL_RC_OFDM_RATE9M] = DESC_RATE9M, + .maps[RTL_RC_OFDM_RATE12M] = DESC_RATE12M, + .maps[RTL_RC_OFDM_RATE18M] = DESC_RATE18M, + .maps[RTL_RC_OFDM_RATE24M] = DESC_RATE24M, + .maps[RTL_RC_OFDM_RATE36M] = DESC_RATE36M, + .maps[RTL_RC_OFDM_RATE48M] = DESC_RATE48M, + .maps[RTL_RC_OFDM_RATE54M] = DESC_RATE54M, + + .maps[RTL_RC_HT_RATEMCS7] = DESC_RATEMCS7, + .maps[RTL_RC_HT_RATEMCS15] = DESC_RATEMCS15, }; static struct pci_device_id rtl92de_pci_ids[] = { diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c index 7fbae49..381a4c0 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c @@ -235,8 +235,8 @@ static void _rtl92de_query_rxphystatus(struct ieee80211_hw *hw, pstats->rx_pwdb_all = pwdb_all; pstats->rxpower = rx_pwr_all; pstats->recvsignalpower = rx_pwr_all; - if (pdesc->rxht && pdesc->rxmcs >= DESC92_RATEMCS8 && - pdesc->rxmcs <= DESC92_RATEMCS15) + if (pdesc->rxht && pdesc->rxmcs >= DESC_RATEMCS8 && + pdesc->rxmcs <= DESC_RATEMCS15) max_spatial_stream = 2; else max_spatial_stream = 1; @@ -611,14 +611,14 @@ void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, } /* 5G have no CCK rate */ if (rtlhal->current_bandtype == BAND_ON_5G) - if (ptcb_desc->hw_rate < DESC92_RATE6M) - ptcb_desc->hw_rate = DESC92_RATE6M; + if (ptcb_desc->hw_rate < DESC_RATE6M) + ptcb_desc->hw_rate = DESC_RATE6M; SET_TX_DESC_TX_RATE(pdesc, ptcb_desc->hw_rate); if (ptcb_desc->use_shortgi || ptcb_desc->use_shortpreamble) SET_TX_DESC_DATA_SHORTGI(pdesc, 1); if (rtlhal->macphymode == DUALMAC_DUALPHY && - ptcb_desc->hw_rate == DESC92_RATEMCS7) + ptcb_desc->hw_rate == DESC_RATEMCS7) SET_TX_DESC_DATA_SHORTGI(pdesc, 1); if (info->flags & IEEE80211_TX_CTL_AMPDU) { @@ -634,13 +634,13 @@ void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, SET_TX_DESC_RTS_STBC(pdesc, ((ptcb_desc->rts_stbc) ? 1 : 0)); /* 5G have no CCK rate */ if (rtlhal->current_bandtype == BAND_ON_5G) - if (ptcb_desc->rts_rate < DESC92_RATE6M) - ptcb_desc->rts_rate = DESC92_RATE6M; + if (ptcb_desc->rts_rate < DESC_RATE6M) + ptcb_desc->rts_rate = DESC_RATE6M; SET_TX_DESC_RTS_RATE(pdesc, ptcb_desc->rts_rate); SET_TX_DESC_RTS_BW(pdesc, 0); SET_TX_DESC_RTS_SC(pdesc, ptcb_desc->rts_sc); SET_TX_DESC_RTS_SHORT(pdesc, ((ptcb_desc->rts_rate <= - DESC92_RATE54M) ? + DESC_RATE54M) ? (ptcb_desc->rts_use_shortpreamble ? 1 : 0) : (ptcb_desc->rts_use_shortgi ? 1 : 0))); if (bw_40) { @@ -755,9 +755,9 @@ void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw, * The braces are needed no matter what checkpatch says */ if (rtlhal->current_bandtype == BAND_ON_5G) { - SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE6M); + SET_TX_DESC_TX_RATE(pdesc, DESC_RATE6M); } else { - SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE1M); + SET_TX_DESC_TX_RATE(pdesc, DESC_RATE1M); } SET_TX_DESC_SEQ(pdesc, 0); SET_TX_DESC_LINIP(pdesc, 0); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c index fb46fb9..3ea7367 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c @@ -187,8 +187,8 @@ static void _rtl92ee_query_rxphystatus(struct ieee80211_hw *hw, pstatus->recvsignalpower = rx_pwr_all; /* (3)EVM of HT rate */ - if (pstatus->rate >= DESC92C_RATEMCS8 && - pstatus->rate <= DESC92C_RATEMCS15) + if (pstatus->rate >= DESC_RATEMCS8 && + pstatus->rate <= DESC_RATEMCS15) max_spatial_stream = 2; else max_spatial_stream = 1; @@ -742,13 +742,13 @@ void rtl92ee_tx_fill_desc(struct ieee80211_hw *hw, } else { if (rtlpriv->ra.is_special_data) { ptcb_desc->use_driver_rate = true; - SET_TX_DESC_TX_RATE(pdesc, DESC92C_RATE11M); + SET_TX_DESC_TX_RATE(pdesc, DESC_RATE11M); } else { ptcb_desc->use_driver_rate = false; } } - if (ptcb_desc->hw_rate > DESC92C_RATEMCS0) + if (ptcb_desc->hw_rate > DESC_RATEMCS0) short_gi = (ptcb_desc->use_shortgi) ? 1 : 0; else short_gi = (ptcb_desc->use_shortpreamble) ? 1 : 0; @@ -768,7 +768,7 @@ void rtl92ee_tx_fill_desc(struct ieee80211_hw *hw, SET_TX_DESC_RTS_RATE(pdesc, ptcb_desc->rts_rate); SET_TX_DESC_RTS_SC(pdesc, ptcb_desc->rts_sc); SET_TX_DESC_RTS_SHORT(pdesc, - ((ptcb_desc->rts_rate <= DESC92C_RATE54M) ? + ((ptcb_desc->rts_rate <= DESC_RATE54M) ? (ptcb_desc->rts_use_shortpreamble ? 1 : 0) : (ptcb_desc->rts_use_shortgi ? 1 : 0))); @@ -879,7 +879,7 @@ void rtl92ee_tx_fill_cmddesc(struct ieee80211_hw *hw, if (firstseg) SET_TX_DESC_OFFSET(pdesc, txdesc_len); - SET_TX_DESC_TX_RATE(pdesc, DESC92C_RATE1M); + SET_TX_DESC_TX_RATE(pdesc, DESC_RATE1M); SET_TX_DESC_SEQ(pdesc, 0); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h index 6f9be1c..45fd9db 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h @@ -591,10 +591,10 @@ do { \ } while (0) #define RTL92EE_RX_HAL_IS_CCK_RATE(rxmcs)\ - (rxmcs == DESC92C_RATE1M ||\ - rxmcs == DESC92C_RATE2M ||\ - rxmcs == DESC92C_RATE5_5M ||\ - rxmcs == DESC92C_RATE11M) + (rxmcs == DESC_RATE1M ||\ + rxmcs == DESC_RATE2M ||\ + rxmcs == DESC_RATE5_5M ||\ + rxmcs == DESC_RATE11M) #define IS_LITTLE_ENDIAN 1 diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/def.h b/drivers/net/wireless/rtlwifi/rtl8192se/def.h index 6e7a70b..ef87c09 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/def.h +++ b/drivers/net/wireless/rtlwifi/rtl8192se/def.h @@ -450,10 +450,10 @@ SHIFT_AND_MASK_LE(__pdesc + 24, 0, 32) #define SE_RX_HAL_IS_CCK_RATE(_pdesc)\ - (GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE1M || \ - GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE2M || \ - GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE5_5M ||\ - GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE11M) + (GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC_RATE1M || \ + GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC_RATE2M || \ + GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC_RATE5_5M ||\ + GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC_RATE11M) enum rf_optype { RF_OP_BY_SW_3WIRE = 0, diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c index fb00386..e1fd27c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c @@ -383,21 +383,21 @@ static struct rtl_hal_cfg rtl92se_hal_cfg = { .maps[RTL_IMR_ROK] = IMR_ROK, .maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER), - .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M, - .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M, - .maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M, - .maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M, - .maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M, - .maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M, - .maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M, - .maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M, - .maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M, - .maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M, - .maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M, - .maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M, - - .maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7, - .maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15, + .maps[RTL_RC_CCK_RATE1M] = DESC_RATE1M, + .maps[RTL_RC_CCK_RATE2M] = DESC_RATE2M, + .maps[RTL_RC_CCK_RATE5_5M] = DESC_RATE5_5M, + .maps[RTL_RC_CCK_RATE11M] = DESC_RATE11M, + .maps[RTL_RC_OFDM_RATE6M] = DESC_RATE6M, + .maps[RTL_RC_OFDM_RATE9M] = DESC_RATE9M, + .maps[RTL_RC_OFDM_RATE12M] = DESC_RATE12M, + .maps[RTL_RC_OFDM_RATE18M] = DESC_RATE18M, + .maps[RTL_RC_OFDM_RATE24M] = DESC_RATE24M, + .maps[RTL_RC_OFDM_RATE36M] = DESC_RATE36M, + .maps[RTL_RC_OFDM_RATE48M] = DESC_RATE48M, + .maps[RTL_RC_OFDM_RATE54M] = DESC_RATE54M, + + .maps[RTL_RC_HT_RATEMCS7] = DESC_RATEMCS7, + .maps[RTL_RC_HT_RATEMCS15] = DESC_RATEMCS15, }; static struct pci_device_id rtl92se_pci_ids[] = { diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c index da053cb..95eebff 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c @@ -191,8 +191,8 @@ static void _rtl92se_query_rxphystatus(struct ieee80211_hw *hw, pstats->rxpower = rx_pwr_all; pstats->recvsignalpower = rx_pwr_all; - if (pstats->is_ht && pstats->rate >= DESC92_RATEMCS8 && - pstats->rate <= DESC92_RATEMCS15) + if (pstats->is_ht && pstats->rate >= DESC_RATEMCS8 && + pstats->rate <= DESC_RATEMCS15) max_spatial_stream = 2; else max_spatial_stream = 1; @@ -393,14 +393,14 @@ void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, SET_TX_DESC_RSVD_MACID(pdesc, reserved_macid); SET_TX_DESC_TXHT(pdesc, ((ptcb_desc->hw_rate >= - DESC92_RATEMCS0) ? 1 : 0)); + DESC_RATEMCS0) ? 1 : 0)); if (rtlhal->version == VERSION_8192S_ACUT) { - if (ptcb_desc->hw_rate == DESC92_RATE1M || - ptcb_desc->hw_rate == DESC92_RATE2M || - ptcb_desc->hw_rate == DESC92_RATE5_5M || - ptcb_desc->hw_rate == DESC92_RATE11M) { - ptcb_desc->hw_rate = DESC92_RATE12M; + if (ptcb_desc->hw_rate == DESC_RATE1M || + ptcb_desc->hw_rate == DESC_RATE2M || + ptcb_desc->hw_rate == DESC_RATE5_5M || + ptcb_desc->hw_rate == DESC_RATE11M) { + ptcb_desc->hw_rate = DESC_RATE12M; } } @@ -429,7 +429,7 @@ void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, SET_TX_DESC_RTS_BANDWIDTH(pdesc, 0); SET_TX_DESC_RTS_SUB_CARRIER(pdesc, ptcb_desc->rts_sc); SET_TX_DESC_RTS_SHORT(pdesc, ((ptcb_desc->rts_rate <= - DESC92_RATE54M) ? + DESC_RATE54M) ? (ptcb_desc->rts_use_shortpreamble ? 1 : 0) : (ptcb_desc->rts_use_shortgi ? 1 : 0))); diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/def.h b/drivers/net/wireless/rtlwifi/rtl8821ae/def.h index a730985..53dc522 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/def.h +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/def.h @@ -374,37 +374,6 @@ enum rtl_desc_qsel { }; enum rtl_desc8821ae_rate { - DESC_RATE1M = 0x00, - DESC_RATE2M = 0x01, - DESC_RATE5_5M = 0x02, - DESC_RATE11M = 0x03, - - DESC_RATE6M = 0x04, - DESC_RATE9M = 0x05, - DESC_RATE12M = 0x06, - DESC_RATE18M = 0x07, - DESC_RATE24M = 0x08, - DESC_RATE36M = 0x09, - DESC_RATE48M = 0x0a, - DESC_RATE54M = 0x0b, - - DESC_RATEMCS0 = 0x0c, - DESC_RATEMCS1 = 0x0d, - DESC_RATEMCS2 = 0x0e, - DESC_RATEMCS3 = 0x0f, - DESC_RATEMCS4 = 0x10, - DESC_RATEMCS5 = 0x11, - DESC_RATEMCS6 = 0x12, - DESC_RATEMCS7 = 0x13, - DESC_RATEMCS8 = 0x14, - DESC_RATEMCS9 = 0x15, - DESC_RATEMCS10 = 0x16, - DESC_RATEMCS11 = 0x17, - DESC_RATEMCS12 = 0x18, - DESC_RATEMCS13 = 0x19, - DESC_RATEMCS14 = 0x1a, - DESC_RATEMCS15 = 0x1b, - DESC_RATEVHT1SS_MCS0 = 0x2c, DESC_RATEVHT1SS_MCS1 = 0x2d, DESC_RATEVHT1SS_MCS2 = 0x2e, diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index a233b26..26fd960 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -331,10 +331,10 @@ enum hardware_type { (IS_HARDWARE_TYPE_8723E(rtlhal) || IS_HARDWARE_TYPE_8723U(rtlhal)) #define RX_HAL_IS_CCK_RATE(rxmcs) \ - ((rxmcs) == DESC92_RATE1M || \ - (rxmcs) == DESC92_RATE2M || \ - (rxmcs) == DESC92_RATE5_5M || \ - (rxmcs) == DESC92_RATE11M) + ((rxmcs) == DESC_RATE1M || \ + (rxmcs) == DESC_RATE2M || \ + (rxmcs) == DESC_RATE5_5M || \ + (rxmcs) == DESC_RATE11M) enum scan_operation_backup_opt { SCAN_OPT_BACKUP = 0, @@ -579,38 +579,38 @@ enum rtl_hal_state { }; enum rtl_desc92_rate { - DESC92_RATE1M = 0x00, - DESC92_RATE2M = 0x01, - DESC92_RATE5_5M = 0x02, - DESC92_RATE11M = 0x03, - - DESC92_RATE6M = 0x04, - DESC92_RATE9M = 0x05, - DESC92_RATE12M = 0x06, - DESC92_RATE18M = 0x07, - DESC92_RATE24M = 0x08, - DESC92_RATE36M = 0x09, - DESC92_RATE48M = 0x0a, - DESC92_RATE54M = 0x0b, - - DESC92_RATEMCS0 = 0x0c, - DESC92_RATEMCS1 = 0x0d, - DESC92_RATEMCS2 = 0x0e, - DESC92_RATEMCS3 = 0x0f, - DESC92_RATEMCS4 = 0x10, - DESC92_RATEMCS5 = 0x11, - DESC92_RATEMCS6 = 0x12, - DESC92_RATEMCS7 = 0x13, - DESC92_RATEMCS8 = 0x14, - DESC92_RATEMCS9 = 0x15, - DESC92_RATEMCS10 = 0x16, - DESC92_RATEMCS11 = 0x17, - DESC92_RATEMCS12 = 0x18, - DESC92_RATEMCS13 = 0x19, - DESC92_RATEMCS14 = 0x1a, - DESC92_RATEMCS15 = 0x1b, - DESC92_RATEMCS15_SG = 0x1c, - DESC92_RATEMCS32 = 0x20, + DESC_RATE1M = 0x00, + DESC_RATE2M = 0x01, + DESC_RATE5_5M = 0x02, + DESC_RATE11M = 0x03, + + DESC_RATE6M = 0x04, + DESC_RATE9M = 0x05, + DESC_RATE12M = 0x06, + DESC_RATE18M = 0x07, + DESC_RATE24M = 0x08, + DESC_RATE36M = 0x09, + DESC_RATE48M = 0x0a, + DESC_RATE54M = 0x0b, + + DESC_RATEMCS0 = 0x0c, + DESC_RATEMCS1 = 0x0d, + DESC_RATEMCS2 = 0x0e, + DESC_RATEMCS3 = 0x0f, + DESC_RATEMCS4 = 0x10, + DESC_RATEMCS5 = 0x11, + DESC_RATEMCS6 = 0x12, + DESC_RATEMCS7 = 0x13, + DESC_RATEMCS8 = 0x14, + DESC_RATEMCS9 = 0x15, + DESC_RATEMCS10 = 0x16, + DESC_RATEMCS11 = 0x17, + DESC_RATEMCS12 = 0x18, + DESC_RATEMCS13 = 0x19, + DESC_RATEMCS14 = 0x1a, + DESC_RATEMCS15 = 0x1b, + DESC_RATEMCS15_SG = 0x1c, + DESC_RATEMCS32 = 0x20, }; enum rtl_var_map { -- cgit v0.10.2 From 5a0791d0f5511bac894bc753cc68705ed5dea2c4 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 18 Dec 2014 03:05:37 -0600 Subject: rtlwifi: rtl8821ae: Add VHT rate descriptors Device RTL8821AE is the first if the rtlwifi devices to implement 802.11ac capability. As a result, VHT rate descriptors are needed. In addition, the driver is converted to use the descriptors in rtlwifi. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/def.h b/drivers/net/wireless/rtlwifi/rtl8821ae/def.h index 53dc522..ee7c208 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/def.h +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/def.h @@ -373,29 +373,6 @@ enum rtl_desc_qsel { QSLT_CMD = 0x13, }; -enum rtl_desc8821ae_rate { - DESC_RATEVHT1SS_MCS0 = 0x2c, - DESC_RATEVHT1SS_MCS1 = 0x2d, - DESC_RATEVHT1SS_MCS2 = 0x2e, - DESC_RATEVHT1SS_MCS3 = 0x2f, - DESC_RATEVHT1SS_MCS4 = 0x30, - DESC_RATEVHT1SS_MCS5 = 0x31, - DESC_RATEVHT1SS_MCS6 = 0x32, - DESC_RATEVHT1SS_MCS7 = 0x33, - DESC_RATEVHT1SS_MCS8 = 0x34, - DESC_RATEVHT1SS_MCS9 = 0x35, - DESC_RATEVHT2SS_MCS0 = 0x36, - DESC_RATEVHT2SS_MCS1 = 0x37, - DESC_RATEVHT2SS_MCS2 = 0x38, - DESC_RATEVHT2SS_MCS3 = 0x39, - DESC_RATEVHT2SS_MCS4 = 0x3a, - DESC_RATEVHT2SS_MCS5 = 0x3b, - DESC_RATEVHT2SS_MCS6 = 0x3c, - DESC_RATEVHT2SS_MCS7 = 0x3d, - DESC_RATEVHT2SS_MCS8 = 0x3e, - DESC_RATEVHT2SS_MCS9 = 0x3f, -}; - enum rx_packet_type { NORMAL_RX, TX_REPORT1, diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index 26fd960..7a718fd 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -611,6 +611,27 @@ enum rtl_desc92_rate { DESC_RATEMCS15 = 0x1b, DESC_RATEMCS15_SG = 0x1c, DESC_RATEMCS32 = 0x20, + + DESC_RATEVHT1SS_MCS0 = 0x2c, + DESC_RATEVHT1SS_MCS1 = 0x2d, + DESC_RATEVHT1SS_MCS2 = 0x2e, + DESC_RATEVHT1SS_MCS3 = 0x2f, + DESC_RATEVHT1SS_MCS4 = 0x30, + DESC_RATEVHT1SS_MCS5 = 0x31, + DESC_RATEVHT1SS_MCS6 = 0x32, + DESC_RATEVHT1SS_MCS7 = 0x33, + DESC_RATEVHT1SS_MCS8 = 0x34, + DESC_RATEVHT1SS_MCS9 = 0x35, + DESC_RATEVHT2SS_MCS0 = 0x36, + DESC_RATEVHT2SS_MCS1 = 0x37, + DESC_RATEVHT2SS_MCS2 = 0x38, + DESC_RATEVHT2SS_MCS3 = 0x39, + DESC_RATEVHT2SS_MCS4 = 0x3a, + DESC_RATEVHT2SS_MCS5 = 0x3b, + DESC_RATEVHT2SS_MCS6 = 0x3c, + DESC_RATEVHT2SS_MCS7 = 0x3d, + DESC_RATEVHT2SS_MCS8 = 0x3e, + DESC_RATEVHT2SS_MCS9 = 0x3f, }; enum rtl_var_map { -- cgit v0.10.2 From 6a7fd777078f02ba9c14fe1af4c4c3f3a5285a41 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 18 Dec 2014 03:05:38 -0600 Subject: rtlwifi: rtl8192cu: Rework calls to rate-control routine The code uses macros to determine the parameters that are passed to the rate setting routine. A simpler method is implemented. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c index fe29876..5eccaba 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c @@ -325,6 +325,7 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw, && (GET_RX_DESC_FAGGR(pdesc) == 1)); stats->timestamp_low = GET_RX_DESC_TSFL(pdesc); stats->rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(pdesc); + stats->is_ht = (bool)GET_RX_DESC_RX_HT(pdesc); rx_status->freq = hw->conf.chandef.chan->center_freq; rx_status->band = hw->conf.chandef.chan->band; if (GET_RX_DESC_CRC32(pdesc)) @@ -338,9 +339,8 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_MACTIME_START; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; - rx_status->rate_idx = rtlwifi_rate_mapping(hw, - (bool)GET_RX_DESC_RX_HT(pdesc), - (u8)GET_RX_DESC_RX_MCS(pdesc)); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht, + stats->rate); rx_status->mactime = GET_RX_DESC_TSFL(pdesc); if (phystatus) { p_drvinfo = (struct rx_fwinfo_92c *)(skb->data + @@ -392,6 +392,7 @@ static void _rtl_rx_process(struct ieee80211_hw *hw, struct sk_buff *skb) && (GET_RX_DESC_FAGGR(rxdesc) == 1)); stats.timestamp_low = GET_RX_DESC_TSFL(rxdesc); stats.rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(rxdesc); + stats.is_ht = (bool)GET_RX_DESC_RX_HT(rxdesc); /* TODO: is center_freq changed when doing scan? */ /* TODO: Shall we add protection or just skip those two step? */ rx_status->freq = hw->conf.chandef.chan->center_freq; @@ -405,9 +406,8 @@ static void _rtl_rx_process(struct ieee80211_hw *hw, struct sk_buff *skb) if (GET_RX_DESC_RX_HT(rxdesc)) rx_status->flag |= RX_FLAG_HT; /* Data rate */ - rx_status->rate_idx = rtlwifi_rate_mapping(hw, - (bool)GET_RX_DESC_RX_HT(rxdesc), - (u8)GET_RX_DESC_RX_MCS(rxdesc)); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats.is_ht, + stats.rate); /* There is a phy status after this rx descriptor. */ if (GET_RX_DESC_PHY_STATUS(rxdesc)) { p_drvinfo = (struct rx_fwinfo_92c *)(rxdesc + RTL_RX_DESC_SIZE); -- cgit v0.10.2 From a160ba06c1bde5e0609f7ac475a9650224337933 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 18 Dec 2014 03:05:39 -0600 Subject: rtlwifi: rtl8192de: Rework calls to rate-control routine The code uses macros to determine the parameters that are passed to the rate setting routine. A simpler method is implemented. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c index 381a4c0..547c6d0 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c @@ -499,6 +499,7 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, && (GET_RX_DESC_FAGGR(pdesc) == 1)); stats->timestamp_low = GET_RX_DESC_TSFL(pdesc); stats->rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(pdesc); + stats->is_ht = (bool)GET_RX_DESC_RXHT(pdesc); rx_status->freq = hw->conf.chandef.chan->center_freq; rx_status->band = hw->conf.chandef.chan->band; if (GET_RX_DESC_CRC32(pdesc)) @@ -512,9 +513,8 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, rx_status->flag |= RX_FLAG_MACTIME_START; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; - rx_status->rate_idx = rtlwifi_rate_mapping(hw, - (bool)GET_RX_DESC_RXHT(pdesc), - (u8)GET_RX_DESC_RXMCS(pdesc)); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht, + stats->rate); rx_status->mactime = GET_RX_DESC_TSFL(pdesc); if (phystatus) { p_drvinfo = (struct rx_fwinfo_92d *)(skb->data + -- cgit v0.10.2 From fd3cb22ad87fd53eb47dc64fd0cafd665d4124a1 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 18 Dec 2014 03:05:40 -0600 Subject: rtlwifi: rtl8821ae: Switch to use common rate control routine With this change, all of the drivers now use the common routine. As this driver has VHT capability, an additional parameter is needed, thus all the drivers had to be modified. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index 23fc51e..1d46774 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -881,12 +881,84 @@ static u8 _rtl_get_highest_n_rate(struct ieee80211_hw *hw, * N rate: * (rx_status->flag & RX_FLAG_HT) = 1, * DESC_RATEMCS0-->DESC_RATEMCS15 ==> idx is 0-->15 + * + * VHT rates: + * DESC_RATEVHT1SS_MCS0-->DESC_RATEVHT1SS_MCS9 ==> idx is 0-->9 + * DESC_RATEVHT2SS_MCS0-->DESC_RATEVHT2SS_MCS9 ==> idx is 0-->9 */ -int rtlwifi_rate_mapping(struct ieee80211_hw *hw, - bool isht, u8 desc_rate) +int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool isht, bool isvht, + u8 desc_rate) { int rate_idx; + if (isvht) { + switch (desc_rate) { + case DESC_RATEVHT1SS_MCS0: + rate_idx = 0; + break; + case DESC_RATEVHT1SS_MCS1: + rate_idx = 1; + break; + case DESC_RATEVHT1SS_MCS2: + rate_idx = 2; + break; + case DESC_RATEVHT1SS_MCS3: + rate_idx = 3; + break; + case DESC_RATEVHT1SS_MCS4: + rate_idx = 4; + break; + case DESC_RATEVHT1SS_MCS5: + rate_idx = 5; + break; + case DESC_RATEVHT1SS_MCS6: + rate_idx = 6; + break; + case DESC_RATEVHT1SS_MCS7: + rate_idx = 7; + break; + case DESC_RATEVHT1SS_MCS8: + rate_idx = 8; + break; + case DESC_RATEVHT1SS_MCS9: + rate_idx = 9; + break; + case DESC_RATEVHT2SS_MCS0: + rate_idx = 0; + break; + case DESC_RATEVHT2SS_MCS1: + rate_idx = 1; + break; + case DESC_RATEVHT2SS_MCS2: + rate_idx = 2; + break; + case DESC_RATEVHT2SS_MCS3: + rate_idx = 3; + break; + case DESC_RATEVHT2SS_MCS4: + rate_idx = 4; + break; + case DESC_RATEVHT2SS_MCS5: + rate_idx = 5; + break; + case DESC_RATEVHT2SS_MCS6: + rate_idx = 6; + break; + case DESC_RATEVHT2SS_MCS7: + rate_idx = 7; + break; + case DESC_RATEVHT2SS_MCS8: + rate_idx = 8; + break; + case DESC_RATEVHT2SS_MCS9: + rate_idx = 9; + break; + default: + rate_idx = 0; + break; + } + return rate_idx; + } if (false == isht) { if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) { switch (desc_rate) { diff --git a/drivers/net/wireless/rtlwifi/base.h b/drivers/net/wireless/rtlwifi/base.h index 936c2bb..c6cb49c 100644 --- a/drivers/net/wireless/rtlwifi/base.h +++ b/drivers/net/wireless/rtlwifi/base.h @@ -123,8 +123,8 @@ void rtl_watch_dog_timer_callback(unsigned long data); void rtl_deinit_deferred_work(struct ieee80211_hw *hw); bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx); -int rtlwifi_rate_mapping(struct ieee80211_hw *hw, - bool isht, u8 desc_rate); +int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool isht, + bool isvht, u8 desc_rate); bool rtl_tx_mgmt_proc(struct ieee80211_hw *hw, struct sk_buff *skb); u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx); diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c index d430884..791efbe 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c @@ -473,7 +473,7 @@ bool rtl88ee_rx_query_desc(struct ieee80211_hw *hw, * Notice: this is diff with windows define */ rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht, - status->rate); + false, status->rate); rx_status->mactime = status->timestamp_low; if (phystatus == true) { diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c index 685faea..84ddd4d 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c @@ -401,7 +401,7 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw, * Notice: this is diff with windows define */ rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht, - stats->rate); + false, stats->rate); rx_status->mactime = stats->timestamp_low; if (phystatus) { diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c index 5eccaba..cbead00 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c @@ -340,7 +340,7 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw, if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht, - stats->rate); + false, stats->rate); rx_status->mactime = GET_RX_DESC_TSFL(pdesc); if (phystatus) { p_drvinfo = (struct rx_fwinfo_92c *)(skb->data + @@ -407,7 +407,7 @@ static void _rtl_rx_process(struct ieee80211_hw *hw, struct sk_buff *skb) rx_status->flag |= RX_FLAG_HT; /* Data rate */ rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats.is_ht, - stats.rate); + false, stats.rate); /* There is a phy status after this rx descriptor. */ if (GET_RX_DESC_PHY_STATUS(rxdesc)) { p_drvinfo = (struct rx_fwinfo_92c *)(rxdesc + RTL_RX_DESC_SIZE); diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c index 547c6d0..1feaa62 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c @@ -514,7 +514,7 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht, - stats->rate); + false, stats->rate); rx_status->mactime = GET_RX_DESC_TSFL(pdesc); if (phystatus) { p_drvinfo = (struct rx_fwinfo_92d *)(skb->data + diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c index 3ea7367..51806ac 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c @@ -419,7 +419,7 @@ bool rtl92ee_rx_query_desc(struct ieee80211_hw *hw, * Notice: this is diff with windows define */ rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht, - status->rate); + false, status->rate); rx_status->mactime = status->timestamp_low; if (phystatus) { diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c index 95eebff..125b29b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c @@ -319,7 +319,7 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, } rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht, - stats->rate); + false, stats->rate); rx_status->mactime = stats->timestamp_low; if (phystatus) { diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c index 662a094..2f7c144 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c @@ -346,7 +346,7 @@ bool rtl8723e_rx_query_desc(struct ieee80211_hw *hw, * Notice: this is diff with windows define */ rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht, - status->rate); + false, status->rate); rx_status->mactime = status->timestamp_low; if (phystatus == true) { diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/trx.c b/drivers/net/wireless/rtlwifi/rtl8723be/trx.c index cb23bed..338ec9a 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/trx.c @@ -401,7 +401,7 @@ bool rtl8723be_rx_query_desc(struct ieee80211_hw *hw, * are use (RX_FLAG_HT) */ rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht, - status->rate); + false, status->rate); rx_status->mactime = status->timestamp_low; if (phystatus) { diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8821ae/trx.c index 383b86b..72af4b9 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/trx.c @@ -48,232 +48,6 @@ static u8 _rtl8821ae_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) return skb->priority; } -/* mac80211's rate_idx is like this: - * - * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ - * - * B/G rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC_RATE1M-->DESC_RATE54M ==> idx is 0-->11, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC_RATEMCS0-->DESC_RATEMCS15 ==> idx is 0-->15 - * - * 5G band:rx_status->band == IEEE80211_BAND_5GHZ - * A rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC_RATE6M-->DESC_RATE54M ==> idx is 0-->7, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC_RATEMCS0-->DESC_RATEMCS15 ==> idx is 0-->15 - */ -static int _rtl8821ae_rate_mapping(struct ieee80211_hw *hw, - bool isht, bool isvht, u8 desc_rate) -{ - int rate_idx; - - if (!isht) { - if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) { - switch (desc_rate) { - case DESC_RATE1M: - rate_idx = 0; - break; - case DESC_RATE2M: - rate_idx = 1; - break; - case DESC_RATE5_5M: - rate_idx = 2; - break; - case DESC_RATE11M: - rate_idx = 3; - break; - case DESC_RATE6M: - rate_idx = 4; - break; - case DESC_RATE9M: - rate_idx = 5; - break; - case DESC_RATE12M: - rate_idx = 6; - break; - case DESC_RATE18M: - rate_idx = 7; - break; - case DESC_RATE24M: - rate_idx = 8; - break; - case DESC_RATE36M: - rate_idx = 9; - break; - case DESC_RATE48M: - rate_idx = 10; - break; - case DESC_RATE54M: - rate_idx = 11; - break; - default: - rate_idx = 0; - break; - } - } else { - switch (desc_rate) { - case DESC_RATE6M: - rate_idx = 0; - break; - case DESC_RATE9M: - rate_idx = 1; - break; - case DESC_RATE12M: - rate_idx = 2; - break; - case DESC_RATE18M: - rate_idx = 3; - break; - case DESC_RATE24M: - rate_idx = 4; - break; - case DESC_RATE36M: - rate_idx = 5; - break; - case DESC_RATE48M: - rate_idx = 6; - break; - case DESC_RATE54M: - rate_idx = 7; - break; - default: - rate_idx = 0; - break; - } - } - } else { - switch (desc_rate) { - case DESC_RATEMCS0: - rate_idx = 0; - break; - case DESC_RATEMCS1: - rate_idx = 1; - break; - case DESC_RATEMCS2: - rate_idx = 2; - break; - case DESC_RATEMCS3: - rate_idx = 3; - break; - case DESC_RATEMCS4: - rate_idx = 4; - break; - case DESC_RATEMCS5: - rate_idx = 5; - break; - case DESC_RATEMCS6: - rate_idx = 6; - break; - case DESC_RATEMCS7: - rate_idx = 7; - break; - case DESC_RATEMCS8: - rate_idx = 8; - break; - case DESC_RATEMCS9: - rate_idx = 9; - break; - case DESC_RATEMCS10: - rate_idx = 10; - break; - case DESC_RATEMCS11: - rate_idx = 11; - break; - case DESC_RATEMCS12: - rate_idx = 12; - break; - case DESC_RATEMCS13: - rate_idx = 13; - break; - case DESC_RATEMCS14: - rate_idx = 14; - break; - case DESC_RATEMCS15: - rate_idx = 15; - break; - default: - rate_idx = 0; - break; - } - } - - if (isvht) { - switch (desc_rate) { - case DESC_RATEVHT1SS_MCS0: - rate_idx = 0; - break; - case DESC_RATEVHT1SS_MCS1: - rate_idx = 1; - break; - case DESC_RATEVHT1SS_MCS2: - rate_idx = 2; - break; - case DESC_RATEVHT1SS_MCS3: - rate_idx = 3; - break; - case DESC_RATEVHT1SS_MCS4: - rate_idx = 4; - break; - case DESC_RATEVHT1SS_MCS5: - rate_idx = 5; - break; - case DESC_RATEVHT1SS_MCS6: - rate_idx = 6; - break; - case DESC_RATEVHT1SS_MCS7: - rate_idx = 7; - break; - case DESC_RATEVHT1SS_MCS8: - rate_idx = 8; - break; - case DESC_RATEVHT1SS_MCS9: - rate_idx = 9; - break; - case DESC_RATEVHT2SS_MCS0: - rate_idx = 0; - break; - case DESC_RATEVHT2SS_MCS1: - rate_idx = 1; - break; - case DESC_RATEVHT2SS_MCS2: - rate_idx = 2; - break; - case DESC_RATEVHT2SS_MCS3: - rate_idx = 3; - break; - case DESC_RATEVHT2SS_MCS4: - rate_idx = 4; - break; - case DESC_RATEVHT2SS_MCS5: - rate_idx = 5; - break; - case DESC_RATEVHT2SS_MCS6: - rate_idx = 6; - break; - case DESC_RATEVHT2SS_MCS7: - rate_idx = 7; - break; - case DESC_RATEVHT2SS_MCS8: - rate_idx = 8; - break; - case DESC_RATEVHT2SS_MCS9: - rate_idx = 9; - break; - default: - rate_idx = 0; - break; - } - } - return rate_idx; -} - static u16 odm_cfo(char value) { int ret_val; @@ -766,9 +540,9 @@ bool rtl8821ae_rx_query_desc(struct ieee80211_hw *hw, * supported rates or MCS index if HT rates * are use (RX_FLAG_HT) */ - rx_status->rate_idx = - _rtl8821ae_rate_mapping(hw, status->is_ht, - status->is_vht, status->rate); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht, + status->is_vht, + status->rate); rx_status->mactime = status->timestamp_low; if (phystatus) { -- cgit v0.10.2 From 9898b77536b8569be2704964d3c519ef070e4a27 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sat, 20 Dec 2014 13:48:14 +0100 Subject: rtlwifi: rtl8192ee: trx.c: Remove unused function Remove the function rtl92ee_get_available_desc() that is not used anywhere. This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Acked-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c index 51806ac..55d1da5 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c @@ -551,27 +551,6 @@ static u16 get_desc_addr_fr_q_idx(u16 queue_index) return desc_address; } -void rtl92ee_get_available_desc(struct ieee80211_hw *hw, u8 q_idx) -{ - struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - struct rtl_priv *rtlpriv = rtl_priv(hw); - u16 point_diff = 0; - u16 current_tx_read_point = 0, current_tx_write_point = 0; - u32 tmp_4byte; - - tmp_4byte = rtl_read_dword(rtlpriv, - get_desc_addr_fr_q_idx(q_idx)); - current_tx_read_point = (u16)((tmp_4byte >> 16) & 0x0fff); - current_tx_write_point = (u16)((tmp_4byte) & 0x0fff); - - point_diff = ((current_tx_read_point > current_tx_write_point) ? - (current_tx_read_point - current_tx_write_point) : - (TX_DESC_NUM_92E - current_tx_write_point + - current_tx_read_point)); - - rtlpci->tx_ring[q_idx].avl_desc = point_diff; -} - void rtl92ee_pre_fill_tx_bd_desc(struct ieee80211_hw *hw, u8 *tx_bd_desc, u8 *desc, u8 queue_index, struct sk_buff *skb, dma_addr_t addr) diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h index 45fd9db..48504c2 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h @@ -829,7 +829,6 @@ void rtl92ee_rx_check_dma_ok(struct ieee80211_hw *hw, u8 *header_desc, u8 queue_index); u16 rtl92ee_rx_desc_buff_remained_cnt(struct ieee80211_hw *hw, u8 queue_index); -void rtl92ee_get_available_desc(struct ieee80211_hw *hw, u8 queue_index); void rtl92ee_pre_fill_tx_bd_desc(struct ieee80211_hw *hw, u8 *tx_bd_desc, u8 *desc, u8 queue_index, struct sk_buff *skb, dma_addr_t addr); -- cgit v0.10.2 From b30b2e0f3dbcde630b911bdc8fda8ae8300fff6a Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sat, 20 Dec 2014 23:59:18 +0100 Subject: rtlwifi: rtl8723be: phy.c: Remove unused function Remove the function rtl8723be_phy_get_txpower_level() that is not used anywhere. This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Acked-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/phy.c b/drivers/net/wireless/rtlwifi/rtl8723be/phy.c index 20dcc25..b7b73cb 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/phy.c @@ -874,31 +874,6 @@ void rtl8723be_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw) ROFDM0_RXDETECTOR3, rtlphy->framesync); } -void rtl8723be_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_phy *rtlphy = &rtlpriv->phy; - u8 txpwr_level; - long txpwr_dbm; - - txpwr_level = rtlphy->cur_cck_txpwridx; - txpwr_dbm = rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_B, - txpwr_level); - txpwr_level = rtlphy->cur_ofdm24g_txpwridx; - if (rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, txpwr_level) > - txpwr_dbm) - txpwr_dbm = - rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, - txpwr_level); - txpwr_level = rtlphy->cur_ofdm24g_txpwridx; - if (rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G, - txpwr_level) > txpwr_dbm) - txpwr_dbm = - rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G, - txpwr_level); - *powerlevel = txpwr_dbm; -} - static u8 _rtl8723be_phy_get_ratesection_intxpower_byrate(enum radio_path path, u8 rate) { diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/phy.h b/drivers/net/wireless/rtlwifi/rtl8723be/phy.h index 6339738..9021d47 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/phy.h +++ b/drivers/net/wireless/rtlwifi/rtl8723be/phy.h @@ -114,8 +114,6 @@ bool rtl8723be_phy_mac_config(struct ieee80211_hw *hw); bool rtl8723be_phy_bb_config(struct ieee80211_hw *hw); bool rtl8723be_phy_rf_config(struct ieee80211_hw *hw); void rtl8723be_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); -void rtl8723be_phy_get_txpower_level(struct ieee80211_hw *hw, - long *powerlevel); void rtl8723be_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); void rtl8723be_phy_scan_operation_backup(struct ieee80211_hw *hw, -- cgit v0.10.2 From a9abe3023ace441ce9e27425b263f2dabd28cf32 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Fri, 19 Dec 2014 00:18:12 +0100 Subject: ath9k: enable TPC by default Enable hw TPC by default on AR9003 based chips Signed-off-by: Lorenzo Bianconi Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 6d4b273..258c4d2 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -422,6 +422,9 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah) ah->power_mode = ATH9K_PM_UNDEFINED; ah->htc_reset_init = true; + /* ar9002 does not support TPC for the moment */ + ah->tpc_enabled = !!AR_SREV_9300_20_OR_LATER(ah); + ah->ani_function = ATH9K_ANI_ALL; if (!AR_SREV_9300_20_OR_LATER(ah)) ah->ani_function &= ~ATH9K_ANI_MRC_CCK; -- cgit v0.10.2 From 8718389b1ca8c911b5a143090b880c3474da508b Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Fri, 19 Dec 2014 00:18:13 +0100 Subject: ath9k: add debugfs support for hw TPC Add tpc entry to ath9k debugfs in order to enable/disable hw TPC Signed-off-by: Lorenzo Bianconi Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 871e969..c43e2ad 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -1115,6 +1115,75 @@ static const struct file_operations fops_ackto = { }; #endif +static ssize_t read_file_tpc(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_hw *ah = sc->sc_ah; + unsigned int len = 0, size = 32; + ssize_t retval; + char *buf; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len += scnprintf(buf + len, size - len, "%s\n", + ah->tpc_enabled ? "ENABLED" : "DISABLED"); + + if (len > size) + len = size; + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +static ssize_t write_file_tpc(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_hw *ah = sc->sc_ah; + unsigned long val; + char buf[32]; + ssize_t len; + bool tpc_enabled; + + if (!AR_SREV_9300_20_OR_LATER(ah)) { + /* ar9002 does not support TPC for the moment */ + return -EOPNOTSUPP; + } + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 0 || val > 1) + return -EINVAL; + + tpc_enabled = !!val; + + if (tpc_enabled != ah->tpc_enabled) { + ah->tpc_enabled = tpc_enabled; + ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false); + } + + return count; +} + +static const struct file_operations fops_tpc = { + .read = read_file_tpc, + .write = write_file_tpc, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + /* Ethtool support for get-stats */ #define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO" @@ -1324,6 +1393,8 @@ int ath9k_init_debug(struct ath_hw *ah) debugfs_create_file("ack_to", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_ackto); #endif + debugfs_create_file("tpc", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, &fops_tpc); return 0; } -- cgit v0.10.2 From 354f473ee2c5d01c1cf90f747f95218ee3e73e95 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Fri, 19 Dec 2014 00:18:14 +0100 Subject: ath9k: fix typo Fix typo Signed-off-by: Lorenzo Bianconi Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index e9bd02c..52d63de 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1106,7 +1106,7 @@ static u8 ath_get_rate_txpower(struct ath_softc *sc, struct ath_buf *bf, return MAX_RATE_POWER; if (!AR_SREV_9300_20_OR_LATER(ah)) { - /* ar9002 is not sipported for the moment */ + /* ar9002 does not support TPC for the moment */ return MAX_RATE_POWER; } -- cgit v0.10.2 From 0628ee7c81a8ced9b10f9ee300707f7f79fdecf1 Mon Sep 17 00:00:00 2001 From: Nicholas Krause Date: Sun, 21 Dec 2014 22:36:37 -0500 Subject: libata: s/ata_id_removeable()/ata_id_removable()/ Changes the spelling typos of removeable to removable where ata_id_removeable is defined in ata.h and called in libata-scsi.c respectively. Signed-off-by: Nicholas Krause Signed-off-by: Tejun Heo diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index e364e86..7659d64 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1995,8 +1995,8 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf) VPRINTK("ENTER\n"); - /* set scsi removeable (RMB) bit per ata bit */ - if (ata_id_removeable(args->id)) + /* set scsi removable (RMB) bit per ata bit */ + if (ata_id_removable(args->id)) hdr[1] |= (1 << 7); if (args->dev->class == ATA_DEV_ZAC) { diff --git a/include/linux/ata.h b/include/linux/ata.h index f2f4d8d..1648026 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -503,7 +503,7 @@ struct ata_bmdma_prd { #define ata_id_has_dma(id) ((id)[ATA_ID_CAPABILITY] & (1 << 8)) #define ata_id_has_ncq(id) ((id)[ATA_ID_SATA_CAPABILITY] & (1 << 8)) #define ata_id_queue_depth(id) (((id)[ATA_ID_QUEUE_DEPTH] & 0x1f) + 1) -#define ata_id_removeable(id) ((id)[ATA_ID_CONFIG] & (1 << 7)) +#define ata_id_removable(id) ((id)[ATA_ID_CONFIG] & (1 << 7)) #define ata_id_has_atapi_AN(id) \ ((((id)[ATA_ID_SATA_CAPABILITY] != 0x0000) && \ ((id)[ATA_ID_SATA_CAPABILITY] != 0xffff)) && \ -- cgit v0.10.2 From ebd8146bba4d1716e2e47cbd5583f6836eeafb48 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sat, 20 Dec 2014 17:46:02 +0100 Subject: ALSA: ice1712: wm8776.c: Remove some unused functions Removes some functions that are not used anywhere: snd_wm8776_set_master_mode() snd_wm8776_set_adc_if() snd_wm8776_set_dac_if() This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/wm8776.c b/sound/pci/ice1712/wm8776.c index e66c0da..ebd2fe4 100644 --- a/sound/pci/ice1712/wm8776.c +++ b/sound/pci/ice1712/wm8776.c @@ -452,21 +452,6 @@ void snd_wm8776_resume(struct snd_wm8776 *wm) snd_wm8776_write(wm, i, wm->regs[i]); } -void snd_wm8776_set_dac_if(struct snd_wm8776 *wm, u16 dac) -{ - snd_wm8776_write(wm, WM8776_REG_DACIFCTRL, dac); -} - -void snd_wm8776_set_adc_if(struct snd_wm8776 *wm, u16 adc) -{ - snd_wm8776_write(wm, WM8776_REG_ADCIFCTRL, adc); -} - -void snd_wm8776_set_master_mode(struct snd_wm8776 *wm, u16 mode) -{ - snd_wm8776_write(wm, WM8776_REG_MSTRCTRL, mode); -} - void snd_wm8776_set_power(struct snd_wm8776 *wm, u16 power) { snd_wm8776_write(wm, WM8776_REG_PWRDOWN, power); diff --git a/sound/pci/ice1712/wm8776.h b/sound/pci/ice1712/wm8776.h index 93a2d69..42acef0 100644 --- a/sound/pci/ice1712/wm8776.h +++ b/sound/pci/ice1712/wm8776.h @@ -216,9 +216,6 @@ struct snd_wm8776 { void snd_wm8776_init(struct snd_wm8776 *wm); void snd_wm8776_resume(struct snd_wm8776 *wm); -void snd_wm8776_set_dac_if(struct snd_wm8776 *wm, u16 dac); -void snd_wm8776_set_adc_if(struct snd_wm8776 *wm, u16 adc); -void snd_wm8776_set_master_mode(struct snd_wm8776 *wm, u16 mode); void snd_wm8776_set_power(struct snd_wm8776 *wm, u16 power); void snd_wm8776_volume_restore(struct snd_wm8776 *wm); int snd_wm8776_build_controls(struct snd_wm8776 *wm); -- cgit v0.10.2 From 57da6bdd4823ad70ac58c5cd968943a4be95d739 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 21 Dec 2014 13:35:12 +0100 Subject: ALSA: hda - patch_analog.c: Remove some unused functions Removes some functions that are not used anywhere: ad198x_ch_mode_get() ad198x_ch_mode_info() ad198x_ch_mode_info() This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index a9d78e2..d285904 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -739,39 +739,6 @@ static int patch_ad1981(struct hda_codec *codec) * E/F quad mic array */ -#ifdef ENABLE_AD_STATIC_QUIRKS -static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode, - spec->num_channel_mode); -} - -static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, spec->multiout.max_channels); -} - -static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, - &spec->multiout.max_channels); - if (err >= 0 && spec->need_dac_fix) - spec->multiout.num_dacs = spec->multiout.max_channels / 2; - return err; -} -#endif /* ENABLE_AD_STATIC_QUIRKS */ - static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { -- cgit v0.10.2 From eb58d4844b4ee797bbefa6a75db3a999b71209b1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 26 Dec 2014 12:22:35 +0100 Subject: ALSA: gus: Remove unused gus_instr.c The instrument layer codes have been dropped years ago, but this file was left intentionally with a slight hope for the new implementations. But, looking at the reality, the probability we'll have a new code for ISA GUS board is very low, and I won't bet it. So, rather clean this legacy stuff up. Developers can still refer to the old code via git history. Reported-by: Rickard Strandqvist Signed-off-by: Takashi Iwai diff --git a/sound/isa/gus/gus_instr.c b/sound/isa/gus/gus_instr.c deleted file mode 100644 index 4dc9caf..0000000 --- a/sound/isa/gus/gus_instr.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Routines for Gravis UltraSound soundcards - Synthesizer - * Copyright (c) by Jaroslav Kysela - * - * - * 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 -#include -#include - -/* - * - */ - -int snd_gus_iwffff_put_sample(void *private_data, struct iwffff_wave *wave, - char __user *data, long len, int atomic) -{ - struct snd_gus_card *gus = private_data; - struct snd_gf1_mem_block *block; - int err; - - if (wave->format & IWFFFF_WAVE_ROM) - return 0; /* it's probably ok - verify the address? */ - if (wave->format & IWFFFF_WAVE_STEREO) - return -EINVAL; /* not supported */ - block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, - SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF, - NULL, wave->size, - wave->format & IWFFFF_WAVE_16BIT, 1, - wave->share_id); - if (block == NULL) - return -ENOMEM; - err = snd_gus_dram_write(gus, data, - block->ptr, wave->size); - if (err < 0) { - snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); - snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); - snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); - return err; - } - wave->address.memory = block->ptr; - return 0; -} - -int snd_gus_iwffff_get_sample(void *private_data, struct iwffff_wave *wave, - char __user *data, long len, int atomic) -{ - struct snd_gus_card *gus = private_data; - - return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, - wave->format & IWFFFF_WAVE_ROM ? 1 : 0); -} - -int snd_gus_iwffff_remove_sample(void *private_data, struct iwffff_wave *wave, - int atomic) -{ - struct snd_gus_card *gus = private_data; - - if (wave->format & IWFFFF_WAVE_ROM) - return 0; /* it's probably ok - verify the address? */ - return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory); -} - -/* - * - */ - -int snd_gus_gf1_put_sample(void *private_data, struct gf1_wave *wave, - char __user *data, long len, int atomic) -{ - struct snd_gus_card *gus = private_data; - struct snd_gf1_mem_block *block; - int err; - - if (wave->format & GF1_WAVE_STEREO) - return -EINVAL; /* not supported */ - block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, - SNDRV_GF1_MEM_OWNER_WAVE_GF1, - NULL, wave->size, - wave->format & GF1_WAVE_16BIT, 1, - wave->share_id); - if (block == NULL) - return -ENOMEM; - err = snd_gus_dram_write(gus, data, - block->ptr, wave->size); - if (err < 0) { - snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); - snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); - snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); - return err; - } - wave->address.memory = block->ptr; - return 0; -} - -int snd_gus_gf1_get_sample(void *private_data, struct gf1_wave *wave, - char __user *data, long len, int atomic) -{ - struct snd_gus_card *gus = private_data; - - return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, 0); -} - -int snd_gus_gf1_remove_sample(void *private_data, struct gf1_wave *wave, - int atomic) -{ - struct snd_gus_card *gus = private_data; - - return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory); -} - -/* - * - */ - -int snd_gus_simple_put_sample(void *private_data, struct simple_instrument *instr, - char __user *data, long len, int atomic) -{ - struct snd_gus_card *gus = private_data; - struct snd_gf1_mem_block *block; - int err; - - if (instr->format & SIMPLE_WAVE_STEREO) - return -EINVAL; /* not supported */ - block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, - SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE, - NULL, instr->size, - instr->format & SIMPLE_WAVE_16BIT, 1, - instr->share_id); - if (block == NULL) - return -ENOMEM; - err = snd_gus_dram_write(gus, data, block->ptr, instr->size); - if (err < 0) { - snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); - snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); - snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); - return err; - } - instr->address.memory = block->ptr; - return 0; -} - -int snd_gus_simple_get_sample(void *private_data, struct simple_instrument *instr, - char __user *data, long len, int atomic) -{ - struct snd_gus_card *gus = private_data; - - return snd_gus_dram_read(gus, data, instr->address.memory, instr->size, 0); -} - -int snd_gus_simple_remove_sample(void *private_data, struct simple_instrument *instr, - int atomic) -{ - struct snd_gus_card *gus = private_data; - - return snd_gf1_mem_free(&gus->gf1.mem_alloc, instr->address.memory); -} -- cgit v0.10.2 From 711ffa78f4b23e25a6c91cb80be436a48b5e725d Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 26 Dec 2014 04:42:31 +0100 Subject: Bluetooth: Introduce HCI_QUIRK_BROKEN_LOCAL_COMMANDS constant Some controllers advertise support for Bluetooth 1.2 specification, but they do not support the HCI Read Local Supported Commands command. If that is the case, then the driver can quirk the behavior and force the core to skip this command. This will allow removing vendor specific checks out of the core. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index f39926e..1849a43 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -102,6 +102,16 @@ enum { */ HCI_QUIRK_FIXUP_BUFFER_SIZE, + /* When this quirk is set, then the HCI Read Local Supported + * Commands command is not supported. In general Bluetooth 1.2 + * and later controllers should support this command. However + * some controllers indicate Bluetooth 1.2 support, but do + * not support this command. + * + * This quirk must be set before hci_register_dev is called. + */ + HCI_QUIRK_BROKEN_LOCAL_COMMANDS, + /* When this quirk is set, then no stored link key handling * is performed. This is mainly due to the fact that the * HCI Delete Stored Link Key command is advertised, but -- cgit v0.10.2 From 9ef80d40e901feb875660101dabdeddaa27e837c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 26 Dec 2014 04:42:32 +0100 Subject: Bluetooth: bfusb: Set the HCI_QUIRK_BROKEN_LOCAL_COMMANDS quirk The AVM BlueFritz! 1.0 USB dongles do not support the HCI command for reading the local supported commands. So set this quirk to let the core know about it. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index b2e7e94..fcfb72e 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -696,6 +696,8 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i hdev->flush = bfusb_flush; hdev->send = bfusb_send_frame; + set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks); + if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); hci_free_dev(hdev); -- cgit v0.10.2 From d57dbe779e0e14a8cdc7327b5cfcd02122728d0f Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 26 Dec 2014 04:42:33 +0100 Subject: Bluetooth: btusb: Set the HCI_QUIRK_BROKEN_LOCAL_COMMANDS quirk The AVM BlueFritz! 2.0 USB dongles do not support the HCI command for reading the local supported commands. So set this quirk to let the core know about it. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index c926835..f051a93 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -49,6 +49,7 @@ static struct usb_driver btusb_driver; #define BTUSB_INTEL_BOOT 0x200 #define BTUSB_BCM_PATCHRAM 0x400 #define BTUSB_MARVELL 0x800 +#define BTUSB_AVM 0x1000 static const struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ @@ -85,7 +86,7 @@ static const struct usb_device_id btusb_table[] = { { USB_DEVICE(0x05ac, 0x8281) }, /* AVM BlueFRITZ! USB v2.0 */ - { USB_DEVICE(0x057c, 0x3800) }, + { USB_DEVICE(0x057c, 0x3800), .driver_info = BTUSB_AVM }, /* Bluetooth Ultraport Module from IBM */ { USB_DEVICE(0x04bf, 0x030a) }, @@ -2080,6 +2081,9 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_MARVELL) hdev->set_bdaddr = btusb_set_bdaddr_marvell; + if (id->driver_info & BTUSB_AVM) + set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks); + if (id->driver_info & BTUSB_INTEL_BOOT) set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); -- cgit v0.10.2 From 0f3adeae6011fe82ccdee858dad6b7b2bd790add Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 26 Dec 2014 04:42:34 +0100 Subject: Bluetooth: Remove BlueFritz! specific check from initialization The AVM BlueFritz! USB controllers had a special handling in the Bluetooth core when it comes to reading the supported commands. Both drivers now set the HCI_QUIRK_BROKEN_LOCAL_COMMANDS and with that it is no longer needed to look for vendor specific details. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 47f0311..5ef5221 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -623,10 +623,16 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt) if (lmp_le_capable(hdev)) le_setup(req); - /* AVM Berlin (31), aka "BlueFRITZ!", doesn't support the read - * local supported commands HCI command. + /* All Bluetooth 1.2 and later controllers should support the + * HCI command for reading the local supported commands. + * + * Unfortunately some controllers indicate Bluetooth 1.2 support, + * but do not have support for this command. If that is the case, + * the driver can quirk the behavior and skip reading the local + * supported commands. */ - if (hdev->manufacturer != 31 && hdev->hci_ver > BLUETOOTH_VER_1_1) + if (hdev->hci_ver > BLUETOOTH_VER_1_1 && + !test_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks)) hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL); if (lmp_ssp_capable(hdev)) { -- cgit v0.10.2 From ea3651fee6c92a29244830334979396465ed8587 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 14 Nov 2014 14:19:03 -0800 Subject: PCI: keystone: Fix error handling of irq_of_parse_and_map() Return value of irq_of_parse_and_map() is unsigned int, with 0 indicating failure, so testing for negative result never works. Signed-off-by: Dmitry Torokhov Signed-off-by: Bjorn Helgaas Acked-By: Murali Karicheri diff --git a/drivers/pci/host/pci-keystone.c b/drivers/pci/host/pci-keystone.c index 78f79e3..23a1d97 100644 --- a/drivers/pci/host/pci-keystone.c +++ b/drivers/pci/host/pci-keystone.c @@ -197,7 +197,7 @@ static int ks_pcie_get_irq_controller_info(struct keystone_pcie *ks_pcie, */ for (temp = 0; temp < max_host_irqs; temp++) { host_irqs[temp] = irq_of_parse_and_map(*np_temp, temp); - if (host_irqs[temp] < 0) + if (!host_irqs[temp]) break; } if (temp) { -- cgit v0.10.2 From c51d411fe1e5f5f378b4db6b10dbe89e2e8688e4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 14 Nov 2014 14:21:53 -0800 Subject: PCI: rcar: Fix error handling of irq_of_parse_and_map() Return value of irq_of_parse_and_map() is unsigned int, with 0 indicating failure, so testing for negative result never works. Signed-off-by: Dmitry Torokhov Signed-off-by: Bjorn Helgaas Acked-by: Phil Edworthy diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c index 748786c..b258744 100644 --- a/drivers/pci/host/pcie-rcar.c +++ b/drivers/pci/host/pcie-rcar.c @@ -757,7 +757,7 @@ static int rcar_pcie_get_resources(struct platform_device *pdev, goto err_map_reg; i = irq_of_parse_and_map(pdev->dev.of_node, 0); - if (i < 0) { + if (!i) { dev_err(pcie->dev, "cannot get platform resources for msi interrupt\n"); err = -ENOENT; goto err_map_reg; @@ -765,7 +765,7 @@ static int rcar_pcie_get_resources(struct platform_device *pdev, pcie->msi.irq1 = i; i = irq_of_parse_and_map(pdev->dev.of_node, 1); - if (i < 0) { + if (!i) { dev_err(pcie->dev, "cannot get platform resources for msi interrupt\n"); err = -ENOENT; goto err_map_reg; -- cgit v0.10.2 From 80f6d910dc46f4df91a6f0a52d94e025c053987b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 18 Nov 2014 17:18:20 +0100 Subject: PCI: layerscape: Fix platform_no_drv_owner.cocci warnings No need to set .owner here. The core will do it. Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: Julia Lawall Signed-off-by: Bjorn Helgaas diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/host/pci-layerscape.c index 6697b1a..68c9e5e 100644 --- a/drivers/pci/host/pci-layerscape.c +++ b/drivers/pci/host/pci-layerscape.c @@ -167,7 +167,6 @@ MODULE_DEVICE_TABLE(of, ls_pcie_of_match); static struct platform_driver ls_pcie_driver = { .driver = { .name = "layerscape-pcie", - .owner = THIS_MODULE, .of_match_table = ls_pcie_of_match, }, }; -- cgit v0.10.2 From 19bd9c0b98372fceeec507383f7cb54f4fa51b12 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Thu, 13 Nov 2014 20:37:37 +0100 Subject: PCI: tegra: Remove unnecessary tegra_pcie_fixup_bridge() The bridge setup is already done by generic code while scanning the buses. Do not duplicate (or potentially alter) this setup as a fixup. Tested-by: Alexandre Courbot Reviewed-by: Thierry Reding Tested-by: Thierry Reding Signed-off-by: Lucas Stach Signed-off-by: Bjorn Helgaas Acked-by: Thierry Reding diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index a800ae9..6f9c29f 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -625,19 +625,6 @@ static void tegra_pcie_port_free(struct tegra_pcie_port *port) devm_kfree(pcie->dev, port); } -static void tegra_pcie_fixup_bridge(struct pci_dev *dev) -{ - u16 reg; - - if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) { - pci_read_config_word(dev, PCI_COMMAND, ®); - reg |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER | PCI_COMMAND_SERR); - pci_write_config_word(dev, PCI_COMMAND, reg); - } -} -DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_fixup_bridge); - /* Tegra PCIE root complex wrongly reports device class */ static void tegra_pcie_fixup_class(struct pci_dev *dev) { -- cgit v0.10.2 From 04dae5509c0573fbdab52766a14da5dd60c29a6f Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Thu, 13 Nov 2014 20:37:37 +0100 Subject: PCI: tegra: Remove unnecessary tegra_pcie_fixup_bridge() The bridge setup is already done by generic code while scanning the buses. Do not duplicate (or potentially alter) this setup as a fixup. Tested-by: Alexandre Courbot Tested-by: Thierry Reding Signed-off-by: Lucas Stach Signed-off-by: Bjorn Helgaas Reviewed-by: Thierry Reding Acked-by: Thierry Reding diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index a800ae9..6f9c29f 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -625,19 +625,6 @@ static void tegra_pcie_port_free(struct tegra_pcie_port *port) devm_kfree(pcie->dev, port); } -static void tegra_pcie_fixup_bridge(struct pci_dev *dev) -{ - u16 reg; - - if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) { - pci_read_config_word(dev, PCI_COMMAND, ®); - reg |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER | PCI_COMMAND_SERR); - pci_write_config_word(dev, PCI_COMMAND, reg); - } -} -DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_fixup_bridge); - /* Tegra PCIE root complex wrongly reports device class */ static void tegra_pcie_fixup_class(struct pci_dev *dev) { -- cgit v0.10.2 From 17f14b51f2dc2fce2636ee49c5fbf49ff27aecb0 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 26 Dec 2014 16:28:08 -0700 Subject: PCI: Delete unnecessary NULL pointer checks The pci_dev_put() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Bjorn Helgaas diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c index a5a7fd8..46db293 100644 --- a/drivers/pci/hotplug/cpci_hotplug_core.c +++ b/drivers/pci/hotplug/cpci_hotplug_core.c @@ -214,8 +214,7 @@ static void release_slot(struct hotplug_slot *hotplug_slot) kfree(slot->hotplug_slot->info); kfree(slot->hotplug_slot); - if (slot->dev) - pci_dev_put(slot->dev); + pci_dev_put(slot->dev); kfree(slot); } -- cgit v0.10.2 From 270384ccfc2ba3849b9d02f7a37c395987c30ed2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 26 Dec 2014 14:27:58 +0100 Subject: ALSA: atmel: fix building the ac97 driver for at91-multiplatform at91 will no longer export the mach/cpu.h and mach/hardware.h header files in the future, which would break building the atmel ac97c driver. Since the cpu_is_* check is only used to find out whether we are running on avr32 or arm/at91, we can hardcode that check in the ARM case. Signed-off-by: Arnd Bergmann Link: http://www.spinics.net/lists/arm-kernel/msg382068.html Signed-off-by: Alexandre Belloni Signed-off-by: Takashi Iwai diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index 4f6b14d..5e6a1db 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -34,10 +34,10 @@ #include #include +#ifdef CONFIG_AVR32 #include - -#ifdef CONFIG_ARCH_AT91 -#include +#else +#define cpu_is_at32ap7000() 0 #endif #include "ac97c.h" -- cgit v0.10.2 From 7c674700098c87b305b99652e3c694c4ef195866 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Sat, 27 Dec 2014 18:19:12 -0700 Subject: PCI: Move domain assignment from arm64 to generic code The current logic in arm64 pci_bus_assign_domain_nr() is flawed in that depending on the host controller configuration for a platform and the initialization sequence, core code may end up allocating PCI domain numbers from both DT and the generic domain counter, which would result in PCI domain allocation aliases/errors. Fix the logic behind the PCI domain number assignment and move the resulting code to the PCI core so the same domain allocation logic is used on all platforms that select CONFIG_PCI_DOMAINS_GENERIC. [bhelgaas: tidy changelog] Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Acked-by: Liviu Dudau Acked-by: Arnd Bergmann CC: Rob Herring CC: Catalin Marinas diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c index ce5836c..6f93c24 100644 --- a/arch/arm64/kernel/pci.c +++ b/arch/arm64/kernel/pci.c @@ -46,25 +46,3 @@ int pcibios_add_device(struct pci_dev *dev) return 0; } - - -#ifdef CONFIG_PCI_DOMAINS_GENERIC -static bool dt_domain_found = false; - -void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) -{ - int domain = of_get_pci_domain_nr(parent->of_node); - - if (domain >= 0) { - dt_domain_found = true; - } else if (dt_domain_found == true) { - dev_err(parent, "Node %s is missing \"linux,pci-domain\" property in DT\n", - parent->of_node->full_name); - return; - } else { - domain = pci_get_new_domain_nr(); - } - - bus->domain_nr = domain; -} -#endif diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index cab05f3..c419554 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include #include @@ -4439,6 +4441,53 @@ int pci_get_new_domain_nr(void) { return atomic_inc_return(&__domain_nr); } + +#ifdef CONFIG_PCI_DOMAINS_GENERIC +void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) +{ + static int use_dt_domains = -1; + int domain = of_get_pci_domain_nr(parent->of_node); + + /* + * Check DT domain and use_dt_domains values. + * + * If DT domain property is valid (domain >= 0) and + * use_dt_domains != 0, the DT assignment is valid since this means + * we have not previously allocated a domain number by using + * pci_get_new_domain_nr(); we should also update use_dt_domains to + * 1, to indicate that we have just assigned a domain number from + * DT. + * + * If DT domain property value is not valid (ie domain < 0), and we + * have not previously assigned a domain number from DT + * (use_dt_domains != 1) we should assign a domain number by + * using the: + * + * pci_get_new_domain_nr() + * + * API and update the use_dt_domains value to keep track of method we + * are using to assign domain numbers (use_dt_domains = 0). + * + * All other combinations imply we have a platform that is trying + * to mix domain numbers obtained from DT and pci_get_new_domain_nr(), + * which is a recipe for domain mishandling and it is prevented by + * invalidating the domain value (domain = -1) and printing a + * corresponding error. + */ + if (domain >= 0 && use_dt_domains) { + use_dt_domains = 1; + } else if (domain < 0 && use_dt_domains != 1) { + use_dt_domains = 0; + domain = pci_get_new_domain_nr(); + } else { + dev_err(parent, "Node %s has inconsistent \"linux,pci-domain\" property in DT\n", + parent->of_node->full_name); + domain = -1; + } + + bus->domain_nr = domain; +} +#endif #endif /** -- cgit v0.10.2 From c88d54ba0c3d89b925bfb8e98b13a3f55b55cde4 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Fri, 21 Nov 2014 11:29:25 +0000 Subject: CNS3xxx: Remove artificial dependency on pci_sys_data domain. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On cns3xxx platforms the PCI controller probing code relies on an artificial dependency on the domain number to look-up the internal data structures. This patch reworks the host controller control data structure and adds a domain equivalent field named port in it so that the dependency on pci_sys_data domain field can be eventually removed. Acked-by: Krzysztof Hałasa Signed-off-by: Arnd Bergmann [lp: added commit log, removed pci_sys_data domain references] Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas diff --git a/arch/arm/mach-cns3xxx/pcie.c b/arch/arm/mach-cns3xxx/pcie.c index 45d6bd0..f6bf9f6 100644 --- a/arch/arm/mach-cns3xxx/pcie.c +++ b/arch/arm/mach-cns3xxx/pcie.c @@ -30,18 +30,15 @@ struct cns3xxx_pcie { unsigned int irqs[2]; struct resource res_io; struct resource res_mem; - struct hw_pci hw_pci; - + int port; bool linked; }; -static struct cns3xxx_pcie cns3xxx_pcie[]; /* forward decl. */ - static struct cns3xxx_pcie *sysdata_to_cnspci(void *sysdata) { struct pci_sys_data *root = sysdata; - return &cns3xxx_pcie[root->domain]; + return root->private_data; } static struct cns3xxx_pcie *pdev_to_cnspci(const struct pci_dev *dev) @@ -192,13 +189,7 @@ static struct cns3xxx_pcie cns3xxx_pcie[] = { .flags = IORESOURCE_MEM, }, .irqs = { IRQ_CNS3XXX_PCIE0_RC, IRQ_CNS3XXX_PCIE0_DEVICE, }, - .hw_pci = { - .domain = 0, - .nr_controllers = 1, - .ops = &cns3xxx_pcie_ops, - .setup = cns3xxx_pci_setup, - .map_irq = cns3xxx_pcie_map_irq, - }, + .port = 0, }, [1] = { .host_regs = (void __iomem *)CNS3XXX_PCIE1_HOST_BASE_VIRT, @@ -217,19 +208,13 @@ static struct cns3xxx_pcie cns3xxx_pcie[] = { .flags = IORESOURCE_MEM, }, .irqs = { IRQ_CNS3XXX_PCIE1_RC, IRQ_CNS3XXX_PCIE1_DEVICE, }, - .hw_pci = { - .domain = 1, - .nr_controllers = 1, - .ops = &cns3xxx_pcie_ops, - .setup = cns3xxx_pci_setup, - .map_irq = cns3xxx_pcie_map_irq, - }, + .port = 1, }, }; static void __init cns3xxx_pcie_check_link(struct cns3xxx_pcie *cnspci) { - int port = cnspci->hw_pci.domain; + int port = cnspci->port; u32 reg; unsigned long time; @@ -260,9 +245,9 @@ static void __init cns3xxx_pcie_check_link(struct cns3xxx_pcie *cnspci) static void __init cns3xxx_pcie_hw_init(struct cns3xxx_pcie *cnspci) { - int port = cnspci->hw_pci.domain; + int port = cnspci->port; struct pci_sys_data sd = { - .domain = port, + .private_data = cnspci, }; struct pci_bus bus = { .number = 0, @@ -323,6 +308,14 @@ static int cns3xxx_pcie_abort_handler(unsigned long addr, unsigned int fsr, void __init cns3xxx_pcie_init_late(void) { int i; + void *private_data; + struct hw_pci hw_pci = { + .nr_controllers = 1, + .ops = &cns3xxx_pcie_ops, + .setup = cns3xxx_pci_setup, + .map_irq = cns3xxx_pcie_map_irq, + .private_data = &private_data, + }; pcibios_min_io = 0; pcibios_min_mem = 0; @@ -335,7 +328,8 @@ void __init cns3xxx_pcie_init_late(void) cns3xxx_pwr_soft_rst(0x1 << PM_SOFT_RST_REG_OFFST_PCIE(i)); cns3xxx_pcie_check_link(&cns3xxx_pcie[i]); cns3xxx_pcie_hw_init(&cns3xxx_pcie[i]); - pci_common_init(&cns3xxx_pcie[i].hw_pci); + private_data = &cns3xxx_pcie[i]; + pci_common_init(&hw_pci); } pci_assign_unassigned_resources(); -- cgit v0.10.2 From 8c7d14746abce601b768533c3ccb3f3e64f98551 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Fri, 21 Nov 2014 11:29:26 +0000 Subject: ARM/PCI: Move to generic PCI domains Most if not all ARM PCI host controller device drivers either ignore the domain field in the pci_sys_data structure or just increment it every time a host controller is probed, using it as a domain counter. Therefore, instead of relying on pci_sys_data to stash the domain number in a standard location, ARM pcibios code can be moved to the newly introduced generic PCI domains code, implemented in commits: 41e5c0f81d3e ("of/pci: Add pci_get_new_domain_nr() and of_get_pci_domain_nr()") 670ba0c8883b ("PCI: Add generic domain handling") ARM code is made to select PCI_DOMAINS_GENERIC by default, which builds core PCI code that assigns the domain number through the generic function: void pci_bus_assign_domain_nr(...) that relies on a DT property to define the domain number or falls back to a counter according to a predefined logic; its usage replaces the current domain assignment code in PCI host controllers present in the kernel. Tested-by: Lucas Stach Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Yijing Wang Reviewed-By: Jason Gunthorpe # mvebu Acked-by: Russell King Acked-by: Lucas Stach Acked-by: Jingoo Han Acked-by: Phil Edworthy Acked-by: Arnd Bergmann CC: Mohit Kumar diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 97d07ed..dcb2e0c 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1279,6 +1279,9 @@ config PCI_DOMAINS bool depends on PCI +config PCI_DOMAINS_GENERIC + def_bool PCI_DOMAINS + config PCI_NANOENGINE bool "BSE nanoEngine PCI support" depends on SA1100_NANOENGINE diff --git a/arch/arm/include/asm/mach/pci.h b/arch/arm/include/asm/mach/pci.h index 8292b5f..28b9bb3 100644 --- a/arch/arm/include/asm/mach/pci.h +++ b/arch/arm/include/asm/mach/pci.h @@ -19,9 +19,6 @@ struct pci_bus; struct device; struct hw_pci { -#ifdef CONFIG_PCI_DOMAINS - int domain; -#endif #ifdef CONFIG_PCI_MSI struct msi_controller *msi_ctrl; #endif @@ -45,9 +42,6 @@ struct hw_pci { * Per-controller structure */ struct pci_sys_data { -#ifdef CONFIG_PCI_DOMAINS - int domain; -#endif #ifdef CONFIG_PCI_MSI struct msi_controller *msi_ctrl; #endif diff --git a/arch/arm/include/asm/pci.h b/arch/arm/include/asm/pci.h index 7e95d85..585dc33 100644 --- a/arch/arm/include/asm/pci.h +++ b/arch/arm/include/asm/pci.h @@ -18,13 +18,6 @@ static inline int pcibios_assign_all_busses(void) } #ifdef CONFIG_PCI_DOMAINS -static inline int pci_domain_nr(struct pci_bus *bus) -{ - struct pci_sys_data *root = bus->sysdata; - - return root->domain; -} - static inline int pci_proc_domain(struct pci_bus *bus) { return pci_domain_nr(bus); diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index a4effd6..ddd75c5 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -463,9 +463,6 @@ static void pcibios_init_hw(struct device *parent, struct hw_pci *hw, if (!sys) panic("PCI: unable to allocate sys data!"); -#ifdef CONFIG_PCI_DOMAINS - sys->domain = hw->domain; -#endif #ifdef CONFIG_PCI_MSI sys->msi_ctrl = hw->msi_ctrl; #endif diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 1dd7595..1309cfb 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -101,9 +101,7 @@ struct mvebu_pcie { struct mvebu_pcie_port *ports; struct msi_controller *msi; struct resource io; - char io_name[30]; struct resource realio; - char mem_name[30]; struct resource mem; struct resource busn; int nports; @@ -723,18 +721,9 @@ static int mvebu_pcie_setup(int nr, struct pci_sys_data *sys) { struct mvebu_pcie *pcie = sys_to_pcie(sys); int i; - int domain = 0; -#ifdef CONFIG_PCI_DOMAINS - domain = sys->domain; -#endif - - snprintf(pcie->mem_name, sizeof(pcie->mem_name), "PCI MEM %04x", - domain); - pcie->mem.name = pcie->mem_name; - - snprintf(pcie->io_name, sizeof(pcie->io_name), "PCI I/O %04x", domain); - pcie->realio.name = pcie->io_name; + pcie->mem.name = "PCI MEM"; + pcie->realio.name = "PCI I/O"; if (request_resource(&iomem_resource, &pcie->mem)) return 0; diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index df781cd..eef3111 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -508,9 +508,6 @@ int __init dw_pcie_host_init(struct pcie_port *pp) dw_pci.private_data = (void **)&pp; pci_common_init_dev(pp->dev, &dw_pci); -#ifdef CONFIG_PCI_DOMAINS - dw_pci.domain++; -#endif return 0; } diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c index 748786c..ba28078 100644 --- a/drivers/pci/host/pcie-rcar.c +++ b/drivers/pci/host/pcie-rcar.c @@ -397,9 +397,6 @@ static void rcar_pcie_enable(struct rcar_pcie *pcie) #endif pci_common_init_dev(&pdev->dev, &rcar_pci); -#ifdef CONFIG_PCI_DOMAINS - rcar_pci.domain++; -#endif } static int phy_wait_for_ack(struct rcar_pcie *pcie) -- cgit v0.10.2 From 5fafed3e5612e9f308d20dc94adf5fc3d4a1a2a8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sat, 27 Dec 2014 20:37:37 -0800 Subject: Input: add tps65218 power button driver With this driver, we can report KEY_POWER on AM437x SK. This patch has been tested with said board. Signed-off-by: Felipe Balbi Signed-off-by: Dmitry Torokhov diff --git a/Documentation/devicetree/bindings/input/tps65218-pwrbutton.txt b/Documentation/devicetree/bindings/input/tps65218-pwrbutton.txt new file mode 100644 index 0000000..e30e0b9 --- /dev/null +++ b/Documentation/devicetree/bindings/input/tps65218-pwrbutton.txt @@ -0,0 +1,17 @@ +Texas Instruments TPS65218 power button + +This driver provides a simple power button event via an Interrupt. + +Required properties: +- compatible: should be "ti,tps65218-pwrbutton" +- interrupts: should be one of the following + - <3 IRQ_TYPE_EDGE_BOTH>: For controllers compatible with tps65218 + +Example: + +&tps { + power-button { + compatible = "ti,tps65218-pwrbutton"; + interrupts = <3 IRQ_TYPE_EDGE_BOTH>; + }; +}; diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 1da0a20..9591917 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -416,6 +416,16 @@ config INPUT_RETU_PWRBUTTON To compile this driver as a module, choose M here. The module will be called retu-pwrbutton. +config INPUT_TPS65218_PWRBUTTON + tristate "TPS65218 Power button driver" + depends on MFD_TPS65218 + help + Say Y here if you want to enable power buttong reporting for + the TPS65218 Power Management IC device. + + To compile this driver as a module, choose M here. The module will + be called tps65218-pwrbutton. + config INPUT_TWL4030_PWRBUTTON tristate "TWL4030 Power button Driver" depends on TWL4030_CORE diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 1f135af..9a1083f 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o +obj-$(CONFIG_INPUT_TPS65218_PWRBUTTON) += tps65218-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o obj-$(CONFIG_INPUT_TWL6040_VIBRA) += twl6040-vibra.o diff --git a/drivers/input/misc/tps65218-pwrbutton.c b/drivers/input/misc/tps65218-pwrbutton.c new file mode 100644 index 0000000..54508de --- /dev/null +++ b/drivers/input/misc/tps65218-pwrbutton.c @@ -0,0 +1,126 @@ +/* + * Texas Instruments' TPS65218 Power Button Input Driver + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Felipe Balbi + * + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct tps65218_pwrbutton { + struct device *dev; + struct tps65218 *tps; + struct input_dev *idev; +}; + +static irqreturn_t tps65218_pwr_irq(int irq, void *_pwr) +{ + struct tps65218_pwrbutton *pwr = _pwr; + unsigned int reg; + int error; + + error = tps65218_reg_read(pwr->tps, TPS65218_REG_STATUS, ®); + if (error) { + dev_err(pwr->dev, "can't read register: %d\n", error); + goto out; + } + + if (reg & TPS65218_STATUS_PB_STATE) { + input_report_key(pwr->idev, KEY_POWER, 1); + pm_wakeup_event(pwr->dev, 0); + } else { + input_report_key(pwr->idev, KEY_POWER, 0); + } + + input_sync(pwr->idev); + +out: + return IRQ_HANDLED; +} + +static int tps65218_pwron_probe(struct platform_device *pdev) +{ + struct tps65218 *tps = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct tps65218_pwrbutton *pwr; + struct input_dev *idev; + int error; + int irq; + + pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL); + if (!pwr) + return -ENOMEM; + + idev = devm_input_allocate_device(dev); + if (!idev) + return -ENOMEM; + + idev->name = "tps65218_pwrbutton"; + idev->phys = "tps65218_pwrbutton/input0"; + idev->dev.parent = dev; + idev->id.bustype = BUS_I2C; + + input_set_capability(idev, EV_KEY, KEY_POWER); + + pwr->tps = tps; + pwr->dev = dev; + pwr->idev = idev; + platform_set_drvdata(pdev, pwr); + device_init_wakeup(dev, true); + + irq = platform_get_irq(pdev, 0); + error = devm_request_threaded_irq(dev, irq, NULL, tps65218_pwr_irq, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "tps65218-pwrbutton", pwr); + if (error) { + dev_err(dev, "failed to request IRQ #%d: %d\n", + irq, error); + return error; + } + + error= input_register_device(idev); + if (error) { + dev_err(dev, "Can't register power button: %d\n", error); + return error; + } + + return 0; +} + +static struct of_device_id of_tps65218_pwr_match[] = { + { .compatible = "ti,tps65218-pwrbutton" }, + { }, +}; +MODULE_DEVICE_TABLE(of, of_tps65218_pwr_match); + +static struct platform_driver tps65218_pwron_driver = { + .probe = tps65218_pwron_probe, + .driver = { + .name = "tps65218_pwrbutton", + .of_match_table = of_tps65218_pwr_match, + }, +}; +module_platform_driver(tps65218_pwron_driver); + +MODULE_DESCRIPTION("TPS65218 Power Button"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Felipe Balbi "); -- cgit v0.10.2 From e32ec12fd11fbccc1cb4656e34f35ad457e19e29 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 22 Oct 2014 11:22:56 +0200 Subject: iwlwifi: remove MODULE_VERSION The module version "in-tree:" or "in-tree:d" is useless; there should be better (functional) ways to detect whether debugging is enabled and other than that the version says nothing. Therefore remove the driver version completely. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 0b7f46f..fcdc128 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -64,22 +64,8 @@ * ******************************************************************************/ -/* - * module name, copyright, version, etc. - */ #define DRV_DESCRIPTION "Intel(R) Wireless WiFi Link AGN driver for Linux" - -#ifdef CONFIG_IWLWIFI_DEBUG -#define VD "d" -#else -#define VD -#endif - -#define DRV_VERSION IWLWIFI_VERSION VD - - MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 38de151..36ae191 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -84,21 +84,8 @@ * ******************************************************************************/ -/* - * module name, copyright, version, etc. - */ #define DRV_DESCRIPTION "Intel(R) Wireless WiFi driver for Linux" - -#ifdef CONFIG_IWLWIFI_DEBUG -#define VD "d" -#else -#define VD -#endif - -#define DRV_VERSION IWLWIFI_VERSION VD - MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); @@ -1492,7 +1479,7 @@ static int __init iwl_drv_init(void) for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) INIT_LIST_HEAD(&iwlwifi_opmode_table[i].drv); - pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); + pr_info(DRV_DESCRIPTION "\n"); pr_info(DRV_COPYRIGHT "\n"); #ifdef CONFIG_IWLWIFI_DEBUGFS diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.h b/drivers/net/wireless/iwlwifi/iwl-drv.h index be4f897..adf522c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.h +++ b/drivers/net/wireless/iwlwifi/iwl-drv.h @@ -68,7 +68,6 @@ /* for all modules */ #define DRV_NAME "iwlwifi" -#define IWLWIFI_VERSION "in-tree:" #define DRV_COPYRIGHT "Copyright(c) 2003- 2014 Intel Corporation" #define DRV_AUTHOR "" diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 97dfba5..552a776 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -84,15 +84,8 @@ #include "time-event.h" #include "iwl-fw-error-dump.h" -/* - * module name, copyright, version, etc. - */ #define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux" - -#define DRV_VERSION IWLWIFI_VERSION - MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); -- cgit v0.10.2 From f53bf4c758c0fbe728c031621f4c5f9979cdb044 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Mon, 1 Dec 2014 10:44:18 +0200 Subject: iwlwifi: mvm: add fw runtime stack to dump data The allocation of the DCCM between the data and the stack can theoretically change without notice to the driver, but the total size is HW-fixed. Since the stack CCM (runtime stack) has also data important to the FW - this patch allows pulling the whole DCCM in one piece and adds it to the dump data. If the size isn't known - just use the data part of the DCCM as it appears in the FW TLVs. Signed-off-by: Liad Kaufman Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index e5be2d2..9e76799 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -92,6 +92,12 @@ #define IWL7265D_NVM_VERSION 0x0c11 #define IWL7265_TX_POWER_VERSION 0xffff /* meaningless */ +/* DCCM offsets and lengths */ +#define IWL7000_DCCM_OFFSET 0x800000 +#define IWL7260_DCCM_LEN 0x14000 +#define IWL3160_DCCM_LEN 0x10000 +#define IWL7265_DCCM_LEN 0x17A00 + #define IWL7260_FW_PRE "iwlwifi-7260-" #define IWL7260_MODULE_FIRMWARE(api) IWL7260_FW_PRE __stringify(api) ".ucode" @@ -138,7 +144,8 @@ static const struct iwl_ht_params iwl7000_ht_params = { .led_mode = IWL_LED_RF_STATE, \ .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_7000, \ .non_shared_ant = ANT_A, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ + .dccm_offset = IWL7000_DCCM_OFFSET const struct iwl_cfg iwl7260_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 7260", @@ -149,6 +156,7 @@ const struct iwl_cfg iwl7260_2ac_cfg = { .nvm_calib_ver = IWL7260_TX_POWER_VERSION, .host_interrupt_operation_mode = true, .lp_xtal_workaround = true, + .dccm_len = IWL7260_DCCM_LEN, }; const struct iwl_cfg iwl7260_2ac_cfg_high_temp = { @@ -161,6 +169,7 @@ const struct iwl_cfg iwl7260_2ac_cfg_high_temp = { .high_temp = true, .host_interrupt_operation_mode = true, .lp_xtal_workaround = true, + .dccm_len = IWL7260_DCCM_LEN, }; const struct iwl_cfg iwl7260_2n_cfg = { @@ -172,6 +181,7 @@ const struct iwl_cfg iwl7260_2n_cfg = { .nvm_calib_ver = IWL7260_TX_POWER_VERSION, .host_interrupt_operation_mode = true, .lp_xtal_workaround = true, + .dccm_len = IWL7260_DCCM_LEN, }; const struct iwl_cfg iwl7260_n_cfg = { @@ -183,6 +193,7 @@ const struct iwl_cfg iwl7260_n_cfg = { .nvm_calib_ver = IWL7260_TX_POWER_VERSION, .host_interrupt_operation_mode = true, .lp_xtal_workaround = true, + .dccm_len = IWL7260_DCCM_LEN, }; const struct iwl_cfg iwl3160_2ac_cfg = { @@ -193,6 +204,7 @@ const struct iwl_cfg iwl3160_2ac_cfg = { .nvm_ver = IWL3160_NVM_VERSION, .nvm_calib_ver = IWL3160_TX_POWER_VERSION, .host_interrupt_operation_mode = true, + .dccm_len = IWL3160_DCCM_LEN, }; const struct iwl_cfg iwl3160_2n_cfg = { @@ -203,6 +215,7 @@ const struct iwl_cfg iwl3160_2n_cfg = { .nvm_ver = IWL3160_NVM_VERSION, .nvm_calib_ver = IWL3160_TX_POWER_VERSION, .host_interrupt_operation_mode = true, + .dccm_len = IWL3160_DCCM_LEN, }; const struct iwl_cfg iwl3160_n_cfg = { @@ -213,6 +226,7 @@ const struct iwl_cfg iwl3160_n_cfg = { .nvm_ver = IWL3160_NVM_VERSION, .nvm_calib_ver = IWL3160_TX_POWER_VERSION, .host_interrupt_operation_mode = true, + .dccm_len = IWL3160_DCCM_LEN, }; static const struct iwl_pwr_tx_backoff iwl7265_pwr_tx_backoffs[] = { @@ -240,6 +254,7 @@ const struct iwl_cfg iwl3165_2ac_cfg = { .nvm_ver = IWL3165_NVM_VERSION, .nvm_calib_ver = IWL3165_TX_POWER_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, }; const struct iwl_cfg iwl7265_2ac_cfg = { @@ -250,6 +265,7 @@ const struct iwl_cfg iwl7265_2ac_cfg = { .nvm_ver = IWL7265_NVM_VERSION, .nvm_calib_ver = IWL7265_TX_POWER_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, }; const struct iwl_cfg iwl7265_2n_cfg = { @@ -260,6 +276,7 @@ const struct iwl_cfg iwl7265_2n_cfg = { .nvm_ver = IWL7265_NVM_VERSION, .nvm_calib_ver = IWL7265_TX_POWER_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, }; const struct iwl_cfg iwl7265_n_cfg = { @@ -270,6 +287,7 @@ const struct iwl_cfg iwl7265_n_cfg = { .nvm_ver = IWL7265_NVM_VERSION, .nvm_calib_ver = IWL7265_TX_POWER_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, }; const struct iwl_cfg iwl7265d_2ac_cfg = { @@ -280,6 +298,7 @@ const struct iwl_cfg iwl7265d_2ac_cfg = { .nvm_ver = IWL7265D_NVM_VERSION, .nvm_calib_ver = IWL7265_TX_POWER_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, }; const struct iwl_cfg iwl7265d_2n_cfg = { @@ -290,6 +309,7 @@ const struct iwl_cfg iwl7265d_2n_cfg = { .nvm_ver = IWL7265D_NVM_VERSION, .nvm_calib_ver = IWL7265_TX_POWER_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, }; const struct iwl_cfg iwl7265d_n_cfg = { @@ -300,6 +320,7 @@ const struct iwl_cfg iwl7265d_n_cfg = { .nvm_ver = IWL7265D_NVM_VERSION, .nvm_calib_ver = IWL7265_TX_POWER_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, }; MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c index bf0a95c..6c5c558 100644 --- a/drivers/net/wireless/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/iwlwifi/iwl-8000.c @@ -81,6 +81,10 @@ #define IWL8000_NVM_VERSION 0x0a1d #define IWL8000_TX_POWER_VERSION 0xffff /* meaningless */ +/* DCCM offsets and lengths */ +#define IWL8260_DCCM_OFFSET 0x800000 +#define IWL8260_DCCM_LEN 0x18000 + #define IWL8000_FW_PRE "iwlwifi-8000" #define IWL8000_MODULE_FIRMWARE(api) \ IWL8000_FW_PRE "-" __stringify(api) ".ucode" @@ -124,7 +128,9 @@ static const struct iwl_ht_params iwl8000_ht_params = { .led_mode = IWL_LED_RF_STATE, \ .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000, \ .d0i3 = true, \ - .non_shared_ant = ANT_A + .non_shared_ant = ANT_A, \ + .dccm_offset = IWL8260_DCCM_OFFSET, \ + .dccm_len = IWL8260_DCCM_LEN const struct iwl_cfg iwl8260_2n_cfg = { .name = "Intel(R) Dual Band Wireless N 8260", diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index 3a4b9c7..31c67df 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -261,6 +261,8 @@ struct iwl_pwr_tx_backoff { * station can receive in HT * @max_vht_ampdu_exponent: the exponent of the max length of A-MPDU that the * station can receive in VHT + * @dccm_offset: offset from which DCCM begins + * @dccm_len: length of DCCM (including runtime stack CCM) * * We enable the driver to be backward compatible wrt. hardware features. * API differences in uCode shouldn't be handled here but through TLVs @@ -303,6 +305,8 @@ struct iwl_cfg { unsigned int max_tx_agg_size; unsigned int max_ht_ampdu_exponent; unsigned int max_vht_ampdu_exponent; + const u32 dccm_offset; + const u32 dccm_len; }; /* diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 31a5b3f..2d62b56 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -767,7 +767,6 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) struct iwl_fw_error_dump_data *dump_data; struct iwl_fw_error_dump_info *dump_info; struct iwl_mvm_dump_ptrs *fw_error_dump; - const struct fw_img *img; u32 sram_len, sram_ofs; u32 file_len, rxf_len; unsigned long flags; @@ -779,9 +778,17 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) if (!fw_error_dump) return; - img = &mvm->fw->img[mvm->cur_ucode]; - sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; - sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; + /* SRAM - include stack CCM if driver knows the values for it */ + if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) { + const struct fw_img *img; + + img = &mvm->fw->img[mvm->cur_ucode]; + sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; + sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; + } else { + sram_ofs = mvm->cfg->dccm_offset; + sram_len = mvm->cfg->dccm_len; + } /* reading buffer size */ reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR); -- cgit v0.10.2 From 7074cc4280463c27161a1eba89dfaa01685161bd Mon Sep 17 00:00:00 2001 From: Dor Shaish Date: Wed, 10 Dec 2014 12:44:57 +0200 Subject: Revert "iwlwifi: use correct fw file in 8000 b-step" Signed-off-by: Dor Shaish Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 36ae191..afa63f7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -237,9 +237,6 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first) /* * Starting 8000B - FW name format has changed. This overwrites the * previous name and uses the new format. - * - * TODO: - * Once there is only one supported step for 8000 family - delete this! */ if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) { char rev_step[2] = { @@ -250,13 +247,6 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first) if (CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_A_STEP) rev_step[0] = 0; - /* - * If hw_rev wasn't set yet - default as B-step. If it IS A-step - * we'll reload that FW later instead. - */ - if (drv->trans->hw_rev == 0) - rev_step[0] = 'B'; - snprintf(drv->firmware_name, sizeof(drv->firmware_name), "%s%s-%s.ucode", name_pre, rev_step, tag); } @@ -1069,7 +1059,6 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) u32 api_ver; int i; bool load_module = false; - u32 hw_rev = drv->trans->hw_rev; fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH; fw->ucode_capa.standard_phy_calibration_size = @@ -1262,50 +1251,6 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) op->name, err); #endif } - - /* - * We may have loaded the wrong FW file in 8000 HW family if it is an - * A-step card, and if drv->trans->hw_rev wasn't properly read when - * the FW file had been loaded. (This might happen in SDIO.) In such a - * case - unload and reload the correct file. - * - * TODO: - * Once there is only one supported step for 8000 family - delete this! - */ - if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 && - CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_A_STEP && - drv->trans->hw_rev != hw_rev) { - char firmware_name[32]; - - /* Free previous FW resources */ - if (drv->op_mode) - _iwl_op_mode_stop(drv); - iwl_dealloc_ucode(drv); - - /* Build name of correct-step FW */ - snprintf(firmware_name, sizeof(firmware_name), - strrchr(drv->firmware_name, '-')); - snprintf(drv->firmware_name, sizeof(drv->firmware_name), - "%s%s", drv->cfg->fw_name_pre, firmware_name); - - /* Clear data before loading correct FW */ - list_del(&drv->list); - - /* Request correct FW file this time */ - IWL_DEBUG_INFO(drv, "attempting to load A-step FW %s\n", - drv->firmware_name); - err = request_firmware(&ucode_raw, drv->firmware_name, - drv->trans->dev); - if (err) { - IWL_ERR(drv, "Failed swapping FW!\n"); - goto out_unbind; - } - - /* Redo callback function - this time with right FW */ - iwl_req_fw_callback(ucode_raw, context); - } - - kfree(pieces); return; try_again: -- cgit v0.10.2 From a054427244158552e91dacc0a4fc0a1b5e7342b9 Mon Sep 17 00:00:00 2001 From: Moshe Harel Date: Mon, 8 Dec 2014 21:13:14 +0200 Subject: iwlwifi: mvm: support LnP 1x1 antenna configuration The antenna configuration has to be read also from OTP Currently read only from FW image Guideline: An antenna exists only if appears both in FW image & NVM Signed-off-by: Moshe Harel Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 06e02fc..c74f1a4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -468,6 +468,8 @@ static void iwl_set_radio_cfg(const struct iwl_cfg *cfg, data->radio_cfg_step = NVM_RF_CFG_STEP_MSK_FAMILY_8000(radio_cfg); data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK_FAMILY_8000(radio_cfg); data->radio_cfg_pnum = NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(radio_cfg); + data->valid_tx_ant = NVM_RF_CFG_TX_ANT_MSK_FAMILY_8000(radio_cfg); + data->valid_rx_ant = NVM_RF_CFG_RX_ANT_MSK_FAMILY_8000(radio_cfg); } static void iwl_set_hw_address(const struct iwl_cfg *cfg, @@ -592,6 +594,10 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw); iwl_set_radio_cfg(cfg, data, radio_cfg); + if (data->valid_tx_ant) + tx_chains &= data->valid_tx_ant; + if (data->valid_rx_ant) + rx_chains &= data->valid_rx_ant; sku = iwl_get_sku(cfg, nvm_sw); data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ; diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 33bf915..0507647 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -933,7 +933,7 @@ iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf, return -EINVAL; if (scan_rx_ant > ANT_ABC) return -EINVAL; - if (scan_rx_ant & ~mvm->fw->valid_rx_ant) + if (scan_rx_ant & ~(iwl_mvm_get_valid_rx_ant(mvm))) return -EINVAL; if (mvm->scan_rx_ant != scan_rx_ant) { diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index d0fa6e9..60d0c9f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -269,7 +269,7 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) enum iwl_ucode_type ucode_type = mvm->cur_ucode; /* Set parameters */ - phy_cfg_cmd.phy_cfg = cpu_to_le32(mvm->fw->phy_config); + phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_get_phy_config(mvm)); phy_cfg_cmd.calib_control.event_trigger = mvm->fw->default_calib[ucode_type].event_trigger; phy_cfg_cmd.calib_control.flow_trigger = @@ -346,7 +346,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) mvm->calibrating = true; /* Send TX valid antennas before triggering calibrations */ - ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant); + ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); if (ret) goto error; @@ -489,7 +489,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) mvm->fw_dbg_conf = FW_DBG_INVALID; iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_CUSTOM); - ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant); + ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); if (ret) goto error; @@ -584,7 +584,7 @@ int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm) goto error; } - ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant); + ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); if (ret) goto error; diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index f6d86cc..7196b4d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -975,7 +975,7 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags); mvm->mgmt_last_antenna_idx = - iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant, + iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), mvm->mgmt_last_antenna_idx); beacon_cmd.tx.rate_n_flags = diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index d24660f..f3cb089 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -937,6 +937,33 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic); int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm); +static inline u8 iwl_mvm_get_valid_tx_ant(struct iwl_mvm *mvm) +{ + return mvm->nvm_data && mvm->nvm_data->valid_tx_ant ? + mvm->fw->valid_tx_ant & mvm->nvm_data->valid_tx_ant : + mvm->fw->valid_tx_ant; +} + +static inline u8 iwl_mvm_get_valid_rx_ant(struct iwl_mvm *mvm) +{ + return mvm->nvm_data && mvm->nvm_data->valid_rx_ant ? + mvm->fw->valid_rx_ant & mvm->nvm_data->valid_rx_ant : + mvm->fw->valid_rx_ant; +} + +static inline u32 iwl_mvm_get_phy_config(struct iwl_mvm *mvm) +{ + u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN | + FW_PHY_CFG_RX_CHAIN); + u32 valid_rx_ant = iwl_mvm_get_valid_rx_ant(mvm); + u32 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); + + phy_config |= valid_tx_ant << FW_PHY_CFG_TX_CHAIN_POS | + valid_rx_ant << FW_PHY_CFG_RX_CHAIN_POS; + + return mvm->fw->phy_config & phy_config; +} + int iwl_mvm_up(struct iwl_mvm *mvm); int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm); diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 552a776..146554c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -139,13 +139,14 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash; u32 reg_val = 0; - - radio_cfg_type = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_TYPE) >> - FW_PHY_CFG_RADIO_TYPE_POS; - radio_cfg_step = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_STEP) >> - FW_PHY_CFG_RADIO_STEP_POS; - radio_cfg_dash = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_DASH) >> - FW_PHY_CFG_RADIO_DASH_POS; + u32 phy_config = iwl_mvm_get_phy_config(mvm); + + radio_cfg_type = (phy_config & FW_PHY_CFG_RADIO_TYPE) >> + FW_PHY_CFG_RADIO_TYPE_POS; + radio_cfg_step = (phy_config & FW_PHY_CFG_RADIO_STEP) >> + FW_PHY_CFG_RADIO_STEP_POS; + radio_cfg_dash = (phy_config & FW_PHY_CFG_RADIO_DASH) >> + FW_PHY_CFG_RADIO_DASH_POS; /* SKU control */ reg_val |= CSR_HW_REV_STEP(mvm->trans->hw_rev) << diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c index 1c0d4a4..540c36b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c @@ -176,7 +176,7 @@ static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm, cmd->rxchain_info |= cpu_to_le32(active_cnt << PHY_RX_CHAIN_MIMO_CNT_POS); - cmd->txchain_info = cpu_to_le32(mvm->fw->valid_tx_ant); + cmd->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); } /* diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 30ceb67..b9682d7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -173,7 +173,7 @@ static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, if (sta->smps_mode == IEEE80211_SMPS_STATIC) return false; - if (num_of_ant(mvm->fw->valid_tx_ant) < 2) + if (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) < 2) return false; if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) @@ -1004,7 +1004,7 @@ static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta, } if (num_of_ant(rate->ant) > 1) - rate->ant = first_antenna(mvm->fw->valid_tx_ant); + rate->ant = first_antenna(iwl_mvm_get_valid_tx_ant(mvm)); /* Relevant in both switching to SISO or Legacy */ rate->sgi = false; @@ -1567,7 +1567,7 @@ static enum rs_column rs_get_next_column(struct iwl_mvm *mvm, const struct rs_tx_column *curr_col = &rs_tx_columns[tbl->column]; const struct rs_tx_column *next_col; allow_column_func_t allow_func; - u8 valid_ants = mvm->fw->valid_tx_ant; + u8 valid_ants = iwl_mvm_get_valid_tx_ant(mvm); const u16 *expected_tpt_tbl; u16 tpt, max_expected_tpt; @@ -2385,7 +2385,7 @@ static void rs_get_initial_rate(struct iwl_mvm *mvm, int i, nentries; s8 best_rssi = S8_MIN; u8 best_ant = ANT_NONE; - u8 valid_tx_ant = mvm->fw->valid_tx_ant; + u8 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); const struct rs_init_rate_info *initial_rates; for (i = 0; i < ARRAY_SIZE(lq_sta->pers.chain_signal); i++) { @@ -2745,7 +2745,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, lq_sta->ldpc = true; if (mvm->cfg->ht_params->stbc && - (num_of_ant(mvm->fw->valid_tx_ant) > 1) && + (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)) lq_sta->stbc = true; } else { @@ -2757,7 +2757,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, lq_sta->ldpc = true; if (mvm->cfg->ht_params->stbc && - (num_of_ant(mvm->fw->valid_tx_ant) > 1) && + (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK)) lq_sta->stbc = true; } @@ -2785,7 +2785,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, /* These values will be overridden later */ lq_sta->lq.single_stream_ant_msk = - first_antenna(mvm->fw->valid_tx_ant); + first_antenna(iwl_mvm_get_valid_tx_ant(mvm)); lq_sta->lq.dual_stream_ant_msk = ANT_AB; /* as default allow aggregation for all tids */ @@ -2913,7 +2913,7 @@ static void rs_build_rates_table(struct iwl_mvm *mvm, memcpy(&rate, initial_rate, sizeof(rate)); - valid_tx_ant = mvm->fw->valid_tx_ant; + valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); rate.stbc = rs_stbc_allow(mvm, sta, lq_sta); if (is_siso(&rate)) { @@ -3167,9 +3167,9 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, desc += sprintf(buff+desc, "fixed rate 0x%X\n", lq_sta->pers.dbg_fixed_rate); desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n", - (mvm->fw->valid_tx_ant & ANT_A) ? "ANT_A," : "", - (mvm->fw->valid_tx_ant & ANT_B) ? "ANT_B," : "", - (mvm->fw->valid_tx_ant & ANT_C) ? "ANT_C" : ""); + (iwl_mvm_get_valid_tx_ant(mvm) & ANT_A) ? "ANT_A," : "", + (iwl_mvm_get_valid_tx_ant(mvm) & ANT_B) ? "ANT_B," : "", + (iwl_mvm_get_valid_tx_ant(mvm) & ANT_C) ? "ANT_C" : ""); desc += sprintf(buff+desc, "lq type %s\n", (is_legacy(rate)) ? "legacy" : is_vht(rate) ? "VHT" : "HT"); diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index e5294d0..2433676 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -97,7 +97,7 @@ static u8 iwl_mvm_scan_rx_ant(struct iwl_mvm *mvm) { if (mvm->scan_rx_ant != ANT_NONE) return mvm->scan_rx_ant; - return mvm->fw->valid_rx_ant; + return iwl_mvm_get_valid_rx_ant(mvm); } static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm) @@ -128,7 +128,7 @@ iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band, u32 tx_ant; mvm->scan_last_antenna_idx = - iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant, + iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), mvm->scan_last_antenna_idx); tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS; @@ -1603,7 +1603,7 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) SCAN_CONFIG_FLAG_SET_MAC_ADDR | SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS| SCAN_CONFIG_N_CHANNELS(num_channels)); - scan_config->tx_chains = cpu_to_le32(mvm->fw->valid_tx_ant); + scan_config->tx_chains = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); scan_config->rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm)); scan_config->legacy_rates = iwl_mvm_scan_config_rates(mvm); scan_config->out_of_channel_time = cpu_to_le32(170); diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 4f15d9d..d6cdb77 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -209,7 +209,7 @@ void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd, rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx); mvm->mgmt_last_antenna_idx = - iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant, + iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), mvm->mgmt_last_antenna_idx); if (info->band == IEEE80211_BAND_2GHZ && diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index e56e77e..f0a1141 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -620,7 +620,7 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, lockdep_assert_held(&mvm->mutex); /* SMPS is irrelevant for NICs that don't have at least 2 RX antenna */ - if (num_of_ant(mvm->fw->valid_rx_ant) == 1) + if (num_of_ant(iwl_mvm_get_valid_rx_ant(mvm)) == 1) return; if (vif->type == NL80211_IFTYPE_AP) @@ -662,7 +662,7 @@ bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm) lockdep_assert_held(&mvm->mutex); - if (num_of_ant(mvm->fw->valid_rx_ant) == 1) + if (num_of_ant(iwl_mvm_get_valid_rx_ant(mvm)) == 1) return false; if (!mvm->cfg->rx_with_siso_diversity) -- cgit v0.10.2 From b7aaeae478d352bca5e877f97898a9668c2c74fb Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 7 Dec 2014 19:44:30 +0200 Subject: iwlwifi: pcie: let the Manageability Engine know when we leave When the driver is unload, the Manageability Engine should know about that - send an event to inform it about this event. Reviewed-by: Reuven Borok Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index aff63c3..7f40cf3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -184,6 +184,7 @@ #define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */ #define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */ #define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */ +#define CSR_HW_IF_CONFIG_REG_ENABLE_PME (0x10000000) #define CSR_HW_IF_CONFIG_REG_PERSIST_MODE (0x40000000) /* PERSISTENCE */ #define CSR_MBOX_SET_REG_OS_ALIVE BIT(5) diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 2df51ea..ea3b970 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -99,6 +99,7 @@ #define APMG_PCIDEV_STT_VAL_PERSIST_DIS (0x00000200) #define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) +#define APMG_PCIDEV_STT_VAL_WAKE_ME (0x00004000) #define APMG_RTC_INT_STT_RFKILL (0x10000000) diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 5d79a1f..4b42de3 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -443,10 +443,25 @@ static int iwl_pcie_apm_stop_master(struct iwl_trans *trans) return ret; } -static void iwl_pcie_apm_stop(struct iwl_trans *trans) +static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave) { IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n"); + if (op_mode_leave) { + if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status)) + iwl_pcie_apm_init(trans); + + /* inform ME that we are leaving */ + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) + iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_WAKE_ME); + else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_PREPARE | + CSR_HW_IF_CONFIG_REG_ENABLE_PME); + mdelay(5); + } + clear_bit(STATUS_DEVICE_ENABLED, &trans->status); /* Stop device's DMA activity */ @@ -1010,7 +1025,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); /* Stop the device, and put it in low power state */ - iwl_pcie_apm_stop(trans); + iwl_pcie_apm_stop(trans, false); /* Upon stop, the APM issues an interrupt if HW RF kill is set. * Clean again the interrupt here @@ -1187,7 +1202,7 @@ static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans) iwl_disable_interrupts(trans); spin_unlock(&trans_pcie->irq_lock); - iwl_pcie_apm_stop(trans); + iwl_pcie_apm_stop(trans, true); spin_lock(&trans_pcie->irq_lock); iwl_disable_interrupts(trans); -- cgit v0.10.2 From 8c23f95cca0efd2b6e0a8fedd9c9285b352579c8 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 4 Dec 2014 10:07:47 +0200 Subject: iwlwifi: mvm: add debugfs to trigger fw debug logs collection This allows to collect the logs even if the firmware hasn't crashed. Of course, crashing the firmware is an option, but this is easier and nicer. Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index ea3b970..83ab423 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -366,11 +366,15 @@ enum secure_load_status_reg { #define RXF_FIFO_RD_FENCE_ADDR (0xa00c0c) /* FW monitor */ +#define MON_BUFF_SAMPLE_CTL (0xa03c00) #define MON_BUFF_BASE_ADDR (0xa03c3c) #define MON_BUFF_END_ADDR (0xa03c40) #define MON_BUFF_WRPTR (0xa03c44) #define MON_BUFF_CYCLE_CNT (0xa03c48) +#define DBGC_IN_SAMPLE (0xa03c00) +#define DBGC_OUT_CTRL (0xa03c0c) + /* FW chicken bits */ #define LMPM_CHICK 0xA01FF8 enum { diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 0507647..a1b276c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -945,6 +945,56 @@ iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf, return count; } +static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + enum iwl_fw_dbg_conf conf; + char buf[8]; + const size_t bufsz = sizeof(buf); + int pos = 0; + + mutex_lock(&mvm->mutex); + conf = mvm->fw_dbg_conf; + mutex_unlock(&mvm->mutex); + + pos += scnprintf(buf + pos, bufsz - pos, "%d\n", conf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm, + char *buf, size_t count, + loff_t *ppos) +{ + int ret, conf_id; + + ret = kstrtoint(buf, 0, &conf_id); + if (ret) + return ret; + + if (WARN_ON(conf_id >= FW_DBG_MAX)) + return -EINVAL; + + mutex_lock(&mvm->mutex); + ret = iwl_mvm_start_fw_dbg_conf(mvm, conf_id); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm, + char *buf, size_t count, + loff_t *ppos) +{ + mutex_lock(&mvm->mutex); + iwl_mvm_fw_dbg_collect(mvm); + mutex_unlock(&mvm->mutex); + + return count; +} + #define ADD_TEXT(...) pos += scnprintf(buf + pos, bufsz - pos, __VA_ARGS__) #ifdef CONFIG_IWLWIFI_BCAST_FILTERING static ssize_t iwl_dbgfs_bcast_filters_read(struct file *file, @@ -1459,6 +1509,8 @@ MVM_DEBUGFS_WRITE_FILE_OPS(bt_tx_prio, 10); MVM_DEBUGFS_WRITE_FILE_OPS(bt_force_ant, 10); MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8); MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8); +MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 8); #ifdef CONFIG_IWLWIFI_BCAST_FILTERING MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256); @@ -1500,6 +1552,8 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) S_IWUSR | S_IRUSR); MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, S_IWUSR | S_IRUSR); MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR); #ifdef CONFIG_IWLWIFI_BCAST_FILTERING if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) { diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index 60d0c9f..534ee31 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -70,6 +70,7 @@ #include "iwl-debug.h" #include "iwl-csr.h" /* for iwl_mvm_rx_card_state_notif */ #include "iwl-io.h" /* for iwl_mvm_rx_card_state_notif */ +#include "iwl-prph.h" #include "iwl-eeprom-parse.h" #include "mvm.h" @@ -399,8 +400,26 @@ out: return ret; } -static int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, - enum iwl_fw_dbg_conf conf_id) +void iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm) +{ + lockdep_assert_held(&mvm->mutex); + + /* stop recording */ + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) { + iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100); + } else { + iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 0); + iwl_write_prph(mvm->trans, DBGC_OUT_CTRL, 0); + } + + iwl_mvm_fw_error_dump(mvm); + + /* start recording again */ + WARN_ON_ONCE(mvm->fw->dbg_dest_tlv && + iwl_mvm_start_fw_dbg_conf(mvm, mvm->fw_dbg_conf)); +} + +int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, enum iwl_fw_dbg_conf conf_id) { u8 *ptr; int ret; diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index f3cb089..ff1a409 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -1371,4 +1371,7 @@ struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm); void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error); void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm); +int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, enum iwl_fw_dbg_conf id); +void iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm); + #endif /* __IWL_MVM_H__ */ -- cgit v0.10.2 From f1a68542424191df21953da3cf9ac2d4116dc482 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 16 Dec 2014 12:31:13 +0200 Subject: iwlwifi: mvm: allow RSSI compensation The firmware is able to compensate the rssi when we hear the frame on a different channel. This is true for an offset up to 3 channels. Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 2d62b56..bec588d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -377,6 +377,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->max_remain_on_channel_duration = 10000; hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; + /* we can compensate an offset of up to 3 channels = 15 MHz */ + hw->wiphy->max_adj_channel_rssi_comp = 3 * 5; /* Extract MAC address */ memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN); -- cgit v0.10.2 From 435da2ce52365d7d7c0056786eca2464d70e90a5 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Thu, 11 Dec 2014 16:11:01 -0500 Subject: iwlwifi: mvm: Set the HW step in the core dump The HW step member was left out of the core dump information. Fix this. Signed-off-by: Ido Yariv Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index bec588d..2293a51 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -823,6 +823,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ? cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) : cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8); + dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev)); memcpy(dump_info->fw_human_readable, mvm->fw->human_readable, sizeof(dump_info->fw_human_readable)); strncpy(dump_info->dev_human_readable, mvm->cfg->name, -- cgit v0.10.2 From 19789abbe5dcd9ac1730b6acf04f2bddfeadefce Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Mon, 15 Dec 2014 14:15:15 +0200 Subject: iwlwifi: mvm: clear tt values when entering CT-kill Clear the thermal throttling values when entering CT-kill, since everything will be reinitialized anyway when we exit CT-kill. Additionally, clear the dynamic_smps value in the initialization funciton, for consistency. Signed-off-by: Luciano Coelho Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c index 2b1e61f..ba615ad 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/iwlwifi/mvm/tt.c @@ -69,6 +69,7 @@ static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm) { + struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; u32 duration = mvm->thermal_throttle.params->ct_kill_duration; if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) @@ -77,12 +78,15 @@ static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm) IWL_ERR(mvm, "Enter CT Kill\n"); iwl_mvm_set_hw_ctkill_state(mvm, true); + tt->throttle = false; + tt->dynamic_smps = false; + /* Don't schedule an exit work if we're in test mode, since * the temperature will not change unless we manually set it * again (or disable testing). */ if (!mvm->temperature_test) - schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit, + schedule_delayed_work(&tt->ct_kill_exit, round_jiffies_relative(duration * HZ)); } @@ -452,6 +456,7 @@ void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff) tt->params = &iwl7000_tt_params; tt->throttle = false; + tt->dynamic_smps = false; tt->min_backoff = min_backoff; INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill); } -- cgit v0.10.2 From 6a028d9a26446a937fa5c2eaca9407985ef98add Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Sun, 7 Dec 2014 18:18:27 +0200 Subject: iwlwifi: mvm: rs: fix max rate allowed if no rate is allowed In case the rate mask for one of the modulations was zero the max rate idx for that modulation was set to 32 (BITS_PER_LONG). This is bad as it would later lead to an out of bounds access to the expected tpt table. In most cases there was no real effect as the expected tpt was set to 0 and this led to avoiding the modulation effectively. Fix the out of bounds access and explicitly skip the modulation in case there's no rate allowed in it. Signed-off-by: Eyal Shapira Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index b9682d7..999efc3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -1318,6 +1318,13 @@ static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy, lq_sta->visited_columns = 0; } +static inline int rs_get_max_rate_from_mask(unsigned long rate_mask) +{ + if (rate_mask) + return find_last_bit(&rate_mask, BITS_PER_LONG); + return IWL_RATE_INVALID; +} + static int rs_get_max_allowed_rate(struct iwl_lq_sta *lq_sta, const struct rs_tx_column *column) { @@ -1613,8 +1620,12 @@ static enum rs_column rs_get_next_column(struct iwl_mvm *mvm, continue; max_rate = rs_get_max_allowed_rate(lq_sta, next_col); - if (WARN_ON_ONCE(max_rate == IWL_RATE_INVALID)) + if (max_rate == IWL_RATE_INVALID) { + IWL_DEBUG_RATE(mvm, + "Skip column %d: no rate is allowed in this column\n", + next_col_id); continue; + } max_expected_tpt = expected_tpt_tbl[max_rate]; if (tpt >= max_expected_tpt) { @@ -2765,12 +2776,12 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, if (IWL_MVM_RS_DISABLE_MIMO) lq_sta->active_mimo2_rate = 0; - lq_sta->max_legacy_rate_idx = find_last_bit(&lq_sta->active_legacy_rate, - BITS_PER_LONG); - lq_sta->max_siso_rate_idx = find_last_bit(&lq_sta->active_siso_rate, - BITS_PER_LONG); - lq_sta->max_mimo2_rate_idx = find_last_bit(&lq_sta->active_mimo2_rate, - BITS_PER_LONG); + lq_sta->max_legacy_rate_idx = + rs_get_max_rate_from_mask(lq_sta->active_legacy_rate); + lq_sta->max_siso_rate_idx = + rs_get_max_rate_from_mask(lq_sta->active_siso_rate); + lq_sta->max_mimo2_rate_idx = + rs_get_max_rate_from_mask(lq_sta->active_mimo2_rate); IWL_DEBUG_RATE(mvm, "RATE MASK: LEGACY=%lX SISO=%lX MIMO2=%lX VHT=%d LDPC=%d STBC%d\n", -- cgit v0.10.2 From 363039be5b9dcbb9df9136c7106d8775f71e43c6 Mon Sep 17 00:00:00 2001 From: Eran Harary Date: Tue, 2 Dec 2014 15:19:22 +0200 Subject: iwlwifi: mvm: support additional nvm_file in family 8000 B step nvm_file in family 8000 B step and A step differ. This means that the driver should support 2 file name as default. Signed-off-by: Eran Harary Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c index 6c5c558..8f64928 100644 --- a/drivers/net/wireless/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/iwlwifi/iwl-8000.c @@ -90,7 +90,8 @@ IWL8000_FW_PRE "-" __stringify(api) ".ucode" #define NVM_HW_SECTION_NUM_FAMILY_8000 10 -#define DEFAULT_NVM_FILE_FAMILY_8000 "iwl_nvm_8000.bin" +#define DEFAULT_NVM_FILE_FAMILY_8000A "iwl_nvm_8000.bin" +#define DEFAULT_NVM_FILE_FAMILY_8000 "iwl_nvm_8000B.bin" /* Max SDIO RX aggregation size of the ADDBA request/response */ #define MAX_RX_AGG_SIZE_8260_SDIO 28 @@ -159,6 +160,7 @@ const struct iwl_cfg iwl8260_2ac_sdio_cfg = { .nvm_ver = IWL8000_NVM_VERSION, .nvm_calib_ver = IWL8000_TX_POWER_VERSION, .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000, + .default_nvm_file_8000A = DEFAULT_NVM_FILE_FAMILY_8000A, .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO, .disable_dummy_notification = true, .max_ht_ampdu_exponent = MAX_HT_AMPDU_EXPONENT_8260_SDIO, @@ -173,6 +175,7 @@ const struct iwl_cfg iwl4165_2ac_sdio_cfg = { .nvm_ver = IWL8000_NVM_VERSION, .nvm_calib_ver = IWL8000_TX_POWER_VERSION, .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000, + .default_nvm_file_8000A = DEFAULT_NVM_FILE_FAMILY_8000A, .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO, .bt_shared_single_ant = true, .disable_dummy_notification = true, diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index 31c67df..2daabab 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -300,6 +300,7 @@ struct iwl_cfg { const struct iwl_pwr_tx_backoff *pwr_tx_backoffs; bool no_power_up_nic_in_init; const char *default_nvm_file; + const char *default_nvm_file_8000A; unsigned int max_rx_agg_size; bool disable_dummy_notification; unsigned int max_tx_agg_size; diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 2293a51..f53bfd6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -85,6 +85,7 @@ #include "testmode.h" #include "iwl-fw-error-dump.h" #include "iwl-prph.h" +#include "iwl-csr.h" static const struct ieee80211_iface_limit iwl_mvm_limits[] = { { diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 146554c..b2fa3b8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -511,10 +511,15 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, min_backoff = calc_min_backoff(trans, cfg); iwl_mvm_tt_initialize(mvm, min_backoff); /* set the nvm_file_name according to priority */ - if (iwlwifi_mod_params.nvm_file) + if (iwlwifi_mod_params.nvm_file) { mvm->nvm_file_name = iwlwifi_mod_params.nvm_file; - else - mvm->nvm_file_name = mvm->cfg->default_nvm_file; + } else { + if ((trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) && + (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_A_STEP)) + mvm->nvm_file_name = mvm->cfg->default_nvm_file_8000A; + else + mvm->nvm_file_name = mvm->cfg->default_nvm_file; + } if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name, "not allowing power-up and not having nvm_file\n")) -- cgit v0.10.2 From addfaada8fd044ad8cb365a792a09b1b3265376f Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Tue, 2 Dec 2014 11:16:04 +0200 Subject: iwlwifi: mvm: add smem content to dump data In NICs that have SMEM - add its content to the dump data for later debug. Signed-off-by: Liad Kaufman Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c index 8f64928..46e9c9a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/iwlwifi/iwl-8000.c @@ -81,9 +81,11 @@ #define IWL8000_NVM_VERSION 0x0a1d #define IWL8000_TX_POWER_VERSION 0xffff /* meaningless */ -/* DCCM offsets and lengths */ +/* Memory offsets and lengths */ #define IWL8260_DCCM_OFFSET 0x800000 #define IWL8260_DCCM_LEN 0x18000 +#define IWL8260_SMEM_OFFSET 0x400000 +#define IWL8260_SMEM_LEN 0x68000 #define IWL8000_FW_PRE "iwlwifi-8000" #define IWL8000_MODULE_FIRMWARE(api) \ @@ -131,7 +133,9 @@ static const struct iwl_ht_params iwl8000_ht_params = { .d0i3 = true, \ .non_shared_ant = ANT_A, \ .dccm_offset = IWL8260_DCCM_OFFSET, \ - .dccm_len = IWL8260_DCCM_LEN + .dccm_len = IWL8260_DCCM_LEN, \ + .smem_offset = IWL8260_SMEM_OFFSET, \ + .smem_len = IWL8260_SMEM_LEN const struct iwl_cfg iwl8260_2n_cfg = { .name = "Intel(R) Dual Band Wireless N 8260", diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index 2daabab..3985051 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -263,6 +263,8 @@ struct iwl_pwr_tx_backoff { * station can receive in VHT * @dccm_offset: offset from which DCCM begins * @dccm_len: length of DCCM (including runtime stack CCM) + * @smem_offset: offset from which the SMEM begins + * @smem_len: the length of SMEM * * We enable the driver to be backward compatible wrt. hardware features. * API differences in uCode shouldn't be handled here but through TLVs @@ -308,6 +310,8 @@ struct iwl_cfg { unsigned int max_vht_ampdu_exponent; const u32 dccm_offset; const u32 dccm_len; + const u32 smem_offset; + const u32 smem_len; }; /* diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h index 20a8a64..c6a81a7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h @@ -82,6 +82,7 @@ * @IWL_FW_ERROR_DUMP_PRPH: range of periphery registers - there can be several * sections like this in a single file. * @IWL_FW_ERROR_DUMP_FH_REGS: range of FH registers + * @IWL_FW_ERROR_DUMP_SMEM: */ enum iwl_fw_error_dump_type { IWL_FW_ERROR_DUMP_SRAM = 0, @@ -93,6 +94,7 @@ enum iwl_fw_error_dump_type { IWL_FW_ERROR_DUMP_PRPH = 6, IWL_FW_ERROR_DUMP_TXF = 7, IWL_FW_ERROR_DUMP_FH_REGS = 8, + IWL_FW_ERROR_DUMP_SMEM = 9, IWL_FW_ERROR_DUMP_MAX, }; diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index f53bfd6..d5d1cda 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -774,9 +774,16 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) u32 file_len, rxf_len; unsigned long flags; int reg_val; + u32 smem_len = mvm->cfg->smem_len; lockdep_assert_held(&mvm->mutex); + /* W/A for 8000 HW family A-step */ + if (mvm->cfg->smem_len && + mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && + CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_A_STEP) + smem_len = 0x38000; + fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL); if (!fw_error_dump) return; @@ -806,6 +813,10 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) rxf_len + sizeof(*dump_info); + /* Make room for the SMEM, if it exists */ + if (smem_len) + file_len += sizeof(*dump_data) + smem_len; + dump_file = vzalloc(file_len); if (!dump_file) { kfree(fw_error_dump); @@ -856,6 +867,14 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data, sram_len); + if (smem_len) { + dump_data = iwl_fw_error_next_data(dump_data); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SMEM); + dump_data->len = cpu_to_le32(smem_len); + iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset, + dump_data->data, smem_len); + } + fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans); fw_error_dump->op_mode_len = file_len; if (fw_error_dump->trans_ptr) -- cgit v0.10.2 From e06d8437cdf903a39bf93f16338c5b5a37810017 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 9 Dec 2014 14:36:41 +0200 Subject: iwlwifi: mvm: change SMEM dump to general purpose memory dump Instead of adding a dump type for each type of memory, change the SMEM type to be a general purpose memory dump. Add the type of the memory and its offset in the device in the dump itself. This will allow an external parser to know where this memory came from. Note that since this type isn't really in use yet, this is not a real problem. Reviewed-by: Liad Kaufman Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h index c6a81a7..c0fe82c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h @@ -82,7 +82,7 @@ * @IWL_FW_ERROR_DUMP_PRPH: range of periphery registers - there can be several * sections like this in a single file. * @IWL_FW_ERROR_DUMP_FH_REGS: range of FH registers - * @IWL_FW_ERROR_DUMP_SMEM: + * @IWL_FW_ERROR_DUMP_MEM: chunk of memory */ enum iwl_fw_error_dump_type { IWL_FW_ERROR_DUMP_SRAM = 0, @@ -94,7 +94,7 @@ enum iwl_fw_error_dump_type { IWL_FW_ERROR_DUMP_PRPH = 6, IWL_FW_ERROR_DUMP_TXF = 7, IWL_FW_ERROR_DUMP_FH_REGS = 8, - IWL_FW_ERROR_DUMP_SMEM = 9, + IWL_FW_ERROR_DUMP_MEM = 9, IWL_FW_ERROR_DUMP_MAX, }; @@ -182,6 +182,23 @@ struct iwl_fw_error_dump_prph { __le32 data[]; }; +enum iwl_fw_error_dump_mem_type { + IWL_FW_ERROR_DUMP_MEM_SRAM, + IWL_FW_ERROR_DUMP_MEM_SMEM, +}; + +/** + * struct iwl_fw_error_dump_mem - chunk of memory + * @type: %enum iwl_fw_error_dump_mem_type + * @offset: the offset from which the memory was read + * @data: the content of the memory + */ +struct iwl_fw_error_dump_mem { + __le32 type; + __le32 offset; + u8 data[]; +}; + /** * iwl_fw_error_next_data - advance fw error dump data pointer * @data: previous data block diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index d5d1cda..e24de97 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -815,7 +815,8 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) /* Make room for the SMEM, if it exists */ if (smem_len) - file_len += sizeof(*dump_data) + smem_len; + file_len += sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_mem) + smem_len; dump_file = vzalloc(file_len); if (!dump_file) { @@ -868,11 +869,16 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) sram_len); if (smem_len) { + struct iwl_fw_error_dump_mem *dump_mem; + dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SMEM); - dump_data->len = cpu_to_le32(smem_len); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM); + dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset); iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset, - dump_data->data, smem_len); + dump_mem->data, smem_len); } fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans); -- cgit v0.10.2 From a549b296228497cec90d3a5f5ecaa1934cec4bf1 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 9 Dec 2014 14:47:57 +0200 Subject: iwlwifi: mvm: convert the SRAM dump to the generic memory dump This allows to add the offset. The type of the generic memory dump will let the parser know that this is SRAM. Reviewed-by: Liad Kaufman Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h index c0fe82c..ec115bd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h @@ -71,7 +71,6 @@ /** * enum iwl_fw_error_dump_type - types of data in the dump file - * @IWL_FW_ERROR_DUMP_SRAM: * @IWL_FW_ERROR_DUMP_CSR: Control Status Registers - from offset 0 * @IWL_FW_ERROR_DUMP_RXF: * @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as @@ -85,7 +84,7 @@ * @IWL_FW_ERROR_DUMP_MEM: chunk of memory */ enum iwl_fw_error_dump_type { - IWL_FW_ERROR_DUMP_SRAM = 0, + /* 0 is deprecated */ IWL_FW_ERROR_DUMP_CSR = 1, IWL_FW_ERROR_DUMP_RXF = 2, IWL_FW_ERROR_DUMP_TXCMD = 3, diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index e24de97..b2f4ea5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -769,6 +769,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) struct iwl_fw_error_dump_file *dump_file; struct iwl_fw_error_dump_data *dump_data; struct iwl_fw_error_dump_info *dump_info; + struct iwl_fw_error_dump_mem *dump_mem; struct iwl_mvm_dump_ptrs *fw_error_dump; u32 sram_len, sram_ofs; u32 file_len, rxf_len; @@ -809,14 +810,13 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) file_len = sizeof(*dump_file) + sizeof(*dump_data) * 3 + - sram_len + + sram_len + sizeof(*dump_mem) + rxf_len + sizeof(*dump_info); /* Make room for the SMEM, if it exists */ if (smem_len) - file_len += sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_mem) + smem_len; + file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len; dump_file = vzalloc(file_len); if (!dump_file) { @@ -863,14 +863,15 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) } dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM); - dump_data->len = cpu_to_le32(sram_len); - iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data, + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); + dump_mem->offset = cpu_to_le32(sram_ofs); + iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data, sram_len); if (smem_len) { - struct iwl_fw_error_dump_mem *dump_mem; - dump_data = iwl_fw_error_next_data(dump_data); dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem)); -- cgit v0.10.2 From 7616f334e6d441aa9824221b1352ebec9de57ad7 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 20 Nov 2014 17:33:43 +0200 Subject: iwlwifi: pcie: add basic reference accounting Implement the ref/unref trans ops and track both tx and host command queues (and hold references while they are not empty). Signed-off-by: Eliad Peller Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index afa63f7..0381dc4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -1362,6 +1362,7 @@ struct iwl_mod_params iwlwifi_mod_params = { .bt_coex_active = true, .power_level = IWL_POWER_INDEX_1, .wd_disable = true, + .d0i3_disable = true, #ifndef CONFIG_IWLWIFI_UAPSD .uapsd_disable = true, #endif /* CONFIG_IWLWIFI_UAPSD */ @@ -1478,6 +1479,10 @@ MODULE_PARM_DESC(wd_disable, module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO); MODULE_PARM_DESC(nvm_file, "NVM file name"); +module_param_named(d0i3_disable, iwlwifi_mod_params.d0i3_disable, + bool, S_IRUGO); +MODULE_PARM_DESC(d0i3_disable, "disable d0i3 functionality (default: Y)"); + module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, bool, S_IRUGO); #ifdef CONFIG_IWLWIFI_UAPSD diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h index 71507cf..2a8cf4b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h @@ -103,6 +103,7 @@ enum iwl_disable_11n { * @power_level: power level, default = 1 * @debug_level: levels are IWL_DL_* * @ant_coupling: antenna coupling in dB, default = 0 + * @d0i3_disable: disable d0i3, default = 1, * @fw_monitor: allow to use firmware monitor */ struct iwl_mod_params { @@ -121,6 +122,7 @@ struct iwl_mod_params { int ant_coupling; char *nvm_file; bool uapsd_disable; + bool d0i3_disable; bool fw_monitor; }; diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 1aea6b6..e5652d8 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -318,6 +318,11 @@ struct iwl_trans_pcie { /*protect hw register */ spinlock_t reg_lock; bool cmd_in_flight; + bool ref_cmd_in_flight; + + /* protect ref counter */ + spinlock_t ref_lock; + u32 ref_count; dma_addr_t fw_mon_phys; struct page *fw_mon_page; @@ -381,6 +386,9 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, struct sk_buff_head *skbs); void iwl_trans_pcie_tx_reset(struct iwl_trans *trans); +void iwl_trans_pcie_ref(struct iwl_trans *trans); +void iwl_trans_pcie_unref(struct iwl_trans *trans); + static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx) { struct iwl_tfd_tb *tb = &tfd->tbs[idx]; diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 4b42de3..fbdbec8 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -931,6 +931,7 @@ static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans, static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, const struct fw_img *fw, bool run_in_rfkill) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; bool hw_rfkill; @@ -960,6 +961,9 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, return ret; } + /* init ref_count to 1 (should be cleared when ucode is loaded) */ + trans_pcie->ref_count = 1; + /* make sure rfkill handshake bits are cleared */ iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, @@ -1550,6 +1554,38 @@ static void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg, spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); } +void iwl_trans_pcie_ref(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; + + if (iwlwifi_mod_params.d0i3_disable) + return; + + spin_lock_irqsave(&trans_pcie->ref_lock, flags); + IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count); + trans_pcie->ref_count++; + spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); +} + +void iwl_trans_pcie_unref(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; + + if (iwlwifi_mod_params.d0i3_disable) + return; + + spin_lock_irqsave(&trans_pcie->ref_lock, flags); + IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count); + if (WARN_ON_ONCE(trans_pcie->ref_count == 0)) { + spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); + return; + } + trans_pcie->ref_count--; + spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); +} + static const char *get_csr_string(int cmd) { #define IWL_CMD(x) case x: return #x @@ -2274,6 +2310,9 @@ static const struct iwl_trans_ops trans_ops_pcie = { .release_nic_access = iwl_trans_pcie_release_nic_access, .set_bits_mask = iwl_trans_pcie_set_bits_mask, + .ref = iwl_trans_pcie_ref, + .unref = iwl_trans_pcie_unref, + .dump_data = iwl_trans_pcie_dump_data, }; diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 8a6c7a0..c1c4c7502 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -985,17 +985,31 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, if (iwl_queue_space(&txq->q) > txq->q.low_mark) iwl_wake_queue(trans, txq); + + if (q->read_ptr == q->write_ptr) { + IWL_DEBUG_RPM(trans, "Q %d - last tx reclaimed\n", q->id); + iwl_trans_pcie_unref(trans); + } + out: spin_unlock_bh(&txq->lock); } -static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans) +static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans, + const struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; lockdep_assert_held(&trans_pcie->reg_lock); + if (!(cmd->flags & CMD_SEND_IN_IDLE) && + !trans_pcie->ref_cmd_in_flight) { + trans_pcie->ref_cmd_in_flight = true; + IWL_DEBUG_RPM(trans, "set ref_cmd_in_flight - ref\n"); + iwl_trans_pcie_ref(trans); + } + if (trans_pcie->cmd_in_flight) return 0; @@ -1036,6 +1050,12 @@ static int iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans) lockdep_assert_held(&trans_pcie->reg_lock); + if (trans_pcie->ref_cmd_in_flight) { + trans_pcie->ref_cmd_in_flight = false; + IWL_DEBUG_RPM(trans, "clear ref_cmd_in_flight - unref\n"); + iwl_trans_pcie_unref(trans); + } + if (WARN_ON(!trans_pcie->cmd_in_flight)) return 0; @@ -1473,7 +1493,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); spin_lock_irqsave(&trans_pcie->reg_lock, flags); - ret = iwl_pcie_set_cmd_in_flight(trans); + ret = iwl_pcie_set_cmd_in_flight(trans, cmd); if (ret < 0) { idx = ret; spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); @@ -1819,9 +1839,13 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, wait_write_ptr = ieee80211_has_morefrags(fc); /* start timer if queue currently empty */ - if (txq->need_update && q->read_ptr == q->write_ptr && - trans_pcie->wd_timeout) - mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); + if (q->read_ptr == q->write_ptr) { + if (txq->need_update && trans_pcie->wd_timeout) + mod_timer(&txq->stuck_timer, + jiffies + trans_pcie->wd_timeout); + IWL_DEBUG_RPM(trans, "Q: %d first tx - take ref\n", q->id); + iwl_trans_pcie_ref(trans); + } /* Tell device the write index *just past* this latest filled TFD */ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr); -- cgit v0.10.2 From 91742449479bd8666f9533efd9594a7d9f87bd0d Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 9 Dec 2014 15:54:46 +0200 Subject: iwlwifi: mvm: allow both d0i3 and d3 wowlan configuration modes d3 and d0i3 shouldn't be mutually exclusive. Set supported wowlan triggers by looking for each of them, and check on suspend/resume which flow should be used ("any" trigger is supported by d0i3, and all the others by d3) Signed-off-by: Eliad Peller Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 744de26..a64894c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -1142,7 +1142,8 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); iwl_trans_suspend(mvm->trans); - if (iwl_mvm_is_d0i3_supported(mvm)) { + if (wowlan->any) { + /* 'any' trigger means d0i3 usage */ mutex_lock(&mvm->d0i3_suspend_mutex); __set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags); mutex_unlock(&mvm->d0i3_suspend_mutex); @@ -1876,8 +1877,10 @@ int iwl_mvm_resume(struct ieee80211_hw *hw) iwl_trans_resume(mvm->trans); - if (iwl_mvm_is_d0i3_supported(mvm)) + if (mvm->hw->wiphy->wowlan_config->any) { + /* 'any' trigger means d0i3 usage */ return 0; + } return __iwl_mvm_resume(mvm, false); } diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index b2f4ea5..fd0f8ce 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -462,15 +462,17 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) device_can_wakeup(mvm->trans->dev)) { mvm->wowlan.flags = WIPHY_WOWLAN_ANY; hw->wiphy->wowlan = &mvm->wowlan; - } else if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len && + } + + if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len && mvm->trans->ops->d3_suspend && mvm->trans->ops->d3_resume && device_can_wakeup(mvm->trans->dev)) { - mvm->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | - WIPHY_WOWLAN_DISCONNECT | - WIPHY_WOWLAN_EAP_IDENTITY_REQ | - WIPHY_WOWLAN_RFKILL_RELEASE | - WIPHY_WOWLAN_NET_DETECT; + mvm->wowlan.flags |= WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_EAP_IDENTITY_REQ | + WIPHY_WOWLAN_RFKILL_RELEASE | + WIPHY_WOWLAN_NET_DETECT; if (!iwlwifi_mod_params.sw_crypto) mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | WIPHY_WOWLAN_GTK_REKEY_FAILURE | -- cgit v0.10.2 From 0f8f93d6c81817d902fb46b0d741164992cc685a Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 9 Dec 2014 15:11:56 +0200 Subject: iwlwifi: support multiple d0i3 modes Allow configuring additional d0i3 mode, in which the fw will be configured to enter d0i3 only on suspend (while keeping the wake_lock accounting as usual) The d0i3 mode to use will be determined by the underlying trans layer. Signed-off-by: Eliad Peller Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 028408a..10ae9d7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -552,6 +552,21 @@ enum iwl_trans_state { }; /** + * enum iwl_d0i3_mode - d0i3 mode + * + * @IWL_D0I3_MODE_OFF - d0i3 is disabled + * @IWL_D0I3_MODE_ON_IDLE - enter d0i3 when device is idle + * (e.g. no active references) + * @IWL_D0I3_MODE_ON_SUSPEND - enter d0i3 only on suspend + * (in case of 'any' trigger) + */ +enum iwl_d0i3_mode { + IWL_D0I3_MODE_OFF = 0, + IWL_D0I3_MODE_ON_IDLE, + IWL_D0I3_MODE_ON_SUSPEND, +}; + +/** * struct iwl_trans - transport common data * * @ops - pointer to iwl_trans_ops @@ -612,6 +627,8 @@ struct iwl_trans { const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX]; u8 dbg_dest_reg_num; + enum iwl_d0i3_mode d0i3_mode; + /* pointer to trans specific struct */ /*Ensure that this pointer will always be aligned to sizeof pointer */ char trans_specific[0] __aligned(sizeof(void *)); diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index ff1a409..e608dc4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -850,6 +850,7 @@ iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id) static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm) { return mvm->trans->cfg->d0i3 && + mvm->trans->d0i3_mode != IWL_D0I3_MODE_OFF && (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_D0I3_SUPPORT); } -- cgit v0.10.2 From 6735943fafeaf55223b8704e52568b89ac3129e3 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 9 Dec 2014 15:23:54 +0200 Subject: iwlwifi: mvm: support IWL_D0I3_MODE_ON_SUSPEND d0i3 mode Enter d0i3 on suspend, and exit d0i3. Wait for the command responses in both cases. Use this mode in case of pcie trans. Signed-off-by: Eliad Peller Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index a64894c..4b5849e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -1137,6 +1137,29 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, return ret; } +static int iwl_mvm_enter_d0i3_sync(struct iwl_mvm *mvm) +{ + struct iwl_notification_wait wait_d3; + static const u8 d3_notif[] = { D3_CONFIG_CMD }; + int ret; + + iwl_init_notification_wait(&mvm->notif_wait, &wait_d3, + d3_notif, ARRAY_SIZE(d3_notif), + NULL, NULL); + + ret = iwl_mvm_enter_d0i3(mvm->hw->priv); + if (ret) + goto remove_notif; + + ret = iwl_wait_notification(&mvm->notif_wait, &wait_d3, HZ); + WARN_ON_ONCE(ret); + return ret; + +remove_notif: + iwl_remove_notification(&mvm->notif_wait, &wait_d3); + return ret; +} + int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -1144,6 +1167,13 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) iwl_trans_suspend(mvm->trans); if (wowlan->any) { /* 'any' trigger means d0i3 usage */ + if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) { + int ret = iwl_mvm_enter_d0i3_sync(mvm); + + if (ret) + return ret; + } + mutex_lock(&mvm->d0i3_suspend_mutex); __set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags); mutex_unlock(&mvm->d0i3_suspend_mutex); @@ -1879,6 +1909,16 @@ int iwl_mvm_resume(struct ieee80211_hw *hw) if (mvm->hw->wiphy->wowlan_config->any) { /* 'any' trigger means d0i3 usage */ + if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) { + int ret = iwl_mvm_exit_d0i3(hw->priv); + + if (ret) + return ret; + /* + * d0i3 exit will be deferred until reconfig_complete. + * make sure there we are out of d0i3. + */ + } return 0; } diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index fd0f8ce..29f0feb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1021,6 +1021,13 @@ static void iwl_mvm_resume_complete(struct iwl_mvm *mvm) IWL_DEBUG_RPM(mvm, "Run deferred d0i3 exit\n"); _iwl_mvm_exit_d0i3(mvm); } + + if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) + if (!wait_event_timeout(mvm->d0i3_exit_waitq, + !test_bit(IWL_MVM_STATUS_IN_D0I3, + &mvm->status), + HZ)) + WARN_ONCE(1, "D0i3 exit on resume timed out\n"); } static void diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index e608dc4..33a674a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -1187,6 +1187,8 @@ void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); bool iwl_mvm_ref_taken(struct iwl_mvm *mvm); void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq); +int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode); +int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode); int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm); /* BT Coex */ diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index b2fa3b8..17f58ca 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -1030,7 +1030,8 @@ static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm, out: rcu_read_unlock(); } -static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) + +int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE; @@ -1244,7 +1245,7 @@ out: return ret; } -static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode) +int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index fbdbec8..daec2f8 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -2453,6 +2453,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, } trans_pcie->inta_mask = CSR_INI_SET_MASK; + trans->d0i3_mode = IWL_D0I3_MODE_ON_SUSPEND; return trans; -- cgit v0.10.2 From b0677f71333032c384641b874c26b8871431a009 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 14 Dec 2014 17:49:28 +0200 Subject: iwlwifi: mvm: consider d0i3_disable in iwl_mvm_is_d0i3_supported() Consider the iwlwifi module param d0i3_disable when considering whether d0i3 is supported. (There is currently no need to differentiate between supported and enabled, so keep the function as-is) Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 33a674a..b2100b4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -851,6 +851,7 @@ static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm) { return mvm->trans->cfg->d0i3 && mvm->trans->d0i3_mode != IWL_D0I3_MODE_OFF && + !iwlwifi_mod_params.d0i3_disable && (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_D0I3_SUPPORT); } -- cgit v0.10.2 From 37948fcfd0bef2344df4245f231947cbadb6341a Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 11 Dec 2014 10:48:18 +0200 Subject: iwlwifi: mvm: wait for d0i3 exit on hw restart On hw restart, make sure to wait for d0i3 exit (by checking the IN_D0I3 status bit). This is needed in order to avoid the stale d0i3_exit_work from doing harm (e.g. unref cleared reference). Signed-off-by: Eliad Peller Reviewed-by: Gregory Greenman Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 29f0feb..9ccecbc 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -972,6 +972,19 @@ static int iwl_mvm_mac_start(struct ieee80211_hw *hw) struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; + /* Some hw restart cleanups must not hold the mutex */ + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { + /* + * Make sure we are out of d0i3. This is needed + * to make sure the reference accounting is correct + * (and there is no stale d0i3_exit_work). + */ + wait_event_timeout(mvm->d0i3_exit_waitq, + !test_bit(IWL_MVM_STATUS_IN_D0I3, + &mvm->status), + HZ); + } + mutex_lock(&mvm->mutex); ret = __iwl_mvm_mac_start(mvm); mutex_unlock(&mvm->mutex); -- cgit v0.10.2 From 2624a5ca76eff87e92ec2be9ea76dd382993bd19 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 16 Dec 2014 13:02:03 +0200 Subject: iwlwifi: mvm: support 2 different channels The driver and the firmware now support 2 different channels at the same time. Advertise this capability to the stack. Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 9ccecbc..4db051d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -106,7 +106,7 @@ static const struct ieee80211_iface_limit iwl_mvm_limits[] = { static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = { { - .num_different_channels = 1, + .num_different_channels = 2, .max_interfaces = 3, .limits = iwl_mvm_limits, .n_limits = ARRAY_SIZE(iwl_mvm_limits), -- cgit v0.10.2 From c1dc8288fe15425bb0620df9959182f32de51b4c Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 26 Dec 2014 15:35:43 +0100 Subject: iwlwifi: dvm: tt: Use setup_timer Convert a call to init_timer and accompanying intializations of the timer's data and function fields to a call to setup_timer. A simplified version of the semantic match that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression t,f,d; @@ -init_timer(&t); +setup_timer(&t,f,d); -t.data = d; -t.function = f; // Signed-off-by: Julia Lawall Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.c b/drivers/net/wireless/iwlwifi/dvm/tt.c index acb981a..c4736c8 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tt.c +++ b/drivers/net/wireless/iwlwifi/dvm/tt.c @@ -612,15 +612,10 @@ void iwl_tt_initialize(struct iwl_priv *priv) memset(tt, 0, sizeof(struct iwl_tt_mgmt)); tt->state = IWL_TI_0; - init_timer(&priv->thermal_throttle.ct_kill_exit_tm); - priv->thermal_throttle.ct_kill_exit_tm.data = (unsigned long)priv; - priv->thermal_throttle.ct_kill_exit_tm.function = - iwl_tt_check_exit_ct_kill; - init_timer(&priv->thermal_throttle.ct_kill_waiting_tm); - priv->thermal_throttle.ct_kill_waiting_tm.data = - (unsigned long)priv; - priv->thermal_throttle.ct_kill_waiting_tm.function = - iwl_tt_ready_for_ct_kill; + setup_timer(&priv->thermal_throttle.ct_kill_exit_tm, + iwl_tt_check_exit_ct_kill, (unsigned long)priv); + setup_timer(&priv->thermal_throttle.ct_kill_waiting_tm, + iwl_tt_ready_for_ct_kill, (unsigned long)priv); /* setup deferred ct kill work */ INIT_WORK(&priv->tt_work, iwl_bg_tt_work); INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); -- cgit v0.10.2 From bd9c504bfd659e58d3537123e6798a9b83a2263d Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 26 Dec 2014 15:35:44 +0100 Subject: iwlwifi: dvm: main: Use setup_timer Convert a call to init_timer and accompanying intializations of the timer's data and function fields to a call to setup_timer. A simplified version of the semantic match that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression t,f,d; @@ -init_timer(&t); +setup_timer(&t,f,d); -t.data = d; -t.function = f; // Signed-off-by: Julia Lawall Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index fcdc128..de43dd7 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -997,13 +997,11 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv) if (priv->lib->bt_params) iwlagn_bt_setup_deferred_work(priv); - init_timer(&priv->statistics_periodic); - priv->statistics_periodic.data = (unsigned long)priv; - priv->statistics_periodic.function = iwl_bg_statistics_periodic; + setup_timer(&priv->statistics_periodic, iwl_bg_statistics_periodic, + (unsigned long)priv); - init_timer(&priv->ucode_trace); - priv->ucode_trace.data = (unsigned long)priv; - priv->ucode_trace.function = iwl_bg_ucode_trace; + setup_timer(&priv->ucode_trace, iwl_bg_ucode_trace, + (unsigned long)priv); } void iwl_cancel_deferred_work(struct iwl_priv *priv) -- cgit v0.10.2 From 744cb695643ac111c422e4c517961646561b6895 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 10 Dec 2014 18:44:13 +0200 Subject: iwlwifi: mvm: clean refs before stop_device() Some implementations (i.e. mini_rpm) assume the references are managed only while the device is started. Move the stale reference cleanup before stopping the device in order to make them happy. Signed-off-by: Eliad Peller Signed-off-by: Gregory Greenman Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 4db051d..084689b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -903,6 +903,11 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status)) iwl_mvm_fw_error_dump(mvm); + /* cleanup all stale references (scan, roc), but keep the + * ucode_down ref until reconfig is complete + */ + iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN); + iwl_trans_stop_device(mvm->trans); mvm->scan_status = IWL_MVM_SCAN_NONE; @@ -932,10 +937,6 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) ieee80211_wake_queues(mvm->hw); - /* cleanup all stale references (scan, roc), but keep the - * ucode_down ref until reconfig is complete */ - iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN); - /* clear any stale d0i3 state */ clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); -- cgit v0.10.2 From a70393fc4ec15777afeba21df08335ddd09c2ed0 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 18 Dec 2014 09:15:13 +0200 Subject: iwlwifi: remove useless extern definition of iwl4265_2ac_sdio_cfg This device was renamed, but the external definition remained there. Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index 3985051..fa0bc48 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -379,7 +379,6 @@ extern const struct iwl_cfg iwl7265d_n_cfg; extern const struct iwl_cfg iwl8260_2n_cfg; extern const struct iwl_cfg iwl8260_2ac_cfg; extern const struct iwl_cfg iwl8260_2ac_sdio_cfg; -extern const struct iwl_cfg iwl4265_2ac_sdio_cfg; extern const struct iwl_cfg iwl4165_2ac_sdio_cfg; #endif /* CONFIG_IWLMVM */ -- cgit v0.10.2 From 5b530e95a95d17f004702cd829e42e171179f529 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 23 Dec 2014 16:00:17 +0100 Subject: iwlwifi: mvm: use iwl_mvm_vif_from_mac80211() consistently There are a few places not using it, use it at those places. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index a3bfda4..d2fd48c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -989,7 +989,7 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) { - struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_bt_iterator_data *data = _data; struct iwl_mvm *mvm = data->mvm; @@ -1025,7 +1025,7 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum ieee80211_rssi_event rssi_event) { - struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_bt_iterator_data data = { .mvm = mvm, }; diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c index b3210cf..973f288 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c @@ -1034,7 +1034,7 @@ int iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm, static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) { - struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_bt_iterator_data *data = _data; struct iwl_mvm *mvm = data->mvm; @@ -1070,7 +1070,7 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum ieee80211_rssi_event rssi_event) { - struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_bt_iterator_data data = { .mvm = mvm, }; diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index d86fe43..9eaa96e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -1144,7 +1144,7 @@ static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm) static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif, struct ieee80211_sta *sta) { - struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); if (sta) { struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; @@ -1280,7 +1280,7 @@ static inline u8 *iwl_mvm_get_mac_addr(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { - struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); if (sta) return sta->addr; -- cgit v0.10.2 From 9d8ce6afe19e8b5fea175ece550ec1f9d1024f8d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 23 Dec 2014 16:02:40 +0100 Subject: iwlwifi: mvm: use iwl_mvm_sta_from_mac80211() consistently A number of places (still) use a direct operation, use iwl_mvm_sta_from_mac80211() consistently. In one place also move it into the variable initializer. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 4b5849e..14e8fd6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -793,7 +793,7 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm, struct ieee80211_sta *ap_sta) { int ret; - struct iwl_mvm_sta *mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv; + struct iwl_mvm_sta *mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta); /* TODO: wowlan_config_cmd->wowlan_ba_teardown_tids */ @@ -1657,7 +1657,7 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, if (IS_ERR_OR_NULL(ap_sta)) goto out_free; - mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv; + mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta); for (i = 0; i < IWL_MAX_TID_COUNT; i++) { u16 seq = status.qos_seq_ctr[i]; /* firmware stores last-used value, we store next value */ diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c index 9aa2311..8dc3ca9 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c @@ -268,7 +268,7 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file, sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id], lockdep_is_held(&mvm->mutex)); if (!IS_ERR_OR_NULL(sta)) { - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); pos += scnprintf(buf+pos, bufsz-pos, "ap_sta_id %d - reduced Tx power %d\n", diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 084689b..c4552b5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -2137,7 +2137,7 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_sta *sta) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); /* * This is called before mac80211 does RCU synchronisation, @@ -3152,7 +3152,7 @@ static int iwl_mvm_set_tim(struct ieee80211_hw *hw, bool set) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); if (!mvm_sta || !mvm_sta->vif) { IWL_ERR(mvm, "Station is not associated to a vif\n"); diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 17f58ca..6bb6546 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -1146,7 +1146,7 @@ void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq) if (mvm->d0i3_offloading && qos_seq) { /* update qos seq numbers if offloading was enabled */ - mvm_ap_sta = (struct iwl_mvm_sta *)sta->drv_priv; + mvm_ap_sta = iwl_mvm_sta_from_mac80211(sta); for (i = 0; i < IWL_MAX_TID_COUNT; i++) { u16 seq = le16_to_cpu(qos_seq[i]); /* firmware stores last-used one, we store next one */ diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 999efc3..568829d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -1946,7 +1946,7 @@ static bool rs_tpc_perform(struct iwl_mvm *mvm, struct iwl_lq_sta *lq_sta, struct iwl_scale_tbl_info *tbl) { - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); struct ieee80211_vif *vif = mvm_sta->vif; struct ieee80211_chanctx_conf *chanctx_conf; enum ieee80211_band band; @@ -2055,7 +2055,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, u16 high_low; s32 sr; u8 prev_agg = lq_sta->is_agg; - struct iwl_mvm_sta *sta_priv = (void *)sta->drv_priv; + struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data; struct rs_rate *rate; @@ -2541,7 +2541,7 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta, static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta, gfp_t gfp) { - struct iwl_mvm_sta *sta_priv = (struct iwl_mvm_sta *)sta->drv_priv; + struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_rate; struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta; @@ -2694,14 +2694,11 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct ieee80211_hw *hw = mvm->hw; struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; - struct iwl_mvm_sta *sta_priv; - struct iwl_lq_sta *lq_sta; + struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); + struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta; struct ieee80211_supported_band *sband; unsigned long supp; /* must be unsigned long for for_each_set_bit */ - sta_priv = (struct iwl_mvm_sta *)sta->drv_priv; - lq_sta = &sta_priv->lq_sta; - /* clear all non-persistent lq data */ memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers)); diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 9eaa96e..ad32798 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -99,7 +99,7 @@ static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm, int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, bool update) { - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_add_sta_cmd add_sta_cmd = { .sta_id = mvm_sta->sta_id, .mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color), @@ -259,7 +259,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, struct ieee80211_sta *sta) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); int i, ret, sta_id; lockdep_assert_held(&mvm->mutex); @@ -481,7 +481,7 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, struct ieee80211_sta *sta) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); int ret; lockdep_assert_held(&mvm->mutex); @@ -774,7 +774,7 @@ int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, int tid, u16 ssn, bool start) { - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_add_sta_cmd cmd = {}; int ret; u32 status; @@ -834,7 +834,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, int tid, u8 queue, bool start) { - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_add_sta_cmd cmd = {}; int ret; u32 status; @@ -1147,7 +1147,7 @@ static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); if (sta) { - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); return mvm_sta->sta_id; } -- cgit v0.10.2 From b48217670f6e98491b7cff6b66534b4651347763 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Sun, 19 Oct 2014 16:58:15 +0200 Subject: iwlwifi: tlv: add support for IWL_UCODE_TLV_SDIO_ADMA_ADDR TLV A new TLV supplies the ADMA address for SDIO mode, allowing the driver to configure the default base address to be this (as given in the FW), rather than hardcoding the values to use until the FW sends the ALIVE message. Use the value given by the FW in the IWL_UCODE_TLV_SDIO_ADMA_ADDR TLV for setting the default SDTM base address until the FW sends the ALIVE message. If it isn't given in the FW - use the current hardcoded values. Signed-off-by: Liad Kaufman Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 0381dc4..0d34726 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -903,6 +903,12 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, IWL_UCODE_REGULAR_USNIFFER, tlv_len); break; + case IWL_UCODE_TLV_SDIO_ADMA_ADDR: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + drv->fw.sdio_adma_addr = + le32_to_cpup((__le32 *)tlv_data); + break; default: IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type); break; diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index f2a047f..752c72b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -132,6 +132,7 @@ enum iwl_ucode_tlv_type { IWL_UCODE_TLV_ENABLED_CAPABILITIES = 30, IWL_UCODE_TLV_N_SCAN_CHANNELS = 31, IWL_UCODE_TLV_SEC_RT_USNIFFER = 34, + IWL_UCODE_TLV_SDIO_ADMA_ADDR = 35, IWL_UCODE_TLV_FW_DBG_DEST = 38, IWL_UCODE_TLV_FW_DBG_CONF = 39, }; diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index e6dc3b8..ffd785c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -152,6 +152,8 @@ struct iwl_fw_cscheme_list { * @mvm_fw: indicates this is MVM firmware * @cipher_scheme: optional external cipher scheme. * @human_readable: human readable version + * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until + * we get the ALIVE from the uCode * @dbg_dest_tlv: points to the destination TLV for debug * @dbg_conf_tlv: array of pointers to configuration TLVs for debug * @dbg_conf_tlv_len: lengths of the @dbg_conf_tlv entries @@ -181,6 +183,8 @@ struct iwl_fw { struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS]; u8 human_readable[FW_VER_HUMAN_READABLE_SZ]; + u32 sdio_adma_addr; + struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv; struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX]; size_t dbg_conf_tlv_len[FW_DBG_MAX]; diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 10ae9d7..84d8477 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -382,6 +382,8 @@ enum iwl_trans_status { * are considered stuck and will trigger device restart * @command_names: array of command names, must be 256 entries * (one for each command); for debugging only + * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until + * we get the ALIVE from the uCode */ struct iwl_trans_config { struct iwl_op_mode *op_mode; @@ -396,6 +398,8 @@ struct iwl_trans_config { bool scd_set_active; unsigned int queue_watchdog_timeout; const char *const *command_names; + + u32 sdio_adma_addr; }; struct iwl_trans_dump_data { diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 6bb6546..4906967 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -481,6 +481,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD; trans_cfg.scd_set_active = true; + trans_cfg.sdio_adma_addr = fw->sdio_adma_addr; + snprintf(mvm->hw->wiphy->fw_version, sizeof(mvm->hw->wiphy->fw_version), "%s", fw->fw_version); -- cgit v0.10.2 From d9f1fc209fdb18d0edd6cc633a8725adbfb0ca01 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 23 Dec 2014 15:05:14 +0200 Subject: iwlwifi: mvm: ask the fw to wakeup (from d0i3) on sysassert Set the wakeup flag (of the d3 command) to configure the fw to wakeup when sysassert happens while in d0i3. Signed-off-by: Eliad Peller Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 4906967..239f033 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -1049,6 +1049,7 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) }; struct iwl_d3_manager_config d3_cfg_cmd = { .min_sleep_time = cpu_to_le32(1000), + .wakeup_flags = cpu_to_le32(IWL_WAKEUP_D3_CONFIG_FW_ERROR), }; IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n"); -- cgit v0.10.2 From a1ed4025765cceec180f57eab032c770140d6b8f Mon Sep 17 00:00:00 2001 From: Haim Dreyfuss Date: Mon, 1 Dec 2014 23:30:07 +0200 Subject: iwlwifi: mvm: Configure EBS scan ratio This configuration defines the ratio between number of scan iterations where EBS is involved to those where it is not. This configuration was left unconfigured due to inaccurate documentation. Fix documentation as well. Signed-off-by: Haim Dreyfuss Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index 1f2acf4..2d1a81c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h @@ -653,8 +653,11 @@ enum iwl_scan_channel_flags { }; /* iwl_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S - * @flags: enum iwl_scan_channel_flgs - * @non_ebs_ratio: how many regular scan iteration before EBS + * @flags: enum iwl_scan_channel_flags + * @non_ebs_ratio: defines the ratio of number of scan iterations where EBS is + * involved. + * 1 - EBS is disabled. + * 2 - every second scan will be full scan(and so on). */ struct iwl_scan_channel_opt { __le16 flags; diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 2433676..ed229a8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -72,6 +72,8 @@ #define IWL_PLCP_QUIET_THRESH 1 #define IWL_ACTIVE_QUIET_TIME 10 +#define IWL_DENSE_EBS_SCAN_RATIO 5 +#define IWL_SPARSE_EBS_SCAN_RATIO 1 struct iwl_mvm_scan_params { u32 max_out_time; @@ -1296,10 +1298,14 @@ iwl_mvm_build_generic_unified_scan_cmd(struct iwl_mvm *mvm, cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); + cmd->channel_opt[0].non_ebs_ratio = + cpu_to_le16(IWL_DENSE_EBS_SCAN_RATIO); cmd->channel_opt[1].flags = cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); + cmd->channel_opt[1].non_ebs_ratio = + cpu_to_le16(IWL_SPARSE_EBS_SCAN_RATIO); } if (iwl_mvm_rrm_scan_needed(mvm)) -- cgit v0.10.2 From ff2986246471c564ccd7beb02427a32ec7f3f9d5 Mon Sep 17 00:00:00 2001 From: Eran Harary Date: Thu, 11 Dec 2014 13:23:01 +0200 Subject: iwlwifi: mvm: call to pcie_apply_destination also on family 8000 B step In order to config the FW and to allocate monitor buffer driver should run the function iwl_pcie_apply_destination immediately after FW sections are loaded. Signed-off-by: Eran Harary Reviewed-by: Liad Kaufman Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index daec2f8..09696ca 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -908,6 +908,9 @@ static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans, if (ret) return ret; + if (trans->dbg_dest_tlv) + iwl_pcie_apply_destination(trans); + /* Notify FW loading is done */ iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFFFFFF); -- cgit v0.10.2 From c3f8d0a3afbc54db0b7ef079d42bc3c72f06d668 Mon Sep 17 00:00:00 2001 From: Haim Dreyfuss Date: Tue, 2 Dec 2014 11:03:16 +0200 Subject: iwlwifi: mvm: Alter passive scan fragmentation parameters in case of multi-MAC Make passive scan fragmentation depends on the number of active interfaces. In case of single-MAC, make passive scan less fragmented. Signed-off-by: Haim Dreyfuss Reviewed-by: Alexander Bondar Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index ed229a8..348b9c4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -284,11 +284,11 @@ static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - bool *global_bound = data; + int *global_cnt = data; if (vif->type != NL80211_IFTYPE_P2P_DEVICE && mvmvif->phy_ctxt && mvmvif->phy_ctxt->id < MAX_PHYS) - *global_bound = true; + *global_cnt += 1; } static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, @@ -296,16 +296,16 @@ static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, int n_ssids, u32 flags, struct iwl_mvm_scan_params *params) { - bool global_bound = false; + int global_cnt = 0; enum ieee80211_band band; u8 frag_passive_dwell = 0; ieee80211_iterate_active_interfaces_atomic(mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_scan_condition_iterator, - &global_bound); + &global_cnt); - if (!global_bound) + if (!global_cnt) goto not_bound; params->suspend_time = 30; @@ -316,7 +316,11 @@ static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, IWL_UCODE_TLV_API_FRAGMENTED_SCAN) { params->suspend_time = 105; params->max_out_time = 70; - frag_passive_dwell = 20; + /* + * If there is more than one active interface make + * passive scan more fragmented. + */ + frag_passive_dwell = (global_cnt < 2) ? 40 : 20; } else { params->suspend_time = 120; params->max_out_time = 120; -- cgit v0.10.2 From 834437dab96c751bb013aaac86b19974f6cb444e Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Wed, 17 Dec 2014 15:38:58 +0200 Subject: iwlwifi: mvm: rs: organize and cleanup consts Organize and cleanup the consts used by rs. This is part of making some of these configurable. Signed-off-by: Eyal Shapira Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h index 3bd9347..7cd1de8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/iwlwifi/mvm/constants.h @@ -102,5 +102,33 @@ #define IWL_MVM_QUOTA_THRESHOLD 8 #define IWL_MVM_RS_RSSI_BASED_INIT_RATE 0 #define IWL_MVM_RS_DISABLE_MIMO 0 +#define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE 1 +#define IWL_MVM_RS_LEGACY_RETRIES_PER_RATE 1 +#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE 2 +#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE_TW 1 +#define IWL_MVM_RS_INITIAL_MIMO_NUM_RATES 3 +#define IWL_MVM_RS_INITIAL_SISO_NUM_RATES 3 +#define IWL_MVM_RS_INITIAL_LEGACY_NUM_RATES 16 +#define IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES 16 +#define IWL_MVM_RS_SECONDARY_SISO_NUM_RATES 3 +#define IWL_MVM_RS_SECONDARY_SISO_RETRIES 1 +#define IWL_MVM_RS_RATE_MIN_FAILURE_TH 3 +#define IWL_MVM_RS_RATE_MIN_SUCCESS_TH 8 +#define IWL_MVM_RS_STAY_IN_COLUMN_TIMEOUT 5 /* Seconds */ +#define IWL_MVM_RS_IDLE_TIMEOUT 5 /* Seconds */ +#define IWL_MVM_RS_MISSED_RATE_MAX 15 +#define IWL_MVM_RS_LEGACY_FAILURE_LIMIT 160 +#define IWL_MVM_RS_LEGACY_SUCCESS_LIMIT 480 +#define IWL_MVM_RS_LEGACY_TABLE_COUNT 160 +#define IWL_MVM_RS_NON_LEGACY_FAILURE_LIMIT 400 +#define IWL_MVM_RS_NON_LEGACY_SUCCESS_LIMIT 4500 +#define IWL_MVM_RS_NON_LEGACY_TABLE_COUNT 1500 +#define IWL_MVM_RS_SR_FORCE_DECREASE 15 /* percent */ +#define IWL_MVM_RS_SR_NO_DECREASE 85 /* percent */ +#define IWL_MVM_RS_AGG_TIME_LIMIT 4000 /* 4 msecs. valid 100-8000 */ +#define IWL_MVM_RS_AGG_DISABLE_START 3 +#define IWL_MVM_RS_TPC_SR_FORCE_INCREASE 75 /* percent */ +#define IWL_MVM_RS_TPC_SR_NO_INCREASE 85 /* percent */ +#define IWL_MVM_RS_TPC_TX_POWER_STEP 3 #endif /* __MVM_CONSTANTS_H */ diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 568829d..7ba6e5d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -42,25 +42,12 @@ #define RS_NAME "iwl-mvm-rs" -#define NUM_TRY_BEFORE_ANT_TOGGLE 1 -#define RS_LEGACY_RETRIES_PER_RATE 1 -#define RS_HT_VHT_RETRIES_PER_RATE 2 -#define RS_HT_VHT_RETRIES_PER_RATE_TW 1 -#define RS_INITIAL_MIMO_NUM_RATES 3 -#define RS_INITIAL_SISO_NUM_RATES 3 -#define RS_INITIAL_LEGACY_NUM_RATES LINK_QUAL_MAX_RETRY_NUM -#define RS_SECONDARY_LEGACY_NUM_RATES LINK_QUAL_MAX_RETRY_NUM -#define RS_SECONDARY_SISO_NUM_RATES 3 -#define RS_SECONDARY_SISO_RETRIES 1 - #define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ -#define IWL_RATE_MIN_FAILURE_TH 3 /* min failures to calc tpt */ -#define IWL_RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */ -/* max allowed rate miss before sync LQ cmd */ -#define IWL_MISSED_RATE_MAX 15 -#define RS_STAY_IN_COLUMN_TIMEOUT (5*HZ) -#define RS_IDLE_TIMEOUT (5*HZ) +/* Calculations of success ratio are done in fixed point where 12800 is 100%. + * Use this macro when dealing with thresholds consts set as a percentage + */ +#define RS_PERCENT(x) (128 * x) static u8 rs_ht_to_legacy[] = { [IWL_RATE_MCS_0_INDEX] = IWL_RATE_6M_INDEX, @@ -613,7 +600,8 @@ static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index) * at this rate. window->data contains the bitmask of successful * packets. */ -static int _rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, +static int _rs_collect_tx_data(struct iwl_mvm *mvm, + struct iwl_scale_tbl_info *tbl, int scale_index, int attempts, int successes, struct iwl_rate_scale_data *window) { @@ -668,8 +656,8 @@ static int _rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, fail_count = window->counter - window->success_counter; /* Calculate average throughput, if we have enough history. */ - if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) || - (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH)) + if ((fail_count >= IWL_MVM_RS_RATE_MIN_FAILURE_TH) || + (window->success_counter >= IWL_MVM_RS_RATE_MIN_SUCCESS_TH)) window->average_tpt = (window->success_ratio * tpt + 64) / 128; else window->average_tpt = IWL_INVALID_VALUE; @@ -677,7 +665,8 @@ static int _rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, return 0; } -static int rs_collect_tx_data(struct iwl_lq_sta *lq_sta, +static int rs_collect_tx_data(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, struct iwl_scale_tbl_info *tbl, int scale_index, int attempts, int successes, u8 reduced_txp) @@ -698,7 +687,7 @@ static int rs_collect_tx_data(struct iwl_lq_sta *lq_sta, /* Select window for current tx bit rate */ window = &(tbl->win[scale_index]); - ret = _rs_collect_tx_data(tbl, scale_index, attempts, successes, + ret = _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes, window); if (ret) return ret; @@ -707,7 +696,7 @@ static int rs_collect_tx_data(struct iwl_lq_sta *lq_sta, return -EINVAL; window = &tbl->tpc_win[reduced_txp]; - return _rs_collect_tx_data(tbl, scale_index, attempts, successes, + return _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes, window); } @@ -1125,7 +1114,8 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, } if (time_after(jiffies, - (unsigned long)(lq_sta->last_tx + RS_IDLE_TIMEOUT))) { + (unsigned long)(lq_sta->last_tx + + (IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) { int t; IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n"); @@ -1158,7 +1148,7 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, * ... driver. */ lq_sta->missed_rate_counter++; - if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) { + if (lq_sta->missed_rate_counter > IWL_MVM_RS_MISSED_RATE_MAX) { lq_sta->missed_rate_counter = 0; IWL_DEBUG_RATE(mvm, "Too many rates mismatch. Send sync LQ. rs_state %d\n", @@ -1213,7 +1203,7 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, ucode_rate = le32_to_cpu(table->rs_table[0]); rs_rate_from_ucode_rate(ucode_rate, info->band, &rate); - rs_collect_tx_data(lq_sta, curr_tbl, rate.index, + rs_collect_tx_data(mvm, lq_sta, curr_tbl, rate.index, info->status.ampdu_len, info->status.ampdu_ack_len, reduced_txp); @@ -1249,7 +1239,7 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, else continue; - rs_collect_tx_data(lq_sta, tmp_tbl, rate.index, 1, + rs_collect_tx_data(mvm, lq_sta, tmp_tbl, rate.index, 1, i < retries ? 0 : legacy_success, reduced_txp); } @@ -1303,13 +1293,13 @@ static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy, IWL_DEBUG_RATE(mvm, "Moving to RS_STATE_STAY_IN_COLUMN\n"); lq_sta->rs_state = RS_STATE_STAY_IN_COLUMN; if (is_legacy) { - lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT; - lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT; - lq_sta->max_success_limit = IWL_LEGACY_SUCCESS_LIMIT; + lq_sta->table_count_limit = IWL_MVM_RS_LEGACY_TABLE_COUNT; + lq_sta->max_failure_limit = IWL_MVM_RS_LEGACY_FAILURE_LIMIT; + lq_sta->max_success_limit = IWL_MVM_RS_LEGACY_SUCCESS_LIMIT; } else { - lq_sta->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT; - lq_sta->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT; - lq_sta->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT; + lq_sta->table_count_limit = IWL_MVM_RS_NON_LEGACY_TABLE_COUNT; + lq_sta->max_failure_limit = IWL_MVM_RS_NON_LEGACY_FAILURE_LIMIT; + lq_sta->max_success_limit = IWL_MVM_RS_NON_LEGACY_SUCCESS_LIMIT; } lq_sta->table_count = 0; lq_sta->total_failed = 0; @@ -1427,7 +1417,7 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm, u32 target_tpt; int rate_idx; - if (success_ratio > RS_SR_NO_DECREASE) { + if (success_ratio > IWL_MVM_RS_SR_NO_DECREASE) { target_tpt = 100 * expected_current_tpt; IWL_DEBUG_RATE(mvm, "SR %d high. Find rate exceeding EXPECTED_CURRENT %d\n", @@ -1495,7 +1485,7 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) flush_interval_passed = time_after(jiffies, (unsigned long)(lq_sta->flush_timer + - RS_STAY_IN_COLUMN_TIMEOUT)); + (IWL_MVM_RS_STAY_IN_COLUMN_TIMEOUT * HZ))); /* * Check if we should allow search for new modulation mode. @@ -1735,7 +1725,8 @@ static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm, { enum rs_action action = RS_ACTION_STAY; - if ((sr <= RS_SR_FORCE_DECREASE) || (current_tpt == 0)) { + if ((sr <= RS_PERCENT(IWL_MVM_RS_SR_FORCE_DECREASE)) || + (current_tpt == 0)) { IWL_DEBUG_RATE(mvm, "Decrease rate because of low SR\n"); return RS_ACTION_DOWNSCALE; @@ -1794,7 +1785,7 @@ static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm, out: if ((action == RS_ACTION_DOWNSCALE) && (low != IWL_RATE_INVALID)) { - if (sr >= RS_SR_NO_DECREASE) { + if (sr >= RS_PERCENT(IWL_MVM_RS_SR_NO_DECREASE)) { IWL_DEBUG_RATE(mvm, "SR is above NO DECREASE. Avoid downscale\n"); action = RS_ACTION_STAY; @@ -1836,11 +1827,11 @@ static bool rs_stbc_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index, int *weaker, int *stronger) { - *weaker = index + TPC_TX_POWER_STEP; + *weaker = index + IWL_MVM_RS_TPC_TX_POWER_STEP; if (*weaker > TPC_MAX_REDUCTION) *weaker = TPC_INVALID; - *stronger = index - TPC_TX_POWER_STEP; + *stronger = index - IWL_MVM_RS_TPC_TX_POWER_STEP; if (*stronger < 0) *stronger = TPC_INVALID; } @@ -1896,7 +1887,8 @@ static enum tpc_action rs_get_tpc_action(struct iwl_mvm *mvm, } /* Too many failures, increase txp */ - if (sr <= TPC_SR_FORCE_INCREASE || current_tpt == 0) { + if (sr <= RS_PERCENT(IWL_MVM_RS_TPC_SR_FORCE_INCREASE) || + current_tpt == 0) { IWL_DEBUG_RATE(mvm, "increase txp because of weak SR\n"); return TPC_ACTION_NO_RESTIRCTION; } @@ -1919,7 +1911,8 @@ static enum tpc_action rs_get_tpc_action(struct iwl_mvm *mvm, } /* next, increase if needed */ - if (sr < TPC_SR_NO_INCREASE && strong != TPC_INVALID) { + if (sr < RS_PERCENT(IWL_MVM_RS_TPC_SR_NO_INCREASE) && + strong != TPC_INVALID) { if (weak_tpt == IWL_INVALID_VALUE && strong_tpt != IWL_INVALID_VALUE && current_tpt < strong_tpt) { @@ -2117,8 +2110,8 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, * in current association (use new rate found above). */ fail_count = window->counter - window->success_counter; - if ((fail_count < IWL_RATE_MIN_FAILURE_TH) && - (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) { + if ((fail_count < IWL_MVM_RS_RATE_MIN_FAILURE_TH) && + (window->success_counter < IWL_MVM_RS_RATE_MIN_SUCCESS_TH)) { IWL_DEBUG_RATE(mvm, "(%s: %d): Test Window: succ %d total %d\n", rs_pretty_lq_type(rate->type), @@ -2720,7 +2713,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, * previous packets? Need to have IEEE 802.1X auth succeed immediately * after assoc.. */ - lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX; + lq_sta->missed_rate_counter = IWL_MVM_RS_MISSED_RATE_MAX; lq_sta->band = sband->band; /* * active legacy rates as per supported rates bitmap @@ -2925,14 +2918,14 @@ static void rs_build_rates_table(struct iwl_mvm *mvm, rate.stbc = rs_stbc_allow(mvm, sta, lq_sta); if (is_siso(&rate)) { - num_rates = RS_INITIAL_SISO_NUM_RATES; - num_retries = RS_HT_VHT_RETRIES_PER_RATE; + num_rates = IWL_MVM_RS_INITIAL_SISO_NUM_RATES; + num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE; } else if (is_mimo(&rate)) { - num_rates = RS_INITIAL_MIMO_NUM_RATES; - num_retries = RS_HT_VHT_RETRIES_PER_RATE; + num_rates = IWL_MVM_RS_INITIAL_MIMO_NUM_RATES; + num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE; } else { - num_rates = RS_INITIAL_LEGACY_NUM_RATES; - num_retries = RS_LEGACY_RETRIES_PER_RATE; + num_rates = IWL_MVM_RS_INITIAL_LEGACY_NUM_RATES; + num_retries = IWL_MVM_RS_LEGACY_RETRIES_PER_RATE; toggle_ant = true; } @@ -2943,12 +2936,12 @@ static void rs_build_rates_table(struct iwl_mvm *mvm, rs_get_lower_rate_down_column(lq_sta, &rate); if (is_siso(&rate)) { - num_rates = RS_SECONDARY_SISO_NUM_RATES; - num_retries = RS_SECONDARY_SISO_RETRIES; + num_rates = IWL_MVM_RS_SECONDARY_SISO_NUM_RATES; + num_retries = IWL_MVM_RS_SECONDARY_SISO_RETRIES; lq_cmd->mimo_delim = index; } else if (is_legacy(&rate)) { - num_rates = RS_SECONDARY_LEGACY_NUM_RATES; - num_retries = RS_LEGACY_RETRIES_PER_RATE; + num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES; + num_retries = IWL_MVM_RS_LEGACY_RETRIES_PER_RATE; } else { WARN_ON_ONCE(1); } @@ -2961,8 +2954,8 @@ static void rs_build_rates_table(struct iwl_mvm *mvm, rs_get_lower_rate_down_column(lq_sta, &rate); - num_rates = RS_SECONDARY_LEGACY_NUM_RATES; - num_retries = RS_LEGACY_RETRIES_PER_RATE; + num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES; + num_retries = IWL_MVM_RS_LEGACY_RETRIES_PER_RATE; rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, num_rates, num_retries, valid_tx_ant, @@ -2979,9 +2972,9 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta; struct iwl_mvm_vif *mvmvif; - lq_cmd->agg_disable_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; + lq_cmd->agg_disable_start_th = IWL_MVM_RS_AGG_DISABLE_START; lq_cmd->agg_time_limit = - cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); + cpu_to_le16(IWL_MVM_RS_AGG_TIME_LIMIT); #ifdef CONFIG_MAC80211_DEBUGFS if (lq_sta->pers.dbg_fixed_rate) { diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h index defd70a..f8f5bf2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/iwlwifi/mvm/rs.h @@ -137,42 +137,10 @@ enum { #define IWL_INVALID_VALUE -1 -#define IWL_MIN_RSSI_VAL -100 -#define IWL_MAX_RSSI_VAL 0 - -/* These values specify how many Tx frame attempts before - * searching for a new modulation mode */ -#define IWL_LEGACY_FAILURE_LIMIT 160 -#define IWL_LEGACY_SUCCESS_LIMIT 480 -#define IWL_LEGACY_TABLE_COUNT 160 - -#define IWL_NONE_LEGACY_FAILURE_LIMIT 400 -#define IWL_NONE_LEGACY_SUCCESS_LIMIT 4500 -#define IWL_NONE_LEGACY_TABLE_COUNT 1500 - -/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */ -#define IWL_RS_GOOD_RATIO 12800 /* 100% */ -#define IWL_RATE_SCALE_SWITCH 10880 /* 85% */ -#define IWL_RATE_HIGH_TH 10880 /* 85% */ -#define IWL_RATE_INCREASE_TH 6400 /* 50% */ -#define RS_SR_FORCE_DECREASE 1920 /* 15% */ -#define RS_SR_NO_DECREASE 10880 /* 85% */ - -#define TPC_SR_FORCE_INCREASE 9600 /* 75% */ -#define TPC_SR_NO_INCREASE 10880 /* 85% */ -#define TPC_TX_POWER_STEP 3 #define TPC_MAX_REDUCTION 15 #define TPC_NO_REDUCTION 0 #define TPC_INVALID 0xff -#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */ -#define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000) -#define LINK_QUAL_AGG_TIME_LIMIT_MIN (100) - -#define LINK_QUAL_AGG_DISABLE_START_DEF (3) -#define LINK_QUAL_AGG_DISABLE_START_MAX (255) -#define LINK_QUAL_AGG_DISABLE_START_MIN (0) - #define LINK_QUAL_AGG_FRAME_LIMIT_DEF (63) #define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) #define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) @@ -181,14 +149,7 @@ enum { /* load per tid defines for A-MPDU activation */ #define IWL_AGG_TPT_THREHOLD 0 -#define IWL_AGG_LOAD_THRESHOLD 10 #define IWL_AGG_ALL_TID 0xff -#define TID_QUEUE_CELL_SPACING 50 /*mS */ -#define TID_QUEUE_MAX_SIZE 20 -#define TID_ROUND_VALUE 5 /* mS */ - -#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING) -#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y)) enum iwl_table_type { LQ_NONE, -- cgit v0.10.2 From e3a8446a6a1bff8c2345cacd4f3b8bdea0ba36d8 Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Mon, 12 May 2014 20:09:55 -0700 Subject: powerpc/pseries: relocate "config DTL" so kconfig nests properly Moving config DTL up so it is below config PPC_SPLPAR means that menuconfig will show config DTL nicely indented right below config PPC_SPLPAR when PPC_SPLPAR is enabled. To contrast that, right now if I enable PPC_SPLPAR in menuconfig, all I can immediately tell is that "something showed up further down the list where I wasn't looking", and I end up having to toggle the option a few times to figure out what showed up, or look at the KConfig to find out that config DTL depends on config PPC_SPLPAR. Signed-off-by: Cody P Schafer Signed-off-by: Michael Ellerman diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index 756b482..a758a9c 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -34,6 +34,16 @@ config PPC_SPLPAR processors, that is, which share physical processors between two or more partitions. +config DTL + bool "Dispatch Trace Log" + depends on PPC_SPLPAR && DEBUG_FS + help + SPLPAR machines can log hypervisor preempt & dispatch events to a + kernel buffer. Saying Y here will enable logging these events, + which are accessible through a debugfs file. + + Say N if you are unsure. + config PSERIES_MSI bool depends on PCI_MSI && PPC_PSERIES && EEH @@ -123,13 +133,3 @@ config HV_PERF_CTRS systems. 24x7 is available on Power 8 systems. If unsure, select Y. - -config DTL - bool "Dispatch Trace Log" - depends on PPC_SPLPAR && DEBUG_FS - help - SPLPAR machines can log hypervisor preempt & dispatch events to a - kernel buffer. Saying Y here will enable logging these events, - which are accessible through a debugfs file. - - Say N if you are unsure. -- cgit v0.10.2 From 05b981f493e07856371ecd4a9294f187f5b50cf6 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Tue, 15 Jul 2014 13:43:47 +0200 Subject: powerpc/xmon: use isspace/isxdigit/isalnum from linux/ctype.h isxdigit() macro definition is the same. isalnum() from linux/ctype.h will accept additional latin non-ASCII characters. This is harmless since this macro is used in scanhex() which parses user input. isspace() from linux/ctype.h will accept vertical tab and form feed but not NULL. The use of this macro is modified to accept NULL as well. Additional characters are harmless since this macro is also only used in scanhex(). Signed-off-by: Vincent Bernat Signed-off-by: Michael Ellerman diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 5b150f0..e66ace7 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -183,14 +184,6 @@ extern void xmon_leave(void); #define GETWORD(v) (((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3]) #endif -#define isxdigit(c) (('0' <= (c) && (c) <= '9') \ - || ('a' <= (c) && (c) <= 'f') \ - || ('A' <= (c) && (c) <= 'F')) -#define isalnum(c) (('0' <= (c) && (c) <= '9') \ - || ('a' <= (c) && (c) <= 'z') \ - || ('A' <= (c) && (c) <= 'Z')) -#define isspace(c) (c == ' ' || c == '\t' || c == 10 || c == 13 || c == 0) - static char *help_string = "\ Commands:\n\ b show breakpoints\n\ @@ -2164,9 +2157,6 @@ static void dump_pacas(void) } #endif -#define isxdigit(c) (('0' <= (c) && (c) <= '9') \ - || ('a' <= (c) && (c) <= 'f') \ - || ('A' <= (c) && (c) <= 'F')) static void dump(void) { @@ -2569,7 +2559,7 @@ scanhex(unsigned long *vp) int i; for (i=0; i<63; i++) { c = inchar(); - if (isspace(c)) { + if (isspace(c) || c == '\0') { termch = c; break; } -- cgit v0.10.2 From e144d4edbce67ea269d181f81b9c5ef631743430 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 20 Jul 2014 15:20:59 +0800 Subject: powerpc/4xx: Fix return value check in hsta_msi_probe() In case of error, the function ioremap() returns NULL not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Signed-off-by: Wei Yongjun Signed-off-by: Michael Ellerman diff --git a/arch/powerpc/sysdev/ppc4xx_hsta_msi.c b/arch/powerpc/sysdev/ppc4xx_hsta_msi.c index ed9970f..f366d2d 100644 --- a/arch/powerpc/sysdev/ppc4xx_hsta_msi.c +++ b/arch/powerpc/sysdev/ppc4xx_hsta_msi.c @@ -145,7 +145,7 @@ static int hsta_msi_probe(struct platform_device *pdev) ppc4xx_hsta_msi.address = mem->start; ppc4xx_hsta_msi.data = ioremap(mem->start, resource_size(mem)); ppc4xx_hsta_msi.irq_count = irq_count; - if (IS_ERR(ppc4xx_hsta_msi.data)) { + if (!ppc4xx_hsta_msi.data) { dev_err(dev, "Unable to map memory\n"); return -ENOMEM; } -- cgit v0.10.2 From 456295e284beb7b61a55ead9500d30f94ab06d52 Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Mon, 8 Dec 2014 19:17:57 +1100 Subject: cxl: Fix leaking interrupts if attach process fails In this particular error path we have already allocated the AFU interrupts, but have not yet set the status to STARTED. The detach context code will only attempt to release the interrupts if the context is in state STARTED, so in this case the interrupts would remain allocated. This patch releases the AFU interrupts immediately if the attach call fails to prevent them leaking. Signed-off-by: Ian Munsie Signed-off-by: Michael Ellerman diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c index e9f2f10..b09be44 100644 --- a/drivers/misc/cxl/file.c +++ b/drivers/misc/cxl/file.c @@ -185,8 +185,10 @@ static long afu_ioctl_start_work(struct cxl_context *ctx, ctx->pid = get_pid(get_task_pid(current, PIDTYPE_PID)); if ((rc = cxl_attach_process(ctx, false, work.work_element_descriptor, - amr))) + amr))) { + afu_release_irqs(ctx); goto out; + } ctx->status = STARTED; rc = 0; -- cgit v0.10.2 From 13da704682471669685ccc3fe111fd6c0127b2eb Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Mon, 8 Dec 2014 19:17:58 +1100 Subject: cxl: Early return from cxl_handle_fault for a shut down context If a context is being detached and we get a translation fault for it there is little point getting it's mm and handling the fault, so just respond with an address error and return earlier. Signed-off-by: Ian Munsie Signed-off-by: Michael Ellerman diff --git a/drivers/misc/cxl/fault.c b/drivers/misc/cxl/fault.c index f8684bc..e010302 100644 --- a/drivers/misc/cxl/fault.c +++ b/drivers/misc/cxl/fault.c @@ -180,6 +180,12 @@ void cxl_handle_fault(struct work_struct *fault_work) return; } + /* Early return if the context is being / has been detached */ + if (ctx->status == CLOSED) { + cxl_ack_ae(ctx); + return; + } + pr_devel("CXL BOTTOM HALF handling fault for afu pe: %i. " "DSISR: %#llx DAR: %#llx\n", ctx->pe, dsisr, dar); -- cgit v0.10.2 From d6a6af2c181400aade59417e698c7cd9bec8804e Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Mon, 8 Dec 2014 19:17:59 +1100 Subject: cxl: Disable AFU debug flag Upon inspection of the implementation specific registers, it was discovered that the high bit of the implementation specific RXCTL register was enabled, which enables the DEADB00F debug feature. The debug feature causes MMIO reads to a disabled AFU to respond with 0xDEADB00F instead of all Fs. In general this should not be visible as the kernel will only allow MMIO access to enabled AFUs, but there may be some circumstances where an AFU may become disabled while it is use. One such case would be an AFU designed to only be used in the dedicated process mode and to disable itself after it has completed it's work (however even in that case the effects of this debug flag would be limited as the userspace application must have completed any required MMIO accesses before the AFU disables itself with or without the flag). This patch removes the debug flag and replaces the magic value programmed into this register with a preprocessor define so it is clearer what the rest of this initialisation does. Signed-off-by: Ian Munsie Signed-off-by: Michael Ellerman diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index 28078f8..0df0438 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h @@ -287,6 +287,13 @@ static const cxl_p2n_reg_t CXL_PSL_WED_An = {0x0A0}; #define CXL_PE_SOFTWARE_STATE_S (1ul << (31 - 30)) /* Suspend */ #define CXL_PE_SOFTWARE_STATE_T (1ul << (31 - 31)) /* Terminate */ +/****** CXL_PSL_RXCTL_An (Implementation Specific) ************************** + * Controls AFU Hang Pulse, which sets the timeout for the AFU to respond to + * the PSL for any response (except MMIO). Timeouts will occur between 1x to 2x + * of the hang pulse frequency. + */ +#define CXL_PSL_RXCTL_AFUHP_4S 0x7000000000000000ULL + /* SPA->sw_command_status */ #define CXL_SPA_SW_CMD_MASK 0xffff000000000000ULL #define CXL_SPA_SW_CMD_TERMINATE 0x0001000000000000ULL diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index 0f2cc9f8..2ccd0a9 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -348,7 +348,7 @@ static int init_implementation_afu_regs(struct cxl_afu *afu) cxl_p1n_write(afu, CXL_PSL_COALLOC_A, 0xFF000000FEFEFEFEULL); /* for debugging with trace arrays */ cxl_p1n_write(afu, CXL_PSL_SLICE_TRACE, 0x0000FFFF00000000ULL); - cxl_p1n_write(afu, CXL_PSL_RXCTL_A, 0xF000000000000000ULL); + cxl_p1n_write(afu, CXL_PSL_RXCTL_A, CXL_PSL_RXCTL_AFUHP_4S); return 0; } -- cgit v0.10.2 From db7933f392ac4d9719d41d3f203a5f6a1c40f300 Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Mon, 8 Dec 2014 19:18:00 +1100 Subject: cxl: Disable SPAP register when freeing SPA When we deactivate the AFU directed mode we free the scheduled process area, but did not clear the register in the hardware that has a pointer to it. This should be fine since we will have already cleared out every context and we won't do anything that would cause the hardware to access it until after we have allocated a new one, but just to be safe this patch clears out the register when we free the page. Signed-off-by: Ian Munsie Signed-off-by: Michael Ellerman diff --git a/drivers/misc/cxl/native.c b/drivers/misc/cxl/native.c index f2b37b4..0f24fa5 100644 --- a/drivers/misc/cxl/native.c +++ b/drivers/misc/cxl/native.c @@ -185,6 +185,7 @@ static int alloc_spa(struct cxl_afu *afu) static void release_spa(struct cxl_afu *afu) { + cxl_p1n_write(afu, CXL_PSL_SPAP_An, 0); free_pages((unsigned long) afu->spa, afu->spa_order); } -- cgit v0.10.2 From 01b14505c35cdc47f5abc1f6799cee23f2f10149 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Tue, 23 Dec 2014 11:32:07 +1100 Subject: powerpc/44x/Akebono: Remove select of IBM_EMAC_RGMII_WOL Commit 2a2c74b2efcb ("IBM Akebono: Add the Akebono platform") added a select of IBM_EMAC_RGMII_WOL. But that Kconfig symbol isn't (yet) part of the tree. So this select has been a nop since that commit was included in v3.16-rc1. Signed-off-by: Paul Bolle Acked-by: Alistair Popple Signed-off-by: Michael Ellerman diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig index d2ac1c1..5538e57 100644 --- a/arch/powerpc/platforms/44x/Kconfig +++ b/arch/powerpc/platforms/44x/Kconfig @@ -214,7 +214,6 @@ config AKEBONO select ETHERNET select NET_VENDOR_IBM select IBM_EMAC_EMAC4 - select IBM_EMAC_RGMII_WOL select USB if USB_SUPPORT select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD -- cgit v0.10.2 From 803d57de2b27000ed4400d16561c75821efe8333 Mon Sep 17 00:00:00 2001 From: Andreas Ruprecht Date: Wed, 17 Dec 2014 12:05:13 +0100 Subject: powerpc/lib: Do not include string.o in obj-y twice In the Makefile, string.o (which is generated from string.S) is included into the list of objects being built unconditionally (obj-y) in line 12. Additionally, if CONFIG_PPC64 is set, it is included again in line 17. This patch removes the latter unnecessary inclusion. Signed-off-by: Andreas Ruprecht Signed-off-by: Michael Ellerman diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile index 597562f..1b01159 100644 --- a/arch/powerpc/lib/Makefile +++ b/arch/powerpc/lib/Makefile @@ -14,8 +14,7 @@ obj-y := string.o alloc.o \ obj-$(CONFIG_PPC32) += div64.o copy_32.o obj-$(CONFIG_PPC64) += copypage_64.o copyuser_64.o \ - usercopy_64.o mem_64.o string.o \ - hweight_64.o \ + usercopy_64.o mem_64.o hweight_64.o \ copyuser_power7.o string_64.o copypage_power7.o ifeq ($(CONFIG_GENERIC_CSUM),) obj-y += checksum_$(CONFIG_WORD_SIZE).o -- cgit v0.10.2 From 9a4fc4eaf111ca960c9f524b850598e9dbc9697f Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 10 Jul 2014 19:34:31 +1000 Subject: powerpc/kvm: Create proper names for the kvm_host_state PMU fields We have two arrays in kvm_host_state that contain register values for the PMU. Currently we only create an asm-offsets symbol for the base of the arrays, and do the array offset in the assembly code. Creating an asm-offsets symbol for each field individually makes the code much nicer to read, particularly for the MMCRx/SIxR/SDAR fields, and might have helped us notice the recent double restore bug we had in this code. Signed-off-by: Michael Ellerman Acked-by: Alexander Graf diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index e624f96..4717859 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -644,8 +644,19 @@ int main(void) HSTATE_FIELD(HSTATE_SAVED_XIRR, saved_xirr); HSTATE_FIELD(HSTATE_HOST_IPI, host_ipi); HSTATE_FIELD(HSTATE_PTID, ptid); - HSTATE_FIELD(HSTATE_MMCR, host_mmcr); - HSTATE_FIELD(HSTATE_PMC, host_pmc); + HSTATE_FIELD(HSTATE_MMCR0, host_mmcr[0]); + HSTATE_FIELD(HSTATE_MMCR1, host_mmcr[1]); + HSTATE_FIELD(HSTATE_MMCRA, host_mmcr[2]); + HSTATE_FIELD(HSTATE_SIAR, host_mmcr[3]); + HSTATE_FIELD(HSTATE_SDAR, host_mmcr[4]); + HSTATE_FIELD(HSTATE_MMCR2, host_mmcr[5]); + HSTATE_FIELD(HSTATE_SIER, host_mmcr[6]); + HSTATE_FIELD(HSTATE_PMC1, host_pmc[0]); + HSTATE_FIELD(HSTATE_PMC2, host_pmc[1]); + HSTATE_FIELD(HSTATE_PMC3, host_pmc[2]); + HSTATE_FIELD(HSTATE_PMC4, host_pmc[3]); + HSTATE_FIELD(HSTATE_PMC5, host_pmc[4]); + HSTATE_FIELD(HSTATE_PMC6, host_pmc[5]); HSTATE_FIELD(HSTATE_PURR, host_purr); HSTATE_FIELD(HSTATE_SPURR, host_spurr); HSTATE_FIELD(HSTATE_DSCR, host_dscr); diff --git a/arch/powerpc/kvm/book3s_hv_interrupts.S b/arch/powerpc/kvm/book3s_hv_interrupts.S index 36540a9..0fdc4a2 100644 --- a/arch/powerpc/kvm/book3s_hv_interrupts.S +++ b/arch/powerpc/kvm/book3s_hv_interrupts.S @@ -93,15 +93,15 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) mfspr r5, SPRN_MMCR1 mfspr r9, SPRN_SIAR mfspr r10, SPRN_SDAR - std r7, HSTATE_MMCR(r13) - std r5, HSTATE_MMCR + 8(r13) - std r6, HSTATE_MMCR + 16(r13) - std r9, HSTATE_MMCR + 24(r13) - std r10, HSTATE_MMCR + 32(r13) + std r7, HSTATE_MMCR0(r13) + std r5, HSTATE_MMCR1(r13) + std r6, HSTATE_MMCRA(r13) + std r9, HSTATE_SIAR(r13) + std r10, HSTATE_SDAR(r13) BEGIN_FTR_SECTION mfspr r9, SPRN_SIER - std r8, HSTATE_MMCR + 40(r13) - std r9, HSTATE_MMCR + 48(r13) + std r8, HSTATE_MMCR2(r13) + std r9, HSTATE_SIER(r13) END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) mfspr r3, SPRN_PMC1 mfspr r5, SPRN_PMC2 @@ -109,12 +109,12 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) mfspr r7, SPRN_PMC4 mfspr r8, SPRN_PMC5 mfspr r9, SPRN_PMC6 - stw r3, HSTATE_PMC(r13) - stw r5, HSTATE_PMC + 4(r13) - stw r6, HSTATE_PMC + 8(r13) - stw r7, HSTATE_PMC + 12(r13) - stw r8, HSTATE_PMC + 16(r13) - stw r9, HSTATE_PMC + 20(r13) + stw r3, HSTATE_PMC1(r13) + stw r5, HSTATE_PMC2(r13) + stw r6, HSTATE_PMC3(r13) + stw r7, HSTATE_PMC4(r13) + stw r8, HSTATE_PMC5(r13) + stw r9, HSTATE_PMC6(r13) 31: /* diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index 10554df..bb94e6f 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -83,35 +83,35 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S) cmpwi r4, 0 beq 23f /* skip if not */ BEGIN_FTR_SECTION - ld r3, HSTATE_MMCR(r13) + ld r3, HSTATE_MMCR0(r13) andi. r4, r3, MMCR0_PMAO_SYNC | MMCR0_PMAO cmpwi r4, MMCR0_PMAO beql kvmppc_fix_pmao END_FTR_SECTION_IFSET(CPU_FTR_PMAO_BUG) - lwz r3, HSTATE_PMC(r13) - lwz r4, HSTATE_PMC + 4(r13) - lwz r5, HSTATE_PMC + 8(r13) - lwz r6, HSTATE_PMC + 12(r13) - lwz r8, HSTATE_PMC + 16(r13) - lwz r9, HSTATE_PMC + 20(r13) + lwz r3, HSTATE_PMC1(r13) + lwz r4, HSTATE_PMC2(r13) + lwz r5, HSTATE_PMC3(r13) + lwz r6, HSTATE_PMC4(r13) + lwz r8, HSTATE_PMC5(r13) + lwz r9, HSTATE_PMC6(r13) mtspr SPRN_PMC1, r3 mtspr SPRN_PMC2, r4 mtspr SPRN_PMC3, r5 mtspr SPRN_PMC4, r6 mtspr SPRN_PMC5, r8 mtspr SPRN_PMC6, r9 - ld r3, HSTATE_MMCR(r13) - ld r4, HSTATE_MMCR + 8(r13) - ld r5, HSTATE_MMCR + 16(r13) - ld r6, HSTATE_MMCR + 24(r13) - ld r7, HSTATE_MMCR + 32(r13) + ld r3, HSTATE_MMCR0(r13) + ld r4, HSTATE_MMCR1(r13) + ld r5, HSTATE_MMCRA(r13) + ld r6, HSTATE_SIAR(r13) + ld r7, HSTATE_SDAR(r13) mtspr SPRN_MMCR1, r4 mtspr SPRN_MMCRA, r5 mtspr SPRN_SIAR, r6 mtspr SPRN_SDAR, r7 BEGIN_FTR_SECTION - ld r8, HSTATE_MMCR + 40(r13) - ld r9, HSTATE_MMCR + 48(r13) + ld r8, HSTATE_MMCR2(r13) + ld r9, HSTATE_SIER(r13) mtspr SPRN_MMCR2, r8 mtspr SPRN_SIER, r9 END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) -- cgit v0.10.2 From 72dca06f62c50415de3202f204200f58769d0b8b Mon Sep 17 00:00:00 2001 From: Aniroop Mathur Date: Sun, 28 Dec 2014 22:08:38 +0530 Subject: regulator: core: Avoid negative regulator no & initialize it to -1 This patch initializes regulator_no to -1 to avoid extra subtraction operation performed every time we register a regulator and avoid negative regulator no in its name. Signed-off-by: Aniroop Mathur Signed-off-by: Mark Brown diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index e225711..4bc5ea9 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3585,7 +3585,7 @@ regulator_register(const struct regulator_desc *regulator_desc, { const struct regulation_constraints *constraints = NULL; const struct regulator_init_data *init_data; - static atomic_t regulator_no = ATOMIC_INIT(0); + static atomic_t regulator_no = ATOMIC_INIT(-1); struct regulator_dev *rdev; struct device *dev; int ret, i; @@ -3658,8 +3658,8 @@ regulator_register(const struct regulator_desc *regulator_desc, /* register with sysfs */ rdev->dev.class = ®ulator_class; rdev->dev.parent = dev; - dev_set_name(&rdev->dev, "regulator.%d", - atomic_inc_return(®ulator_no) - 1); + dev_set_name(&rdev->dev, "regulator.%lu", + atomic_inc_return(®ulator_no)); ret = device_register(&rdev->dev); if (ret != 0) { put_device(&rdev->dev); -- cgit v0.10.2 From c957e8f084e0d21febcd6b8a0ea9631eccc92f36 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 29 Dec 2014 10:33:36 +0200 Subject: spi/pxa2xx: Clear cur_chip pointer before starting next message Once the current message is finished, the driver notifies SPI core about this by calling spi_finalize_current_message(). This function queues next message to be transferred. If there are more messages in the queue, it is possible that the driver is asked to transfer the next message at this point. When spi_finalize_current_message() returns the driver clears the drv_data->cur_chip pointer to NULL. The problem is that if the driver already started the next message clearing drv_data->cur_chip will cause NULL pointer dereference which crashes the kernel like: BUG: unable to handle kernel NULL pointer dereference at 0000000000000048 IP: [] cs_deassert+0x18/0x70 [spi_pxa2xx_platform] PGD 78bb8067 PUD 37712067 PMD 0 Oops: 0000 [#1] SMP Modules linked in: CPU: 1 PID: 11 Comm: ksoftirqd/1 Tainted: G O 3.18.0-rc4-mjo #5 Hardware name: Intel Corp. VALLEYVIEW B3 PLATFORM/NOTEBOOK, BIOS MNW2CRB1.X64.0071.R30.1408131301 08/13/2014 task: ffff880077f9f290 ti: ffff88007a820000 task.ti: ffff88007a820000 RIP: 0010:[] [] cs_deassert+0x18/0x70 [spi_pxa2xx_platform] RSP: 0018:ffff88007a823d08 EFLAGS: 00010202 RAX: 0000000000000008 RBX: ffff8800379a4430 RCX: 0000000000000026 RDX: 0000000000000000 RSI: 0000000000000246 RDI: ffff8800379a4430 RBP: ffff88007a823d18 R08: 00000000ffffffff R09: 000000007a9bc65a R10: 000000000000028f R11: 0000000000000005 R12: ffff880070123e98 R13: ffff880070123de8 R14: 0000000000000100 R15: ffffc90004888000 FS: 0000000000000000(0000) GS:ffff880079a80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000048 CR3: 000000007029b000 CR4: 00000000001007e0 Stack: ffff88007a823d58 ffff8800379a4430 ffff88007a823d48 ffffffffa0022c89 0000000000000000 ffff8800379a4430 0000000000000000 0000000000000006 ffff88007a823da8 ffffffffa0023be0 ffff88007a823dd8 ffffffff81076204 Call Trace: [] giveback+0x69/0xa0 [spi_pxa2xx_platform] [] pump_transfers+0x710/0x740 [spi_pxa2xx_platform] [] ? pick_next_task_fair+0x744/0x830 [] tasklet_action+0xa9/0xe0 [] __do_softirq+0xee/0x280 [] run_ksoftirqd+0x20/0x40 [] smpboot_thread_fn+0xff/0x1b0 [] ? SyS_setgroups+0x150/0x150 [] kthread+0xcd/0xf0 [] ? kthread_create_on_node+0x180/0x180 [] ret_from_fork+0x7c/0xb0 Fix this by clearing drv_data->cur_chip before we call spi_finalize_current_message(). Reported-by: Martin Oldfield Signed-off-by: Mika Westerberg Acked-by: Robert Jarzmik Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 05c623c..23822e7 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -546,8 +546,8 @@ static void giveback(struct driver_data *drv_data) cs_deassert(drv_data); } - spi_finalize_current_message(drv_data->master); drv_data->cur_chip = NULL; + spi_finalize_current_message(drv_data->master); } static void reset_sccr1(struct driver_data *drv_data) -- cgit v0.10.2 From 39138818a4f5c62d70f35504477c2509f982f211 Mon Sep 17 00:00:00 2001 From: Aniroop Mathur Date: Mon, 29 Dec 2014 22:36:48 +0530 Subject: regulator: core: Fix format specifier warning Signed-off-by: Aniroop Mathur Signed-off-by: Mark Brown diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 4bc5ea9..c2554d8 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3659,7 +3659,7 @@ regulator_register(const struct regulator_desc *regulator_desc, rdev->dev.class = ®ulator_class; rdev->dev.parent = dev; dev_set_name(&rdev->dev, "regulator.%lu", - atomic_inc_return(®ulator_no)); + (unsigned long) atomic_inc_return(®ulator_no)); ret = device_register(&rdev->dev); if (ret != 0) { put_device(&rdev->dev); -- cgit v0.10.2 From 0667dd5f6c203480f2e8dcb3fc039ef723453a2f Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Tue, 16 Dec 2014 12:21:55 +0200 Subject: spi: qup: Add SPI_CPOL configuration support Device support SPI_CPOL, but driver have missed to add support for this configuration. Signed-off-by: Ivan T. Ivanov Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index e7fb5a0..ff9cdbd 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -337,7 +337,7 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) { struct spi_qup *controller = spi_master_get_devdata(spi->master); - u32 config, iomode, mode; + u32 config, iomode, mode, control; int ret, n_words, w_size; if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { @@ -392,6 +392,15 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) writel_relaxed(iomode, controller->base + QUP_IO_M_MODES); + control = readl_relaxed(controller->base + SPI_IO_CONTROL); + + if (spi->mode & SPI_CPOL) + control |= SPI_IO_C_CLK_IDLE_HIGH; + else + control &= ~SPI_IO_C_CLK_IDLE_HIGH; + + writel_relaxed(control, controller->base + SPI_IO_CONTROL); + config = readl_relaxed(controller->base + SPI_CONFIG); if (spi->mode & SPI_LOOP) -- cgit v0.10.2 From 05be1d079ec0b3691783e4384b1ada82149ff7d2 Mon Sep 17 00:00:00 2001 From: Mathias Gottschlag Date: Mon, 29 Dec 2014 09:26:35 -0800 Subject: Input: psmouse - support for the FocalTech PS/2 protocol extensions Most of the protocol for these touchpads has been reverse engineered. This commit adds a basic multitouch-capable driver. A lot of the protocol is still unknown. Especially, we don't know how to identify the device yet apart from the PNP ID. The previous workaround for these devices has been left in place in case the driver is not compiled into the kernel or in case some other device with the same PNP ID is not recognized by the driver yet still has the same problems with the device probing code. Signed-off-by: Mathias Gottschlag Reviewed-by: Hans de Goede Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index d8b46b0..2541bfa 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -146,6 +146,16 @@ config MOUSE_PS2_OLPC If unsure, say N. +config MOUSE_PS2_FOCALTECH + bool "FocalTech PS/2 mouse protocol extension" if EXPERT + default y + depends on MOUSE_PS2 + help + Say Y here if you have a FocalTech PS/2 TouchPad connected to + your system. + + If unsure, say Y. + config MOUSE_SERIAL tristate "Serial mouse" select SERIO diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c index f4d657e..fca38ba 100644 --- a/drivers/input/mouse/focaltech.c +++ b/drivers/input/mouse/focaltech.c @@ -2,6 +2,7 @@ * Focaltech TouchPad PS/2 mouse driver * * Copyright (c) 2014 Red Hat Inc. + * Copyright (c) 2014 Mathias Gottschlag * * 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 @@ -13,15 +14,14 @@ * Hans de Goede */ -/* - * The Focaltech PS/2 touchpad protocol is unknown. This drivers deals with - * detection only, to avoid further detection attempts confusing the touchpad - * this way it at least works in PS/2 mouse compatibility mode. - */ #include #include +#include +#include +#include #include "psmouse.h" +#include "focaltech.h" static const char * const focaltech_pnp_ids[] = { "FLT0101", @@ -30,6 +30,12 @@ static const char * const focaltech_pnp_ids[] = { NULL }; +/* + * Even if the kernel is built without support for Focaltech PS/2 touchpads (or + * when the real driver fails to recognize the device), we still have to detect + * them in order to avoid further detection attempts confusing the touchpad. + * This way it at least works in PS/2 mouse compatibility mode. + */ int focaltech_detect(struct psmouse *psmouse, bool set_properties) { if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids)) @@ -37,16 +43,404 @@ int focaltech_detect(struct psmouse *psmouse, bool set_properties) if (set_properties) { psmouse->vendor = "FocalTech"; - psmouse->name = "FocalTech Touchpad in mouse emulation mode"; + psmouse->name = "FocalTech Touchpad"; } return 0; } -int focaltech_init(struct psmouse *psmouse) +static void focaltech_reset(struct psmouse *psmouse) { ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); psmouse_reset(psmouse); +} + +#ifdef CONFIG_MOUSE_PS2_FOCALTECH + +/* + * Packet types - the numbers are not consecutive, so we might be missing + * something here. + */ +#define FOC_TOUCH 0x3 /* bitmap of active fingers */ +#define FOC_ABS 0x6 /* absolute position of one finger */ +#define FOC_REL 0x9 /* relative position of 1-2 fingers */ + +#define FOC_MAX_FINGERS 5 + +#define FOC_MAX_X 2431 +#define FOC_MAX_Y 1663 + +/* + * Current state of a single finger on the touchpad. + */ +struct focaltech_finger_state { + /* The touchpad has generated a touch event for the finger */ + bool active; + + /* + * The touchpad has sent position data for the finger. The + * flag is 0 when the finger is not active, and there is a + * time between the first touch event for the finger and the + * following absolute position packet for the finger where the + * touchpad has declared the finger to be valid, but we do not + * have any valid position yet. + */ + bool valid; + + /* + * Absolute position (from the bottom left corner) of the + * finger. + */ + unsigned int x; + unsigned int y; +}; + +/* + * Description of the current state of the touchpad hardware. + */ +struct focaltech_hw_state { + /* + * The touchpad tracks the positions of the fingers for us, + * the array indices correspond to the finger indices returned + * in the report packages. + */ + struct focaltech_finger_state fingers[FOC_MAX_FINGERS]; + + /* True if the clickpad has been pressed. */ + bool pressed; +}; + +struct focaltech_data { + unsigned int x_max, y_max; + struct focaltech_hw_state state; +}; + +static void focaltech_report_state(struct psmouse *psmouse) +{ + struct focaltech_data *priv = psmouse->private; + struct focaltech_hw_state *state = &priv->state; + struct input_dev *dev = psmouse->dev; + int i; + + for (i = 0; i < FOC_MAX_FINGERS; i++) { + struct focaltech_finger_state *finger = &state->fingers[i]; + bool active = finger->active && finger->valid; + + input_mt_slot(dev, i); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); + if (active) { + input_report_abs(dev, ABS_MT_POSITION_X, finger->x); + input_report_abs(dev, ABS_MT_POSITION_Y, + FOC_MAX_Y - finger->y); + } + } + input_mt_report_pointer_emulation(dev, true); + + input_report_key(psmouse->dev, BTN_LEFT, state->pressed); + input_sync(psmouse->dev); +} + +static void focaltech_process_touch_packet(struct psmouse *psmouse, + unsigned char *packet) +{ + struct focaltech_data *priv = psmouse->private; + struct focaltech_hw_state *state = &priv->state; + unsigned char fingers = packet[1]; + int i; + + state->pressed = (packet[0] >> 4) & 1; + + /* the second byte contains a bitmap of all fingers touching the pad */ + for (i = 0; i < FOC_MAX_FINGERS; i++) { + state->fingers[i].active = fingers & 0x1; + if (!state->fingers[i].active) { + /* + * Even when the finger becomes active again, we still + * will have to wait for the first valid position. + */ + state->fingers[i].valid = false; + } + fingers >>= 1; + } +} + +static void focaltech_process_abs_packet(struct psmouse *psmouse, + unsigned char *packet) +{ + struct focaltech_data *priv = psmouse->private; + struct focaltech_hw_state *state = &priv->state; + unsigned int finger; + + finger = (packet[1] >> 4) - 1; + if (finger >= FOC_MAX_FINGERS) { + psmouse_err(psmouse, "Invalid finger in abs packet: %d\n", + finger); + return; + } + + state->pressed = (packet[0] >> 4) & 1; + + /* + * packet[5] contains some kind of tool size in the most + * significant nibble. 0xff is a special value (latching) that + * signals a large contact area. + */ + if (packet[5] == 0xff) { + state->fingers[finger].valid = false; + return; + } + + state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2]; + state->fingers[finger].y = (packet[3] << 8) | packet[4]; + state->fingers[finger].valid = true; +} + +static void focaltech_process_rel_packet(struct psmouse *psmouse, + unsigned char *packet) +{ + struct focaltech_data *priv = psmouse->private; + struct focaltech_hw_state *state = &priv->state; + int finger1, finger2; + + state->pressed = packet[0] >> 7; + finger1 = ((packet[0] >> 4) & 0x7) - 1; + if (finger1 < FOC_MAX_FINGERS) { + state->fingers[finger1].x += (char)packet[1]; + state->fingers[finger1].y += (char)packet[2]; + } else { + psmouse_err(psmouse, "First finger in rel packet invalid: %d\n", + finger1); + } + + /* + * If there is an odd number of fingers, the last relative + * packet only contains one finger. In this case, the second + * finger index in the packet is 0 (we subtract 1 in the lines + * above to create array indices, so the finger will overflow + * and be above FOC_MAX_FINGERS). + */ + finger2 = ((packet[3] >> 4) & 0x7) - 1; + if (finger2 < FOC_MAX_FINGERS) { + state->fingers[finger2].x += (char)packet[4]; + state->fingers[finger2].y += (char)packet[5]; + } +} + +static void focaltech_process_packet(struct psmouse *psmouse) +{ + unsigned char *packet = psmouse->packet; + + switch (packet[0] & 0xf) { + case FOC_TOUCH: + focaltech_process_touch_packet(psmouse, packet); + break; + + case FOC_ABS: + focaltech_process_abs_packet(psmouse, packet); + break; + + case FOC_REL: + focaltech_process_rel_packet(psmouse, packet); + break; + + default: + psmouse_err(psmouse, "Unknown packet type: %02x\n", packet[0]); + break; + } + + focaltech_report_state(psmouse); +} + +static psmouse_ret_t focaltech_process_byte(struct psmouse *psmouse) +{ + if (psmouse->pktcnt >= 6) { /* Full packet received */ + focaltech_process_packet(psmouse); + return PSMOUSE_FULL_PACKET; + } + + /* + * We might want to do some validation of the data here, but + * we do not know the protocol well enough + */ + return PSMOUSE_GOOD_DATA; +} + +static int focaltech_switch_protocol(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[3]; + + param[0] = 0; + if (ps2_command(ps2dev, param, 0x10f8)) + return -EIO; + + if (ps2_command(ps2dev, param, 0x10f8)) + return -EIO; + + if (ps2_command(ps2dev, param, 0x10f8)) + return -EIO; + + param[0] = 1; + if (ps2_command(ps2dev, param, 0x10f8)) + return -EIO; + + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11)) + return -EIO; + + if (ps2_command(ps2dev, param, PSMOUSE_CMD_ENABLE)) + return -EIO; + + return 0; +} + +static void focaltech_disconnect(struct psmouse *psmouse) +{ + focaltech_reset(psmouse); + kfree(psmouse->private); + psmouse->private = NULL; +} + +static int focaltech_reconnect(struct psmouse *psmouse) +{ + int error; + + focaltech_reset(psmouse); + + error = focaltech_switch_protocol(psmouse); + if (error) { + psmouse_err(psmouse, "Unable to initialize the device\n"); + return error; + } + + return 0; +} + +static void focaltech_set_input_params(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + struct focaltech_data *priv = psmouse->private; + + /* + * Undo part of setup done for us by psmouse core since touchpad + * is not a relative device. + */ + __clear_bit(EV_REL, dev->evbit); + __clear_bit(REL_X, dev->relbit); + __clear_bit(REL_Y, dev->relbit); + __clear_bit(BTN_RIGHT, dev->keybit); + __clear_bit(BTN_MIDDLE, dev->keybit); + + /* + * Now set up our capabilities. + */ + __set_bit(EV_ABS, dev->evbit); + input_set_abs_params(dev, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0); + input_mt_init_slots(dev, 5, INPUT_MT_POINTER); + __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); +} + +static int focaltech_read_register(struct ps2dev *ps2dev, int reg, + unsigned char *param) +{ + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11)) + return -EIO; + + param[0] = 0; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -EIO; + + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -EIO; + + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -EIO; + + param[0] = reg; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -EIO; + + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + return -EIO; + + return 0; +} + +static int focaltech_read_size(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + struct focaltech_data *priv = psmouse->private; + char param[3]; + + if (focaltech_read_register(ps2dev, 2, param)) + return -EIO; + + /* not sure whether this is 100% correct */ + priv->x_max = (unsigned char)param[1] * 128; + priv->y_max = (unsigned char)param[2] * 128; + + return 0; +} +int focaltech_init(struct psmouse *psmouse) +{ + struct focaltech_data *priv; + int error; + + psmouse->private = priv = kzalloc(sizeof(struct focaltech_data), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + focaltech_reset(psmouse); + + error = focaltech_read_size(psmouse); + if (error) { + psmouse_err(psmouse, + "Unable to read the size of the touchpad\n"); + goto fail; + } + + error = focaltech_switch_protocol(psmouse); + if (error) { + psmouse_err(psmouse, "Unable to initialize the device\n"); + goto fail; + } + + focaltech_set_input_params(psmouse); + + psmouse->protocol_handler = focaltech_process_byte; + psmouse->pktsize = 6; + psmouse->disconnect = focaltech_disconnect; + psmouse->reconnect = focaltech_reconnect; + psmouse->cleanup = focaltech_reset; + /* resync is not supported yet */ + psmouse->resync_time = 0; return 0; + +fail: + focaltech_reset(psmouse); + kfree(priv); + return error; } + +bool focaltech_supported(void) +{ + return true; +} + +#else /* CONFIG_MOUSE_PS2_FOCALTECH */ + +int focaltech_init(struct psmouse *psmouse) +{ + focaltech_reset(psmouse); + + return 0; +} + +bool focaltech_supported(void) +{ + return false; +} + +#endif /* CONFIG_MOUSE_PS2_FOCALTECH */ diff --git a/drivers/input/mouse/focaltech.h b/drivers/input/mouse/focaltech.h index 498650c..71870a9 100644 --- a/drivers/input/mouse/focaltech.h +++ b/drivers/input/mouse/focaltech.h @@ -2,6 +2,7 @@ * Focaltech TouchPad PS/2 mouse driver * * Copyright (c) 2014 Red Hat Inc. + * Copyright (c) 2014 Mathias Gottschlag * * 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 @@ -18,5 +19,6 @@ int focaltech_detect(struct psmouse *psmouse, bool set_properties); int focaltech_init(struct psmouse *psmouse); +bool focaltech_supported(void); #endif diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 26994f6..4a9de33 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -725,16 +725,19 @@ static int psmouse_extensions(struct psmouse *psmouse, /* Always check for focaltech, this is safe as it uses pnp-id matching */ if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) { - if (!set_properties || focaltech_init(psmouse) == 0) { - /* - * Not supported yet, use bare protocol. - * Note that we need to also restrict - * psmouse_max_proto so that psmouse_initialize() - * does not try to reset rate and resolution, - * because even that upsets the device. - */ - psmouse_max_proto = PSMOUSE_PS2; - return PSMOUSE_PS2; + if (max_proto > PSMOUSE_IMEX) { + if (!set_properties || focaltech_init(psmouse) == 0) { + if (focaltech_supported()) + return PSMOUSE_FOCALTECH; + /* + * Note that we need to also restrict + * psmouse_max_proto so that psmouse_initialize() + * does not try to reset rate and resolution, + * because even that upsets the device. + */ + psmouse_max_proto = PSMOUSE_PS2; + return PSMOUSE_PS2; + } } } @@ -1063,6 +1066,15 @@ static const struct psmouse_protocol psmouse_protocols[] = { .alias = "cortps", .detect = cortron_detect, }, +#ifdef CONFIG_MOUSE_PS2_FOCALTECH + { + .type = PSMOUSE_FOCALTECH, + .name = "FocalTechPS/2", + .alias = "focaltech", + .detect = focaltech_detect, + .init = focaltech_init, + }, +#endif { .type = PSMOUSE_AUTO, .name = "auto", diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index f4cf664..c2ff137 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -96,6 +96,7 @@ enum psmouse_type { PSMOUSE_FSP, PSMOUSE_SYNAPTICS_RELATIVE, PSMOUSE_CYPRESS, + PSMOUSE_FOCALTECH, PSMOUSE_AUTO /* This one should always be last */ }; -- cgit v0.10.2 From f361a2febfdad7c91de75a69ca1855f170485604 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Mon, 29 Dec 2014 10:32:24 -0800 Subject: Input: elants_i2c - remove unnecessary version.h inclusion Based on versioncheck. Signed-off-by: Fabian Frederick Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index a510f7e..926c58e 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -33,10 +33,8 @@ #include #include #include -#include #include #include -#include #include #include #include -- cgit v0.10.2 From e9e8520f229bd6881b51d03c010df6c0312bfef8 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 29 Dec 2014 14:15:24 -0800 Subject: Input: synaptics - use in-kernel tracking for reporting mt data The current code tries to consider all states and transitions to properly detect which finger is attached to which slot. The code is quite huge and difficult to read. If the sensor manages to group the touch points but is not reliable in giving tracking ids, we can simply use the kernel tracking method. Note that it is already used by Cr-48 Chromebooks. Incidentaly, this fixes a bug reported by Peter Hutterer: """ on the Lenovo T440, run: evemu-record /dev/input/event4 | grep BTN_ then put one, two, three, two fingers down when you go from 3 to 2 fingers the driver sends a spurious BTN_TOUCH 0 event: E: 0.000000 0001 014a 0001 # EV_KEY / BTN_TOUCH 1 E: 0.000000 0001 0145 0001 # EV_KEY / BTN_TOOL_FINGER 1 E: 0.770008 0001 0145 0000 # EV_KEY / BTN_TOOL_FINGER 0 E: 0.770008 0001 014d 0001 # EV_KEY / BTN_TOOL_DOUBLETAP 1 E: 1.924716 0001 014d 0000 # EV_KEY / BTN_TOOL_DOUBLETAP 0 E: 1.924716 0001 014e 0001 # EV_KEY / BTN_TOOL_TRIPLETAP 1 .. changing from 3 to 2 fingers now E: 3.152641 0001 014a 0000 # EV_KEY / BTN_TOUCH 0 E: 3.152641 0001 014d 0001 # EV_KEY / BTN_TOOL_DOUBLETAP 1 E: 3.152641 0001 014e 0000 # EV_KEY / BTN_TOOL_TRIPLETAP 0 E: 3.176948 0001 014a 0001 # EV_KEY / BTN_TOUCH 1 quick look in the kernel shows it's caused by hw.z going to 0 for a packet, so probably a firmware bug. either way, it makes it hard to track BTN_TOUCH as signal that at least one finger is down. """ The in-kernel tracking is enough to remove this spurious BTN_TOUCH 0. Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 9031a0a..fd89249 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -569,14 +569,6 @@ static void synaptics_pt_create(struct psmouse *psmouse) * Functions to interpret the absolute mode packets ****************************************************************************/ -static void synaptics_mt_state_set(struct synaptics_mt_state *state, int count, - int sgm, int agm) -{ - state->count = count; - state->sgm = sgm; - state->agm = agm; -} - static void synaptics_parse_agm(const unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw) @@ -595,16 +587,13 @@ static void synaptics_parse_agm(const unsigned char buf[], break; case 2: - /* AGM-CONTACT packet: (count, sgm, agm) */ - synaptics_mt_state_set(&agm->mt_state, buf[1], buf[2], buf[4]); + /* AGM-CONTACT packet: we are only interested in the count */ + priv->agm_count = buf[1]; break; default: break; } - - /* Record that at least one AGM has been received since last SGM */ - priv->agm_pending = true; } static bool is_forcepad; @@ -798,388 +787,68 @@ static void synaptics_report_buttons(struct psmouse *psmouse, input_report_key(dev, BTN_0 + i, hw->ext_buttons & (1 << i)); } -static void synaptics_report_slot(struct input_dev *dev, int slot, - const struct synaptics_hw_state *hw) -{ - input_mt_slot(dev, slot); - input_mt_report_slot_state(dev, MT_TOOL_FINGER, (hw != NULL)); - if (!hw) - return; - - input_report_abs(dev, ABS_MT_POSITION_X, hw->x); - input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(hw->y)); - input_report_abs(dev, ABS_MT_PRESSURE, hw->z); -} - static void synaptics_report_mt_data(struct psmouse *psmouse, - struct synaptics_mt_state *mt_state, - const struct synaptics_hw_state *sgm) + const struct synaptics_hw_state *sgm, + int num_fingers) { struct input_dev *dev = psmouse->dev; struct synaptics_data *priv = psmouse->private; - struct synaptics_hw_state *agm = &priv->agm; - struct synaptics_mt_state *old = &priv->mt_state; + const struct synaptics_hw_state *hw[2] = { sgm, &priv->agm }; + struct input_mt_pos pos[2]; + int slot[2], nsemi, i; - switch (mt_state->count) { - case 0: - synaptics_report_slot(dev, 0, NULL); - synaptics_report_slot(dev, 1, NULL); - break; - case 1: - if (mt_state->sgm == -1) { - synaptics_report_slot(dev, 0, NULL); - synaptics_report_slot(dev, 1, NULL); - } else if (mt_state->sgm == 0) { - synaptics_report_slot(dev, 0, sgm); - synaptics_report_slot(dev, 1, NULL); - } else { - synaptics_report_slot(dev, 0, NULL); - synaptics_report_slot(dev, 1, sgm); - } - break; - default: - /* - * If the finger slot contained in SGM is valid, and either - * hasn't changed, or is new, or the old SGM has now moved to - * AGM, then report SGM in MTB slot 0. - * Otherwise, empty MTB slot 0. - */ - if (mt_state->sgm != -1 && - (mt_state->sgm == old->sgm || - old->sgm == -1 || mt_state->agm == old->sgm)) - synaptics_report_slot(dev, 0, sgm); - else - synaptics_report_slot(dev, 0, NULL); + nsemi = clamp_val(num_fingers, 0, 2); - /* - * If the finger slot contained in AGM is valid, and either - * hasn't changed, or is new, then report AGM in MTB slot 1. - * Otherwise, empty MTB slot 1. - * - * However, in the case where the AGM is new, make sure that - * that it is either the same as the old SGM, or there was no - * SGM. - * - * Otherwise, if the SGM was just 1, and the new AGM is 2, then - * the new AGM will keep the old SGM's tracking ID, which can - * cause apparent drumroll. This happens if in the following - * valid finger sequence: - * - * Action SGM AGM (MTB slot:Contact) - * 1. Touch contact 0 (0:0) - * 2. Touch contact 1 (0:0, 1:1) - * 3. Lift contact 0 (1:1) - * 4. Touch contacts 2,3 (0:2, 1:3) - * - * In step 4, contact 3, in AGM must not be given the same - * tracking ID as contact 1 had in step 3. To avoid this, - * the first agm with contact 3 is dropped and slot 1 is - * invalidated (tracking ID = -1). - */ - if (mt_state->agm != -1 && - (mt_state->agm == old->agm || - (old->agm == -1 && - (old->sgm == -1 || mt_state->agm == old->sgm)))) - synaptics_report_slot(dev, 1, agm); - else - synaptics_report_slot(dev, 1, NULL); - break; + for (i = 0; i < nsemi; i++) { + pos[i].x = hw[i]->x; + pos[i].y = synaptics_invert_y(hw[i]->y); } + input_mt_assign_slots(dev, slot, pos, nsemi); + + for (i = 0; i < nsemi; i++) { + input_mt_slot(dev, slot[i]); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); + input_report_abs(dev, ABS_MT_POSITION_X, pos[i].x); + input_report_abs(dev, ABS_MT_POSITION_Y, pos[i].y); + input_report_abs(dev, ABS_MT_PRESSURE, hw[i]->z); + } + + input_mt_drop_unused(dev); + /* Don't use active slot count to generate BTN_TOOL events. */ input_mt_report_pointer_emulation(dev, false); /* Send the number of fingers reported by touchpad itself. */ - input_mt_report_finger_count(dev, mt_state->count); + input_mt_report_finger_count(dev, num_fingers); synaptics_report_buttons(psmouse, sgm); input_sync(dev); } -/* Handle case where mt_state->count = 0 */ -static void synaptics_image_sensor_0f(struct synaptics_data *priv, - struct synaptics_mt_state *mt_state) -{ - synaptics_mt_state_set(mt_state, 0, -1, -1); - priv->mt_state_lost = false; -} - -/* Handle case where mt_state->count = 1 */ -static void synaptics_image_sensor_1f(struct synaptics_data *priv, - struct synaptics_mt_state *mt_state) -{ - struct synaptics_hw_state *agm = &priv->agm; - struct synaptics_mt_state *old = &priv->mt_state; - - /* - * If the last AGM was (0,0,0), and there is only one finger left, - * then we absolutely know that SGM contains slot 0, and all other - * fingers have been removed. - */ - if (priv->agm_pending && agm->z == 0) { - synaptics_mt_state_set(mt_state, 1, 0, -1); - priv->mt_state_lost = false; - return; - } - - switch (old->count) { - case 0: - synaptics_mt_state_set(mt_state, 1, 0, -1); - break; - case 1: - /* - * If mt_state_lost, then the previous transition was 3->1, - * and SGM now contains either slot 0 or 1, but we don't know - * which. So, we just assume that the SGM now contains slot 1. - * - * If pending AGM and either: - * (a) the previous SGM slot contains slot 0, or - * (b) there was no SGM slot - * then, the SGM now contains slot 1 - * - * Case (a) happens with very rapid "drum roll" gestures, where - * slot 0 finger is lifted and a new slot 1 finger touches - * within one reporting interval. - * - * Case (b) happens if initially two or more fingers tap - * briefly, and all but one lift before the end of the first - * reporting interval. - * - * (In both these cases, slot 0 will becomes empty, so SGM - * contains slot 1 with the new finger) - * - * Else, if there was no previous SGM, it now contains slot 0. - * - * Otherwise, SGM still contains the same slot. - */ - if (priv->mt_state_lost || - (priv->agm_pending && old->sgm <= 0)) - synaptics_mt_state_set(mt_state, 1, 1, -1); - else if (old->sgm == -1) - synaptics_mt_state_set(mt_state, 1, 0, -1); - break; - case 2: - /* - * If mt_state_lost, we don't know which finger SGM contains. - * - * So, report 1 finger, but with both slots empty. - * We will use slot 1 on subsequent 1->1 - */ - if (priv->mt_state_lost) { - synaptics_mt_state_set(mt_state, 1, -1, -1); - break; - } - /* - * Since the last AGM was NOT (0,0,0), it was the finger in - * slot 0 that has been removed. - * So, SGM now contains previous AGM's slot, and AGM is now - * empty. - */ - synaptics_mt_state_set(mt_state, 1, old->agm, -1); - break; - case 3: - /* - * Since last AGM was not (0,0,0), we don't know which finger - * is left. - * - * So, report 1 finger, but with both slots empty. - * We will use slot 1 on subsequent 1->1 - */ - synaptics_mt_state_set(mt_state, 1, -1, -1); - priv->mt_state_lost = true; - break; - case 4: - case 5: - /* mt_state was updated by AGM-CONTACT packet */ - break; - } -} - -/* Handle case where mt_state->count = 2 */ -static void synaptics_image_sensor_2f(struct synaptics_data *priv, - struct synaptics_mt_state *mt_state) -{ - struct synaptics_mt_state *old = &priv->mt_state; - - switch (old->count) { - case 0: - synaptics_mt_state_set(mt_state, 2, 0, 1); - break; - case 1: - /* - * If previous SGM contained slot 1 or higher, SGM now contains - * slot 0 (the newly touching finger) and AGM contains SGM's - * previous slot. - * - * Otherwise, SGM still contains slot 0 and AGM now contains - * slot 1. - */ - if (old->sgm >= 1) - synaptics_mt_state_set(mt_state, 2, 0, old->sgm); - else - synaptics_mt_state_set(mt_state, 2, 0, 1); - break; - case 2: - /* - * If mt_state_lost, SGM now contains either finger 1 or 2, but - * we don't know which. - * So, we just assume that the SGM contains slot 0 and AGM 1. - */ - if (priv->mt_state_lost) - synaptics_mt_state_set(mt_state, 2, 0, 1); - /* - * Otherwise, use the same mt_state, since it either hasn't - * changed, or was updated by a recently received AGM-CONTACT - * packet. - */ - break; - case 3: - /* - * 3->2 transitions have two unsolvable problems: - * 1) no indication is given which finger was removed - * 2) no way to tell if agm packet was for finger 3 - * before 3->2, or finger 2 after 3->2. - * - * So, report 2 fingers, but empty all slots. - * We will guess slots [0,1] on subsequent 2->2. - */ - synaptics_mt_state_set(mt_state, 2, -1, -1); - priv->mt_state_lost = true; - break; - case 4: - case 5: - /* mt_state was updated by AGM-CONTACT packet */ - break; - } -} - -/* Handle case where mt_state->count = 3 */ -static void synaptics_image_sensor_3f(struct synaptics_data *priv, - struct synaptics_mt_state *mt_state) -{ - struct synaptics_mt_state *old = &priv->mt_state; - - switch (old->count) { - case 0: - synaptics_mt_state_set(mt_state, 3, 0, 2); - break; - case 1: - /* - * If previous SGM contained slot 2 or higher, SGM now contains - * slot 0 (one of the newly touching fingers) and AGM contains - * SGM's previous slot. - * - * Otherwise, SGM now contains slot 0 and AGM contains slot 2. - */ - if (old->sgm >= 2) - synaptics_mt_state_set(mt_state, 3, 0, old->sgm); - else - synaptics_mt_state_set(mt_state, 3, 0, 2); - break; - case 2: - /* - * If the AGM previously contained slot 3 or higher, then the - * newly touching finger is in the lowest available slot. - * - * If SGM was previously 1 or higher, then the new SGM is - * now slot 0 (with a new finger), otherwise, the new finger - * is now in a hidden slot between 0 and AGM's slot. - * - * In all such cases, the SGM now contains slot 0, and the AGM - * continues to contain the same slot as before. - */ - if (old->agm >= 3) { - synaptics_mt_state_set(mt_state, 3, 0, old->agm); - break; - } - - /* - * After some 3->1 and all 3->2 transitions, we lose track - * of which slot is reported by SGM and AGM. - * - * For 2->3 in this state, report 3 fingers, but empty all - * slots, and we will guess (0,2) on a subsequent 0->3. - * - * To userspace, the resulting transition will look like: - * 2:[0,1] -> 3:[-1,-1] -> 3:[0,2] - */ - if (priv->mt_state_lost) { - synaptics_mt_state_set(mt_state, 3, -1, -1); - break; - } - - /* - * If the (SGM,AGM) really previously contained slots (0, 1), - * then we cannot know what slot was just reported by the AGM, - * because the 2->3 transition can occur either before or after - * the AGM packet. Thus, this most recent AGM could contain - * either the same old slot 1 or the new slot 2. - * Subsequent AGMs will be reporting slot 2. - * - * To userspace, the resulting transition will look like: - * 2:[0,1] -> 3:[0,-1] -> 3:[0,2] - */ - synaptics_mt_state_set(mt_state, 3, 0, -1); - break; - case 3: - /* - * If, for whatever reason, the previous agm was invalid, - * Assume SGM now contains slot 0, AGM now contains slot 2. - */ - if (old->agm <= 2) - synaptics_mt_state_set(mt_state, 3, 0, 2); - /* - * mt_state either hasn't changed, or was updated by a recently - * received AGM-CONTACT packet. - */ - break; - - case 4: - case 5: - /* mt_state was updated by AGM-CONTACT packet */ - break; - } -} - -/* Handle case where mt_state->count = 4, or = 5 */ -static void synaptics_image_sensor_45f(struct synaptics_data *priv, - struct synaptics_mt_state *mt_state) -{ - /* mt_state was updated correctly by AGM-CONTACT packet */ - priv->mt_state_lost = false; -} - static void synaptics_image_sensor_process(struct psmouse *psmouse, struct synaptics_hw_state *sgm) { struct synaptics_data *priv = psmouse->private; - struct synaptics_hw_state *agm = &priv->agm; - struct synaptics_mt_state mt_state; - - /* Initialize using current mt_state (as updated by last agm) */ - mt_state = agm->mt_state; + int num_fingers; /* * Update mt_state using the new finger count and current mt_state. */ if (sgm->z == 0) - synaptics_image_sensor_0f(priv, &mt_state); + num_fingers = 0; else if (sgm->w >= 4) - synaptics_image_sensor_1f(priv, &mt_state); + num_fingers = 1; else if (sgm->w == 0) - synaptics_image_sensor_2f(priv, &mt_state); - else if (sgm->w == 1 && mt_state.count <= 3) - synaptics_image_sensor_3f(priv, &mt_state); + num_fingers = 2; + else if (sgm->w == 1) + num_fingers = priv->agm_count ? priv->agm_count : 3; else - synaptics_image_sensor_45f(priv, &mt_state); + num_fingers = 4; /* Send resulting input events to user space */ - synaptics_report_mt_data(psmouse, &mt_state, sgm); - - /* Store updated mt_state */ - priv->mt_state = agm->mt_state = mt_state; - priv->agm_pending = false; + synaptics_report_mt_data(psmouse, sgm, num_fingers); } static void synaptics_profile_sensor_process(struct psmouse *psmouse, @@ -1439,7 +1108,7 @@ static void set_input_params(struct psmouse *psmouse, ABS_MT_POSITION_Y); /* Image sensors can report per-contact pressure */ input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); - input_mt_init_slots(dev, 2, INPUT_MT_POINTER); + input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK); /* Image sensors can signal 4 and 5 finger clicks */ __set_bit(BTN_TOOL_QUADTAP, dev->keybit); diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index 1bd01f2..6faf9bb 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -119,16 +119,6 @@ #define SYN_REDUCED_FILTER_FUZZ 8 /* - * A structure to describe which internal touchpad finger slots are being - * reported in raw packets. - */ -struct synaptics_mt_state { - int count; /* num fingers being tracked */ - int sgm; /* which slot is reported by sgm pkt */ - int agm; /* which slot is reported by agm pkt*/ -}; - -/* * A structure to describe the state of the touchpad hardware (buttons and pad) */ struct synaptics_hw_state { @@ -143,9 +133,6 @@ struct synaptics_hw_state { unsigned int down:1; unsigned char ext_buttons; signed char scroll; - - /* As reported in last AGM-CONTACT packets */ - struct synaptics_mt_state mt_state; }; struct synaptics_data { @@ -170,15 +157,12 @@ struct synaptics_data { struct serio *pt_port; /* Pass-through serio port */ - struct synaptics_mt_state mt_state; /* Current mt finger state */ - bool mt_state_lost; /* mt_state may be incorrect */ - /* * Last received Advanced Gesture Mode (AGM) packet. An AGM packet * contains position data for a second contact, at half resolution. */ struct synaptics_hw_state agm; - bool agm_pending; /* new AGM packet received */ + unsigned int agm_count; /* finger count reported by agm */ /* ForcePad handling */ unsigned long press_start; -- cgit v0.10.2 From aa104b1aaaa3bd132106daccf7fb10eed806d325 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 29 Dec 2014 14:17:44 -0800 Subject: Input: synaptics - remove duplicated code synaptics_profile_sensor_process() and synaptics_report_mt_data() now share the exact same code. Remove one implementation and rely on the other where it was used. Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index fd89249..8ae1841 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -851,42 +851,6 @@ static void synaptics_image_sensor_process(struct psmouse *psmouse, synaptics_report_mt_data(psmouse, sgm, num_fingers); } -static void synaptics_profile_sensor_process(struct psmouse *psmouse, - struct synaptics_hw_state *sgm, - int num_fingers) -{ - struct input_dev *dev = psmouse->dev; - struct synaptics_data *priv = psmouse->private; - struct synaptics_hw_state *hw[2] = { sgm, &priv->agm }; - struct input_mt_pos pos[2]; - int slot[2], nsemi, i; - - nsemi = clamp_val(num_fingers, 0, 2); - - for (i = 0; i < nsemi; i++) { - pos[i].x = hw[i]->x; - pos[i].y = synaptics_invert_y(hw[i]->y); - } - - input_mt_assign_slots(dev, slot, pos, nsemi); - - for (i = 0; i < nsemi; i++) { - input_mt_slot(dev, slot[i]); - input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); - input_report_abs(dev, ABS_MT_POSITION_X, pos[i].x); - input_report_abs(dev, ABS_MT_POSITION_Y, pos[i].y); - input_report_abs(dev, ABS_MT_PRESSURE, hw[i]->z); - } - - input_mt_drop_unused(dev); - input_mt_report_pointer_emulation(dev, false); - input_mt_report_finger_count(dev, num_fingers); - - synaptics_report_buttons(psmouse, sgm); - - input_sync(dev); -} - /* * called for each full received packet from the touchpad */ @@ -951,7 +915,7 @@ static void synaptics_process_packet(struct psmouse *psmouse) } if (cr48_profile_sensor) { - synaptics_profile_sensor_process(psmouse, &hw, num_fingers); + synaptics_report_mt_data(psmouse, &hw, num_fingers); return; } -- cgit v0.10.2 From 8ea8f3eb9c6aee87e23907fc263108a0f9091c71 Mon Sep 17 00:00:00 2001 From: Henrik Austad Date: Fri, 26 Dec 2014 09:26:22 +0100 Subject: Update of Documentation/00-INDEX Added files - hsi.txt was added by 3a8ab8af (HSI: Add some general description for the HSI subsystem) - lzo.txt was added by d98a0526 (lzo: document part of the encoding) - xillybus.txt was added by 7051924f (xillybus: Move out of staging) - mailbox.txt was added by 15320fbc (add documentation for mailbox framework) Moved files - xommit 214e0aed (Move locking related docs into Documentation/locking/): * lockdep-design.txt * lockstat.txt * mutex-design.txt * rt-mutex-design.txt * rt-mutex.txt * spinlocks.txt * ww-mutex-design.txt - kselftest.txt was moved by 3c415707 (kselftest: Move the docs to the Documentation dir) CC: Davidlohr Bueso CC: Willy Tarreau CC: Randy Dunlap CC: Greg Kroah-Hartman CC: Sebastian Reichel CC: Eli Billauer CC: Jonathan Corbet CC: Jiri Kosina CC: linux-doc@vger.kernel.org CC: linux-kernel@vger.kernel.org Cc: Tim Bird Cc: Shuah Khan Signed-off-by: Henrik Austad Signed-off-by: Jonathan Corbet diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX index 1750fce..0fb40ac 100644 --- a/Documentation/00-INDEX +++ b/Documentation/00-INDEX @@ -209,6 +209,8 @@ hid/ - directory with information on human interface devices highuid.txt - notes on the change from 16 bit to 32 bit user/group IDs. +hsi.txt + - HSI subsystem overview. hwspinlock.txt - hardware spinlock provides hardware assistance for synchronization timers/ @@ -277,6 +279,8 @@ kprobes.txt - documents the kernel probes debugging feature. kref.txt - docs on adding reference counters (krefs) to kernel objects. +kselftest.txt + - small unittests for (some) individual codepaths in the kernel. laptops/ - directory with laptop related info and laptop driver documentation. ldm.txt @@ -285,22 +289,22 @@ leds/ - directory with info about LED handling under Linux. local_ops.txt - semantics and behavior of local atomic operations. -lockdep-design.txt - - documentation on the runtime locking correctness validator. locking/ - directory with info about kernel locking primitives -lockstat.txt - - info on collecting statistics on locks (and contention). lockup-watchdogs.txt - info on soft and hard lockup detectors (aka nmi_watchdog). logo.gif - full colour GIF image of Linux logo (penguin - Tux). logo.txt - info on creator of above logo & site to get additional images from. +lzo.txt + - kernel LZO decompressor input formats m68k/ - directory with info about Linux on Motorola 68k architecture. magic-number.txt - list of magic numbers used to mark/protect kernel data structures. +mailbox.txt + - How to write drivers for the common mailbox framework (IPC). md.txt - info on boot arguments for the multiple devices driver. media-framework.txt @@ -327,8 +331,6 @@ mtd/ - directory with info about memory technology devices (flash) mono.txt - how to execute Mono-based .NET binaries with the help of BINFMT_MISC. -mutex-design.txt - - info on the generic mutex subsystem. namespaces/ - directory with various information about namespaces netlabel/ @@ -395,10 +397,6 @@ robust-futexes.txt - a description of what robust futexes are. rpmsg.txt - info on the Remote Processor Messaging (rpmsg) Framework -rt-mutex-design.txt - - description of the RealTime mutex implementation design. -rt-mutex.txt - - desc. of RT-mutex subsystem with PI (Priority Inheritance) support. rtc.txt - notes on how to use the Real Time Clock (aka CMOS clock) driver. s390/ @@ -425,8 +423,6 @@ sparse.txt - info on how to obtain and use the sparse tool for typechecking. spi/ - overview of Linux kernel Serial Peripheral Interface (SPI) support. -spinlocks.txt - - info on using spinlocks to provide exclusive access in kernel. stable_api_nonsense.txt - info on why the kernel does not have a stable in-kernel api or abi. stable_kernel_rules.txt @@ -483,10 +479,10 @@ wimax/ - directory with info about Intel Wireless Wimax Connections workqueue.txt - information on the Concurrency Managed Workqueue implementation -ww-mutex-design.txt - - Intro to Mutex wait/would deadlock handling.s x86/x86_64/ - directory with info on Linux support for AMD x86-64 (Hammer) machines. +xillybus.txt + - Overview and basic ui of xillybus driver xtensa/ - directory with documents relating to arch/xtensa port/implementation xz.txt diff --git a/Documentation/locking/00-INDEX b/Documentation/locking/00-INDEX new file mode 100644 index 0000000..c256c9b --- /dev/null +++ b/Documentation/locking/00-INDEX @@ -0,0 +1,16 @@ +00-INDEX + - this file. +lockdep-design.txt + - documentation on the runtime locking correctness validator. +lockstat.txt + - info on collecting statistics on locks (and contention). +mutex-design.txt + - info on the generic mutex subsystem. +rt-mutex-design.txt + - description of the RealTime mutex implementation design. +rt-mutex.txt + - desc. of RT-mutex subsystem with PI (Priority Inheritance) support. +spinlocks.txt + - info on using spinlocks to provide exclusive access in kernel. +ww-mutex-design.txt + - Intro to Mutex wait/would deadlock handling.s -- cgit v0.10.2 From 9007fd324163d3f082ff2898b9b0aec6ae3d872b Mon Sep 17 00:00:00 2001 From: Henrik Austad Date: Fri, 26 Dec 2014 09:26:23 +0100 Subject: Update of Documentation/networking/00-INDEX - altera_tse.txt was added by 04add4ab (Add Altera Ethernet (TSE) Documentation) - cdc_mbim.txt was added by a563babe (cdc_mbim: add driver documentation) - dctcp.txt was added by e3118e83 (tcp: add DCTCP congestion control algorithm) CC: Jonathan Corbet CC: "David S. Miller" CC: linux-doc@vger.kernel.org CC: linux-kernel@vger.kernel.org Signed-off-by: Henrik Austad Signed-off-by: Jonathan Corbet diff --git a/Documentation/networking/00-INDEX b/Documentation/networking/00-INDEX index 557b6ef..df27a1a 100644 --- a/Documentation/networking/00-INDEX +++ b/Documentation/networking/00-INDEX @@ -1,7 +1,5 @@ 00-INDEX - this file -3c505.txt - - information on the 3Com EtherLink Plus (3c505) driver. 3c509.txt - information on the 3Com Etherlink III Series Ethernet cards. 6pack.txt @@ -24,6 +22,8 @@ README.sb1000 - info on General Instrument/NextLevel SURFboard1000 cable modem. alias.txt - info on using alias network devices. +altera_tse.txt + - Altera Triple-Speed Ethernet controller. arcnet-hardware.txt - tons of info on ARCnet, hubs, jumper settings for ARCnet cards, etc. arcnet.txt @@ -42,6 +42,8 @@ bridge.txt - where to get user space programs for ethernet bridging with Linux. can.txt - documentation on CAN protocol family. +cdc_mbim.txt + - 3G/LTE USB modem (Mobile Broadband Interface Model) cops.txt - info on the COPS LocalTalk Linux driver cs89x0.txt @@ -54,6 +56,8 @@ cxgb.txt - Release Notes for the Chelsio N210 Linux device driver. dccp.txt - the Datagram Congestion Control Protocol (DCCP) (RFC 4340..42). +dctcp.txt + - DataCenter TCP congestion control de4x5.txt - the Digital EtherWORKS DE4?? and DE5?? PCI Ethernet driver decnet.txt -- cgit v0.10.2 From 5f6c3ac51db846290264325fa1ce2b5d65e8a803 Mon Sep 17 00:00:00 2001 From: Henrik Austad Date: Fri, 26 Dec 2014 09:26:24 +0100 Subject: Update of Documentation/arm/00-INDEX Added: - arm/Makefile was added by adb19fb6 (add makefiles for more targets) - arm/CCN.txt was added by a33b0daa (ARM CCN PMU driver) Removed: - arm/Sharp-LH was removed by 82e6923e (ARM: lh7a40x: remove unmaintained platform support) Not updated: Documentation/arm/msm/ is missing 00-INDEX (1 files) Documentation/arm/Samsung-S3C24XX/ is missing 00-INDEX (12 files) Documentation/arm/nwfpe/ is missing 00-INDEX (4 files) Documentation/arm/OMAP/ is missing 00-INDEX (2 files) Documentation/arm/sunxi/ is missing 00-INDEX (2 files) Documentation/arm/SPEAr/ is missing 00-INDEX (1 files) Documentation/arm/Marvell/ is missing 00-INDEX (1 files) Documentation/arm/SA1100/ is missing 00-INDEX (18 files) Documentation/arm/pxa/ is missing 00-INDEX (1 files) Documentation/arm/sti/ is missing 00-INDEX (4 files) Documentation/arm/SH-Mobile/ is missing 00-INDEX (4 files) Documentation/arm/VFP/ is missing 00-INDEX (1 files) Documentation/arm/Samsung/ is missing 00-INDEX (3 files) Cc: Jonathan Corbet Cc: Pawel Moll Cc: Jiri Kosina Cc: linux-doc@vger.kernel.org (open list:DOCUMENTATION) Cc: linux-kernel@vger.kernel.org (open list) Signed-off-by: Henrik Austad Signed-off-by: Jonathan Corbet diff --git a/Documentation/arm/00-INDEX b/Documentation/arm/00-INDEX index 3b08bc2..8edb900 100644 --- a/Documentation/arm/00-INDEX +++ b/Documentation/arm/00-INDEX @@ -2,11 +2,15 @@ - this file Booting - requirements for booting +CCN.txt + - Cache Coherent Network ring-bus and perf PMU driver. Interrupts - ARM Interrupt subsystem documentation IXP4xx - Intel IXP4xx Network processor. -msm +Makefile + - Build sourcefiles as part of the Documentation-build for arm +msm/ - MSM specific documentation Netwinder - Netwinder specific documentation @@ -18,11 +22,9 @@ README - General ARM documentation SA1100/ - SA1100 documentation -Samsung-S3C24XX +Samsung-S3C24XX/ - S3C24XX ARM Linux Overview -Sharp-LH - - Linux on Sharp LH79524 and LH7A40X System On a Chip (SOC) -SPEAr +SPEAr/ - ST SPEAr platform Linux Overview VFP/ - Release notes for Linux Kernel Vector Floating Point support code -- cgit v0.10.2 From dcf16713c9ebaf50e82ca702a950f0a3256f493f Mon Sep 17 00:00:00 2001 From: Henrik Austad Date: Fri, 26 Dec 2014 09:26:25 +0100 Subject: Update of Documentation/cgroups/00-INDEX unified-hierarchy.txt was added by 65731578 (cgroup: add documentation about unified hierarchy) Cc: Tejun Heo Cc: Li Zefan Cc: Jonathan Corbet Cc: cgroups@vger.kernel.org Cc: linux-doc@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Henrik Austad Signed-off-by: Jonathan Corbet diff --git a/Documentation/cgroups/00-INDEX b/Documentation/cgroups/00-INDEX index bc461b6..96ce071 100644 --- a/Documentation/cgroups/00-INDEX +++ b/Documentation/cgroups/00-INDEX @@ -24,3 +24,5 @@ net_prio.txt - Network priority cgroups details and usages. resource_counter.txt - Resource Counter API. +unified-hierarchy.txt + - Description the new/next cgroup interface. -- cgit v0.10.2 From 912ee9ca37afb517647af3744726a97f764f419f Mon Sep 17 00:00:00 2001 From: Henrik Austad Date: Fri, 26 Dec 2014 09:26:26 +0100 Subject: Update of Documentation/dmaengine/00-INDEX - client.txt was moved by f36d2e67 (dmaengine: Move the current doc to a folder of its own) - dmatmest.txt was moved by 935cdb56 (dmanegine: move dmatest.txt to dmaengine folder) - provider.txt was added by c4d2ae967 (Documentation: dmaengine: Add a documentation for the dma controller API). Cc: Maxime Ripard Cc: Vinod Koul Cc: Jonathan Corbet Cc: dmaengine@vger.kernel.org Cc: linux-doc@vger.kernel.org Signed-off-by: Henrik Austad Signed-off-by: Jonathan Corbet diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX index 0fb40ac..cd077ca 100644 --- a/Documentation/00-INDEX +++ b/Documentation/00-INDEX @@ -29,8 +29,6 @@ DMA-ISA-LPC.txt - How to do DMA with ISA (and LPC) devices. DMA-attributes.txt - listing of the various possible attributes a DMA region can have -dmatest.txt - - how to compile, configure and use the dmatest system. DocBook/ - directory with DocBook templates etc. for kernel documentation. EDID/ @@ -163,8 +161,6 @@ digsig.txt -info on the Digital Signature Verification API dma-buf-sharing.txt - the DMA Buffer Sharing API Guide -dmaengine.txt - -the DMA Engine API Guide dontdiff - file containing a list of files that should never be diff'ed. driver-model/ diff --git a/Documentation/dmaengine/00-INDEX b/Documentation/dmaengine/00-INDEX new file mode 100644 index 0000000..07de657 --- /dev/null +++ b/Documentation/dmaengine/00-INDEX @@ -0,0 +1,8 @@ +00-INDEX + - this file. +client.txt + -the DMA Engine API Guide. +dmatest.txt + - how to compile, configure and use the dmatest system. +provider.txt + - the DMA controller API. \ No newline at end of file -- cgit v0.10.2 From 2cd14f5da6579175f387f93c0561918647a3b6c6 Mon Sep 17 00:00:00 2001 From: Jeremiah Mahler Date: Fri, 26 Dec 2014 06:57:49 -0800 Subject: doc: driver-model: improve wording "is provide the" Improve the wording by changing it from "is provide the" to "is to give the". Signed-off-by: Jeremiah Mahler Signed-off-by: Jonathan Corbet diff --git a/Documentation/driver-model/bus.txt b/Documentation/driver-model/bus.txt index 6754b2d..b577a45 100644 --- a/Documentation/driver-model/bus.txt +++ b/Documentation/driver-model/bus.txt @@ -45,7 +45,7 @@ them are inherently bus-specific. Drivers typically declare an array of device IDs of devices they support that reside in a bus-specific driver structure. -The purpose of the match callback is provide the bus an opportunity to +The purpose of the match callback is to give the bus an opportunity to determine if a particular driver supports a particular device by comparing the device IDs the driver supports with the device ID of a particular device, without sacrificing bus-specific functionality or -- cgit v0.10.2 From bf5777bcdc540661f2f5d531a13e4e9c9fb7ee22 Mon Sep 17 00:00:00 2001 From: Kevin Date: Mon, 22 Dec 2014 20:00:36 -0500 Subject: Documentation: GNU is frequently spelled Gnu The official spelling of GNU is GNU and not Gnu. Bug 89551 https://bugzilla.kernel.org/show_bug.cgi?id=89551 Signed-off-by: Kevin Law Signed-off-by: Jonathan Corbet diff --git a/Documentation/Changes b/Documentation/Changes index 74bdda9..646cdaa 100644 --- a/Documentation/Changes +++ b/Documentation/Changes @@ -21,8 +21,8 @@ running a Linux kernel. Also, not all tools are necessary on all systems; obviously, if you don't have any ISDN hardware, for example, you probably needn't concern yourself with isdn4k-utils. -o Gnu C 3.2 # gcc --version -o Gnu make 3.80 # make --version +o GNU C 3.2 # gcc --version +o GNU make 3.80 # make --version o binutils 2.12 # ld -v o util-linux 2.10o # fdformat --version o module-init-tools 0.9.10 # depmod -V @@ -57,7 +57,7 @@ computer. Make ---- -You will need Gnu make 3.80 or later to build the kernel. +You will need GNU make 3.80 or later to build the kernel. Binutils -------- -- cgit v0.10.2 From 380945365d3824e7c62deb17930453acdcb65eaa Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Wed, 17 Oct 2012 20:29:22 +0400 Subject: Documentation: update seq_file Update descriptions of seq_path() and seq_path_root(): starting with commit v3.2-rc4-1-g02125a8, seq_path_root() no longer changes the value of root; starting with commit v3.2-rc7-104-g8c9379e, some arguments of seq_path() and seq_path_root() are const. Signed-off-by: Dmitry V. Levin Acked-by: Rob Landley Signed-off-by: Jonathan Corbet diff --git a/Documentation/filesystems/seq_file.txt b/Documentation/filesystems/seq_file.txt index b797ed3..9de4303 100644 --- a/Documentation/filesystems/seq_file.txt +++ b/Documentation/filesystems/seq_file.txt @@ -194,16 +194,16 @@ which is in the string esc will be represented in octal form in the output. There are also a pair of functions for printing filenames: - int seq_path(struct seq_file *m, struct path *path, char *esc); - int seq_path_root(struct seq_file *m, struct path *path, - struct path *root, char *esc) + int seq_path(struct seq_file *m, const struct path *path, + const char *esc); + int seq_path_root(struct seq_file *m, const struct path *path, + const struct path *root, const char *esc) Here, path indicates the file of interest, and esc is a set of characters which should be escaped in the output. A call to seq_path() will output the path relative to the current process's filesystem root. If a different -root is desired, it can be used with seq_path_root(). Note that, if it -turns out that path cannot be reached from root, the value of root will be -changed in seq_file_root() to a root which *does* work. +root is desired, it can be used with seq_path_root(). If it turns out that +path cannot be reached from root, seq_path_root() returns SEQ_SKIP. A function producing complicated output may want to check bool seq_has_overflowed(struct seq_file *m); -- cgit v0.10.2 From e71e2c6fbbac673f3e222c7a6b6b62bcf6f40fb1 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Mon, 29 Dec 2014 16:17:36 -0700 Subject: MAINTAINERS: Add the docs-next git tree to the maintainer entry Signed-off-by: Jonathan Corbet diff --git a/MAINTAINERS b/MAINTAINERS index ddb9ac8..76f9c0d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3207,6 +3207,7 @@ F: Documentation/ X: Documentation/ABI/ X: Documentation/devicetree/ X: Documentation/[a-z][a-z]_[A-Z][A-Z]/ +T: git git://git.lwn.net/linux-2.6.git docs-next DOUBLETALK DRIVER M: "James R. Van Zandt" -- cgit v0.10.2 From e00bfcbf043877fb85f77daf330a01e057dfcf3b Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Wed, 17 Dec 2014 17:13:56 -0800 Subject: Documentation/SubmittingPatches: unify whitespace/tabs for the DCO The Developers Certificate of Origin has a mixture of tabs and white spaces which is annoying to view if your editor explicitly views white space characters. Also remove any trailing white spaces found in the file. Signed-off-by: Stefan Beller Signed-off-by: Jonathan Corbet diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 1fa1caa..1671ce3 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -223,9 +223,9 @@ Do not send more than 15 patches at once to the vger mailing lists!!! Linus Torvalds is the final arbiter of all changes accepted into the -Linux kernel. His e-mail address is . +Linux kernel. His e-mail address is . He gets a lot of e-mail, so typically you should do your best to -avoid- -sending him e-mail. +sending him e-mail. Patches which are bug fixes, are "obvious" changes, or similarly require little discussion should be sent or CC'd to Linus. Patches @@ -387,11 +387,11 @@ can certify the below: person who certified (a), (b) or (c) and I have not modified it. - (d) I understand and agree that this project and the contribution - are public and that a record of the contribution (including all - personal information I submit with it, including my sign-off) is - maintained indefinitely and may be redistributed consistent with - this project or the open source license(s) involved. + (d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. then you just add a line saying @@ -401,7 +401,7 @@ using your real name (sorry, no pseudonyms or anonymous contributions.) Some people also put extra tags at the end. They'll just be ignored for now, but you can do this to mark internal company procedures or just -point out some special detail about the sign-off. +point out some special detail about the sign-off. If you are a subsystem or branch maintainer, sometimes you need to slightly modify patches you receive in order to merge them, because the code is not -- cgit v0.10.2 From 589a55b07dfb71d4c45e008a8df8bb94ff813fe6 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 22 Dec 2014 23:16:55 +0100 Subject: net: ieee802154: don't use devm_pinctrl_get_select_default() in probe Since commit ab78029ecc34 (drivers/pinctrl: grab default handles from device core), we can rely on device core for setting the default pins. Signed-off-by: Wolfram Sang Acked-by: Varka Bhadram Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c index c2b7da3..0334734 100644 --- a/drivers/net/ieee802154/cc2520.c +++ b/drivers/net/ieee802154/cc2520.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -841,7 +840,6 @@ done: static int cc2520_probe(struct spi_device *spi) { struct cc2520_private *priv; - struct pinctrl *pinctrl; struct cc2520_platform_data *pdata; int ret; @@ -854,11 +852,6 @@ static int cc2520_probe(struct spi_device *spi) spi_set_drvdata(spi, priv); - pinctrl = devm_pinctrl_get_select_default(&spi->dev); - if (IS_ERR(pinctrl)) - dev_warn(&spi->dev, - "pinctrl pins are not configured\n"); - pdata = cc2520_get_platform_data(spi); if (!pdata) { dev_err(&spi->dev, "no platform data\n"); -- cgit v0.10.2 From 5eb9f8caac333842e840325fe7299465bcdf2111 Mon Sep 17 00:00:00 2001 From: Varka Bhadram Date: Fri, 26 Dec 2014 10:13:38 +0530 Subject: cc2520: use devm_kzalloc(.., sizeof(*pointer), ..) pattern Signed-off-by: Varka Bhadram Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c index 0334734..bc24d9a 100644 --- a/drivers/net/ieee802154/cc2520.c +++ b/drivers/net/ieee802154/cc2520.c @@ -843,8 +843,7 @@ static int cc2520_probe(struct spi_device *spi) struct cc2520_platform_data *pdata; int ret; - priv = devm_kzalloc(&spi->dev, - sizeof(struct cc2520_private), GFP_KERNEL); + priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { ret = -ENOMEM; goto err_ret; -- cgit v0.10.2 From f50f1c37a663b7b76013638f60a400a16ddd2158 Mon Sep 17 00:00:00 2001 From: Varka Bhadram Date: Fri, 26 Dec 2014 10:13:39 +0530 Subject: cc2520: remove 'ret' goto label If allocation of memory fails instead of going to ret goto label and returning from there, we can directly return -ENOMEM on failure. Signed-off-by: Varka Bhadram Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c index bc24d9a..64017f1 100644 --- a/drivers/net/ieee802154/cc2520.c +++ b/drivers/net/ieee802154/cc2520.c @@ -844,10 +844,8 @@ static int cc2520_probe(struct spi_device *spi) int ret; priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - ret = -ENOMEM; - goto err_ret; - } + if (!priv) + return -ENOMEM; spi_set_drvdata(spi, priv); @@ -861,10 +859,8 @@ static int cc2520_probe(struct spi_device *spi) priv->buf = devm_kzalloc(&spi->dev, SPI_COMMAND_BUFFER, GFP_KERNEL); - if (!priv->buf) { - ret = -ENOMEM; - goto err_ret; - } + if (!priv->buf) + return -ENOMEM; mutex_init(&priv->buffer_mutex); INIT_WORK(&priv->fifop_irqwork, cc2520_fifop_irqwork); @@ -981,8 +977,6 @@ static int cc2520_probe(struct spi_device *spi) err_hw_init: mutex_destroy(&priv->buffer_mutex); flush_work(&priv->fifop_irqwork); - -err_ret: return ret; } -- cgit v0.10.2 From 7fc1b2d56f399f65e96559fe449c0ca09d2c36c7 Mon Sep 17 00:00:00 2001 From: Varka Bhadram Date: Fri, 26 Dec 2014 10:13:40 +0530 Subject: cc2520: fix zero perm_extended_addr address It will remove the bug of havine zero perm_extended_addr address. Signed-off-by: Varka Bhadram Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c index 64017f1..a43c8ac 100644 --- a/drivers/net/ieee802154/cc2520.c +++ b/drivers/net/ieee802154/cc2520.c @@ -650,6 +650,7 @@ static int cc2520_register(struct cc2520_private *priv) priv->hw->parent = &priv->spi->dev; priv->hw->extra_tx_headroom = 0; priv->hw->vif_data_size = sizeof(*priv); + ieee802154_random_extended_addr(&priv->hw->phy->perm_extended_addr); /* We do support only 2.4 Ghz */ priv->hw->phy->channels_supported[0] = 0x7FFF800; -- cgit v0.10.2 From 4da50de895588a0268b636314d7c1d08f18b20c9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 29 Dec 2014 12:04:10 +0200 Subject: Bluetooth: Fix const declarations for smp_f5 and smp_f6 These SMP crypto functions should have all their input parameters declared as const. This patch fixes the parameters that were missing the const declaration. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index b67749b..9025e17 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -223,8 +223,9 @@ static int smp_f4(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32], return err; } -static int smp_f5(struct crypto_hash *tfm_cmac, u8 w[32], u8 n1[16], u8 n2[16], - u8 a1[7], u8 a2[7], u8 mackey[16], u8 ltk[16]) +static int smp_f5(struct crypto_hash *tfm_cmac, const u8 w[32], + const u8 n1[16], const u8 n2[16], const u8 a1[7], + const u8 a2[7], u8 mackey[16], u8 ltk[16]) { /* The btle, salt and length "magic" values are as defined in * the SMP section of the Bluetooth core specification. In ASCII @@ -276,7 +277,7 @@ static int smp_f5(struct crypto_hash *tfm_cmac, u8 w[32], u8 n1[16], u8 n2[16], } static int smp_f6(struct crypto_hash *tfm_cmac, const u8 w[16], - const u8 n1[16], u8 n2[16], const u8 r[16], + const u8 n1[16], const u8 n2[16], const u8 r[16], const u8 io_cap[3], const u8 a1[7], const u8 a2[7], u8 res[16]) { -- cgit v0.10.2 From ee485290c6af942f0371def632c32e747d110b1e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 29 Dec 2014 20:48:35 -0800 Subject: Bluetooth: Add support for self testing framework This add support for the Bluetooth self testing framework that allows running certain test cases of sample data to ensure correctness of its basic functionality. With this patch only the basic framework will be added. It contains the build magic that allows running this at module loading time or at late_initcall stage when built into the kernel image. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 29bcafc..3296841 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -64,4 +64,17 @@ config BT_6LOWPAN help IPv6 compression over Bluetooth Low Energy. +config BT_SELFTEST + bool "Bluetooth self testing support" + depends on BT && DEBUG_KERNEL + help + Run self tests when initializing the Bluetooth subsystem. This + is a developer option and can cause significant delay when booting + the system. + + When the Bluetooth subsystem is built as module, then the test + cases are run first thing at module load time. When the Bluetooth + subsystem is compiled into the kernel image, then the test cases + are run late in the initcall hierarchy. + source "drivers/bluetooth/Kconfig" diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 364cff1..8e96e30 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -15,4 +15,6 @@ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \ a2mp.o amp.o ecc.o hci_request.o hci_debugfs.o +bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o + subdir-ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 012e3b0..ce22e0c 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -31,6 +31,8 @@ #include #include +#include "selftest.h" + #define VERSION "2.20" /* Bluetooth sockets */ @@ -716,6 +718,10 @@ static int __init bt_init(void) BT_INFO("Core ver %s", VERSION); + err = bt_selftest(); + if (err < 0) + return err; + bt_debugfs = debugfs_create_dir("bluetooth", NULL); err = bt_sysfs_init(); diff --git a/net/bluetooth/selftest.c b/net/bluetooth/selftest.c new file mode 100644 index 0000000..00cac4a --- /dev/null +++ b/net/bluetooth/selftest.c @@ -0,0 +1,62 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + + Copyright (C) 2014 Intel 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; + + 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 OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +#include + +#include "selftest.h" + +static int __init run_selftest(void) +{ + BT_INFO("Starting self testing"); + + BT_INFO("Finished self testing"); + + return 0; +} + +#if IS_MODULE(CONFIG_BT) + +/* This is run when CONFIG_BT_SELFTEST=y and CONFIG_BT=m and is just a + * wrapper to allow running this at module init. + * + * If CONFIG_BT_SELFTEST=n, then this code is not compiled at all. + */ +int __init bt_selftest(void) +{ + return run_selftest(); +} + +#else + +/* This is run when CONFIG_BT_SELFTEST=y and CONFIG_BT=y and is run + * via late_initcall() as last item in the initialization sequence. + * + * If CONFIG_BT_SELFTEST=n, then this code is not compiled at all. + */ +static int __init bt_selftest_init(void) +{ + return run_selftest(); +} +late_initcall(bt_selftest_init); + +#endif diff --git a/net/bluetooth/selftest.h b/net/bluetooth/selftest.h new file mode 100644 index 0000000..2aa0a34 --- /dev/null +++ b/net/bluetooth/selftest.h @@ -0,0 +1,45 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + Copyright (C) 2014 Intel 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; + + 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 OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +#if IS_ENABLED(CONFIG_BT_SELFTEST) && IS_MODULE(CONFIG_BT) + +/* When CONFIG_BT_SELFTEST=y and the CONFIG_BT=m, then the self testing + * is run at module loading time. + */ +int bt_selftest(void); + +#else + +/* When CONFIG_BT_SELFTEST=y and CONFIG_BT=y, then the self testing + * is run via late_initcall() to make sure that subsys_initcall() of + * the Bluetooth subsystem and device_initcall() of the Crypto subsystem + * do not clash. + * + * When CONFIG_BT_SELFTEST=n, then this turns into an empty call that + * has no impact. + */ +static inline int bt_selftest(void) +{ + return 0; +} + +#endif -- cgit v0.10.2 From 0b6415b65234ff723d32fd7dcdd917ba7ad5fe86 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 29 Dec 2014 20:48:36 -0800 Subject: Bluetooth: Add support for ECDH test cases This patch adds the test cases for ECDH cryptographic functionality used by Bluetooth Low Energy Secure Connections feature. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 3296841..8d3b607 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -77,4 +77,11 @@ config BT_SELFTEST subsystem is compiled into the kernel image, then the test cases are run late in the initcall hierarchy. +config BT_SELFTEST_ECDH + bool "ECDH test cases" + depends on BT_LE && BT_SELFTEST + help + Run test cases for ECDH cryptographic functionality used by the + Bluetooth Low Energy Secure Connections feature. + source "drivers/bluetooth/Kconfig" diff --git a/net/bluetooth/selftest.c b/net/bluetooth/selftest.c index 00cac4a..b9cb33c 100644 --- a/net/bluetooth/selftest.c +++ b/net/bluetooth/selftest.c @@ -23,15 +23,182 @@ #include +#include "ecc.h" #include "selftest.h" +#if IS_ENABLED(CONFIG_BT_SELFTEST_ECDH) + +static const u8 priv_a_1[32] __initconst = { + 0xbd, 0x1a, 0x3c, 0xcd, 0xa6, 0xb8, 0x99, 0x58, + 0x99, 0xb7, 0x40, 0xeb, 0x7b, 0x60, 0xff, 0x4a, + 0x50, 0x3f, 0x10, 0xd2, 0xe3, 0xb3, 0xc9, 0x74, + 0x38, 0x5f, 0xc5, 0xa3, 0xd4, 0xf6, 0x49, 0x3f, +}; +static const u8 priv_b_1[32] __initconst = { + 0xfd, 0xc5, 0x7f, 0xf4, 0x49, 0xdd, 0x4f, 0x6b, + 0xfb, 0x7c, 0x9d, 0xf1, 0xc2, 0x9a, 0xcb, 0x59, + 0x2a, 0xe7, 0xd4, 0xee, 0xfb, 0xfc, 0x0a, 0x90, + 0x9a, 0xbb, 0xf6, 0x32, 0x3d, 0x8b, 0x18, 0x55, +}; +static const u8 pub_a_1[64] __initconst = { + 0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc, + 0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef, + 0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e, + 0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20, + + 0x8b, 0xd2, 0x89, 0x15, 0xd0, 0x8e, 0x1c, 0x74, + 0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76, + 0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63, + 0x6d, 0xeb, 0x2a, 0x65, 0x49, 0x9c, 0x80, 0xdc, +}; +static const u8 pub_b_1[64] __initconst = { + 0x90, 0xa1, 0xaa, 0x2f, 0xb2, 0x77, 0x90, 0x55, + 0x9f, 0xa6, 0x15, 0x86, 0xfd, 0x8a, 0xb5, 0x47, + 0x00, 0x4c, 0x9e, 0xf1, 0x84, 0x22, 0x59, 0x09, + 0x96, 0x1d, 0xaf, 0x1f, 0xf0, 0xf0, 0xa1, 0x1e, + + 0x4a, 0x21, 0xb1, 0x15, 0xf9, 0xaf, 0x89, 0x5f, + 0x76, 0x36, 0x8e, 0xe2, 0x30, 0x11, 0x2d, 0x47, + 0x60, 0x51, 0xb8, 0x9a, 0x3a, 0x70, 0x56, 0x73, + 0x37, 0xad, 0x9d, 0x42, 0x3e, 0xf3, 0x55, 0x4c, +}; +static const u8 dhkey_1[32] __initconst = { + 0x98, 0xa6, 0xbf, 0x73, 0xf3, 0x34, 0x8d, 0x86, + 0xf1, 0x66, 0xf8, 0xb4, 0x13, 0x6b, 0x79, 0x99, + 0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34, + 0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec, +}; + +static const u8 priv_a_2[32] __initconst = { + 0x63, 0x76, 0x45, 0xd0, 0xf7, 0x73, 0xac, 0xb7, + 0xff, 0xdd, 0x03, 0x72, 0xb9, 0x72, 0x85, 0xb4, + 0x41, 0xb6, 0x5d, 0x0c, 0x5d, 0x54, 0x84, 0x60, + 0x1a, 0xa3, 0x9a, 0x3c, 0x69, 0x16, 0xa5, 0x06, +}; +static const u8 priv_b_2[32] __initconst = { + 0xba, 0x30, 0x55, 0x50, 0x19, 0xa2, 0xca, 0xa3, + 0xa5, 0x29, 0x08, 0xc6, 0xb5, 0x03, 0x88, 0x7e, + 0x03, 0x2b, 0x50, 0x73, 0xd4, 0x2e, 0x50, 0x97, + 0x64, 0xcd, 0x72, 0x0d, 0x67, 0xa0, 0x9a, 0x52, +}; +static const u8 pub_a_2[64] __initconst = { + 0xdd, 0x78, 0x5c, 0x74, 0x03, 0x9b, 0x7e, 0x98, + 0xcb, 0x94, 0x87, 0x4a, 0xad, 0xfa, 0xf8, 0xd5, + 0x43, 0x3e, 0x5c, 0xaf, 0xea, 0xb5, 0x4c, 0xf4, + 0x9e, 0x80, 0x79, 0x57, 0x7b, 0xa4, 0x31, 0x2c, + + 0x4f, 0x5d, 0x71, 0x43, 0x77, 0x43, 0xf8, 0xea, + 0xd4, 0x3e, 0xbd, 0x17, 0x91, 0x10, 0x21, 0xd0, + 0x1f, 0x87, 0x43, 0x8e, 0x40, 0xe2, 0x52, 0xcd, + 0xbe, 0xdf, 0x98, 0x38, 0x18, 0x12, 0x95, 0x91, +}; +static const u8 pub_b_2[64] __initconst = { + 0xcc, 0x00, 0x65, 0xe1, 0xf5, 0x6c, 0x0d, 0xcf, + 0xec, 0x96, 0x47, 0x20, 0x66, 0xc9, 0xdb, 0x84, + 0x81, 0x75, 0xa8, 0x4d, 0xc0, 0xdf, 0xc7, 0x9d, + 0x1b, 0x3f, 0x3d, 0xf2, 0x3f, 0xe4, 0x65, 0xf4, + + 0x79, 0xb2, 0xec, 0xd8, 0xca, 0x55, 0xa1, 0xa8, + 0x43, 0x4d, 0x6b, 0xca, 0x10, 0xb0, 0xc2, 0x01, + 0xc2, 0x33, 0x4e, 0x16, 0x24, 0xc4, 0xef, 0xee, + 0x99, 0xd8, 0xbb, 0xbc, 0x48, 0xd0, 0x01, 0x02, +}; +static const u8 dhkey_2[32] __initconst = { + 0x69, 0xeb, 0x21, 0x32, 0xf2, 0xc6, 0x05, 0x41, + 0x60, 0x19, 0xcd, 0x5e, 0x94, 0xe1, 0xe6, 0x5f, + 0x33, 0x07, 0xe3, 0x38, 0x4b, 0x68, 0xe5, 0x62, + 0x3f, 0x88, 0x6d, 0x2f, 0x3a, 0x84, 0x85, 0xab, +}; + +static const u8 priv_a_3[32] __initconst = { + 0xbd, 0x1a, 0x3c, 0xcd, 0xa6, 0xb8, 0x99, 0x58, + 0x99, 0xb7, 0x40, 0xeb, 0x7b, 0x60, 0xff, 0x4a, + 0x50, 0x3f, 0x10, 0xd2, 0xe3, 0xb3, 0xc9, 0x74, + 0x38, 0x5f, 0xc5, 0xa3, 0xd4, 0xf6, 0x49, 0x3f, +}; +static const u8 pub_a_3[64] __initconst = { + 0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc, + 0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef, + 0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e, + 0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20, + + 0x8b, 0xd2, 0x89, 0x15, 0xd0, 0x8e, 0x1c, 0x74, + 0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76, + 0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63, + 0x6d, 0xeb, 0x2a, 0x65, 0x49, 0x9c, 0x80, 0xdc, +}; +static const u8 dhkey_3[32] __initconst = { + 0x2d, 0xab, 0x00, 0x48, 0xcb, 0xb3, 0x7b, 0xda, + 0x55, 0x7b, 0x8b, 0x72, 0xa8, 0x57, 0x87, 0xc3, + 0x87, 0x27, 0x99, 0x32, 0xfc, 0x79, 0x5f, 0xae, + 0x7c, 0x1c, 0xf9, 0x49, 0xe6, 0xd7, 0xaa, 0x70, +}; + +static int __init test_ecdh_sample(const u8 priv_a[32], const u8 priv_b[32], + const u8 pub_a[64], const u8 pub_b[64], + const u8 dhkey[32]) +{ + u8 dhkey_a[32], dhkey_b[32]; + + ecdh_shared_secret(pub_b, priv_a, dhkey_a); + ecdh_shared_secret(pub_a, priv_b, dhkey_b); + + if (memcmp(dhkey_a, dhkey, 32)) + return -EINVAL; + + if (memcmp(dhkey_b, dhkey, 32)) + return -EINVAL; + + return 0; +} + +static int __init test_ecdh(void) +{ + int err; + + err = test_ecdh_sample(priv_a_1, priv_b_1, pub_a_1, pub_b_1, dhkey_1); + if (err) { + BT_ERR("ECDH sample 1 failed"); + return err; + } + + err = test_ecdh_sample(priv_a_2, priv_b_2, pub_a_2, pub_b_2, dhkey_2); + if (err) { + BT_ERR("ECDH sample 2 failed"); + return err; + } + + err = test_ecdh_sample(priv_a_3, priv_a_3, pub_a_3, pub_a_3, dhkey_3); + if (err) { + BT_ERR("ECDH sample 3 failed"); + return err; + } + + BT_INFO("ECDH test passed"); + + return 0; +} + +#else + +static inline int test_ecdh(void) +{ + return 0; +} + +#endif + static int __init run_selftest(void) { + int err; + BT_INFO("Starting self testing"); + err = test_ecdh(); + BT_INFO("Finished self testing"); - return 0; + return err; } #if IS_MODULE(CONFIG_BT) -- cgit v0.10.2 From 0a2b0f0452e2499a1037305fcfc314d0cdeb5260 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 30 Dec 2014 09:50:39 +0200 Subject: Bluetooth: Add skeleton for SMP self-tests This patch adds the initial skeleton and kernel config option for SMP self-tests. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 8d3b607..7de7463 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -84,4 +84,11 @@ config BT_SELFTEST_ECDH Run test cases for ECDH cryptographic functionality used by the Bluetooth Low Energy Secure Connections feature. +config BT_SELFTEST_SMP + bool "SMP test cases" + depends on BT_LE && BT_SELFTEST + help + Run test cases for SMP cryptographic functionality, including both + legacy SMP as well as the Secure Connections features. + source "drivers/bluetooth/Kconfig" diff --git a/net/bluetooth/selftest.c b/net/bluetooth/selftest.c index b9cb33c..a7602b3 100644 --- a/net/bluetooth/selftest.c +++ b/net/bluetooth/selftest.c @@ -22,8 +22,10 @@ */ #include +#include #include "ecc.h" +#include "smp.h" #include "selftest.h" #if IS_ENABLED(CONFIG_BT_SELFTEST_ECDH) @@ -195,7 +197,12 @@ static int __init run_selftest(void) BT_INFO("Starting self testing"); err = test_ecdh(); + if (err) + goto done; + err = bt_selftest_smp(); + +done: BT_INFO("Finished self testing"); return err; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 9025e17..b47528d 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -3022,3 +3022,42 @@ void smp_unregister(struct hci_dev *hdev) smp_del_chan(chan); } } + +#if IS_ENABLED(CONFIG_BT_SELFTEST_SMP) + +static int __init run_selftests(struct crypto_blkcipher *tfm_aes, + struct crypto_hash *tfm_cmac) +{ + BT_INFO("SMP test passed"); + + return 0; +} + +int __init bt_selftest_smp(void) +{ + struct crypto_blkcipher *tfm_aes; + struct crypto_hash *tfm_cmac; + int err; + + tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm_aes)) { + BT_ERR("Unable to create ECB crypto context"); + return PTR_ERR(tfm_aes); + } + + tfm_cmac = crypto_alloc_hash("cmac(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm_cmac)) { + BT_ERR("Unable to create CMAC crypto context"); + crypto_free_blkcipher(tfm_aes); + return PTR_ERR(tfm_cmac); + } + + err = run_selftests(tfm_aes, tfm_cmac); + + crypto_free_hash(tfm_cmac); + crypto_free_blkcipher(tfm_aes); + + return err; +} + +#endif diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h index 3296bf4..60c5b73 100644 --- a/net/bluetooth/smp.h +++ b/net/bluetooth/smp.h @@ -192,4 +192,17 @@ int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa); int smp_register(struct hci_dev *hdev); void smp_unregister(struct hci_dev *hdev); +#if IS_ENABLED(CONFIG_BT_SELFTEST_SMP) + +int bt_selftest_smp(void); + +#else + +static inline int bt_selftest_smp(void) +{ + return 0; +} + +#endif + #endif /* __SMP_H */ -- cgit v0.10.2 From cfc4198e714fcee1a68c558d243e7e4f8de85577 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 30 Dec 2014 09:50:40 +0200 Subject: Bluetooth: Add legacy SMP tests This patch adds self-tests for legacy SMP crypto functions. The sample data has been taken from the core specification. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index b47528d..f76c6d0 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -3025,9 +3025,104 @@ void smp_unregister(struct hci_dev *hdev) #if IS_ENABLED(CONFIG_BT_SELFTEST_SMP) +static int __init test_ah(struct crypto_blkcipher *tfm_aes) +{ + const u8 irk[16] = { + 0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34, + 0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec }; + const u8 r[3] = { 0x94, 0x81, 0x70 }; + const u8 exp[3] = { 0xaa, 0xfb, 0x0d }; + u8 res[3]; + int err; + + err = smp_ah(tfm_aes, irk, r, res); + if (err) + return err; + + if (memcmp(res, exp, 3)) + return -EINVAL; + + return 0; +} + +static int __init test_c1(struct crypto_blkcipher *tfm_aes) +{ + const u8 k[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + const u8 r[16] = { + 0xe0, 0x2e, 0x70, 0xc6, 0x4e, 0x27, 0x88, 0x63, + 0x0e, 0x6f, 0xad, 0x56, 0x21, 0xd5, 0x83, 0x57 }; + const u8 preq[7] = { 0x01, 0x01, 0x00, 0x00, 0x10, 0x07, 0x07 }; + const u8 pres[7] = { 0x02, 0x03, 0x00, 0x00, 0x08, 0x00, 0x05 }; + const u8 _iat = 0x01; + const u8 _rat = 0x00; + const bdaddr_t ra = { { 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1 } }; + const bdaddr_t ia = { { 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1 } }; + const u8 exp[16] = { + 0x86, 0x3b, 0xf1, 0xbe, 0xc5, 0x4d, 0xa7, 0xd2, + 0xea, 0x88, 0x89, 0x87, 0xef, 0x3f, 0x1e, 0x1e }; + u8 res[16]; + int err; + + err = smp_c1(tfm_aes, k, r, preq, pres, _iat, &ia, _rat, &ra, res); + if (err) + return err; + + if (memcmp(res, exp, 16)) + return -EINVAL; + + return 0; +} + +static int __init test_s1(struct crypto_blkcipher *tfm_aes) +{ + const u8 k[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + const u8 r1[16] = { + 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 }; + const u8 r2[16] = { + 0x00, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99 }; + const u8 exp[16] = { + 0x62, 0xa0, 0x6d, 0x79, 0xae, 0x16, 0x42, 0x5b, + 0x9b, 0xf4, 0xb0, 0xe8, 0xf0, 0xe1, 0x1f, 0x9a }; + u8 res[16]; + int err; + + err = smp_s1(tfm_aes, k, r1, r2, res); + if (err) + return err; + + if (memcmp(res, exp, 16)) + return -EINVAL; + + return 0; +} + static int __init run_selftests(struct crypto_blkcipher *tfm_aes, struct crypto_hash *tfm_cmac) { + int err; + + err = test_ah(tfm_aes); + if (err) { + BT_ERR("smp_ah test failed"); + return err; + } + + err = test_c1(tfm_aes); + if (err) { + BT_ERR("smp_c1 test failed"); + return err; + } + + err = test_s1(tfm_aes); + if (err) { + BT_ERR("smp_s1 test failed"); + return err; + } + BT_INFO("SMP test passed"); return 0; -- cgit v0.10.2 From fb2969a3a9a66a93f4c39e0c9f96e8bbb18e37a1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 30 Dec 2014 09:50:41 +0200 Subject: Bluetooth: Add LE Secure Connections tests for SMP This patch adds SMP self-tests for the Secure Connections crypto functions. The sample data has been taken from the core specification. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index f76c6d0..3a4333b 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -3100,6 +3100,162 @@ static int __init test_s1(struct crypto_blkcipher *tfm_aes) return 0; } +static int __init test_f4(struct crypto_hash *tfm_cmac) +{ + const u8 u[32] = { + 0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc, + 0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef, + 0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e, + 0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20 }; + const u8 v[32] = { + 0xfd, 0xc5, 0x7f, 0xf4, 0x49, 0xdd, 0x4f, 0x6b, + 0xfb, 0x7c, 0x9d, 0xf1, 0xc2, 0x9a, 0xcb, 0x59, + 0x2a, 0xe7, 0xd4, 0xee, 0xfb, 0xfc, 0x0a, 0x90, + 0x9a, 0xbb, 0xf6, 0x32, 0x3d, 0x8b, 0x18, 0x55 }; + const u8 x[16] = { + 0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff, + 0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 }; + const u8 z = 0x00; + const u8 exp[16] = { + 0x2d, 0x87, 0x74, 0xa9, 0xbe, 0xa1, 0xed, 0xf1, + 0x1c, 0xbd, 0xa9, 0x07, 0xf1, 0x16, 0xc9, 0xf2 }; + u8 res[16]; + int err; + + err = smp_f4(tfm_cmac, u, v, x, z, res); + if (err) + return err; + + if (memcmp(res, exp, 16)) + return -EINVAL; + + return 0; +} + +static int __init test_f5(struct crypto_hash *tfm_cmac) +{ + const u8 w[32] = { + 0x98, 0xa6, 0xbf, 0x73, 0xf3, 0x34, 0x8d, 0x86, + 0xf1, 0x66, 0xf8, 0xb4, 0x13, 0x6b, 0x79, 0x99, + 0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34, + 0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec }; + const u8 n1[16] = { + 0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff, + 0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 }; + const u8 n2[16] = { + 0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21, + 0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 }; + const u8 a1[7] = { 0xce, 0xbf, 0x37, 0x37, 0x12, 0x56, 0x00 }; + const u8 a2[7] = { 0xc1, 0xcf, 0x2d, 0x70, 0x13, 0xa7, 0x00 }; + const u8 exp_ltk[16] = { + 0x38, 0x0a, 0x75, 0x94, 0xb5, 0x22, 0x05, 0x98, + 0x23, 0xcd, 0xd7, 0x69, 0x11, 0x79, 0x86, 0x69 }; + const u8 exp_mackey[16] = { + 0x20, 0x6e, 0x63, 0xce, 0x20, 0x6a, 0x3f, 0xfd, + 0x02, 0x4a, 0x08, 0xa1, 0x76, 0xf1, 0x65, 0x29 }; + u8 mackey[16], ltk[16]; + int err; + + err = smp_f5(tfm_cmac, w, n1, n2, a1, a2, mackey, ltk); + if (err) + return err; + + if (memcmp(mackey, exp_mackey, 16)) + return -EINVAL; + + if (memcmp(ltk, exp_ltk, 16)) + return -EINVAL; + + return 0; +} + +static int __init test_f6(struct crypto_hash *tfm_cmac) +{ + const u8 w[16] = { + 0x20, 0x6e, 0x63, 0xce, 0x20, 0x6a, 0x3f, 0xfd, + 0x02, 0x4a, 0x08, 0xa1, 0x76, 0xf1, 0x65, 0x29 }; + const u8 n1[16] = { + 0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff, + 0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 }; + const u8 n2[16] = { + 0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21, + 0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 }; + const u8 r[16] = { + 0xc8, 0x0f, 0x2d, 0x0c, 0xd2, 0x42, 0xda, 0x08, + 0x54, 0xbb, 0x53, 0xb4, 0x3b, 0x34, 0xa3, 0x12 }; + const u8 io_cap[3] = { 0x02, 0x01, 0x01 }; + const u8 a1[7] = { 0xce, 0xbf, 0x37, 0x37, 0x12, 0x56, 0x00 }; + const u8 a2[7] = { 0xc1, 0xcf, 0x2d, 0x70, 0x13, 0xa7, 0x00 }; + const u8 exp[16] = { + 0x61, 0x8f, 0x95, 0xda, 0x09, 0x0b, 0x6c, 0xd2, + 0xc5, 0xe8, 0xd0, 0x9c, 0x98, 0x73, 0xc4, 0xe3 }; + u8 res[16]; + int err; + + err = smp_f6(tfm_cmac, w, n1, n2, r, io_cap, a1, a2, res); + if (err) + return err; + + if (memcmp(res, exp, 16)) + return -EINVAL; + + return 0; +} + +static int __init test_g2(struct crypto_hash *tfm_cmac) +{ + const u8 u[32] = { + 0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc, + 0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef, + 0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e, + 0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20 }; + const u8 v[32] = { + 0xfd, 0xc5, 0x7f, 0xf4, 0x49, 0xdd, 0x4f, 0x6b, + 0xfb, 0x7c, 0x9d, 0xf1, 0xc2, 0x9a, 0xcb, 0x59, + 0x2a, 0xe7, 0xd4, 0xee, 0xfb, 0xfc, 0x0a, 0x90, + 0x9a, 0xbb, 0xf6, 0x32, 0x3d, 0x8b, 0x18, 0x55 }; + const u8 x[16] = { + 0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff, + 0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 }; + const u8 y[16] = { + 0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21, + 0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 }; + const u32 exp_val = 0x2f9ed5ba % 1000000; + u32 val; + int err; + + err = smp_g2(tfm_cmac, u, v, x, y, &val); + if (err) + return err; + + if (val != exp_val) + return -EINVAL; + + return 0; +} + +static int __init test_h6(struct crypto_hash *tfm_cmac) +{ + const u8 w[16] = { + 0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34, + 0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec }; + const u8 key_id[4] = { 0x72, 0x62, 0x65, 0x6c }; + const u8 exp[16] = { + 0x99, 0x63, 0xb1, 0x80, 0xe2, 0xa9, 0xd3, 0xe8, + 0x1c, 0xc9, 0x6d, 0xe7, 0x02, 0xe1, 0x9a, 0x2d }; + u8 res[16]; + int err; + + err = smp_h6(tfm_cmac, w, key_id, res); + if (err) + return err; + + if (memcmp(res, exp, 16)) + return -EINVAL; + + return 0; +} + static int __init run_selftests(struct crypto_blkcipher *tfm_aes, struct crypto_hash *tfm_cmac) { @@ -3123,6 +3279,36 @@ static int __init run_selftests(struct crypto_blkcipher *tfm_aes, return err; } + err = test_f4(tfm_cmac); + if (err) { + BT_ERR("smp_f4 test failed"); + return err; + } + + err = test_f5(tfm_cmac); + if (err) { + BT_ERR("smp_f5 test failed"); + return err; + } + + err = test_f6(tfm_cmac); + if (err) { + BT_ERR("smp_f6 test failed"); + return err; + } + + err = test_g2(tfm_cmac); + if (err) { + BT_ERR("smp_g2 test failed"); + return err; + } + + err = test_h6(tfm_cmac); + if (err) { + BT_ERR("smp_h6 test failed"); + return err; + } + BT_INFO("SMP test passed"); return 0; -- cgit v0.10.2 From 255047b0dca31e6b8ce254481a0b65d559d2ebb8 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 30 Dec 2014 00:11:20 -0800 Subject: Bluetooth: Add timing information to SMP test case runs After successful completion of the SMP test cases, print the time it took to run them. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 3a4333b..358264c 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -3259,8 +3259,12 @@ static int __init test_h6(struct crypto_hash *tfm_cmac) static int __init run_selftests(struct crypto_blkcipher *tfm_aes, struct crypto_hash *tfm_cmac) { + ktime_t calltime, delta, rettime; + unsigned long long duration; int err; + calltime = ktime_get(); + err = test_ah(tfm_aes); if (err) { BT_ERR("smp_ah test failed"); @@ -3309,7 +3313,11 @@ static int __init run_selftests(struct crypto_blkcipher *tfm_aes, return err; } - BT_INFO("SMP test passed"); + rettime = ktime_get(); + delta = ktime_sub(rettime, calltime); + duration = (unsigned long long) ktime_to_ns(delta) >> 10; + + BT_INFO("SMP test passed in %lld usecs", duration); return 0; } -- cgit v0.10.2 From e64b4fb66cc428cef0a62ea899b671c1aad05f3a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 30 Dec 2014 00:11:21 -0800 Subject: Bluetooth: Add timing information to ECDH test case runs After successful completion of the ECDH test cases, print the time it took to run them. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/selftest.c b/net/bluetooth/selftest.c index a7602b3..9c67315 100644 --- a/net/bluetooth/selftest.c +++ b/net/bluetooth/selftest.c @@ -156,8 +156,12 @@ static int __init test_ecdh_sample(const u8 priv_a[32], const u8 priv_b[32], static int __init test_ecdh(void) { + ktime_t calltime, delta, rettime; + unsigned long long duration; int err; + calltime = ktime_get(); + err = test_ecdh_sample(priv_a_1, priv_b_1, pub_a_1, pub_b_1, dhkey_1); if (err) { BT_ERR("ECDH sample 1 failed"); @@ -176,7 +180,11 @@ static int __init test_ecdh(void) return err; } - BT_INFO("ECDH test passed"); + rettime = ktime_get(); + delta = ktime_sub(rettime, calltime); + duration = (unsigned long long) ktime_to_ns(delta) >> 10; + + BT_INFO("ECDH test passed in %lld usecs", duration); return 0; } -- cgit v0.10.2 From 4b5d6aadce5e054d33719a7490076294c83d2f88 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 29 Dec 2014 19:38:51 -0200 Subject: spi: spi-imx: Do not store the irq number in the private structure The irq number is only used inside the probe function, so there is really no need to store it in the private structure. Use a local 'irq' variable to hold the the irq number instead. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 961b97d..6a2ff75 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -89,7 +89,6 @@ struct spi_imx_data { struct completion xfer_done; void __iomem *base; - int irq; struct clk *clk_per; struct clk *clk_ipg; unsigned long spi_clk; @@ -1076,7 +1075,7 @@ static int spi_imx_probe(struct platform_device *pdev) struct spi_master *master; struct spi_imx_data *spi_imx; struct resource *res; - int i, ret, num_cs; + int i, ret, num_cs, irq; if (!np && !mxc_platform_info) { dev_err(&pdev->dev, "can't get the platform data\n"); @@ -1143,16 +1142,16 @@ static int spi_imx_probe(struct platform_device *pdev) goto out_master_put; } - spi_imx->irq = platform_get_irq(pdev, 0); - if (spi_imx->irq < 0) { - ret = spi_imx->irq; + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; goto out_master_put; } - ret = devm_request_irq(&pdev->dev, spi_imx->irq, spi_imx_isr, 0, + ret = devm_request_irq(&pdev->dev, irq, spi_imx_isr, 0, dev_name(&pdev->dev), spi_imx); if (ret) { - dev_err(&pdev->dev, "can't get irq%d: %d\n", spi_imx->irq, ret); + dev_err(&pdev->dev, "can't get irq%d: %d\n", irq, ret); goto out_master_put; } -- cgit v0.10.2 From 7c27ba46792d3596a83f28243e235a92cba80d45 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 29 Dec 2014 23:52:35 -0200 Subject: ASoC: fsl_spdif: Use dev_name() for registering the irq The 'name' array is currently stored inside the fsl_spdif_priv private structure only for registering the interrupt name. This can be simplified by registering it with dev_name() instead. Signed-off-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index af042942..73da1f0 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -90,7 +90,6 @@ struct spdif_mixer_control { * @sysclk: system clock for rx clock rate measurement * @dma_params_tx: DMA parameters for transmit channel * @dma_params_rx: DMA parameters for receive channel - * @name: driver name */ struct fsl_spdif_priv { struct spdif_mixer_control fsl_spdif_control; @@ -109,12 +108,8 @@ struct fsl_spdif_priv { struct clk *sysclk; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; - - /* The name space will be allocated dynamically */ - char name[0]; }; - /* DPLL locked and lock loss interrupt handler */ static void spdif_irq_dpll_lock(struct fsl_spdif_priv *spdif_priv) { @@ -1169,19 +1164,15 @@ static int fsl_spdif_probe(struct platform_device *pdev) if (!np) return -ENODEV; - spdif_priv = devm_kzalloc(&pdev->dev, - sizeof(struct fsl_spdif_priv) + strlen(np->name) + 1, - GFP_KERNEL); + spdif_priv = devm_kzalloc(&pdev->dev, sizeof(*spdif_priv), GFP_KERNEL); if (!spdif_priv) return -ENOMEM; - strcpy(spdif_priv->name, np->name); - spdif_priv->pdev = pdev; /* Initialize this copy of the CPU DAI driver structure */ memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai)); - spdif_priv->cpu_dai_drv.name = spdif_priv->name; + spdif_priv->cpu_dai_drv.name = dev_name(&pdev->dev); /* Get the addresses and IRQ */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1203,7 +1194,7 @@ static int fsl_spdif_probe(struct platform_device *pdev) } ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0, - spdif_priv->name, spdif_priv); + dev_name(&pdev->dev), spdif_priv); if (ret) { dev_err(&pdev->dev, "could not claim irq %u\n", irq); return ret; -- cgit v0.10.2 From 888c819d0feb9975216978c797ca7f8dfd48ed08 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 29 Dec 2014 23:52:36 -0200 Subject: ASoC: fsl_asrc: Use dev_name() for registering the irq The 'name' array is currently stored inside the fsl_asrc private structure only for registering the interrupt name. This can be simplified by registering it with dev_name() instead. Signed-off-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 026a801..5326ca7 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -818,7 +818,6 @@ static int fsl_asrc_probe(struct platform_device *pdev) return -ENOMEM; asrc_priv->pdev = pdev; - strncpy(asrc_priv->name, np->name, sizeof(asrc_priv->name) - 1); /* Get the addresses and IRQ */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -842,7 +841,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) } ret = devm_request_irq(&pdev->dev, irq, fsl_asrc_isr, 0, - asrc_priv->name, asrc_priv); + dev_name(&pdev->dev), asrc_priv); if (ret) { dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret); return ret; diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h index a3f211f..4aed63c 100644 --- a/sound/soc/fsl/fsl_asrc.h +++ b/sound/soc/fsl/fsl_asrc.h @@ -433,7 +433,6 @@ struct fsl_asrc_pair { * @channel_avail: non-occupied channel numbers * @asrc_rate: default sample rate for ASoC Back-Ends * @asrc_width: default sample width for ASoC Back-Ends - * @name: driver name */ struct fsl_asrc { struct snd_dmaengine_dai_dma_data dma_params_rx; @@ -452,8 +451,6 @@ struct fsl_asrc { int asrc_rate; int asrc_width; - - char name[32]; }; extern struct snd_soc_platform_driver fsl_asrc_platform; -- cgit v0.10.2 From adc60298c80efef4c2d7a7860b91b450931a7cf8 Mon Sep 17 00:00:00 2001 From: Aurelien BOUIN Date: Mon, 29 Dec 2014 16:13:51 -0800 Subject: ASoC: fsl_esai: Fix incorrect xDC field width of xCCR registers The xDC field should have 5 bit width according to Reference Manual. Thus this patch fixes it. Signed-off-by: Aurelien BOUIN Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/fsl/fsl_esai.h b/sound/soc/fsl/fsl_esai.h index 91a550f..5e793bb 100644 --- a/sound/soc/fsl/fsl_esai.h +++ b/sound/soc/fsl/fsl_esai.h @@ -302,7 +302,7 @@ #define ESAI_xCCR_xFP_MASK (((1 << ESAI_xCCR_xFP_WIDTH) - 1) << ESAI_xCCR_xFP_SHIFT) #define ESAI_xCCR_xFP(v) ((((v) - 1) << ESAI_xCCR_xFP_SHIFT) & ESAI_xCCR_xFP_MASK) #define ESAI_xCCR_xDC_SHIFT 9 -#define ESAI_xCCR_xDC_WIDTH 4 +#define ESAI_xCCR_xDC_WIDTH 5 #define ESAI_xCCR_xDC_MASK (((1 << ESAI_xCCR_xDC_WIDTH) - 1) << ESAI_xCCR_xDC_SHIFT) #define ESAI_xCCR_xDC(v) ((((v) - 1) << ESAI_xCCR_xDC_SHIFT) & ESAI_xCCR_xDC_MASK) #define ESAI_xCCR_xPSR_SHIFT 8 -- cgit v0.10.2 From 3b952436d4cca59bfdc8ede174dd746a4b31c804 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 10 Dec 2014 10:59:13 -0300 Subject: [media] cx23885: do not unregister demod I2C client twice on error Demod I2C client should be NULL after demod is unregistered on error path, otherwise it will be unregistered again when driver is unload. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index c47d182..6bb7935 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -1793,12 +1793,14 @@ static int dvb_register(struct cx23885_tsport *port) client_tuner->dev.driver == NULL) { module_put(client_demod->dev.driver->owner); i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; goto frontend_detach; } if (!try_module_get(client_tuner->dev.driver->owner)) { i2c_unregister_device(client_tuner); module_put(client_demod->dev.driver->owner); i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; goto frontend_detach; } port->i2c_client_tuner = client_tuner; @@ -1843,12 +1845,14 @@ static int dvb_register(struct cx23885_tsport *port) client_tuner->dev.driver == NULL) { module_put(client_demod->dev.driver->owner); i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; goto frontend_detach; } if (!try_module_get(client_tuner->dev.driver->owner)) { i2c_unregister_device(client_tuner); module_put(client_demod->dev.driver->owner); i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; goto frontend_detach; } port->i2c_client_tuner = client_tuner; @@ -1989,6 +1993,7 @@ static int dvb_register(struct cx23885_tsport *port) client_tuner->dev.driver == NULL) { module_put(client_demod->dev.driver->owner); i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; goto frontend_detach; } if (!try_module_get(client_tuner->dev.driver->owner)) { -- cgit v0.10.2 From a593f2cfc1b89d0342b82242ed3d31c1a4494f24 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 10 Dec 2014 11:05:59 -0300 Subject: [media] cx23885: correct some I2C client indentations These comparisons fit single line. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 6bb7935..0f60bc4 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -1823,8 +1823,7 @@ static int dvb_register(struct cx23885_tsport *port) info.platform_data = &si2168_config; request_module(info.type); client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info); - if (client_demod == NULL || - client_demod->dev.driver == NULL) + if (client_demod == NULL || client_demod->dev.driver == NULL) goto frontend_detach; if (!try_module_get(client_demod->dev.driver->owner)) { i2c_unregister_device(client_demod); @@ -1841,8 +1840,7 @@ static int dvb_register(struct cx23885_tsport *port) info.platform_data = &si2157_config; request_module(info.type); client_tuner = i2c_new_device(adapter, &info); - if (client_tuner == NULL || - client_tuner->dev.driver == NULL) { + if (client_tuner == NULL || client_tuner->dev.driver == NULL) { module_put(client_demod->dev.driver->owner); i2c_unregister_device(client_demod); port->i2c_client_demod = NULL; @@ -1878,8 +1876,7 @@ static int dvb_register(struct cx23885_tsport *port) info.platform_data = &m88ts2022_config; request_module(info.type); client_tuner = i2c_new_device(adapter, &info); - if (client_tuner == NULL || - client_tuner->dev.driver == NULL) + if (client_tuner == NULL || client_tuner->dev.driver == NULL) goto frontend_detach; if (!try_module_get(client_tuner->dev.driver->owner)) { i2c_unregister_device(client_tuner); @@ -1925,8 +1922,7 @@ static int dvb_register(struct cx23885_tsport *port) info.platform_data = &m88ts2022_config; request_module(info.type); client_tuner = i2c_new_device(adapter, &info); - if (client_tuner == NULL || - client_tuner->dev.driver == NULL) + if (client_tuner == NULL || client_tuner->dev.driver == NULL) goto frontend_detach; if (!try_module_get(client_tuner->dev.driver->owner)) { i2c_unregister_device(client_tuner); @@ -1971,8 +1967,7 @@ static int dvb_register(struct cx23885_tsport *port) info.platform_data = &si2168_config; request_module(info.type); client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info); - if (client_demod == NULL || - client_demod->dev.driver == NULL) + if (client_demod == NULL || client_demod->dev.driver == NULL) goto frontend_detach; if (!try_module_get(client_demod->dev.driver->owner)) { i2c_unregister_device(client_demod); @@ -1989,8 +1984,7 @@ static int dvb_register(struct cx23885_tsport *port) info.platform_data = &si2157_config; request_module(info.type); client_tuner = i2c_new_device(adapter, &info); - if (client_tuner == NULL || - client_tuner->dev.driver == NULL) { + if (client_tuner == NULL || client_tuner->dev.driver == NULL) { module_put(client_demod->dev.driver->owner); i2c_unregister_device(client_demod); port->i2c_client_demod = NULL; @@ -2115,8 +2109,7 @@ static int dvb_register(struct cx23885_tsport *port) info.platform_data = &sp2_config; request_module(info.type); client_ci = i2c_new_device(&i2c_bus2->i2c_adap, &info); - if (client_ci == NULL || - client_ci->dev.driver == NULL) { + if (client_ci == NULL || client_ci->dev.driver == NULL) { if (client_tuner) { module_put(client_tuner->dev.driver->owner); i2c_unregister_device(client_tuner); -- cgit v0.10.2 From 93009ca673394f0bfb99aa8d8e76f3581a2b38e8 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 11 Dec 2014 15:44:51 -0300 Subject: [media] cx23885: fix I2C scan printout 1) I2C slave addresses were printed so called 8-bit format. Use standard 7-bit notation. 2) I2C slave address was printed with hex formatted without leading zeros, which makes output one digit shorter in a case of address fit to one hex digit. Use 4 char wide hex number with leading zeros as usually used for I2C slave addresses. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx23885/cx23885-i2c.c b/drivers/media/pci/cx23885/cx23885-i2c.c index fd71306..1135ea3 100644 --- a/drivers/media/pci/cx23885/cx23885-i2c.c +++ b/drivers/media/pci/cx23885/cx23885-i2c.c @@ -300,8 +300,8 @@ static void do_i2c_scan(char *name, struct i2c_client *c) rc = i2c_master_recv(c, &buf, 0); if (rc < 0) continue; - printk(KERN_INFO "%s: i2c scan: found device @ 0x%x [%s]\n", - name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); + printk(KERN_INFO "%s: i2c scan: found device @ 0x%04x [%s]\n", + name, i, i2c_devs[i] ? i2c_devs[i] : "???"); } } -- cgit v0.10.2 From 1fc77d013ba85a29e2edfaba02fd21e8c8187fae Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 11 Dec 2014 16:12:46 -0300 Subject: [media] cx23885: Hauppauge WinTV-HVR5525 Add board profile for Hauppauge WinTV-HVR5525. Device is build upon following main components: Conexant CX23888 Montage M88RS6000 Allegro A8293 Silicon Labs Si2168-B40 Silicon Labs Si2157-A30 Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig index f613314..74d774e 100644 --- a/drivers/media/pci/cx23885/Kconfig +++ b/drivers/media/pci/cx23885/Kconfig @@ -41,6 +41,7 @@ config VIDEO_CX23885 select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_M88RS6000T if MEDIA_SUBDRV_AUTOSELECT select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Conexant 23885 based diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index db99ca2..2a92bcd 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -706,6 +706,11 @@ struct cx23885_board cx23885_boards[] = { .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_HAUPPAUGE_HVR5525] = { + .name = "Hauppauge WinTV-HVR5525", + .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -989,6 +994,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x4254, .subdevice = 0x0982, .card = CX23885_BOARD_DVBSKY_T982, + }, { + .subvendor = 0x0070, + .subdevice = 0xf038, + .card = CX23885_BOARD_HAUPPAUGE_HVR5525, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -1161,6 +1170,8 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) case 85721: /* WinTV-HVR1290 (PCIe, OEM, RCA in, IR, Dual channel ATSC and Basic analog */ + case 150329: + /* WinTV-HVR5525 (PCIe, DVB-S/S2, DVB-T/T2/C) */ break; default: printk(KERN_WARNING "%s: warning: " @@ -1632,6 +1643,29 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) msleep(100); cx23885_gpio_set(dev, GPIO_2); break; + case CX23885_BOARD_HAUPPAUGE_HVR5525: + /* + * GPIO-00 IR_WIDE + * GPIO-02 wake# + * GPIO-03 VAUX Pres. + * GPIO-07 PROG# + * GPIO-08 SAT_RESN + * GPIO-09 TER_RESN + * GPIO-10 B2_SENSE + * GPIO-11 B1_SENSE + * GPIO-15 IR_LED_STATUS + * GPIO-19 IR_NARROW + * GPIO-20 Blauster1 + * ALTGPIO VAUX_SWITCH + * AUX_PLL_CLK : Blaster2 + */ + /* Put the parts into reset and back */ + cx23885_gpio_enable(dev, GPIO_8 | GPIO_9, 1); + cx23885_gpio_clear(dev, GPIO_8 | GPIO_9); + msleep(100); + cx23885_gpio_set(dev, GPIO_8 | GPIO_9); + msleep(100); + break; } } @@ -1873,6 +1907,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1290: case CX23885_BOARD_HAUPPAUGE_HVR4400: case CX23885_BOARD_HAUPPAUGE_IMPACTVCBE: + case CX23885_BOARD_HAUPPAUGE_HVR5525: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -1997,6 +2032,14 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_HAUPPAUGE_HVR5525: + ts1->gen_ctrl_val = 0x5; /* Parallel */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 0f60bc4..63c0de3 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -74,6 +74,7 @@ #include "sp2.h" #include "m88ds3103.h" #include "m88ts2022.h" +#include "m88rs6000t.h" static unsigned int debug; @@ -915,6 +916,16 @@ static const struct m88ds3103_config dvbsky_s952_portc_m88ds3103_config = { .agc = 0x99, }; +static const struct m88ds3103_config hauppauge_hvr5525_m88ds3103_config = { + .i2c_addr = 0x69, + .clock = 27000000, + .i2c_wr_max = 33, + .ts_mode = M88DS3103_TS_PARALLEL, + .ts_clk = 16000, + .ts_clk_pol = 1, + .agc = 0x99, +}; + static int netup_altera_fpga_rw(void *device, int flag, int data, int read) { struct cx23885_dev *dev = (struct cx23885_dev *)device; @@ -1999,6 +2010,93 @@ static int dvb_register(struct cx23885_tsport *port) } port->i2c_client_tuner = client_tuner; break; + case CX23885_BOARD_HAUPPAUGE_HVR5525: + switch (port->nr) { + struct m88rs6000t_config m88rs6000t_config; + + /* port b - satellite */ + case 1: + /* attach frontend */ + fe0->dvb.frontend = dvb_attach(m88ds3103_attach, + &hauppauge_hvr5525_m88ds3103_config, + &dev->i2c_bus[0].i2c_adap, &adapter); + if (fe0->dvb.frontend == NULL) + break; + + /* attach SEC */ + if (!dvb_attach(a8293_attach, fe0->dvb.frontend, + &dev->i2c_bus[0].i2c_adap, + &hauppauge_a8293_config)) + goto frontend_detach; + + /* attach tuner */ + memset(&m88rs6000t_config, 0, sizeof(m88rs6000t_config)); + m88rs6000t_config.fe = fe0->dvb.frontend; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "m88rs6000t", I2C_NAME_SIZE); + info.addr = 0x21; + info.platform_data = &m88rs6000t_config; + request_module("%s", info.type); + client_tuner = i2c_new_device(adapter, &info); + if (!client_tuner || !client_tuner->dev.driver) + goto frontend_detach; + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + goto frontend_detach; + } + port->i2c_client_tuner = client_tuner; + + /* delegate signal strength measurement to tuner */ + fe0->dvb.frontend->ops.read_signal_strength = + fe0->dvb.frontend->ops.tuner_ops.get_rf_strength; + break; + /* port c - terrestrial/cable */ + case 2: + /* attach frontend */ + memset(&si2168_config, 0, sizeof(si2168_config)); + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &fe0->dvb.frontend; + si2168_config.ts_mode = SI2168_TS_SERIAL; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2168", I2C_NAME_SIZE); + info.addr = 0x64; + info.platform_data = &si2168_config; + request_module("%s", info.type); + client_demod = i2c_new_device(&dev->i2c_bus[0].i2c_adap, &info); + if (!client_demod || !client_demod->dev.driver) + goto frontend_detach; + if (!try_module_get(client_demod->dev.driver->owner)) { + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_demod = client_demod; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = fe0->dvb.frontend; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = 0x60; + info.platform_data = &si2157_config; + request_module("%s", info.type); + client_tuner = i2c_new_device(&dev->i2c_bus[1].i2c_adap, &info); + if (!client_tuner || !client_tuner->dev.driver) { + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; + goto frontend_detach; + } + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; + goto frontend_detach; + } + port->i2c_client_tuner = client_tuner; + break; + } + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index f55cd12..4f358eb 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -99,6 +99,7 @@ #define CX23885_BOARD_DVBSKY_S950 49 #define CX23885_BOARD_DVBSKY_S952 50 #define CX23885_BOARD_DVBSKY_T982 51 +#define CX23885_BOARD_HAUPPAUGE_HVR5525 52 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 -- cgit v0.10.2 From 19f52fae5adb7f2fd5b75251f9bd761f43a36476 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Dec 2014 18:43:36 +0100 Subject: ALSA: Fix handling of multiple msbits constraints on the same runtime If the sound card is made up of discrete components, each with their own driver (e.g. like in the ASoC case), we might end up with multiple msbits constraint rules installed. Currently this will result in msbits being set to whatever the last rule set it to. This patch updates the behavior of the rule to choose the minimum (other than zero) of all the installed rules. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index ec9e786..b0c1535 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1300,7 +1300,7 @@ static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, unsigned int msbits = l >> 16; struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); if (snd_interval_single(i) && snd_interval_value(i) == width) - params->msbits = msbits; + params->msbits = min_not_zero(params->msbits, msbits); return 0; } -- cgit v0.10.2 From 8ef9df55a72425e269575fa74cbbedec4672bdc4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Dec 2014 18:43:37 +0100 Subject: ALSA: Add support for wildcard msbits constraints Currently the msbits constraints requires to specify a specific sample format width for which the constraint should be applied. But often the number of most significant bits is not sample format specific, but rather a absolute limit. E.g. the PCM interface might accept 32-bit and 24-bit samples, but the DAC has a 16-bit resolution and throws away the LSBs. In this case for both 32-bit and 24-bit format msbits should be set to 16. This patch extends snd_pcm_hw_constraint_msbits() so that a wildcard constraint can be setup that is applied for all formats with a sample width larger than the specified msbits. Choosing the wildcard constraint is done by setting the sample width parameter of the function to 0. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index b0c1535..db05e04 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1299,8 +1299,14 @@ static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, int width = l & 0xffff; unsigned int msbits = l >> 16; struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); - if (snd_interval_single(i) && snd_interval_value(i) == width) + + if (!snd_interval_single(i)) + return 0; + + if ((snd_interval_value(i) == width) || + (width == 0 && snd_interval_value(i) > msbits)) params->msbits = min_not_zero(params->msbits, msbits); + return 0; } @@ -1311,6 +1317,11 @@ static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, * @width: sample bits width * @msbits: msbits width * + * This constraint will set the number of most significant bits (msbits) if a + * sample format with the specified width has been select. If width is set to 0 + * the msbits will be set for any sample format with a width larger than the + * specified msbits. + * * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, -- cgit v0.10.2 From 0e2a37513a1fafa0b9829e992633a80861c3b4b5 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Dec 2014 18:43:38 +0100 Subject: ASoC: pcm: Use wildcard msbits constraints Use the new wildcard msbits constraints instead of installing a constraint for each available sample format width. Signed-off-by: Lars-Peter Clausen Acked-by: Mark Brown Signed-off-by: Takashi Iwai diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index eb87d96e2..d62d6a5 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -301,15 +301,6 @@ static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream) return symmetry; } -/* - * List of sample sizes that might go over the bus for parameter - * application. There ought to be a wildcard sample size for things - * like the DAC/ADC resolution to use but there isn't right now. - */ -static int sample_sizes[] = { - 24, 32, -}; - static void soc_pcm_set_msb(struct snd_pcm_substream *substream, int bits) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -318,17 +309,10 @@ static void soc_pcm_set_msb(struct snd_pcm_substream *substream, int bits) if (!bits) return; - for (i = 0; i < ARRAY_SIZE(sample_sizes); i++) { - if (bits >= sample_sizes[i]) - continue; - - ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0, - sample_sizes[i], bits); - if (ret != 0) - dev_warn(rtd->dev, - "ASoC: Failed to set MSB %d/%d: %d\n", - bits, sample_sizes[i], ret); - } + ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 0, bits); + if (ret != 0) + dev_warn(rtd->dev, "ASoC: Failed to set MSB %d: %d\n", + bits, ret); } static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) -- cgit v0.10.2 From 26c0d8a415e5c4a01faf24165ec9dea821f6a908 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Dec 2014 19:41:38 +0100 Subject: ALSA: pcm_params: Remove unused add/sub functions Those two functions are not used anywhere and also their name is a bit to generic to be in a global header, so remove them. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h index 6b1c78f..15760f5 100644 --- a/include/sound/pcm_params.h +++ b/include/sound/pcm_params.h @@ -325,20 +325,6 @@ static inline int snd_interval_eq(const struct snd_interval *i1, const struct sn i1->max == i2->max && i1->openmax == i2->openmax; } -static inline unsigned int add(unsigned int a, unsigned int b) -{ - if (a >= UINT_MAX - b) - return UINT_MAX; - return a + b; -} - -static inline unsigned int sub(unsigned int a, unsigned int b) -{ - if (a > b) - return a - b; - return 0; -} - #define params_access(p) ((__force snd_pcm_access_t)\ snd_mask_min(hw_param_mask_c((p), SNDRV_PCM_HW_PARAM_ACCESS))) #define params_format(p) ((__force snd_pcm_format_t)\ -- cgit v0.10.2 From b3a8c862cd33b97c920122248ed9931af546de54 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Dec 2014 19:41:39 +0100 Subject: ALSA: pcm: Remove unused SNDRV_PCM_IOCTL1_{FALSE,TRUE} defines Both SNDRV_PCM_IOCTL1_FALSE and SNDRV_PCM_IOCTL1_TRUE are unused and have in fact never been used (at least as far as the git history goes). Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/include/sound/pcm.h b/include/sound/pcm.h index b429b73..3652c94 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -94,9 +94,6 @@ struct snd_pcm_ops { #define SNDRV_PCM_DEVICES 8 #endif -#define SNDRV_PCM_IOCTL1_FALSE ((void *)0) -#define SNDRV_PCM_IOCTL1_TRUE ((void *)1) - #define SNDRV_PCM_IOCTL1_RESET 0 #define SNDRV_PCM_IOCTL1_INFO 1 #define SNDRV_PCM_IOCTL1_CHANNEL_INFO 2 -- cgit v0.10.2 From 744c2ad2f98965473880ef85cac2f8a8ca95959f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Dec 2014 19:41:41 +0100 Subject: ALSA: pcm: Convert params_* mask helpers to static inline functions Use static inline functions instead of macros for the remaining params_*() helpers that have not been converted yet. This is slightly cleaner and offers better type safety. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h index 15760f5..bf51f22 100644 --- a/include/sound/pcm_params.h +++ b/include/sound/pcm_params.h @@ -325,12 +325,24 @@ static inline int snd_interval_eq(const struct snd_interval *i1, const struct sn i1->max == i2->max && i1->openmax == i2->openmax; } -#define params_access(p) ((__force snd_pcm_access_t)\ - snd_mask_min(hw_param_mask_c((p), SNDRV_PCM_HW_PARAM_ACCESS))) -#define params_format(p) ((__force snd_pcm_format_t)\ - snd_mask_min(hw_param_mask_c((p), SNDRV_PCM_HW_PARAM_FORMAT))) -#define params_subformat(p) \ - snd_mask_min(hw_param_mask_c((p), SNDRV_PCM_HW_PARAM_SUBFORMAT)) +static inline snd_pcm_access_t params_access(const struct snd_pcm_hw_params *p) +{ + return (__force snd_pcm_access_t)snd_mask_min(hw_param_mask_c(p, + SNDRV_PCM_HW_PARAM_ACCESS)); +} + +static inline snd_pcm_format_t params_format(const struct snd_pcm_hw_params *p) +{ + return (__force snd_pcm_format_t)snd_mask_min(hw_param_mask_c(p, + SNDRV_PCM_HW_PARAM_FORMAT)); +} + +static inline snd_pcm_subformat_t +params_subformat(const struct snd_pcm_hw_params *p) +{ + return (__force snd_pcm_subformat_t)snd_mask_min(hw_param_mask_c(p, + SNDRV_PCM_HW_PARAM_SUBFORMAT)); +} static inline unsigned int params_period_bytes(const struct snd_pcm_hw_params *p) -- cgit v0.10.2 From 89827ca9dd6173da7dcc035653bea67fa6ac0cbf Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Dec 2014 19:41:42 +0100 Subject: ALSA: pcm: Add kernel doc for params_*() functions Add kernel doc for the remaining undocumented params_*() functions. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h index bf51f22..2e2169e 100644 --- a/include/sound/pcm_params.h +++ b/include/sound/pcm_params.h @@ -325,18 +325,30 @@ static inline int snd_interval_eq(const struct snd_interval *i1, const struct sn i1->max == i2->max && i1->openmax == i2->openmax; } +/** + * params_access - get the access type from the hw params + * @p: hw params + */ static inline snd_pcm_access_t params_access(const struct snd_pcm_hw_params *p) { return (__force snd_pcm_access_t)snd_mask_min(hw_param_mask_c(p, SNDRV_PCM_HW_PARAM_ACCESS)); } +/** + * params_format - get the sample format from the hw params + * @p: hw params + */ static inline snd_pcm_format_t params_format(const struct snd_pcm_hw_params *p) { return (__force snd_pcm_format_t)snd_mask_min(hw_param_mask_c(p, SNDRV_PCM_HW_PARAM_FORMAT)); } +/** + * params_subformat - get the sample subformat from the hw params + * @p: hw params + */ static inline snd_pcm_subformat_t params_subformat(const struct snd_pcm_hw_params *p) { @@ -344,6 +356,10 @@ params_subformat(const struct snd_pcm_hw_params *p) SNDRV_PCM_HW_PARAM_SUBFORMAT)); } +/** + * params_period_bytes - get the period size (in bytes) from the hw params + * @p: hw params + */ static inline unsigned int params_period_bytes(const struct snd_pcm_hw_params *p) { @@ -352,14 +368,27 @@ params_period_bytes(const struct snd_pcm_hw_params *p) params_channels(p)) / 8; } -static inline int -params_width(const struct snd_pcm_hw_params *p) +/** + * params_width - get the number of bits of the sample format from the hw params + * @p: hw params + * + * This function returns the number of bits per sample that the selected sample + * format of the hw params has. + */ +static inline int params_width(const struct snd_pcm_hw_params *p) { return snd_pcm_format_width(params_format(p)); } -static inline int -params_physical_width(const struct snd_pcm_hw_params *p) +/* + * params_physical_width - get the storage size of the sample format from the hw params + * @p: hw params + * + * This functions returns the number of bits per sample that the selected sample + * format of the hw params takes up in memory. This will be equal or larger than + * params_width(). + */ +static inline int params_physical_width(const struct snd_pcm_hw_params *p) { return snd_pcm_format_physical_width(params_format(p)); } -- cgit v0.10.2 From cd9978f1d3dbb9596a7ab9c652cb0d9b355489b5 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Dec 2014 19:41:43 +0100 Subject: ALSA: pcm: Simplify params_period_bytes() The hw_params struct has a parameter that contains the period size in bytes. This can be used instead of deriving the value from other parameters. This is similar to e.g. params_buffer_bytes() Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h index 2e2169e..042049b 100644 --- a/include/sound/pcm_params.h +++ b/include/sound/pcm_params.h @@ -363,9 +363,7 @@ params_subformat(const struct snd_pcm_hw_params *p) static inline unsigned int params_period_bytes(const struct snd_pcm_hw_params *p) { - return (params_period_size(p) * - snd_pcm_format_physical_width(params_format(p)) * - params_channels(p)) / 8; + return hw_param_interval_c(p, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)->min; } /** -- cgit v0.10.2 From 599ee3291ae88700749e2910a11d1c0f0532355e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Dec 2014 19:41:44 +0100 Subject: ALSA: pcm: Use __ffs() instead of ffs() in snd_mask_min() The difference between __ffs and ffs is that ffs will return a one based index whereas __ffs will return a zero based index. Furthermore ffs will check if the passed value is zero and return zero in that case, whereas __ffs behavior is undefined if the passed parameter is 0. Since we already check if the mask is 0 before calling ffs and also subtract 1 from the result __ffs is the better choice. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h index 042049b..c99e20b 100644 --- a/include/sound/pcm_params.h +++ b/include/sound/pcm_params.h @@ -92,7 +92,7 @@ static inline unsigned int snd_mask_min(const struct snd_mask *mask) int i; for (i = 0; i < SNDRV_MASK_SIZE; i++) { if (mask->bits[i]) - return ffs(mask->bits[i]) - 1 + (i << 5); + return __ffs(mask->bits[i]) + (i << 5); } return 0; } -- cgit v0.10.2 From f6dbe1bee59741cd7699e7da3019e67c77d9bd6f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Dec 2014 19:41:45 +0100 Subject: ALSA: snd_pcm_oss_period_size: Use round{up,down}_pow_of_two() Instead of opencoding them use the standard roundup_pow_of_two() and rounddown_pow_of_two() helper functions. This gets rids one of the few users of the custom ld2() function and also makes it a bit more obvious what the code does. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index ada69d7..80423a4c 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -719,7 +719,7 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream, oss_buffer_size = snd_pcm_plug_client_size(substream, snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, NULL)) * oss_frame_size; - oss_buffer_size = 1 << ld2(oss_buffer_size); + oss_buffer_size = rounddown_pow_of_two(oss_buffer_size); if (atomic_read(&substream->mmap_count)) { if (oss_buffer_size > runtime->oss.mmap_bytes) oss_buffer_size = runtime->oss.mmap_bytes; @@ -755,14 +755,14 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream, min_period_size = snd_pcm_plug_client_size(substream, snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL)); min_period_size *= oss_frame_size; - min_period_size = 1 << (ld2(min_period_size - 1) + 1); + min_period_size = roundup_pow_of_two(min_period_size); if (oss_period_size < min_period_size) oss_period_size = min_period_size; max_period_size = snd_pcm_plug_client_size(substream, snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL)); max_period_size *= oss_frame_size; - max_period_size = 1 << ld2(max_period_size); + max_period_size = rounddown_pow_of_two(max_period_size); if (oss_period_size > max_period_size) oss_period_size = max_period_size; -- cgit v0.10.2 From 757b037650c9ecca79a46ab744820b1b3c1bb49d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Dec 2014 19:41:46 +0100 Subject: ALSA: pcm: Replace custom ld2 function with __fls __fls has the same semantics as ld2, so there is no need to re-implement it. Furthermore a lot of architectures have custom implementations of __fls that are able to use special hardware instructions to compute the result. This makes the code slightly shorter and faster. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h index c99e20b..3c45f39 100644 --- a/include/sound/pcm_params.h +++ b/include/sound/pcm_params.h @@ -38,31 +38,6 @@ int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params, #define MASK_OFS(i) ((i) >> 5) #define MASK_BIT(i) (1U << ((i) & 31)) -static inline unsigned int ld2(u_int32_t v) -{ - unsigned r = 0; - - if (v >= 0x10000) { - v >>= 16; - r += 16; - } - if (v >= 0x100) { - v >>= 8; - r += 8; - } - if (v >= 0x10) { - v >>= 4; - r += 4; - } - if (v >= 4) { - v >>= 2; - r += 2; - } - if (v >= 2) - r++; - return r; -} - static inline size_t snd_mask_sizeof(void) { return sizeof(struct snd_mask); @@ -102,7 +77,7 @@ static inline unsigned int snd_mask_max(const struct snd_mask *mask) int i; for (i = SNDRV_MASK_SIZE - 1; i >= 0; i--) { if (mask->bits[i]) - return ld2(mask->bits[i]) + (i << 5); + return __fls(mask->bits[i]) + (i << 5); } return 0; } -- cgit v0.10.2 From 5a1b8a80daa15e5b1920735133b80dd46975765f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 30 Dec 2014 16:10:32 +0200 Subject: ASoC: davinci-mcasp: Support for combined tx/rx interrupt line Some SoC, like da850/OMAP-L138 uses one common interrupt request for TX/RX events. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 30b94d4..95eef58 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -364,6 +364,20 @@ static irqreturn_t davinci_mcasp_rx_irq_handler(int irq, void *data) return IRQ_RETVAL(handled_mask); } +static irqreturn_t davinci_mcasp_common_irq_handler(int irq, void *data) +{ + struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data; + irqreturn_t ret = IRQ_NONE; + + if (mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK]) + ret = davinci_mcasp_tx_irq_handler(irq, data); + + if (mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE]) + ret |= davinci_mcasp_rx_irq_handler(irq, data); + + return ret; +} + static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { @@ -1441,6 +1455,22 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp->dev = &pdev->dev; + irq = platform_get_irq_byname(pdev, "common"); + if (irq >= 0) { + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_common\n", + dev_name(&pdev->dev)); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + davinci_mcasp_common_irq_handler, + IRQF_ONESHOT, irq_name, mcasp); + if (ret) { + dev_err(&pdev->dev, "common IRQ request failed\n"); + goto err; + } + + mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK] = XUNDRN; + mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE] = ROVRN; + } + irq = platform_get_irq_byname(pdev, "rx"); if (irq >= 0) { irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx\n", -- cgit v0.10.2 From 5396ecf7b1920595deec83b902502bd3d2da184e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 30 Dec 2014 12:11:36 -0200 Subject: ASoC: mxs-saif: Do not store the irq number in the private structure The irq number is only used inside the probe function, so there is really no need to store it in the private structure. Use a local 'irq' variable to hold the the irq number instead. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index d986508..c866ade 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -710,7 +710,7 @@ static int mxs_saif_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct resource *iores; struct mxs_saif *saif; - int ret = 0; + int irq, ret = 0; struct device_node *master; if (!np) @@ -763,16 +763,16 @@ static int mxs_saif_probe(struct platform_device *pdev) if (IS_ERR(saif->base)) return PTR_ERR(saif->base); - saif->irq = platform_get_irq(pdev, 0); - if (saif->irq < 0) { - ret = saif->irq; + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; dev_err(&pdev->dev, "failed to get irq resource: %d\n", ret); return ret; } saif->dev = &pdev->dev; - ret = devm_request_irq(&pdev->dev, saif->irq, mxs_saif_irq, 0, + ret = devm_request_irq(&pdev->dev, irq, mxs_saif_irq, 0, dev_name(&pdev->dev), saif); if (ret) { dev_err(&pdev->dev, "failed to request irq\n"); diff --git a/sound/soc/mxs/mxs-saif.h b/sound/soc/mxs/mxs-saif.h index fbaf7ba..9a4c0b2 100644 --- a/sound/soc/mxs/mxs-saif.h +++ b/sound/soc/mxs/mxs-saif.h @@ -116,7 +116,6 @@ struct mxs_saif { unsigned int mclk; unsigned int mclk_in_use; void __iomem *base; - int irq; unsigned int id; unsigned int master_id; unsigned int cur_rate; -- cgit v0.10.2 From a56257c657eab392d579e6be70e2f8430eef1aa3 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Tue, 30 Dec 2014 10:55:43 +0000 Subject: ASoC: dwc: Switch to managed clock resource Simplify error handling during probe by using managed clock resources. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 23a7c13..10219b5 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -396,13 +396,13 @@ static int dw_i2s_probe(struct platform_device *pdev) dev->capability = pdata->cap; dev->i2s_clk_cfg = pdata->i2s_clk_cfg; - dev->clk = clk_get(&pdev->dev, NULL); + dev->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) return PTR_ERR(dev->clk); ret = clk_enable(dev->clk); if (ret < 0) - goto err_clk_put; + return ret; dev_set_drvdata(&pdev->dev, dev); ret = snd_soc_register_component(&pdev->dev, &dw_i2s_component, @@ -416,19 +416,13 @@ static int dw_i2s_probe(struct platform_device *pdev) err_clk_disable: clk_disable(dev->clk); -err_clk_put: - clk_put(dev->clk); return ret; } static int dw_i2s_remove(struct platform_device *pdev) { - struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); - snd_soc_unregister_component(&pdev->dev); - clk_put(dev->clk); - return 0; } -- cgit v0.10.2 From b226efe5818bf01cecc8a3e0fbd0def4ebbcedaa Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Tue, 30 Dec 2014 10:55:45 +0000 Subject: ASoC: dwc: Read I2S block configuration from registers The I2S block provides component parameter registers which describe how the block is instantiated. Use these registers to extract the block's configuration rather than relying on platform data. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 10219b5..d3bdc68 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -54,6 +54,31 @@ #define I2S_COMP_VERSION 0x01F8 #define I2S_COMP_TYPE 0x01FC +/* + * Component parameter register fields - define the I2S block's + * configuration. + */ +#define COMP1_TX_WORDSIZE_3(r) (((r) & GENMASK(27, 25)) >> 25) +#define COMP1_TX_WORDSIZE_2(r) (((r) & GENMASK(24, 22)) >> 22) +#define COMP1_TX_WORDSIZE_1(r) (((r) & GENMASK(21, 19)) >> 19) +#define COMP1_TX_WORDSIZE_0(r) (((r) & GENMASK(18, 16)) >> 16) +#define COMP1_TX_CHANNELS(r) (((r) & GENMASK(10, 9)) >> 9) +#define COMP1_RX_CHANNELS(r) (((r) & GENMASK(8, 7)) >> 7) +#define COMP1_RX_ENABLED(r) (((r) & BIT(6)) >> 6) +#define COMP1_TX_ENABLED(r) (((r) & BIT(5)) >> 5) +#define COMP1_MODE_EN(r) (((r) & BIT(4)) >> 4) +#define COMP1_FIFO_DEPTH_GLOBAL(r) (((r) & GENMASK(3, 2)) >> 2) +#define COMP1_APB_DATA_WIDTH(r) (((r) & GENMASK(1, 0)) >> 0) + +#define COMP2_RX_WORDSIZE_3(r) (((r) & GENMASK(12, 10)) >> 10) +#define COMP2_RX_WORDSIZE_2(r) (((r) & GENMASK(9, 7)) >> 7) +#define COMP2_RX_WORDSIZE_1(r) (((r) & GENMASK(5, 3)) >> 3) +#define COMP2_RX_WORDSIZE_0(r) (((r) & GENMASK(2, 0)) >> 0) + +/* Number of entries in WORDSIZE and DATA_WIDTH parameter registers */ +#define COMP_MAX_WORDSIZE (1 << 3) +#define COMP_MAX_DATA_WIDTH (1 << 2) + #define MAX_CHANNEL_NUM 8 #define MIN_CHANNEL_NUM 2 @@ -324,11 +349,50 @@ static int dw_i2s_resume(struct snd_soc_dai *dai) #define dw_i2s_resume NULL #endif -static void dw_configure_dai_by_pd(struct dw_i2s_dev *dev, +/* + * The following tables allow a direct lookup of various parameters + * defined in the I2S block's configuration in terms of sound system + * parameters. Each table is sized to the number of entries possible + * according to the number of configuration bits describing an I2S + * block parameter. + */ + +/* Width of (DMA) bus */ +static const u32 bus_widths[COMP_MAX_DATA_WIDTH] = { + DMA_SLAVE_BUSWIDTH_1_BYTE, + DMA_SLAVE_BUSWIDTH_2_BYTES, + DMA_SLAVE_BUSWIDTH_4_BYTES, + DMA_SLAVE_BUSWIDTH_UNDEFINED +}; + +/* PCM format to support channel resolution */ +static const u32 formats[COMP_MAX_WORDSIZE] = { + SNDRV_PCM_FMTBIT_S16_LE, + SNDRV_PCM_FMTBIT_S16_LE, + SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_FMTBIT_S32_LE, + 0, + 0, + 0 +}; + +static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, struct snd_soc_dai_driver *dw_i2s_dai, struct resource *res, const struct i2s_platform_data *pdata) { + /* + * Read component parameter registers to extract + * the I2S block's configuration. + */ + u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); + u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2); + u32 idx = COMP1_APB_DATA_WIDTH(comp1); + + if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) + return -EINVAL; + /* Set DMA slaves info */ dev->play_dma_data.data = pdata->play_dma_data; @@ -337,26 +401,36 @@ static void dw_configure_dai_by_pd(struct dw_i2s_dev *dev, dev->capture_dma_data.addr = res->start + I2S_RXDMA; dev->play_dma_data.max_burst = 16; dev->capture_dma_data.max_burst = 16; - dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - dev->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + dev->play_dma_data.addr_width = bus_widths[idx]; + dev->capture_dma_data.addr_width = bus_widths[idx]; dev->play_dma_data.filter = pdata->filter; dev->capture_dma_data.filter = pdata->filter; - if (pdata->cap & DWC_I2S_PLAY) { + if (COMP1_TX_ENABLED(comp1)) { dev_dbg(dev->dev, " designware: play supported\n"); + idx = COMP1_TX_WORDSIZE_0(comp1); + if (WARN_ON(idx >= ARRAY_SIZE(formats))) + return -EINVAL; dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; - dw_i2s_dai->playback.channels_max = pdata->channel; - dw_i2s_dai->playback.formats = pdata->snd_fmts; + dw_i2s_dai->playback.channels_max = + 1 << (COMP1_TX_CHANNELS(comp1) + 1); + dw_i2s_dai->playback.formats = formats[idx]; dw_i2s_dai->playback.rates = pdata->snd_rates; } - if (pdata->cap & DWC_I2S_RECORD) { + if (COMP1_RX_ENABLED(comp1)) { dev_dbg(dev->dev, "designware: record supported\n"); + idx = COMP2_RX_WORDSIZE_0(comp2); + if (WARN_ON(idx >= ARRAY_SIZE(formats))) + return -EINVAL; dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; - dw_i2s_dai->capture.channels_max = pdata->channel; - dw_i2s_dai->capture.formats = pdata->snd_fmts; + dw_i2s_dai->capture.channels_max = + 1 << (COMP1_RX_CHANNELS(comp1) + 1); + dw_i2s_dai->capture.formats = formats[idx]; dw_i2s_dai->capture.rates = pdata->snd_rates; } + + return 0; } static int dw_i2s_probe(struct platform_device *pdev) @@ -392,7 +466,9 @@ static int dw_i2s_probe(struct platform_device *pdev) return PTR_ERR(dev->i2s_base); dev->dev = &pdev->dev; - dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); + ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); + if (ret < 0) + return ret; dev->capability = pdata->cap; dev->i2s_clk_cfg = pdata->i2s_clk_cfg; -- cgit v0.10.2 From 3a19272a507cd5ad214e0082c3ff7355dfe71329 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Tue, 30 Dec 2014 10:55:44 +0000 Subject: ASoC: dwc: Prepare clock before use Some I2S clocks may require some time to get the clock ready for operation and so need to be prepared before they are enabled. So, prepare the clock as well as enabling it, but combine the two through clk_prepare_enable. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index d3bdc68..a6327cc 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -476,7 +476,7 @@ static int dw_i2s_probe(struct platform_device *pdev) if (IS_ERR(dev->clk)) return PTR_ERR(dev->clk); - ret = clk_enable(dev->clk); + ret = clk_prepare_enable(dev->clk); if (ret < 0) return ret; @@ -491,13 +491,16 @@ static int dw_i2s_probe(struct platform_device *pdev) return 0; err_clk_disable: - clk_disable(dev->clk); + clk_disable_unprepare(dev->clk); return ret; } static int dw_i2s_remove(struct platform_device *pdev) { + struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); + clk_disable_unprepare(dev->clk); return 0; } -- cgit v0.10.2 From 758c2debcbf1a4ba6a787d276ca0fb3333105b06 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Tue, 30 Dec 2014 10:55:46 +0000 Subject: ASoC: dwc: Register components with managed interface Register SOC component using managed interface to simplify error handling and future introduction of device tree. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index a6327cc..1b9b18b 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -481,7 +481,7 @@ static int dw_i2s_probe(struct platform_device *pdev) return ret; dev_set_drvdata(&pdev->dev, dev); - ret = snd_soc_register_component(&pdev->dev, &dw_i2s_component, + ret = devm_snd_soc_register_component(&pdev->dev, &dw_i2s_component, dw_i2s_dai, 1); if (ret != 0) { dev_err(&pdev->dev, "not able to register dai\n"); @@ -499,7 +499,6 @@ static int dw_i2s_remove(struct platform_device *pdev) { struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); - snd_soc_unregister_component(&pdev->dev); clk_disable_unprepare(dev->clk); return 0; -- cgit v0.10.2 From d8b58e0b5322f91eb6fcffc337a74083a24c7149 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Tue, 30 Dec 2014 10:55:47 +0000 Subject: ASoC: dwc: Add documentation for I2S DT Add documentation for Designware I2S hardware block. The block requires one clock (for audio sampling) and DMA channels for receive and transmit. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/designware-i2s.txt b/Documentation/devicetree/bindings/sound/designware-i2s.txt new file mode 100644 index 0000000..7bb5424 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/designware-i2s.txt @@ -0,0 +1,31 @@ +DesignWare I2S controller + +Required properties: + - compatible : Must be "snps,designware-i2s" + - reg : Must contain the I2S core's registers location and length + - clocks : Pairs of phandle and specifier referencing the controller's + clocks. The controller expects one clock: the clock used as the sampling + rate reference clock sample. + - clock-names : "i2sclk" for the sample rate reference clock. + - dmas: Pairs of phandle and specifier for the DMA channels that are used by + the core. The core expects one or two dma channels: one for transmit and + one for receive. + - dma-names : "tx" for the transmit channel, "rx" for the receive channel. + +For more details on the 'dma', 'dma-names', 'clock' and 'clock-names' +properties please check: + * resource-names.txt + * clock/clock-bindings.txt + * dma/dma.txt + +Example: + + soc_i2s: i2s@7ff90000 { + compatible = "snps,designware-i2s"; + reg = <0x0 0x7ff90000 0x0 0x1000>; + clocks = <&scpi_i2sclk 0>; + clock-names = "i2sclk"; + #sound-dai-cells = <0>; + dmas = <&dma0 5>; + dma-names = "tx"; + }; -- cgit v0.10.2 From 0d274544bfee721b22b484a10b1480e237c0e258 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Tue, 30 Dec 2014 10:55:48 +0000 Subject: ASoC: dwc: Add devicetree support for Designware I2S Allow the driver to be configured through a device tree rather than platform data. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown diff --git a/sound/soc/dwc/Kconfig b/sound/soc/dwc/Kconfig index e334900..d50e085 100644 --- a/sound/soc/dwc/Kconfig +++ b/sound/soc/dwc/Kconfig @@ -1,6 +1,7 @@ config SND_DESIGNWARE_I2S tristate "Synopsys I2S Device Driver" depends on CLKDEV_LOOKUP + select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for I2S driver for Synopsys desigwnware I2S device. The device supports upto diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 1b9b18b..adefdf0 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -22,6 +22,7 @@ #include #include #include +#include /* common register for all channel */ #define IER 0x000 @@ -82,6 +83,11 @@ #define MAX_CHANNEL_NUM 8 #define MIN_CHANNEL_NUM 2 +union dw_i2s_snd_dma_data { + struct i2s_dma_data pd; + struct snd_dmaengine_dai_dma_data dt; +}; + struct dw_i2s_dev { void __iomem *i2s_base; struct clk *clk; @@ -90,8 +96,8 @@ struct dw_i2s_dev { struct device *dev; /* data related to DMA transfers b/w i2s and DMAC */ - struct i2s_dma_data play_dma_data; - struct i2s_dma_data capture_dma_data; + union dw_i2s_snd_dma_data play_dma_data; + union dw_i2s_snd_dma_data capture_dma_data; struct i2s_clk_config_data config; int (*i2s_clk_cfg)(struct i2s_clk_config_data *config); }; @@ -178,7 +184,7 @@ static int dw_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); - struct i2s_dma_data *dma_data = NULL; + union dw_i2s_snd_dma_data *dma_data = NULL; if (!(dev->capability & DWC_I2S_RECORD) && (substream->stream == SNDRV_PCM_STREAM_CAPTURE)) @@ -270,13 +276,21 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, config->sample_rate = params_rate(params); - if (!dev->i2s_clk_cfg) - return -EINVAL; + if (dev->i2s_clk_cfg) { + ret = dev->i2s_clk_cfg(config); + if (ret < 0) { + dev_err(dev->dev, "runtime audio clk config fail\n"); + return ret; + } + } else { + u32 bitclk = config->sample_rate * config->data_width * 2; - ret = dev->i2s_clk_cfg(config); - if (ret < 0) { - dev_err(dev->dev, "runtime audio clk config fail\n"); - return ret; + ret = clk_set_rate(dev->clk, bitclk); + if (ret) { + dev_err(dev->dev, "Can't set I2S clock rate: %d\n", + ret); + return ret; + } } return 0; @@ -357,6 +371,11 @@ static int dw_i2s_resume(struct snd_soc_dai *dai) * block parameter. */ +/* Maximum bit resolution of a channel - not uniformly spaced */ +static const u32 fifo_width[COMP_MAX_WORDSIZE] = { + 12, 16, 20, 24, 32, 0, 0, 0 +}; + /* Width of (DMA) bus */ static const u32 bus_widths[COMP_MAX_DATA_WIDTH] = { DMA_SLAVE_BUSWIDTH_1_BYTE, @@ -377,10 +396,9 @@ static const u32 formats[COMP_MAX_WORDSIZE] = { 0 }; -static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, +static int dw_configure_dai(struct dw_i2s_dev *dev, struct snd_soc_dai_driver *dw_i2s_dai, - struct resource *res, - const struct i2s_platform_data *pdata) + unsigned int rates) { /* * Read component parameter registers to extract @@ -388,23 +406,7 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, */ u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2); - u32 idx = COMP1_APB_DATA_WIDTH(comp1); - - if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) - return -EINVAL; - - /* Set DMA slaves info */ - - dev->play_dma_data.data = pdata->play_dma_data; - dev->capture_dma_data.data = pdata->capture_dma_data; - dev->play_dma_data.addr = res->start + I2S_TXDMA; - dev->capture_dma_data.addr = res->start + I2S_RXDMA; - dev->play_dma_data.max_burst = 16; - dev->capture_dma_data.max_burst = 16; - dev->play_dma_data.addr_width = bus_widths[idx]; - dev->capture_dma_data.addr_width = bus_widths[idx]; - dev->play_dma_data.filter = pdata->filter; - dev->capture_dma_data.filter = pdata->filter; + u32 idx; if (COMP1_TX_ENABLED(comp1)) { dev_dbg(dev->dev, " designware: play supported\n"); @@ -415,7 +417,7 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, dw_i2s_dai->playback.channels_max = 1 << (COMP1_TX_CHANNELS(comp1) + 1); dw_i2s_dai->playback.formats = formats[idx]; - dw_i2s_dai->playback.rates = pdata->snd_rates; + dw_i2s_dai->playback.rates = rates; } if (COMP1_RX_ENABLED(comp1)) { @@ -427,10 +429,86 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, dw_i2s_dai->capture.channels_max = 1 << (COMP1_RX_CHANNELS(comp1) + 1); dw_i2s_dai->capture.formats = formats[idx]; - dw_i2s_dai->capture.rates = pdata->snd_rates; + dw_i2s_dai->capture.rates = rates; + } + + return 0; +} + +static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, + struct snd_soc_dai_driver *dw_i2s_dai, + struct resource *res, + const struct i2s_platform_data *pdata) +{ + u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); + u32 idx = COMP1_APB_DATA_WIDTH(comp1); + int ret; + + if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) + return -EINVAL; + + ret = dw_configure_dai(dev, dw_i2s_dai, pdata->snd_rates); + if (ret < 0) + return ret; + + /* Set DMA slaves info */ + dev->play_dma_data.pd.data = pdata->play_dma_data; + dev->capture_dma_data.pd.data = pdata->capture_dma_data; + dev->play_dma_data.pd.addr = res->start + I2S_TXDMA; + dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA; + dev->play_dma_data.pd.max_burst = 16; + dev->capture_dma_data.pd.max_burst = 16; + dev->play_dma_data.pd.addr_width = bus_widths[idx]; + dev->capture_dma_data.pd.addr_width = bus_widths[idx]; + dev->play_dma_data.pd.filter = pdata->filter; + dev->capture_dma_data.pd.filter = pdata->filter; + + return 0; +} + +static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev, + struct snd_soc_dai_driver *dw_i2s_dai, + struct resource *res) +{ + u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); + u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2); + u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1)); + u32 idx = COMP1_APB_DATA_WIDTH(comp1); + u32 idx2; + int ret; + + if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) + return -EINVAL; + + ret = dw_configure_dai(dev, dw_i2s_dai, SNDRV_PCM_RATE_8000_192000); + if (ret < 0) + return ret; + + if (COMP1_TX_ENABLED(comp1)) { + idx2 = COMP1_TX_WORDSIZE_0(comp1); + + dev->capability |= DWC_I2S_PLAY; + dev->play_dma_data.dt.addr = res->start + I2S_TXDMA; + dev->play_dma_data.dt.addr_width = bus_widths[idx]; + dev->play_dma_data.dt.chan_name = "TX"; + dev->play_dma_data.dt.fifo_size = fifo_depth * + (fifo_width[idx2]) >> 8; + dev->play_dma_data.dt.maxburst = 16; + } + if (COMP1_RX_ENABLED(comp1)) { + idx2 = COMP2_RX_WORDSIZE_0(comp2); + + dev->capability |= DWC_I2S_RECORD; + dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA; + dev->capture_dma_data.dt.addr_width = bus_widths[idx]; + dev->capture_dma_data.dt.chan_name = "RX"; + dev->capture_dma_data.dt.fifo_size = fifo_depth * + (fifo_width[idx2] >> 8); + dev->capture_dma_data.dt.maxburst = 16; } return 0; + } static int dw_i2s_probe(struct platform_device *pdev) @@ -441,11 +519,6 @@ static int dw_i2s_probe(struct platform_device *pdev) int ret; struct snd_soc_dai_driver *dw_i2s_dai; - if (!pdata) { - dev_err(&pdev->dev, "Invalid platform data\n"); - return -EINVAL; - } - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) { dev_warn(&pdev->dev, "kzalloc fail\n"); @@ -466,15 +539,28 @@ static int dw_i2s_probe(struct platform_device *pdev) return PTR_ERR(dev->i2s_base); dev->dev = &pdev->dev; - ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); - if (ret < 0) - return ret; + if (pdata) { + ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); + if (ret < 0) + return ret; + + dev->capability = pdata->cap; + dev->i2s_clk_cfg = pdata->i2s_clk_cfg; + if (!dev->i2s_clk_cfg) { + dev_err(&pdev->dev, "no clock configure method\n"); + return -ENODEV; + } + + dev->clk = devm_clk_get(&pdev->dev, NULL); + } else { + ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res); + if (ret < 0) + return ret; - dev->capability = pdata->cap; - dev->i2s_clk_cfg = pdata->i2s_clk_cfg; - dev->clk = devm_clk_get(&pdev->dev, NULL); + dev->clk = devm_clk_get(&pdev->dev, "i2sclk"); + } if (IS_ERR(dev->clk)) - return PTR_ERR(dev->clk); + return PTR_ERR(dev->clk); ret = clk_prepare_enable(dev->clk); if (ret < 0) @@ -488,6 +574,15 @@ static int dw_i2s_probe(struct platform_device *pdev) goto err_clk_disable; } + if (!pdata) { + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) { + dev_err(&pdev->dev, + "Could not register PCM: %d\n", ret); + goto err_clk_disable; + } + } + return 0; err_clk_disable: @@ -504,11 +599,23 @@ static int dw_i2s_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id dw_i2s_of_match[] = { + { .compatible = "snps,designware-i2s", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, dw_i2s_of_match); +#endif + static struct platform_driver dw_i2s_driver = { .probe = dw_i2s_probe, .remove = dw_i2s_remove, .driver = { .name = "designware-i2s", +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(dw_i2s_of_match), +#endif }, }; -- cgit v0.10.2 From 74d23cc704d19732e70ef1579a669f7d5f09dd9a Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Sun, 21 Dec 2014 19:46:56 +0100 Subject: time: move the timecounter/cyclecounter code into its own file. The timecounter code has almost nothing to do with the clocksource code. Let it live in its own file. This will help isolate the timecounter users from the clocksource users in the source tree. Signed-off-by: Richard Cochran Acked-by: Jeff Kirsher Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index f9ec762..2af6aff 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -124,7 +124,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index c3a6072..792ba72 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -22,7 +22,7 @@ #include #include -#include +#include /* compilation time flags */ diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 469691a..df8bbdd 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -16,6 +16,7 @@ #include #include #include +#include #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ defined(CONFIG_M520x) || defined(CONFIG_M532x) || \ diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index 7785240..9416e5a 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 82d891e..ee22da3 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -29,7 +29,7 @@ #include "e1000_mac.h" #include "e1000_82575.h" -#include +#include #include #include #include diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index b6137be..38fc64c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -38,7 +38,7 @@ #include #include -#include +#include #include #include diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h index 1a581ef..69a46b9 100644 --- a/drivers/net/ethernet/ti/cpts.h +++ b/drivers/net/ethernet/ti/cpts.h @@ -27,6 +27,7 @@ #include #include #include +#include struct cpsw_cpts { u32 idver; /* Identification and version */ diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h index 6d26b40..9916d0e 100644 --- a/include/clocksource/arm_arch_timer.h +++ b/include/clocksource/arm_arch_timer.h @@ -16,7 +16,7 @@ #ifndef __CLKSOURCE_ARM_ARCH_TIMER_H #define __CLKSOURCE_ARM_ARCH_TIMER_H -#include +#include #include #define ARCH_TIMER_CTRL_ENABLE (1 << 0) diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index abcafaa..9c78d15 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -18,8 +18,6 @@ #include #include -/* clocksource cycle base type */ -typedef u64 cycle_t; struct clocksource; struct module; @@ -28,106 +26,6 @@ struct module; #endif /** - * struct cyclecounter - hardware abstraction for a free running counter - * Provides completely state-free accessors to the underlying hardware. - * Depending on which hardware it reads, the cycle counter may wrap - * around quickly. Locking rules (if necessary) have to be defined - * by the implementor and user of specific instances of this API. - * - * @read: returns the current cycle value - * @mask: bitmask for two's complement - * subtraction of non 64 bit counters, - * see CLOCKSOURCE_MASK() helper macro - * @mult: cycle to nanosecond multiplier - * @shift: cycle to nanosecond divisor (power of two) - */ -struct cyclecounter { - cycle_t (*read)(const struct cyclecounter *cc); - cycle_t mask; - u32 mult; - u32 shift; -}; - -/** - * struct timecounter - layer above a %struct cyclecounter which counts nanoseconds - * Contains the state needed by timecounter_read() to detect - * cycle counter wrap around. Initialize with - * timecounter_init(). Also used to convert cycle counts into the - * corresponding nanosecond counts with timecounter_cyc2time(). Users - * of this code are responsible for initializing the underlying - * cycle counter hardware, locking issues and reading the time - * more often than the cycle counter wraps around. The nanosecond - * counter will only wrap around after ~585 years. - * - * @cc: the cycle counter used by this instance - * @cycle_last: most recent cycle counter value seen by - * timecounter_read() - * @nsec: continuously increasing count - */ -struct timecounter { - const struct cyclecounter *cc; - cycle_t cycle_last; - u64 nsec; -}; - -/** - * cyclecounter_cyc2ns - converts cycle counter cycles to nanoseconds - * @cc: Pointer to cycle counter. - * @cycles: Cycles - * - * XXX - This could use some mult_lxl_ll() asm optimization. Same code - * as in cyc2ns, but with unsigned result. - */ -static inline u64 cyclecounter_cyc2ns(const struct cyclecounter *cc, - cycle_t cycles) -{ - u64 ret = (u64)cycles; - ret = (ret * cc->mult) >> cc->shift; - return ret; -} - -/** - * timecounter_init - initialize a time counter - * @tc: Pointer to time counter which is to be initialized/reset - * @cc: A cycle counter, ready to be used. - * @start_tstamp: Arbitrary initial time stamp. - * - * After this call the current cycle register (roughly) corresponds to - * the initial time stamp. Every call to timecounter_read() increments - * the time stamp counter by the number of elapsed nanoseconds. - */ -extern void timecounter_init(struct timecounter *tc, - const struct cyclecounter *cc, - u64 start_tstamp); - -/** - * timecounter_read - return nanoseconds elapsed since timecounter_init() - * plus the initial time stamp - * @tc: Pointer to time counter. - * - * In other words, keeps track of time since the same epoch as - * the function which generated the initial time stamp. - */ -extern u64 timecounter_read(struct timecounter *tc); - -/** - * timecounter_cyc2time - convert a cycle counter to same - * time base as values returned by - * timecounter_read() - * @tc: Pointer to time counter. - * @cycle_tstamp: a value returned by tc->cc->read() - * - * Cycle counts that are converted correctly as long as they - * fall into the interval [-1/2 max cycle count, +1/2 max cycle count], - * with "max cycle count" == cs->mask+1. - * - * This allows conversion of cycle counter values which were generated - * in the past. - */ -extern u64 timecounter_cyc2time(struct timecounter *tc, - cycle_t cycle_tstamp); - -/** * struct clocksource - hardware abstraction for a free running counter * Provides mostly state-free accessors to the underlying hardware. * This is the structure used for system time. diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 25c791e..f1e41b3 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -42,7 +42,7 @@ #include -#include +#include #define MAX_MSIX_P_PORT 17 #define MAX_MSIX 64 diff --git a/include/linux/timecounter.h b/include/linux/timecounter.h new file mode 100644 index 0000000..146f07a --- /dev/null +++ b/include/linux/timecounter.h @@ -0,0 +1,122 @@ +/* + * linux/include/linux/timecounter.h + * + * based on code that migrated away from + * linux/include/linux/clocksource.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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_TIMECOUNTER_H +#define _LINUX_TIMECOUNTER_H + +#include + +/** + * struct cyclecounter - hardware abstraction for a free running counter + * Provides completely state-free accessors to the underlying hardware. + * Depending on which hardware it reads, the cycle counter may wrap + * around quickly. Locking rules (if necessary) have to be defined + * by the implementor and user of specific instances of this API. + * + * @read: returns the current cycle value + * @mask: bitmask for two's complement + * subtraction of non 64 bit counters, + * see CLOCKSOURCE_MASK() helper macro + * @mult: cycle to nanosecond multiplier + * @shift: cycle to nanosecond divisor (power of two) + */ +struct cyclecounter { + cycle_t (*read)(const struct cyclecounter *cc); + cycle_t mask; + u32 mult; + u32 shift; +}; + +/** + * struct timecounter - layer above a %struct cyclecounter which counts nanoseconds + * Contains the state needed by timecounter_read() to detect + * cycle counter wrap around. Initialize with + * timecounter_init(). Also used to convert cycle counts into the + * corresponding nanosecond counts with timecounter_cyc2time(). Users + * of this code are responsible for initializing the underlying + * cycle counter hardware, locking issues and reading the time + * more often than the cycle counter wraps around. The nanosecond + * counter will only wrap around after ~585 years. + * + * @cc: the cycle counter used by this instance + * @cycle_last: most recent cycle counter value seen by + * timecounter_read() + * @nsec: continuously increasing count + */ +struct timecounter { + const struct cyclecounter *cc; + cycle_t cycle_last; + u64 nsec; +}; + +/** + * cyclecounter_cyc2ns - converts cycle counter cycles to nanoseconds + * @cc: Pointer to cycle counter. + * @cycles: Cycles + * + * XXX - This could use some mult_lxl_ll() asm optimization. Same code + * as in cyc2ns, but with unsigned result. + */ +static inline u64 cyclecounter_cyc2ns(const struct cyclecounter *cc, + cycle_t cycles) +{ + u64 ret = (u64)cycles; + ret = (ret * cc->mult) >> cc->shift; + return ret; +} + +/** + * timecounter_init - initialize a time counter + * @tc: Pointer to time counter which is to be initialized/reset + * @cc: A cycle counter, ready to be used. + * @start_tstamp: Arbitrary initial time stamp. + * + * After this call the current cycle register (roughly) corresponds to + * the initial time stamp. Every call to timecounter_read() increments + * the time stamp counter by the number of elapsed nanoseconds. + */ +extern void timecounter_init(struct timecounter *tc, + const struct cyclecounter *cc, + u64 start_tstamp); + +/** + * timecounter_read - return nanoseconds elapsed since timecounter_init() + * plus the initial time stamp + * @tc: Pointer to time counter. + * + * In other words, keeps track of time since the same epoch as + * the function which generated the initial time stamp. + */ +extern u64 timecounter_read(struct timecounter *tc); + +/** + * timecounter_cyc2time - convert a cycle counter to same + * time base as values returned by + * timecounter_read() + * @tc: Pointer to time counter. + * @cycle_tstamp: a value returned by tc->cc->read() + * + * Cycle counts that are converted correctly as long as they + * fall into the interval [-1/2 max cycle count, +1/2 max cycle count], + * with "max cycle count" == cs->mask+1. + * + * This allows conversion of cycle counter values which were generated + * in the past. + */ +extern u64 timecounter_cyc2time(struct timecounter *tc, + cycle_t cycle_tstamp); + +#endif diff --git a/include/linux/types.h b/include/linux/types.h index a0bb704..6232382 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -213,5 +213,8 @@ struct callback_head { }; #define rcu_head callback_head +/* clocksource cycle base type */ +typedef u64 cycle_t; + #endif /* __ASSEMBLY__ */ #endif /* _LINUX_TYPES_H */ diff --git a/kernel/time/Makefile b/kernel/time/Makefile index f622cf2..c09c078 100644 --- a/kernel/time/Makefile +++ b/kernel/time/Makefile @@ -1,6 +1,6 @@ obj-y += time.o timer.o hrtimer.o itimer.o posix-timers.o posix-cpu-timers.o obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o -obj-y += timeconv.o posix-clock.o alarmtimer.o +obj-y += timeconv.o timecounter.o posix-clock.o alarmtimer.o obj-$(CONFIG_GENERIC_CLOCKEVENTS_BUILD) += clockevents.o obj-$(CONFIG_GENERIC_CLOCKEVENTS) += tick-common.o diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index b79f39b..4892352 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -34,82 +34,6 @@ #include "tick-internal.h" #include "timekeeping_internal.h" -void timecounter_init(struct timecounter *tc, - const struct cyclecounter *cc, - u64 start_tstamp) -{ - tc->cc = cc; - tc->cycle_last = cc->read(cc); - tc->nsec = start_tstamp; -} -EXPORT_SYMBOL_GPL(timecounter_init); - -/** - * timecounter_read_delta - get nanoseconds since last call of this function - * @tc: Pointer to time counter - * - * When the underlying cycle counter runs over, this will be handled - * correctly as long as it does not run over more than once between - * calls. - * - * The first call to this function for a new time counter initializes - * the time tracking and returns an undefined result. - */ -static u64 timecounter_read_delta(struct timecounter *tc) -{ - cycle_t cycle_now, cycle_delta; - u64 ns_offset; - - /* read cycle counter: */ - cycle_now = tc->cc->read(tc->cc); - - /* calculate the delta since the last timecounter_read_delta(): */ - cycle_delta = (cycle_now - tc->cycle_last) & tc->cc->mask; - - /* convert to nanoseconds: */ - ns_offset = cyclecounter_cyc2ns(tc->cc, cycle_delta); - - /* update time stamp of timecounter_read_delta() call: */ - tc->cycle_last = cycle_now; - - return ns_offset; -} - -u64 timecounter_read(struct timecounter *tc) -{ - u64 nsec; - - /* increment time by nanoseconds since last call */ - nsec = timecounter_read_delta(tc); - nsec += tc->nsec; - tc->nsec = nsec; - - return nsec; -} -EXPORT_SYMBOL_GPL(timecounter_read); - -u64 timecounter_cyc2time(struct timecounter *tc, - cycle_t cycle_tstamp) -{ - u64 cycle_delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask; - u64 nsec; - - /* - * Instead of always treating cycle_tstamp as more recent - * than tc->cycle_last, detect when it is too far in the - * future and treat it as old time stamp instead. - */ - if (cycle_delta > tc->cc->mask / 2) { - cycle_delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask; - nsec = tc->nsec - cyclecounter_cyc2ns(tc->cc, cycle_delta); - } else { - nsec = cyclecounter_cyc2ns(tc->cc, cycle_delta) + tc->nsec; - } - - return nsec; -} -EXPORT_SYMBOL_GPL(timecounter_cyc2time); - /** * clocks_calc_mult_shift - calculate mult/shift factors for scaled math of clocks * @mult: pointer to mult variable diff --git a/kernel/time/timecounter.c b/kernel/time/timecounter.c new file mode 100644 index 0000000..59a1ec3 --- /dev/null +++ b/kernel/time/timecounter.c @@ -0,0 +1,95 @@ +/* + * linux/kernel/time/timecounter.c + * + * based on code that migrated away from + * linux/kernel/time/clocksource.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +void timecounter_init(struct timecounter *tc, + const struct cyclecounter *cc, + u64 start_tstamp) +{ + tc->cc = cc; + tc->cycle_last = cc->read(cc); + tc->nsec = start_tstamp; +} +EXPORT_SYMBOL_GPL(timecounter_init); + +/** + * timecounter_read_delta - get nanoseconds since last call of this function + * @tc: Pointer to time counter + * + * When the underlying cycle counter runs over, this will be handled + * correctly as long as it does not run over more than once between + * calls. + * + * The first call to this function for a new time counter initializes + * the time tracking and returns an undefined result. + */ +static u64 timecounter_read_delta(struct timecounter *tc) +{ + cycle_t cycle_now, cycle_delta; + u64 ns_offset; + + /* read cycle counter: */ + cycle_now = tc->cc->read(tc->cc); + + /* calculate the delta since the last timecounter_read_delta(): */ + cycle_delta = (cycle_now - tc->cycle_last) & tc->cc->mask; + + /* convert to nanoseconds: */ + ns_offset = cyclecounter_cyc2ns(tc->cc, cycle_delta); + + /* update time stamp of timecounter_read_delta() call: */ + tc->cycle_last = cycle_now; + + return ns_offset; +} + +u64 timecounter_read(struct timecounter *tc) +{ + u64 nsec; + + /* increment time by nanoseconds since last call */ + nsec = timecounter_read_delta(tc); + nsec += tc->nsec; + tc->nsec = nsec; + + return nsec; +} +EXPORT_SYMBOL_GPL(timecounter_read); + +u64 timecounter_cyc2time(struct timecounter *tc, + cycle_t cycle_tstamp) +{ + u64 cycle_delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask; + u64 nsec; + + /* + * Instead of always treating cycle_tstamp as more recent + * than tc->cycle_last, detect when it is too far in the + * future and treat it as old time stamp instead. + */ + if (cycle_delta > tc->cc->mask / 2) { + cycle_delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask; + nsec = tc->nsec - cyclecounter_cyc2ns(tc->cc, cycle_delta); + } else { + nsec = cyclecounter_cyc2ns(tc->cc, cycle_delta) + tc->nsec; + } + + return nsec; +} +EXPORT_SYMBOL_GPL(timecounter_cyc2time); diff --git a/sound/pci/hda/hda_priv.h b/sound/pci/hda/hda_priv.h index 166e3e8..daf4582 100644 --- a/sound/pci/hda/hda_priv.h +++ b/sound/pci/hda/hda_priv.h @@ -15,7 +15,7 @@ #ifndef __SOUND_HDA_PRIV_H #define __SOUND_HDA_PRIV_H -#include +#include #include #include -- cgit v0.10.2 From 796c1efd6fa0ed696d550b68f4410ab1a1749d01 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Sun, 21 Dec 2014 19:46:57 +0100 Subject: timecounter: provide a helper function to shift the time. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some PTP Hardware Clock drivers use a struct timecounter to represent their clock. To adjust the time by a given offset, these drivers all perform a two step read/write of their timecounter. However, it is better and simpler just to adjust the offset in one step. This patch introduces a little routine to help drivers implement the adjtime method. Suggested-by: Janusz Użycki Signed-off-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/include/linux/timecounter.h b/include/linux/timecounter.h index 146f07a..af3dfa4 100644 --- a/include/linux/timecounter.h +++ b/include/linux/timecounter.h @@ -79,6 +79,15 @@ static inline u64 cyclecounter_cyc2ns(const struct cyclecounter *cc, } /** + * timecounter_adjtime - Shifts the time of the clock. + * @delta: Desired change in nanoseconds. + */ +static inline void timecounter_adjtime(struct timecounter *tc, s64 delta) +{ + tc->nsec += delta; +} + +/** * timecounter_init - initialize a time counter * @tc: Pointer to time counter which is to be initialized/reset * @cc: A cycle counter, ready to be used. -- cgit v0.10.2 From 8b976e97772cfb7e0e4eb8682d5017861dbd8796 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Sun, 21 Dec 2014 19:46:58 +0100 Subject: net: xgbe: convert to timecounter adjtime. This patch changes the driver to use the new and improved method for adjusting the offset of a timecounter. Compile tested only. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c b/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c index a1bf9d1c..f5acf4c 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c @@ -171,15 +171,9 @@ static int xgbe_adjtime(struct ptp_clock_info *info, s64 delta) struct xgbe_prv_data, ptp_clock_info); unsigned long flags; - u64 nsec; spin_lock_irqsave(&pdata->tstamp_lock, flags); - - nsec = timecounter_read(&pdata->tstamp_tc); - - nsec += delta; - timecounter_init(&pdata->tstamp_tc, &pdata->tstamp_cc, nsec); - + timecounter_adjtime(&pdata->tstamp_tc, delta); spin_unlock_irqrestore(&pdata->tstamp_lock, flags); return 0; -- cgit v0.10.2 From 2e5601f9aca1a83980a031f9385a95062d12e689 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Sun, 21 Dec 2014 19:46:59 +0100 Subject: net: bnx2x: convert to timecounter adjtime. This patch changes the driver to use the new and improved method for adjusting the offset of a timecounter. Compile tested only. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 72eef9f..2c95132 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -13267,14 +13267,10 @@ static int bnx2x_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) static int bnx2x_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) { struct bnx2x *bp = container_of(ptp, struct bnx2x, ptp_clock_info); - u64 now; DP(BNX2X_MSG_PTP, "PTP adjtime called, delta = %llx\n", delta); - now = timecounter_read(&bp->timecounter); - now += delta; - /* Re-init the timecounter */ - timecounter_init(&bp->timecounter, &bp->cyclecounter, now); + timecounter_adjtime(&bp->timecounter, delta); return 0; } -- cgit v0.10.2 From 59e16961c688a50d4dae9827cc3e5c69ae98e0cb Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Sun, 21 Dec 2014 19:47:00 +0100 Subject: net: fec: convert to timecounter adjtime. This patch changes the driver to use the new and improved method for adjusting the offset of a timecounter. Compile tested only. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index 992c8c3..1f9cf23 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -374,23 +374,9 @@ static int fec_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) struct fec_enet_private *fep = container_of(ptp, struct fec_enet_private, ptp_caps); unsigned long flags; - u64 now; - u32 counter; spin_lock_irqsave(&fep->tmreg_lock, flags); - - now = timecounter_read(&fep->tc); - now += delta; - - /* Get the timer value based on adjusted timestamp. - * Update the counter with the masked value. - */ - counter = now & fep->cc.mask; - writel(counter, fep->hwp + FEC_ATIME); - - /* reset the timecounter */ - timecounter_init(&fep->tc, &fep->cc, now); - + timecounter_adjtime(&fep->tc, delta); spin_unlock_irqrestore(&fep->tmreg_lock, flags); return 0; -- cgit v0.10.2 From f4de2b95688eba646d677dd2bed8c49540c00f1e Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Sun, 21 Dec 2014 19:47:01 +0100 Subject: net: e1000e: convert to timecounter adjtime. This patch changes the driver to use the new and improved method for adjusting the offset of a timecounter. Signed-off-by: Richard Cochran Acked-by: Jeff Kirsher Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/e1000e/ptp.c b/drivers/net/ethernet/intel/e1000e/ptp.c index fb1a914..978ef9c 100644 --- a/drivers/net/ethernet/intel/e1000e/ptp.c +++ b/drivers/net/ethernet/intel/e1000e/ptp.c @@ -90,12 +90,9 @@ static int e1000e_phc_adjtime(struct ptp_clock_info *ptp, s64 delta) struct e1000_adapter *adapter = container_of(ptp, struct e1000_adapter, ptp_clock_info); unsigned long flags; - s64 now; spin_lock_irqsave(&adapter->systim_lock, flags); - now = timecounter_read(&adapter->tc); - now += delta; - timecounter_init(&adapter->tc, &adapter->cc, now); + timecounter_adjtime(&adapter->tc, delta); spin_unlock_irqrestore(&adapter->systim_lock, flags); return 0; -- cgit v0.10.2 From 5ee698e3677a1af8b731cc891e94be33b6572cfd Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Sun, 21 Dec 2014 19:47:02 +0100 Subject: net: igb: convert to timecounter adjtime. This patch changes the driver to use the new and improved method for adjusting the offset of a timecounter. Signed-off-by: Richard Cochran Acked-by: Jeff Kirsher Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index 794c139..1d27f2d 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -256,14 +256,9 @@ static int igb_ptp_adjtime_82576(struct ptp_clock_info *ptp, s64 delta) struct igb_adapter *igb = container_of(ptp, struct igb_adapter, ptp_caps); unsigned long flags; - s64 now; spin_lock_irqsave(&igb->tmreg_lock, flags); - - now = timecounter_read(&igb->tc); - now += delta; - timecounter_init(&igb->tc, &igb->cc, now); - + timecounter_adjtime(&igb->tc, delta); spin_unlock_irqrestore(&igb->tmreg_lock, flags); return 0; -- cgit v0.10.2 From 826ef90dc4727c9f9cd999c830023e54d9ea3f58 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Sun, 21 Dec 2014 19:47:03 +0100 Subject: net: ixgbe: convert to timecounter adjtime. This patch changes the driver to use the new and improved method for adjusting the offset of a timecounter. Compile tested only. Signed-off-by: Richard Cochran Acked-by: Jeff Kirsher Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c index 5fd4b52..47c29ea 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c @@ -261,18 +261,9 @@ static int ixgbe_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) struct ixgbe_adapter *adapter = container_of(ptp, struct ixgbe_adapter, ptp_caps); unsigned long flags; - u64 now; spin_lock_irqsave(&adapter->tmreg_lock, flags); - - now = timecounter_read(&adapter->tc); - now += delta; - - /* reset the timecounter */ - timecounter_init(&adapter->tc, - &adapter->cc, - now); - + timecounter_adjtime(&adapter->tc, delta); spin_unlock_irqrestore(&adapter->tmreg_lock, flags); ixgbe_ptp_setup_sdp(adapter); -- cgit v0.10.2 From ce51ff0937bd5596aeafd67f5a60d36016d5e040 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Sun, 21 Dec 2014 19:47:04 +0100 Subject: net: mlx4: convert to timecounter adjtime. This patch changes the driver to use the new and improved method for adjusting the offset of a timecounter. Compile tested only. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/en_clock.c b/drivers/net/ethernet/mellanox/mlx4/en_clock.c index 9990144..df35d0e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_clock.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_clock.c @@ -147,12 +147,9 @@ static int mlx4_en_phc_adjtime(struct ptp_clock_info *ptp, s64 delta) struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev, ptp_clock_info); unsigned long flags; - s64 now; write_lock_irqsave(&mdev->clock_lock, flags); - now = timecounter_read(&mdev->clock); - now += delta; - timecounter_init(&mdev->clock, &mdev->cycles, now); + timecounter_adjtime(&mdev->clock, delta); write_unlock_irqrestore(&mdev->clock_lock, flags); return 0; -- cgit v0.10.2 From f25a30be359d0535fb1c7c1619cabb0ad17cfbf1 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Sun, 21 Dec 2014 19:47:05 +0100 Subject: net: cpts: convert to timecounter adjtime. This patch changes the driver to use the new and improved method for adjusting the offset of a timecounter. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 4a4388b..fbe42cb 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -157,14 +157,11 @@ static int cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) static int cpts_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) { - s64 now; unsigned long flags; struct cpts *cpts = container_of(ptp, struct cpts, info); spin_lock_irqsave(&cpts->lock, flags); - now = timecounter_read(&cpts->tc); - now += delta; - timecounter_init(&cpts->tc, &cpts->cc, now); + timecounter_adjtime(&cpts->tc, delta); spin_unlock_irqrestore(&cpts->lock, flags); return 0; -- cgit v0.10.2 From 2eebdde6528a722fbf8e2cffcf7aa52cbb4c2de0 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Sun, 21 Dec 2014 19:47:06 +0100 Subject: timecounter: keep track of accumulated fractional nanoseconds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current timecounter implementation will drop a variable amount of resolution, depending on the magnitude of the time delta. In other words, reading the clock too often or too close to a time stamp conversion will introduce errors into the time values. This patch fixes the issue by introducing a fractional nanosecond field that accumulates the low order bits. Reported-by: Janusz Użycki Signed-off-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/en_clock.c b/drivers/net/ethernet/mellanox/mlx4/en_clock.c index df35d0e..e9cce4f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_clock.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_clock.c @@ -240,7 +240,7 @@ void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev) { struct mlx4_dev *dev = mdev->dev; unsigned long flags; - u64 ns; + u64 ns, zero = 0; rwlock_init(&mdev->clock_lock); @@ -265,7 +265,7 @@ void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev) /* Calculate period in seconds to call the overflow watchdog - to make * sure counter is checked at least once every wrap around. */ - ns = cyclecounter_cyc2ns(&mdev->cycles, mdev->cycles.mask); + ns = cyclecounter_cyc2ns(&mdev->cycles, mdev->cycles.mask, zero, &zero); do_div(ns, NSEC_PER_SEC / 2 / HZ); mdev->overflow_period = ns; diff --git a/include/linux/timecounter.h b/include/linux/timecounter.h index af3dfa4..74f4549 100644 --- a/include/linux/timecounter.h +++ b/include/linux/timecounter.h @@ -55,27 +55,32 @@ struct cyclecounter { * @cycle_last: most recent cycle counter value seen by * timecounter_read() * @nsec: continuously increasing count + * @mask: bit mask for maintaining the 'frac' field + * @frac: accumulated fractional nanoseconds */ struct timecounter { const struct cyclecounter *cc; cycle_t cycle_last; u64 nsec; + u64 mask; + u64 frac; }; /** * cyclecounter_cyc2ns - converts cycle counter cycles to nanoseconds * @cc: Pointer to cycle counter. * @cycles: Cycles - * - * XXX - This could use some mult_lxl_ll() asm optimization. Same code - * as in cyc2ns, but with unsigned result. + * @mask: bit mask for maintaining the 'frac' field + * @frac: pointer to storage for the fractional nanoseconds. */ static inline u64 cyclecounter_cyc2ns(const struct cyclecounter *cc, - cycle_t cycles) + cycle_t cycles, u64 mask, u64 *frac) { - u64 ret = (u64)cycles; - ret = (ret * cc->mult) >> cc->shift; - return ret; + u64 ns = (u64) cycles; + + ns = (ns * cc->mult) + *frac; + *frac = ns & mask; + return ns >> cc->shift; } /** diff --git a/kernel/time/timecounter.c b/kernel/time/timecounter.c index 59a1ec3..4687b31 100644 --- a/kernel/time/timecounter.c +++ b/kernel/time/timecounter.c @@ -25,6 +25,8 @@ void timecounter_init(struct timecounter *tc, tc->cc = cc; tc->cycle_last = cc->read(cc); tc->nsec = start_tstamp; + tc->mask = (1ULL << cc->shift) - 1; + tc->frac = 0; } EXPORT_SYMBOL_GPL(timecounter_init); @@ -51,7 +53,8 @@ static u64 timecounter_read_delta(struct timecounter *tc) cycle_delta = (cycle_now - tc->cycle_last) & tc->cc->mask; /* convert to nanoseconds: */ - ns_offset = cyclecounter_cyc2ns(tc->cc, cycle_delta); + ns_offset = cyclecounter_cyc2ns(tc->cc, cycle_delta, + tc->mask, &tc->frac); /* update time stamp of timecounter_read_delta() call: */ tc->cycle_last = cycle_now; @@ -72,22 +75,36 @@ u64 timecounter_read(struct timecounter *tc) } EXPORT_SYMBOL_GPL(timecounter_read); +/* + * This is like cyclecounter_cyc2ns(), but it is used for computing a + * time previous to the time stored in the cycle counter. + */ +static u64 cc_cyc2ns_backwards(const struct cyclecounter *cc, + cycle_t cycles, u64 mask, u64 frac) +{ + u64 ns = (u64) cycles; + + ns = ((ns * cc->mult) - frac) >> cc->shift; + + return ns; +} + u64 timecounter_cyc2time(struct timecounter *tc, cycle_t cycle_tstamp) { - u64 cycle_delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask; - u64 nsec; + u64 delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask; + u64 nsec = tc->nsec, frac = tc->frac; /* * Instead of always treating cycle_tstamp as more recent * than tc->cycle_last, detect when it is too far in the * future and treat it as old time stamp instead. */ - if (cycle_delta > tc->cc->mask / 2) { - cycle_delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask; - nsec = tc->nsec - cyclecounter_cyc2ns(tc->cc, cycle_delta); + if (delta > tc->cc->mask / 2) { + delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask; + nsec -= cc_cyc2ns_backwards(tc->cc, delta, tc->mask, frac); } else { - nsec = cyclecounter_cyc2ns(tc->cc, cycle_delta) + tc->nsec; + nsec += cyclecounter_cyc2ns(tc->cc, delta, tc->mask, &frac); } return nsec; diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c index 1c0772b..6e54f35 100644 --- a/virt/kvm/arm/arch_timer.c +++ b/virt/kvm/arm/arch_timer.c @@ -152,7 +152,8 @@ void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) return; } - ns = cyclecounter_cyc2ns(timecounter->cc, cval - now); + ns = cyclecounter_cyc2ns(timecounter->cc, cval - now, timecounter->mask, + &timecounter->frac); timer_arm(timer, ns); } -- cgit v0.10.2 From d0e520ef63a23f8bd7806450f5d2820d35594ba9 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 26 Dec 2014 15:35:34 +0100 Subject: atheros: atlx: Use setup_timer Convert a call to init_timer and accompanying intializations of the timer's data and function fields to a call to setup_timer. A simplified version of the semantic match that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression t,f,d; @@ -init_timer(&t); +setup_timer(&t,f,d); -t.function = f; -t.data = d; // Signed-off-by: Julia Lawall Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/atheros/atlx/atl2.c b/drivers/net/ethernet/atheros/atlx/atl2.c index 84a09e8..482a7cab 100644 --- a/drivers/net/ethernet/atheros/atlx/atl2.c +++ b/drivers/net/ethernet/atheros/atlx/atl2.c @@ -1436,13 +1436,11 @@ static int atl2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) atl2_check_options(adapter); - init_timer(&adapter->watchdog_timer); - adapter->watchdog_timer.function = atl2_watchdog; - adapter->watchdog_timer.data = (unsigned long) adapter; + setup_timer(&adapter->watchdog_timer, atl2_watchdog, + (unsigned long)adapter); - init_timer(&adapter->phy_config_timer); - adapter->phy_config_timer.function = atl2_phy_config; - adapter->phy_config_timer.data = (unsigned long) adapter; + setup_timer(&adapter->phy_config_timer, atl2_phy_config, + (unsigned long)adapter); INIT_WORK(&adapter->reset_task, atl2_reset_task); INIT_WORK(&adapter->link_chg_task, atl2_link_chg_task); -- cgit v0.10.2 From cc73e41f17be97eb4e387610bd95e952127e3216 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 26 Dec 2014 15:35:35 +0100 Subject: atl1e: Use setup_timer Convert a call to init_timer and accompanying intializations of the timer's data and function fields to a call to setup_timer. A simplified version of the semantic match that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression t,f,d; @@ -init_timer(&t); +setup_timer(&t,f,d); -t.function = f; -t.data = d; // Signed-off-by: Julia Lawall Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c index 2326579..c88abf5 100644 --- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c @@ -2373,9 +2373,8 @@ static int atl1e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netif_napi_add(netdev, &adapter->napi, atl1e_clean, 64); - init_timer(&adapter->phy_config_timer); - adapter->phy_config_timer.function = atl1e_phy_config; - adapter->phy_config_timer.data = (unsigned long) adapter; + setup_timer(&adapter->phy_config_timer, atl1e_phy_config, + (unsigned long)adapter); /* get user settings */ atl1e_check_options(adapter); -- cgit v0.10.2 From fea3cb063bf9b9d6b4efd5c973217a708812f5e2 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 26 Dec 2014 15:35:42 +0100 Subject: ksz884x: Use setup_timer Convert a call to init_timer and accompanying intializations of the timer's data and function fields to a call to setup_timer. A simplified version of the semantic match that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression t,f,d; @@ -init_timer(&t); +setup_timer(&t,f,d); -t.function = f; -t.data = d; // Signed-off-by: Julia Lawall Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c index 2fa6ae0..10988fb 100644 --- a/drivers/net/ethernet/micrel/ksz884x.c +++ b/drivers/net/ethernet/micrel/ksz884x.c @@ -4342,9 +4342,7 @@ static void ksz_init_timer(struct ksz_timer_info *info, int period, { info->max = 0; info->period = period; - init_timer(&info->timer); - info->timer.function = function; - info->timer.data = (unsigned long) data; + setup_timer(&info->timer, function, (unsigned long)data); } static void ksz_update_timer(struct ksz_timer_info *info) -- cgit v0.10.2 From 12141337af54b57e222b7a52d52415aa5e332ff0 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 26 Dec 2014 15:35:46 +0100 Subject: net: sxgbe: Use setup_timer Convert a call to init_timer and accompanying intializations of the timer's data and function fields to a call to setup_timer. A simplified version of the semantic match that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression t,f,d; @@ -init_timer(&t); +setup_timer(&t,f,d); -t.function = f; -t.data = d; // Signed-off-by: Julia Lawall Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c index 6984944..b6612d6 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c @@ -133,9 +133,8 @@ bool sxgbe_eee_init(struct sxgbe_priv_data * const priv) return false; priv->eee_active = 1; - init_timer(&priv->eee_ctrl_timer); - priv->eee_ctrl_timer.function = sxgbe_eee_ctrl_timer; - priv->eee_ctrl_timer.data = (unsigned long)priv; + setup_timer(&priv->eee_ctrl_timer, sxgbe_eee_ctrl_timer, + (unsigned long)priv); priv->eee_ctrl_timer.expires = SXGBE_LPI_TIMER(eee_timer); add_timer(&priv->eee_ctrl_timer); @@ -1009,10 +1008,9 @@ static void sxgbe_tx_init_coalesce(struct sxgbe_priv_data *priv) struct sxgbe_tx_queue *p = priv->txq[queue_num]; p->tx_coal_frames = SXGBE_TX_FRAMES; p->tx_coal_timer = SXGBE_COAL_TX_TIMER; - init_timer(&p->txtimer); + setup_timer(&p->txtimer, sxgbe_tx_timer, + (unsigned long)&priv->txq[queue_num]); p->txtimer.expires = SXGBE_COAL_TIMER(p->tx_coal_timer); - p->txtimer.data = (unsigned long)&priv->txq[queue_num]; - p->txtimer.function = sxgbe_tx_timer; add_timer(&p->txtimer); } } -- cgit v0.10.2 From dd450777990baae668c1143064f2f234dbab1b9b Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Wed, 24 Dec 2014 01:14:14 +0300 Subject: arm: sa1100: move irda header to linux/platform_data In the end asm/mach/irda.h header is not used by anybody except sa1100. Move the header to the platform data includes dir and rename it to irda-sa11x0.h. Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: David S. Miller diff --git a/arch/arm/include/asm/mach/irda.h b/arch/arm/include/asm/mach/irda.h deleted file mode 100644 index 38f77b5..0000000 --- a/arch/arm/include/asm/mach/irda.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * arch/arm/include/asm/mach/irda.h - * - * Copyright (C) 2004 Russell King. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef __ASM_ARM_MACH_IRDA_H -#define __ASM_ARM_MACH_IRDA_H - -struct irda_platform_data { - int (*startup)(struct device *); - void (*shutdown)(struct device *); - int (*set_power)(struct device *, unsigned int state); - void (*set_speed)(struct device *, unsigned int speed); -}; - -#endif diff --git a/arch/arm/mach-sa1100/assabet.c b/arch/arm/mach-sa1100/assabet.c index 7dd894e..d28ecb9 100644 --- a/arch/arm/mach-sa1100/assabet.c +++ b/arch/arm/mach-sa1100/assabet.c @@ -37,7 +37,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-sa1100/collie.c b/arch/arm/mach-sa1100/collie.c index b90c7d8..7fcbe3d 100644 --- a/arch/arm/mach-sa1100/collie.c +++ b/arch/arm/mach-sa1100/collie.c @@ -43,7 +43,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/arm/mach-sa1100/h3100.c b/arch/arm/mach-sa1100/h3100.c index 3c43219..c6b4120 100644 --- a/arch/arm/mach-sa1100/h3100.c +++ b/arch/arm/mach-sa1100/h3100.c @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include diff --git a/arch/arm/mach-sa1100/h3600.c b/arch/arm/mach-sa1100/h3600.c index 5be54c2..118338e 100644 --- a/arch/arm/mach-sa1100/h3600.c +++ b/arch/arm/mach-sa1100/h3600.c @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c index 7b17fa2..b6e44ff 100644 --- a/drivers/net/irda/sa1100_ir.c +++ b/drivers/net/irda/sa1100_ir.c @@ -38,7 +38,7 @@ #include #include -#include +#include static int power_level = 3; static int tx_lpm; diff --git a/include/linux/platform_data/irda-sa11x0.h b/include/linux/platform_data/irda-sa11x0.h new file mode 100644 index 0000000..38f77b5 --- /dev/null +++ b/include/linux/platform_data/irda-sa11x0.h @@ -0,0 +1,20 @@ +/* + * arch/arm/include/asm/mach/irda.h + * + * Copyright (C) 2004 Russell King. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_ARM_MACH_IRDA_H +#define __ASM_ARM_MACH_IRDA_H + +struct irda_platform_data { + int (*startup)(struct device *); + void (*shutdown)(struct device *); + int (*set_power)(struct device *, unsigned int state); + void (*set_speed)(struct device *, unsigned int speed); +}; + +#endif -- cgit v0.10.2 From 41f2f1273caee29ce9e2854c9aba996b56303b4a Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 24 Dec 2014 11:03:52 +0800 Subject: virtio-net: don't do header check for dodgy gso packets There's no need to do header check for virtio-net since: - Host sets dodgy for all gso packets from guest and check the header. - Host should be prepared for all kinds of evil packets from guest, since malicious guest can send any kinds of packet. So this patch sets NETIF_F_GSO_ROBUST for virtio-net to skip the check. Cc: Rusty Russell Cc: Michael S. Tsirkin Acked-by: Michael S. Tsirkin Signed-off-by: Jason Wang Signed-off-by: David S. Miller diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 5ca9771..11e2e81 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1759,6 +1759,8 @@ static int virtnet_probe(struct virtio_device *vdev) if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_ECN)) dev->hw_features |= NETIF_F_TSO_ECN; + dev->features |= NETIF_F_GSO_ROBUST; + if (gso) dev->features |= dev->hw_features & NETIF_F_ALL_TSO; /* (!csum && gso) case will be fixed by register_netdev() */ -- cgit v0.10.2 From 91c53f76352980ef37ed816a73bf356de3d1705a Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Wed, 24 Dec 2014 14:05:44 +0800 Subject: net: gianfar: mark the local functions static Signed-off-by: Kevin Hao Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 5645342..d2a67ed 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -116,7 +116,8 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev); static void gfar_reset_task(struct work_struct *work); static void gfar_timeout(struct net_device *dev); static int gfar_close(struct net_device *dev); -struct sk_buff *gfar_new_skb(struct net_device *dev, dma_addr_t *bufaddr); +static struct sk_buff *gfar_new_skb(struct net_device *dev, + dma_addr_t *bufaddr); static int gfar_set_mac_address(struct net_device *dev); static int gfar_change_mtu(struct net_device *dev, int new_mtu); static irqreturn_t gfar_error(int irq, void *dev_id); @@ -554,7 +555,7 @@ static void gfar_ints_enable(struct gfar_private *priv) } } -void lock_tx_qs(struct gfar_private *priv) +static void lock_tx_qs(struct gfar_private *priv) { int i; @@ -562,7 +563,7 @@ void lock_tx_qs(struct gfar_private *priv) spin_lock(&priv->tx_queue[i]->txlock); } -void unlock_tx_qs(struct gfar_private *priv) +static void unlock_tx_qs(struct gfar_private *priv) { int i; @@ -2671,7 +2672,7 @@ static struct sk_buff *gfar_alloc_skb(struct net_device *dev) return skb; } -struct sk_buff *gfar_new_skb(struct net_device *dev, dma_addr_t *bufaddr) +static struct sk_buff *gfar_new_skb(struct net_device *dev, dma_addr_t *bufaddr) { struct gfar_private *priv = netdev_priv(dev); struct sk_buff *skb; -- cgit v0.10.2 From 03366a33db91abd298457b0f707187247f1a6b7d Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Wed, 24 Dec 2014 14:05:45 +0800 Subject: net: gianfar: add missing __iomem annotation Fix the following spare warning: drivers/net/ethernet/freescale/gianfar.c:3521:60: warning: incorrect type in argument 1 (different address spaces) drivers/net/ethernet/freescale/gianfar.c:3521:60: expected unsigned int [noderef] *addr drivers/net/ethernet/freescale/gianfar.c:3521:60: got unsigned int [usertype] *rfbptr drivers/net/ethernet/freescale/gianfar.c:205:16: warning: incorrect type in assignment (different address spaces) drivers/net/ethernet/freescale/gianfar.c:205:16: expected unsigned int [usertype] *rfbptr drivers/net/ethernet/freescale/gianfar.c:205:16: got unsigned int [noderef] * drivers/net/ethernet/freescale/gianfar.c:2918:44: warning: incorrect type in argument 1 (different address spaces) drivers/net/ethernet/freescale/gianfar.c:2918:44: expected unsigned int [noderef] *addr drivers/net/ethernet/freescale/gianfar.c:2918:44: got unsigned int [usertype] *rfbptr Signed-off-by: Kevin Hao Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index d2a67ed..e54b1e3 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -177,7 +177,7 @@ static int gfar_init_bds(struct net_device *ndev) struct gfar_priv_rx_q *rx_queue = NULL; struct txbd8 *txbdp; struct rxbd8 *rxbdp; - u32 *rfbptr; + u32 __iomem *rfbptr; int i, j; dma_addr_t bufaddr; diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h index b581b88..9e18024 100644 --- a/drivers/net/ethernet/freescale/gianfar.h +++ b/drivers/net/ethernet/freescale/gianfar.h @@ -1039,7 +1039,7 @@ struct gfar_priv_rx_q { /* RX Coalescing values */ unsigned char rxcoalescing; unsigned long rxic; - u32 *rfbptr; + u32 __iomem *rfbptr; }; enum gfar_irqinfo_id { -- cgit v0.10.2 From 734d16801349fbe951d2f780191d32c5b8a892d1 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 21 Nov 2014 14:45:12 -0800 Subject: rcu: Make rcu_nmi_enter() handle nesting The x86 architecture has multiple types of NMI-like interrupts: real NMIs, machine checks, and, for some values of NMI-like, debugging and breakpoint interrupts. These interrupts can nest inside each other. Andy Lutomirski is adding RCU support to these interrupts, so rcu_nmi_enter() and rcu_nmi_exit() must now correctly handle nesting. This commit therefore introduces nesting, using a clever NMI-coordination algorithm suggested by Andy. The trick is to atomically increment ->dynticks (if needed) before manipulating ->dynticks_nmi_nesting on entry (and, accordingly, after on exit). In addition, ->dynticks_nmi_nesting is incremented by one if ->dynticks was incremented and by two otherwise. This means that when rcu_nmi_exit() sees ->dynticks_nmi_nesting equal to one, it knows that ->dynticks must be atomically incremented. This NMI-coordination algorithms has been validated by the following Promela model: ------------------------------------------------------------------------ /* * Promela model for Andy Lutomirski's suggested change to rcu_nmi_enter() * that allows nesting. * * 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, you can access it online at * http://www.gnu.org/licenses/gpl-2.0.html. * * Copyright IBM Corporation, 2014 * * Author: Paul E. McKenney */ byte dynticks_nmi_nesting = 0; byte dynticks = 0; /* * Promela verision of rcu_nmi_enter(). */ inline rcu_nmi_enter() { byte incby; byte tmp; incby = BUSY_INCBY; assert(dynticks_nmi_nesting >= 0); if :: (dynticks & 1) == 0 -> atomic { dynticks = dynticks + 1; } assert((dynticks & 1) == 1); incby = 1; :: else -> skip; fi; tmp = dynticks_nmi_nesting; tmp = tmp + incby; dynticks_nmi_nesting = tmp; assert(dynticks_nmi_nesting >= 1); } /* * Promela verision of rcu_nmi_exit(). */ inline rcu_nmi_exit() { byte tmp; assert(dynticks_nmi_nesting > 0); assert((dynticks & 1) != 0); if :: dynticks_nmi_nesting != 1 -> tmp = dynticks_nmi_nesting; tmp = tmp - BUSY_INCBY; dynticks_nmi_nesting = tmp; :: else -> dynticks_nmi_nesting = 0; atomic { dynticks = dynticks + 1; } assert((dynticks & 1) == 0); fi; } /* * Base-level NMI runs non-atomically. Crudely emulates process-level * dynticks-idle entry/exit. */ proctype base_NMI() { byte busy; busy = 0; do :: /* Emulate base-level dynticks and not. */ if :: 1 -> atomic { dynticks = dynticks + 1; } busy = 1; :: 1 -> skip; fi; /* Verify that we only sometimes have base-level dynticks. */ if :: busy == 0 -> skip; :: busy == 1 -> skip; fi; /* Model RCU's NMI entry and exit actions. */ rcu_nmi_enter(); assert((dynticks & 1) == 1); rcu_nmi_exit(); /* Emulated re-entering base-level dynticks and not. */ if :: !busy -> skip; :: busy -> atomic { dynticks = dynticks + 1; } busy = 0; fi; /* We had better now be in dyntick-idle mode. */ assert((dynticks & 1) == 0); od; } /* * Nested NMI runs atomically to emulate interrupting base_level(). */ proctype nested_NMI() { do :: /* * Use an atomic section to model a nested NMI. This is * guaranteed to interleave into base_NMI() between a pair * of base_NMI() statements, just as a nested NMI would. */ atomic { /* Verify that we only sometimes are in dynticks. */ if :: (dynticks & 1) == 0 -> skip; :: (dynticks & 1) == 1 -> skip; fi; /* Model RCU's NMI entry and exit actions. */ rcu_nmi_enter(); assert((dynticks & 1) == 1); rcu_nmi_exit(); } od; } init { run base_NMI(); run nested_NMI(); } ------------------------------------------------------------------------ The following script can be used to run this model if placed in rcu_nmi.spin: ------------------------------------------------------------------------ if ! spin -a rcu_nmi.spin then echo Spin errors!!! exit 1 fi if ! cc -DSAFETY -o pan pan.c then echo Compilation errors!!! exit 1 fi ./pan -m100000 Signed-off-by: Paul E. McKenney Reviewed-by: Lai Jiangshan diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 7680fc2..4c106fc 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -759,39 +759,71 @@ void rcu_irq_enter(void) /** * rcu_nmi_enter - inform RCU of entry to NMI context * - * If the CPU was idle with dynamic ticks active, and there is no - * irq handler running, this updates rdtp->dynticks_nmi to let the - * RCU grace-period handling know that the CPU is active. + * If the CPU was idle from RCU's viewpoint, update rdtp->dynticks and + * rdtp->dynticks_nmi_nesting to let the RCU grace-period handling know + * that the CPU is active. This implementation permits nested NMIs, as + * long as the nesting level does not overflow an int. (You will probably + * run out of stack space first.) */ void rcu_nmi_enter(void) { struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks); + int incby = 2; - if (rdtp->dynticks_nmi_nesting == 0 && - (atomic_read(&rdtp->dynticks) & 0x1)) - return; - rdtp->dynticks_nmi_nesting++; - smp_mb__before_atomic(); /* Force delay from prior write. */ - atomic_inc(&rdtp->dynticks); - /* CPUs seeing atomic_inc() must see later RCU read-side crit sects */ - smp_mb__after_atomic(); /* See above. */ - WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks) & 0x1)); + /* Complain about underflow. */ + WARN_ON_ONCE(rdtp->dynticks_nmi_nesting < 0); + + /* + * If idle from RCU viewpoint, atomically increment ->dynticks + * to mark non-idle and increment ->dynticks_nmi_nesting by one. + * Otherwise, increment ->dynticks_nmi_nesting by two. This means + * if ->dynticks_nmi_nesting is equal to one, we are guaranteed + * to be in the outermost NMI handler that interrupted an RCU-idle + * period (observation due to Andy Lutomirski). + */ + if (!(atomic_read(&rdtp->dynticks) & 0x1)) { + smp_mb__before_atomic(); /* Force delay from prior write. */ + atomic_inc(&rdtp->dynticks); + /* atomic_inc() before later RCU read-side crit sects */ + smp_mb__after_atomic(); /* See above. */ + WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks) & 0x1)); + incby = 1; + } + rdtp->dynticks_nmi_nesting += incby; + barrier(); } /** * rcu_nmi_exit - inform RCU of exit from NMI context * - * If the CPU was idle with dynamic ticks active, and there is no - * irq handler running, this updates rdtp->dynticks_nmi to let the - * RCU grace-period handling know that the CPU is no longer active. + * If we are returning from the outermost NMI handler that interrupted an + * RCU-idle period, update rdtp->dynticks and rdtp->dynticks_nmi_nesting + * to let the RCU grace-period handling know that the CPU is back to + * being RCU-idle. */ void rcu_nmi_exit(void) { struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks); - if (rdtp->dynticks_nmi_nesting == 0 || - --rdtp->dynticks_nmi_nesting != 0) + /* + * Check for ->dynticks_nmi_nesting underflow and bad ->dynticks. + * (We are exiting an NMI handler, so RCU better be paying attention + * to us!) + */ + WARN_ON_ONCE(rdtp->dynticks_nmi_nesting <= 0); + WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks) & 0x1)); + + /* + * If the nesting level is not 1, the CPU wasn't RCU-idle, so + * leave it in non-RCU-idle state. + */ + if (rdtp->dynticks_nmi_nesting != 1) { + rdtp->dynticks_nmi_nesting -= 2; return; + } + + /* This NMI interrupted an RCU-idle CPU, restore RCU-idleness. */ + rdtp->dynticks_nmi_nesting = 0; /* CPUs seeing atomic_inc() must see prior RCU read-side crit sects */ smp_mb__before_atomic(); /* See above. */ atomic_inc(&rdtp->dynticks); -- cgit v0.10.2 From ca9558a33f658155c3b69f92897c2e6a848684f5 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Fri, 31 Oct 2014 14:55:05 +0000 Subject: rcu: Remove redundant rcu_is_cpu_rrupt_from_idle() from tiny RCU Let's start assuming that something in the idle loop posts a callback, and scheduling-clock interrupt occurs: 1. The system is idle and stays that way, no runnable tasks. 2. Scheduling-clock interrupt occurs, rcu_check_callbacks() is called as result, which in turn calls rcu_is_cpu_rrupt_from_idle(). 3. rcu_is_cpu_rrupt_from_idle() reports the CPU was interrupted from idle, which results in rcu_sched_qs() call, which does a raise_softirq(RCU_SOFTIRQ). 4. Upon return from interrupt, rcu_irq_exit() is invoked, which calls rcu_idle_enter_common(), which in turn calls rcu_sched_qs() again, which does another raise_softirq(RCU_SOFTIRQ). 5. The softirq happens shortly and invokes rcu_process_callbacks(), which invokes __rcu_process_callbacks(). 6. So now callbacks can be invoked. At least they can be if ->donetail has been updated. Which it will have been because rcu_sched_qs() invokes rcu_qsctr_help(). In the described scenario rcu_sched_qs() and raise_softirq(RCU_SOFTIRQ) get called twice in steps 3 and 4. This redundancy could be eliminated by removing rcu_is_cpu_rrupt_from_idle() function. Signed-off-by: Alexander Gordeev Cc: Paul E. McKenney Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tiny.c b/kernel/rcu/tiny.c index 0db5649..805b6d5 100644 --- a/kernel/rcu/tiny.c +++ b/kernel/rcu/tiny.c @@ -186,16 +186,6 @@ EXPORT_SYMBOL(__rcu_is_watching); #endif /* defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) */ /* - * Test whether the current CPU was interrupted from idle. Nested - * interrupts don't count, we must be running at the first interrupt - * level. - */ -static int rcu_is_cpu_rrupt_from_idle(void) -{ - return rcu_dynticks_nesting <= 1; -} - -/* * Helper function for rcu_sched_qs() and rcu_bh_qs(). * Also irqs are disabled to avoid confusion due to interrupt handlers * invoking call_rcu(). @@ -250,7 +240,7 @@ void rcu_bh_qs(void) void rcu_check_callbacks(int user) { RCU_TRACE(check_cpu_stalls()); - if (user || rcu_is_cpu_rrupt_from_idle()) + if (user) rcu_sched_qs(); else if (!in_softirq()) rcu_bh_qs(); -- cgit v0.10.2 From 924df8a0117aa2725750f1ec4d282867534d9a89 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 29 Oct 2014 15:37:58 -0700 Subject: rcu: Fix invoke_rcu_callbacks() comment Despite what the comment says, it is only softirqs that are disabled, not interrupts. This commit therefore fixes the comment. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 4c106fc..9c25b99 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -2601,7 +2601,7 @@ static void rcu_process_callbacks(struct softirq_action *unused) * Schedule RCU callback invocation. If the specified type of RCU * does not support RCU priority boosting, just do a direct call, * otherwise wake up the per-CPU kernel kthread. Note that because we - * are running on the current CPU with interrupts disabled, the + * are running on the current CPU with softirqs disabled, the * rcu_cpu_kthread_task cannot disappear out from under us. */ static void invoke_rcu_callbacks(struct rcu_state *rsp, struct rcu_data *rdp) -- cgit v0.10.2 From 0d02e1292715d00674a49626146e3a854b504cb1 Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Wed, 31 Dec 2014 23:48:32 +1300 Subject: ALSA: asihpi: fix an information leak in asihpi_hpi_ioctl() Add missing limits to keep copied data within allocated buffer. Reported-by: Dan Carpenter Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c index 2414d7a..2d63648 100644 --- a/sound/pci/asihpi/hpi6000.c +++ b/sound/pci/asihpi/hpi6000.c @@ -47,7 +47,7 @@ /* operational/messaging errors */ #define HPI6000_ERROR_MSG_RESP_IDLE_TIMEOUT 901 - +#define HPI6000_ERROR_RESP_GET_LEN 902 #define HPI6000_ERROR_MSG_RESP_GET_RESP_ACK 903 #define HPI6000_ERROR_MSG_GET_ADR 904 #define HPI6000_ERROR_RESP_GET_ADR 905 @@ -1365,7 +1365,10 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao, length = hpi_read_word(pdo, HPI_HIF_ADDR(length)); } while (hpi6000_check_PCI2040_error_flag(pao, H6READ) && --timeout); if (!timeout) - length = sizeof(struct hpi_response); + return HPI6000_ERROR_RESP_GET_LEN; + + if (length > phr->size) + return HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL; /* get the response */ p_data = (u32 *)phr; diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index 6aa677e..72af66b 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -153,6 +153,8 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) goto out; } + res_max_size = min_t(size_t, res_max_size, sizeof(*hr)); + switch (hm->h.function) { case HPI_SUBSYS_CREATE_ADAPTER: case HPI_ADAPTER_DELETE: -- cgit v0.10.2 From 48d882978eb0a15547093b4542a82b5c22548041 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Wed, 31 Dec 2014 22:09:54 +0800 Subject: ALSA: pcm: add SNDRV_PCM_TRIGGER_DRAIN trigger Add SNDRV_PCM_TRIGGER_DRAIN trigger for pcm drain. Some audio devices require notification of drain events in order to properly drain and shutdown an audio stream. Signed-off-by: Libin Yang Signed-off-by: Takashi Iwai diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 3652c94..bf32cea 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -106,6 +106,7 @@ struct snd_pcm_ops { #define SNDRV_PCM_TRIGGER_PAUSE_RELEASE 4 #define SNDRV_PCM_TRIGGER_SUSPEND 5 #define SNDRV_PCM_TRIGGER_RESUME 6 +#define SNDRV_PCM_TRIGGER_DRAIN 7 #define SNDRV_PCM_POS_XRUN ((snd_pcm_uframes_t)-1) diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 1f23cd6..0e88e7a 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -268,6 +268,7 @@ typedef int __bitwise snd_pcm_subformat_t; #define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */ #define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP 0x00800000 /* period wakeup can be disabled */ #define SNDRV_PCM_INFO_HAS_WALL_CLOCK 0x01000000 /* has audio wall clock for audio/system time sync */ +#define SNDRV_PCM_INFO_DRAIN_TRIGGER 0x40000000 /* internal kernel flag - trigger in drain */ #define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */ typedef int __bitwise snd_pcm_state_t; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 095d957..ff3abc3 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -420,7 +420,8 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, hw = &substream->runtime->hw; if (!params->info) { - params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES; + params->info = hw->info & ~(SNDRV_PCM_INFO_FIFO_IN_FRAMES | + SNDRV_PCM_INFO_DRAIN_TRIGGER); if (!hw_support_mmap(substream)) params->info &= ~(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID); @@ -1566,6 +1567,13 @@ static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream, int state) snd_pcm_post_stop(substream, new_state); } } + + if (runtime->status->state == SNDRV_PCM_STATE_DRAINING && + runtime->trigger_master == substream && + (runtime->hw.info & SNDRV_PCM_INFO_DRAIN_TRIGGER)) + return substream->ops->trigger(substream, + SNDRV_PCM_TRIGGER_DRAIN); + return 0; } -- cgit v0.10.2 From c6068d3a7b408554a832181b98091ee5a00d8b85 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 31 Dec 2014 17:10:34 +0100 Subject: ASoC: pcm: Fix unused variable warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sound/soc/soc-pcm.c: In function ‘soc_pcm_set_msb’: sound/soc/soc-pcm.c:307:11: warning: unused variable ‘i’ [-Wunused-variable] Fixes: 0e2a37513a1f ('ASoC: pcm: Use wildcard msbits constraints') Signed-off-by: Takashi Iwai diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index d62d6a5..c076993 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -304,7 +304,7 @@ static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream) static void soc_pcm_set_msb(struct snd_pcm_substream *substream, int bits) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - int ret, i; + int ret; if (!bits) return; -- cgit v0.10.2 From de40ed31b3c577cefd7b54972365a272ecbe9dd6 Mon Sep 17 00:00:00 2001 From: Nimrod Andy Date: Wed, 24 Dec 2014 17:30:39 +0800 Subject: net: fec: add Wake-on-LAN support Support for Wake-on-LAN using Magic Packet. ENET IP supports sleep mode in low power status, when system enter suspend status, Magic packet can wake up system even if all SOC clocks are gate. The patch doing below things: - flagging the device as a wakeup source for the system, as well as its Wake-on-LAN interrupt - prepare the hardware for entering WoL mode - add standard ethtool WOL interface - enable the ENET interrupt to wake us Tested on i.MX6q/dl sabresd, sabreauto boards, i.MX6SX arm2 boards. Signed-off-by: Fugang Duan Signed-off-by: David S. Miller diff --git a/Documentation/devicetree/bindings/net/fsl-fec.txt b/Documentation/devicetree/bindings/net/fsl-fec.txt index 0c8775c..a9eb611 100644 --- a/Documentation/devicetree/bindings/net/fsl-fec.txt +++ b/Documentation/devicetree/bindings/net/fsl-fec.txt @@ -22,6 +22,8 @@ Optional properties: - fsl,num-rx-queues : The property is valid for enet-avb IP, which supports hw multi queues. Should specify the rx queue number, otherwise set rx queue number to 1. +- fsl,magic-packet : If present, indicates that the hardware supports waking + up via magic packet. Optional subnodes: - mdio : specifies the mdio bus in the FEC, used as a container for phy nodes diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index df8bbdd..d77a96f 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -357,6 +357,7 @@ struct bufdesc_ex { #define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ #define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ #define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ +#define FEC_ENET_WAKEUP ((uint)0x00020000) /* Wakeup request */ #define FEC_ENET_TXF (FEC_ENET_TXF_0 | FEC_ENET_TXF_1 | FEC_ENET_TXF_2) #define FEC_ENET_RXF (FEC_ENET_RXF_0 | FEC_ENET_RXF_1 | FEC_ENET_RXF_2) #define FEC_ENET_TS_AVAIL ((uint)0x00010000) @@ -512,6 +513,7 @@ struct fec_enet_private { int irq[FEC_IRQ_NUM]; bool bufdesc_ex; int pause_flag; + int wol_flag; u32 quirks; struct napi_struct napi; diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 5ebdf8d..49cd358 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -187,6 +187,9 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); #define FEC_MMFR_RA(v) ((v & 0x1f) << 18) #define FEC_MMFR_TA (2 << 16) #define FEC_MMFR_DATA(v) (v & 0xffff) +/* FEC ECR bits definition */ +#define FEC_ECR_MAGICEN (1 << 2) +#define FEC_ECR_SLEEP (1 << 3) #define FEC_MII_TIMEOUT 30000 /* us */ @@ -195,6 +198,9 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); #define FEC_PAUSE_FLAG_AUTONEG 0x1 #define FEC_PAUSE_FLAG_ENABLE 0x2 +#define FEC_WOL_HAS_MAGIC_PACKET (0x1 << 0) +#define FEC_WOL_FLAG_ENABLE (0x1 << 1) +#define FEC_WOL_FLAG_SLEEP_ON (0x1 << 2) #define COPYBREAK_DEFAULT 256 @@ -1089,7 +1095,9 @@ static void fec_stop(struct net_device *ndev) { struct fec_enet_private *fep = netdev_priv(ndev); + struct fec_platform_data *pdata = fep->pdev->dev.platform_data; u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); + u32 val; /* We cannot expect a graceful transmit stop without link !!! */ if (fep->link) { @@ -1103,17 +1111,28 @@ fec_stop(struct net_device *ndev) * For i.MX6SX SOC, enet use AXI bus, we use disable MAC * instead of reset MAC itself. */ - if (fep->quirks & FEC_QUIRK_HAS_AVB) { - writel(0, fep->hwp + FEC_ECNTRL); + if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { + if (fep->quirks & FEC_QUIRK_HAS_AVB) { + writel(0, fep->hwp + FEC_ECNTRL); + } else { + writel(1, fep->hwp + FEC_ECNTRL); + udelay(10); + } + writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); } else { - writel(1, fep->hwp + FEC_ECNTRL); - udelay(10); + writel(FEC_DEFAULT_IMASK | FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK); + val = readl(fep->hwp + FEC_ECNTRL); + val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); + writel(val, fep->hwp + FEC_ECNTRL); + + if (pdata && pdata->sleep_mode_enable) + pdata->sleep_mode_enable(true); } writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); - writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); /* We have to keep ENET enabled to have MII interrupt stay working */ - if (fep->quirks & FEC_QUIRK_ENET_MAC) { + if (fep->quirks & FEC_QUIRK_ENET_MAC && + !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { writel(2, fep->hwp + FEC_ECNTRL); writel(rmii_mode, fep->hwp + FEC_R_CNTRL); } @@ -2427,6 +2446,44 @@ static int fec_enet_set_tunable(struct net_device *netdev, return ret; } +static void +fec_enet_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + + if (fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) { + wol->supported = WAKE_MAGIC; + wol->wolopts = fep->wol_flag & FEC_WOL_FLAG_ENABLE ? WAKE_MAGIC : 0; + } else { + wol->supported = wol->wolopts = 0; + } +} + +static int +fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + + if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET)) + return -EINVAL; + + if (wol->wolopts & ~WAKE_MAGIC) + return -EINVAL; + + device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC); + if (device_may_wakeup(&ndev->dev)) { + fep->wol_flag |= FEC_WOL_FLAG_ENABLE; + if (fep->irq[0] > 0) + enable_irq_wake(fep->irq[0]); + } else { + fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE); + if (fep->irq[0] > 0) + disable_irq_wake(fep->irq[0]); + } + + return 0; +} + static const struct ethtool_ops fec_enet_ethtool_ops = { .get_settings = fec_enet_get_settings, .set_settings = fec_enet_set_settings, @@ -2445,6 +2502,8 @@ static const struct ethtool_ops fec_enet_ethtool_ops = { .get_ts_info = fec_enet_get_ts_info, .get_tunable = fec_enet_get_tunable, .set_tunable = fec_enet_set_tunable, + .get_wol = fec_enet_get_wol, + .set_wol = fec_enet_set_wol, }; static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) @@ -2705,6 +2764,9 @@ fec_enet_open(struct net_device *ndev) phy_start(fep->phy_dev); netif_tx_start_all_queues(ndev); + device_set_wakeup_enable(&ndev->dev, fep->wol_flag & + FEC_WOL_FLAG_ENABLE); + return 0; err_enet_mii_probe: @@ -3153,6 +3215,9 @@ fec_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ndev); + if (of_get_property(np, "fsl,magic-packet", NULL)) + fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; + phy_node = of_parse_phandle(np, "phy-handle", 0); if (!phy_node && of_phy_is_fixed_link(np)) { ret = of_phy_register_fixed_link(np); @@ -3247,6 +3312,8 @@ fec_probe(struct platform_device *pdev) 0, pdev->name, ndev); if (ret) goto failed_irq; + + fep->irq[i] = irq; } init_completion(&fep->mdio_done); @@ -3263,6 +3330,9 @@ fec_probe(struct platform_device *pdev) if (ret) goto failed_register; + device_init_wakeup(&ndev->dev, fep->wol_flag & + FEC_WOL_HAS_MAGIC_PACKET); + if (fep->bufdesc_ex && fep->ptp_clock) netdev_info(ndev, "registered PHC device %d\n", fep->dev_id); @@ -3316,6 +3386,8 @@ static int __maybe_unused fec_suspend(struct device *dev) rtnl_lock(); if (netif_running(ndev)) { + if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) + fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON; phy_stop(fep->phy_dev); napi_disable(&fep->napi); netif_tx_lock_bh(ndev); @@ -3323,11 +3395,12 @@ static int __maybe_unused fec_suspend(struct device *dev) netif_tx_unlock_bh(ndev); fec_stop(ndev); fec_enet_clk_enable(ndev, false); - pinctrl_pm_select_sleep_state(&fep->pdev->dev); + if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) + pinctrl_pm_select_sleep_state(&fep->pdev->dev); } rtnl_unlock(); - if (fep->reg_phy) + if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) regulator_disable(fep->reg_phy); /* SOC supply clock to phy, when clock is disabled, phy link down @@ -3343,9 +3416,11 @@ static int __maybe_unused fec_resume(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); struct fec_enet_private *fep = netdev_priv(ndev); + struct fec_platform_data *pdata = fep->pdev->dev.platform_data; int ret; + int val; - if (fep->reg_phy) { + if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { ret = regulator_enable(fep->reg_phy); if (ret) return ret; @@ -3353,12 +3428,21 @@ static int __maybe_unused fec_resume(struct device *dev) rtnl_lock(); if (netif_running(ndev)) { - pinctrl_pm_select_default_state(&fep->pdev->dev); ret = fec_enet_clk_enable(ndev, true); if (ret) { rtnl_unlock(); goto failed_clk; } + if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) { + if (pdata && pdata->sleep_mode_enable) + pdata->sleep_mode_enable(false); + val = readl(fep->hwp + FEC_ECNTRL); + val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP); + writel(val, fep->hwp + FEC_ECNTRL); + fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON; + } else { + pinctrl_pm_select_default_state(&fep->pdev->dev); + } fec_restart(ndev); netif_tx_lock_bh(ndev); netif_device_attach(ndev); diff --git a/include/linux/fec.h b/include/linux/fec.h index bcff455..1454a50 100644 --- a/include/linux/fec.h +++ b/include/linux/fec.h @@ -19,6 +19,7 @@ struct fec_platform_data { phy_interface_t phy; unsigned char mac[ETH_ALEN]; + void (*sleep_mode_enable)(int enabled); }; #endif -- cgit v0.10.2 From 456062b3ec6f5b96ffe7dd31c84e8666747f8720 Mon Sep 17 00:00:00 2001 From: Nimrod Andy Date: Wed, 24 Dec 2014 17:30:40 +0800 Subject: ARM: imx: add FEC sleep mode callback function i.MX6q/dl, i.MX6SX SOCs enet support sleep mode that magic packet can wake up system in suspend status. For different SOCs, there have some SOC specifical GPR register to set sleep on/off mode. So add these to callback function for driver. Signed-off-by: Fugang Duan Signed-off-by: David S. Miller diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index 5057d61..2f76168 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include #include @@ -39,6 +41,35 @@ #include "cpuidle.h" #include "hardware.h" +static struct fec_platform_data fec_pdata; + +static void imx6q_fec_sleep_enable(int enabled) +{ + struct regmap *gpr; + + gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + if (!IS_ERR(gpr)) { + if (enabled) + regmap_update_bits(gpr, IOMUXC_GPR13, + IMX6Q_GPR13_ENET_STOP_REQ, + IMX6Q_GPR13_ENET_STOP_REQ); + + else + regmap_update_bits(gpr, IOMUXC_GPR13, + IMX6Q_GPR13_ENET_STOP_REQ, 0); + } else + pr_err("failed to find fsl,imx6q-iomux-gpr regmap\n"); +} + +static void __init imx6q_enet_plt_init(void) +{ + struct device_node *np; + + np = of_find_node_by_path("/soc/aips-bus@02100000/ethernet@02188000"); + if (np && of_get_property(np, "fsl,magic-packet", NULL)) + fec_pdata.sleep_mode_enable = imx6q_fec_sleep_enable; +} + /* For imx6q sabrelite board: set KSZ9021RN RGMII pad skew */ static int ksz9021rn_phy_fixup(struct phy_device *phydev) { @@ -261,6 +292,12 @@ static void __init imx6q_axi_init(void) } } +/* Add auxdata to pass platform data */ +static const struct of_dev_auxdata imx6q_auxdata_lookup[] __initconst = { + OF_DEV_AUXDATA("fsl,imx6q-fec", 0x02188000, NULL, &fec_pdata), + { /* sentinel */ } +}; + static void __init imx6q_init_machine(void) { struct device *parent; @@ -274,11 +311,13 @@ static void __init imx6q_init_machine(void) imx6q_enet_phy_init(); - of_platform_populate(NULL, of_default_bus_match_table, NULL, parent); + of_platform_populate(NULL, of_default_bus_match_table, + imx6q_auxdata_lookup, parent); imx_anatop_init(); cpu_is_imx6q() ? imx6q_pm_init() : imx6dl_pm_init(); imx6q_1588_init(); + imx6q_enet_plt_init(); imx6q_axi_init(); } diff --git a/arch/arm/mach-imx/mach-imx6sx.c b/arch/arm/mach-imx/mach-imx6sx.c index 7a96c65..747b012 100644 --- a/arch/arm/mach-imx/mach-imx6sx.c +++ b/arch/arm/mach-imx/mach-imx6sx.c @@ -12,12 +12,62 @@ #include #include #include +#include +#include #include #include #include "common.h" #include "cpuidle.h" +static struct fec_platform_data fec_pdata[2]; + +static void imx6sx_fec1_sleep_enable(int enabled) +{ + struct regmap *gpr; + + gpr = syscon_regmap_lookup_by_compatible("fsl,imx6sx-iomuxc-gpr"); + if (!IS_ERR(gpr)) { + if (enabled) + regmap_update_bits(gpr, IOMUXC_GPR4, + IMX6SX_GPR4_FEC_ENET1_STOP_REQ, + IMX6SX_GPR4_FEC_ENET1_STOP_REQ); + else + regmap_update_bits(gpr, IOMUXC_GPR4, + IMX6SX_GPR4_FEC_ENET1_STOP_REQ, 0); + } else + pr_err("failed to find fsl,imx6sx-iomux-gpr regmap\n"); +} + +static void imx6sx_fec2_sleep_enable(int enabled) +{ + struct regmap *gpr; + + gpr = syscon_regmap_lookup_by_compatible("fsl,imx6sx-iomuxc-gpr"); + if (!IS_ERR(gpr)) { + if (enabled) + regmap_update_bits(gpr, IOMUXC_GPR4, + IMX6SX_GPR4_FEC_ENET2_STOP_REQ, + IMX6SX_GPR4_FEC_ENET2_STOP_REQ); + else + regmap_update_bits(gpr, IOMUXC_GPR4, + IMX6SX_GPR4_FEC_ENET2_STOP_REQ, 0); + } else + pr_err("failed to find fsl,imx6sx-iomux-gpr regmap\n"); +} + +static void __init imx6sx_enet_plt_init(void) +{ + struct device_node *np; + + np = of_find_node_by_path("/soc/aips-bus@02100000/ethernet@02188000"); + if (np && of_get_property(np, "fsl,magic-packet", NULL)) + fec_pdata[0].sleep_mode_enable = imx6sx_fec1_sleep_enable; + np = of_find_node_by_path("/soc/aips-bus@02100000/ethernet@021b4000"); + if (np && of_get_property(np, "fsl,magic-packet", NULL)) + fec_pdata[1].sleep_mode_enable = imx6sx_fec2_sleep_enable; +} + static int ar8031_phy_fixup(struct phy_device *dev) { u16 val; -- cgit v0.10.2 From 07b4d2dda0c00f56248ac35e2200474609c294b7 Mon Sep 17 00:00:00 2001 From: Nimrod Andy Date: Wed, 24 Dec 2014 17:30:41 +0800 Subject: ARM: dts: imx6qdl: enable FEC magic-packet feature Add FEC magic-packet feature support. Signed-off-by: Fugang Duan Signed-off-by: David S. Miller diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi index 009abd6..327d362 100644 --- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi @@ -67,6 +67,7 @@ phy-mode = "rgmii"; interrupts-extended = <&gpio1 6 IRQ_TYPE_LEVEL_HIGH>, <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>; + fsl,magic-packet; status = "okay"; }; diff --git a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi index f1cd214..6bfd0bc 100644 --- a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi @@ -160,6 +160,7 @@ pinctrl-0 = <&pinctrl_enet>; phy-mode = "rgmii"; phy-reset-gpios = <&gpio1 25 0>; + fsl,magic-packet; status = "okay"; }; -- cgit v0.10.2 From 5e32066d0085af3c53834323c90b7c69a8f788f6 Mon Sep 17 00:00:00 2001 From: Govindarajulu Varadarajan <_govind@gmx.com> Date: Wed, 24 Dec 2014 15:59:36 +0530 Subject: enic: make vnic_wq_buf doubly linked This patch makes vnic_wq_buf doubly liked list. This is needed for dma_mapping error check, in case some frag's dma map fails, we need to move back and remove previously queued buffers. Signed-off-by: Govindarajulu Varadarajan <_govind@gmx.com> Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/cisco/enic/vnic_wq.c b/drivers/net/ethernet/cisco/enic/vnic_wq.c index 3e6b8d5..b5a1c93 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_wq.c +++ b/drivers/net/ethernet/cisco/enic/vnic_wq.c @@ -47,11 +47,14 @@ static int vnic_wq_alloc_bufs(struct vnic_wq *wq) wq->ring.desc_size * buf->index; if (buf->index + 1 == count) { buf->next = wq->bufs[0]; + buf->next->prev = buf; break; } else if (j + 1 == VNIC_WQ_BUF_BLK_ENTRIES(count)) { buf->next = wq->bufs[i + 1]; + buf->next->prev = buf; } else { buf->next = buf + 1; + buf->next->prev = buf; buf++; } } diff --git a/drivers/net/ethernet/cisco/enic/vnic_wq.h b/drivers/net/ethernet/cisco/enic/vnic_wq.h index 816f1ad..2961543 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_wq.h +++ b/drivers/net/ethernet/cisco/enic/vnic_wq.h @@ -62,6 +62,7 @@ struct vnic_wq_buf { uint8_t cq_entry; /* Gets completion event from hw */ uint8_t desc_skip_cnt; /* Num descs to occupy */ uint8_t compressed_send; /* Both hdr and payload in one desc */ + struct vnic_wq_buf *prev; }; /* Break the vnic_wq_buf allocations into blocks of 32/64 entries */ -- cgit v0.10.2 From 065df159ec588630a360a689e4e3043c6622a6e9 Mon Sep 17 00:00:00 2001 From: Govindarajulu Varadarajan <_govind@gmx.com> Date: Wed, 24 Dec 2014 15:59:37 +0530 Subject: enic: check dma_mapping_error This patch checks for pci_dma_mapping_error() after dma mapping the data. If the dma mapping fails we remove the previously queued frags and return NETDEV_TX_OK. Reported-by: Jan Stancek Signed-off-by: Govindarajulu Varadarajan <_govind@gmx.com> Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h index 25c4d88..b2ea35a 100644 --- a/drivers/net/ethernet/cisco/enic/enic.h +++ b/drivers/net/ethernet/cisco/enic/enic.h @@ -242,6 +242,18 @@ static inline unsigned int enic_msix_notify_intr(struct enic *enic) return enic->rq_count + enic->wq_count + 1; } +static inline int enic_dma_map_check(struct enic *enic, dma_addr_t dma_addr) +{ + if (unlikely(pci_dma_mapping_error(enic->pdev, dma_addr))) { + net_warn_ratelimited("%s: PCI dma mapping failed!\n", + enic->netdev->name); + + return -ENOMEM; + } + + return 0; +} + void enic_reset_addr_lists(struct enic *enic); int enic_sriov_enabled(struct enic *enic); int enic_is_valid_vf(struct enic *enic, int vf); diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 705f334..142c9b5 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -351,80 +351,94 @@ static irqreturn_t enic_isr_msix_notify(int irq, void *data) return IRQ_HANDLED; } -static inline void enic_queue_wq_skb_cont(struct enic *enic, - struct vnic_wq *wq, struct sk_buff *skb, - unsigned int len_left, int loopback) +static int enic_queue_wq_skb_cont(struct enic *enic, struct vnic_wq *wq, + struct sk_buff *skb, unsigned int len_left, + int loopback) { const skb_frag_t *frag; + dma_addr_t dma_addr; /* Queue additional data fragments */ for (frag = skb_shinfo(skb)->frags; len_left; frag++) { len_left -= skb_frag_size(frag); - enic_queue_wq_desc_cont(wq, skb, - skb_frag_dma_map(&enic->pdev->dev, - frag, 0, skb_frag_size(frag), - DMA_TO_DEVICE), - skb_frag_size(frag), - (len_left == 0), /* EOP? */ - loopback); + dma_addr = skb_frag_dma_map(&enic->pdev->dev, frag, 0, + skb_frag_size(frag), + DMA_TO_DEVICE); + if (unlikely(enic_dma_map_check(enic, dma_addr))) + return -ENOMEM; + enic_queue_wq_desc_cont(wq, skb, dma_addr, skb_frag_size(frag), + (len_left == 0), /* EOP? */ + loopback); } + + return 0; } -static inline void enic_queue_wq_skb_vlan(struct enic *enic, - struct vnic_wq *wq, struct sk_buff *skb, - int vlan_tag_insert, unsigned int vlan_tag, int loopback) +static int enic_queue_wq_skb_vlan(struct enic *enic, struct vnic_wq *wq, + struct sk_buff *skb, int vlan_tag_insert, + unsigned int vlan_tag, int loopback) { unsigned int head_len = skb_headlen(skb); unsigned int len_left = skb->len - head_len; int eop = (len_left == 0); + dma_addr_t dma_addr; + int err = 0; + + dma_addr = pci_map_single(enic->pdev, skb->data, head_len, + PCI_DMA_TODEVICE); + if (unlikely(enic_dma_map_check(enic, dma_addr))) + return -ENOMEM; /* Queue the main skb fragment. The fragments are no larger * than max MTU(9000)+ETH_HDR_LEN(14) bytes, which is less * than WQ_ENET_MAX_DESC_LEN length. So only one descriptor * per fragment is queued. */ - enic_queue_wq_desc(wq, skb, - pci_map_single(enic->pdev, skb->data, - head_len, PCI_DMA_TODEVICE), - head_len, - vlan_tag_insert, vlan_tag, - eop, loopback); + enic_queue_wq_desc(wq, skb, dma_addr, head_len, vlan_tag_insert, + vlan_tag, eop, loopback); if (!eop) - enic_queue_wq_skb_cont(enic, wq, skb, len_left, loopback); + err = enic_queue_wq_skb_cont(enic, wq, skb, len_left, loopback); + + return err; } -static inline void enic_queue_wq_skb_csum_l4(struct enic *enic, - struct vnic_wq *wq, struct sk_buff *skb, - int vlan_tag_insert, unsigned int vlan_tag, int loopback) +static int enic_queue_wq_skb_csum_l4(struct enic *enic, struct vnic_wq *wq, + struct sk_buff *skb, int vlan_tag_insert, + unsigned int vlan_tag, int loopback) { unsigned int head_len = skb_headlen(skb); unsigned int len_left = skb->len - head_len; unsigned int hdr_len = skb_checksum_start_offset(skb); unsigned int csum_offset = hdr_len + skb->csum_offset; int eop = (len_left == 0); + dma_addr_t dma_addr; + int err = 0; + + dma_addr = pci_map_single(enic->pdev, skb->data, head_len, + PCI_DMA_TODEVICE); + if (unlikely(enic_dma_map_check(enic, dma_addr))) + return -ENOMEM; /* Queue the main skb fragment. The fragments are no larger * than max MTU(9000)+ETH_HDR_LEN(14) bytes, which is less * than WQ_ENET_MAX_DESC_LEN length. So only one descriptor * per fragment is queued. */ - enic_queue_wq_desc_csum_l4(wq, skb, - pci_map_single(enic->pdev, skb->data, - head_len, PCI_DMA_TODEVICE), - head_len, - csum_offset, - hdr_len, - vlan_tag_insert, vlan_tag, - eop, loopback); + enic_queue_wq_desc_csum_l4(wq, skb, dma_addr, head_len, csum_offset, + hdr_len, vlan_tag_insert, vlan_tag, eop, + loopback); if (!eop) - enic_queue_wq_skb_cont(enic, wq, skb, len_left, loopback); + err = enic_queue_wq_skb_cont(enic, wq, skb, len_left, loopback); + + return err; } -static inline void enic_queue_wq_skb_tso(struct enic *enic, - struct vnic_wq *wq, struct sk_buff *skb, unsigned int mss, - int vlan_tag_insert, unsigned int vlan_tag, int loopback) +static int enic_queue_wq_skb_tso(struct enic *enic, struct vnic_wq *wq, + struct sk_buff *skb, unsigned int mss, + int vlan_tag_insert, unsigned int vlan_tag, + int loopback) { unsigned int frag_len_left = skb_headlen(skb); unsigned int len_left = skb->len - frag_len_left; @@ -454,20 +468,19 @@ static inline void enic_queue_wq_skb_tso(struct enic *enic, */ while (frag_len_left) { len = min(frag_len_left, (unsigned int)WQ_ENET_MAX_DESC_LEN); - dma_addr = pci_map_single(enic->pdev, skb->data + offset, - len, PCI_DMA_TODEVICE); - enic_queue_wq_desc_tso(wq, skb, - dma_addr, - len, - mss, hdr_len, - vlan_tag_insert, vlan_tag, - eop && (len == frag_len_left), loopback); + dma_addr = pci_map_single(enic->pdev, skb->data + offset, len, + PCI_DMA_TODEVICE); + if (unlikely(enic_dma_map_check(enic, dma_addr))) + return -ENOMEM; + enic_queue_wq_desc_tso(wq, skb, dma_addr, len, mss, hdr_len, + vlan_tag_insert, vlan_tag, + eop && (len == frag_len_left), loopback); frag_len_left -= len; offset += len; } if (eop) - return; + return 0; /* Queue WQ_ENET_MAX_DESC_LEN length descriptors * for additional data fragments @@ -483,16 +496,18 @@ static inline void enic_queue_wq_skb_tso(struct enic *enic, dma_addr = skb_frag_dma_map(&enic->pdev->dev, frag, offset, len, DMA_TO_DEVICE); - enic_queue_wq_desc_cont(wq, skb, - dma_addr, - len, - (len_left == 0) && - (len == frag_len_left), /* EOP? */ - loopback); + if (unlikely(enic_dma_map_check(enic, dma_addr))) + return -ENOMEM; + enic_queue_wq_desc_cont(wq, skb, dma_addr, len, + (len_left == 0) && + (len == frag_len_left),/*EOP*/ + loopback); frag_len_left -= len; offset += len; } } + + return 0; } static inline void enic_queue_wq_skb(struct enic *enic, @@ -502,6 +517,7 @@ static inline void enic_queue_wq_skb(struct enic *enic, unsigned int vlan_tag = 0; int vlan_tag_insert = 0; int loopback = 0; + int err; if (vlan_tx_tag_present(skb)) { /* VLAN tag from trunking driver */ @@ -513,14 +529,30 @@ static inline void enic_queue_wq_skb(struct enic *enic, } if (mss) - enic_queue_wq_skb_tso(enic, wq, skb, mss, - vlan_tag_insert, vlan_tag, loopback); + err = enic_queue_wq_skb_tso(enic, wq, skb, mss, + vlan_tag_insert, vlan_tag, + loopback); else if (skb->ip_summed == CHECKSUM_PARTIAL) - enic_queue_wq_skb_csum_l4(enic, wq, skb, - vlan_tag_insert, vlan_tag, loopback); + err = enic_queue_wq_skb_csum_l4(enic, wq, skb, vlan_tag_insert, + vlan_tag, loopback); else - enic_queue_wq_skb_vlan(enic, wq, skb, - vlan_tag_insert, vlan_tag, loopback); + err = enic_queue_wq_skb_vlan(enic, wq, skb, vlan_tag_insert, + vlan_tag, loopback); + if (unlikely(err)) { + struct vnic_wq_buf *buf; + + buf = wq->to_use->prev; + /* while not EOP of previous pkt && queue not empty. + * For all non EOP bufs, os_buf is NULL. + */ + while (!buf->os_buf && (buf->next != wq->to_clean)) { + enic_free_wq_buf(wq, buf); + wq->ring.desc_avail++; + buf = buf->prev; + } + wq->to_use = buf->next; + dev_kfree_skb(skb); + } } /* netif_tx_lock held, process context with BHs disabled, or BH */ @@ -950,8 +982,12 @@ static int enic_rq_alloc_buf(struct vnic_rq *rq) if (!skb) return -ENOMEM; - dma_addr = pci_map_single(enic->pdev, skb->data, - len, PCI_DMA_FROMDEVICE); + dma_addr = pci_map_single(enic->pdev, skb->data, len, + PCI_DMA_FROMDEVICE); + if (unlikely(enic_dma_map_check(enic, dma_addr))) { + dev_kfree_skb(skb); + return -ENOMEM; + } enic_queue_rq_desc(rq, skb, os_buf_index, dma_addr, len); -- cgit v0.10.2 From 58feff07e9c964a88f82d13d272d384bd4274a33 Mon Sep 17 00:00:00 2001 From: Govindarajulu Varadarajan <_govind@gmx.com> Date: Wed, 24 Dec 2014 15:59:38 +0530 Subject: enic: add stats for dma mapping error This patch adds generic statistics for enic. As of now dma_map_error is the only member. dma_map_erro is incremented every time dma maping error happens. Signed-off-by: Govindarajulu Varadarajan <_govind@gmx.com> Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h index b2ea35a..d2a1035 100644 --- a/drivers/net/ethernet/cisco/enic/enic.h +++ b/drivers/net/ethernet/cisco/enic/enic.h @@ -188,6 +188,7 @@ struct enic { struct enic_rfs_flw_tbl rfs_h; u32 rx_copybreak; u8 rss_key[ENIC_RSS_LEN]; + struct vnic_gen_stats gen_stats; }; static inline struct device *enic_get_dev(struct enic *enic) @@ -247,6 +248,7 @@ static inline int enic_dma_map_check(struct enic *enic, dma_addr_t dma_addr) if (unlikely(pci_dma_mapping_error(enic->pdev, dma_addr))) { net_warn_ratelimited("%s: PCI dma mapping failed!\n", enic->netdev->name); + enic->gen_stats.dma_map_error++; return -ENOMEM; } diff --git a/drivers/net/ethernet/cisco/enic/enic_ethtool.c b/drivers/net/ethernet/cisco/enic/enic_ethtool.c index eba1eb8..0c396c1 100644 --- a/drivers/net/ethernet/cisco/enic/enic_ethtool.c +++ b/drivers/net/ethernet/cisco/enic/enic_ethtool.c @@ -24,6 +24,7 @@ #include "enic_dev.h" #include "enic_clsf.h" #include "vnic_rss.h" +#include "vnic_stats.h" struct enic_stat { char name[ETH_GSTRING_LEN]; @@ -40,6 +41,11 @@ struct enic_stat { .index = offsetof(struct vnic_rx_stats, stat) / sizeof(u64) \ } +#define ENIC_GEN_STAT(stat) { \ + .name = #stat, \ + .index = offsetof(struct vnic_gen_stats, stat) / sizeof(u64)\ +} + static const struct enic_stat enic_tx_stats[] = { ENIC_TX_STAT(tx_frames_ok), ENIC_TX_STAT(tx_unicast_frames_ok), @@ -78,8 +84,13 @@ static const struct enic_stat enic_rx_stats[] = { ENIC_RX_STAT(rx_frames_to_max), }; +static const struct enic_stat enic_gen_stats[] = { + ENIC_GEN_STAT(dma_map_error), +}; + static const unsigned int enic_n_tx_stats = ARRAY_SIZE(enic_tx_stats); static const unsigned int enic_n_rx_stats = ARRAY_SIZE(enic_rx_stats); +static const unsigned int enic_n_gen_stats = ARRAY_SIZE(enic_gen_stats); void enic_intr_coal_set_rx(struct enic *enic, u32 timer) { @@ -146,6 +157,10 @@ static void enic_get_strings(struct net_device *netdev, u32 stringset, memcpy(data, enic_rx_stats[i].name, ETH_GSTRING_LEN); data += ETH_GSTRING_LEN; } + for (i = 0; i < enic_n_gen_stats; i++) { + memcpy(data, enic_gen_stats[i].name, ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } break; } } @@ -154,7 +169,7 @@ static int enic_get_sset_count(struct net_device *netdev, int sset) { switch (sset) { case ETH_SS_STATS: - return enic_n_tx_stats + enic_n_rx_stats; + return enic_n_tx_stats + enic_n_rx_stats + enic_n_gen_stats; default: return -EOPNOTSUPP; } @@ -173,6 +188,8 @@ static void enic_get_ethtool_stats(struct net_device *netdev, *(data++) = ((u64 *)&vstats->tx)[enic_tx_stats[i].index]; for (i = 0; i < enic_n_rx_stats; i++) *(data++) = ((u64 *)&vstats->rx)[enic_rx_stats[i].index]; + for (i = 0; i < enic_n_gen_stats; i++) + *(data++) = ((u64 *)&enic->gen_stats)[enic_gen_stats[i].index]; } static u32 enic_get_msglevel(struct net_device *netdev) diff --git a/drivers/net/ethernet/cisco/enic/vnic_stats.h b/drivers/net/ethernet/cisco/enic/vnic_stats.h index 77750ec..74c81ed 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_stats.h +++ b/drivers/net/ethernet/cisco/enic/vnic_stats.h @@ -62,6 +62,11 @@ struct vnic_rx_stats { u64 rsvd[16]; }; +/* Generic statistics */ +struct vnic_gen_stats { + u64 dma_map_error; +}; + struct vnic_stats { struct vnic_tx_stats tx; struct vnic_rx_stats rx; -- cgit v0.10.2 From 886eaa1fe668e75615a48df2ff68e23b762e31e8 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Thu, 25 Dec 2014 12:05:50 +0100 Subject: tipc: replace 0 by NULL for pointers Fix sparse warning: net/tipc/link.c:1924:40: warning: Using plain integer as NULL pointer Signed-off-by: Fabian Frederick Acked-by: Ying Xue Signed-off-by: David S. Miller diff --git a/net/tipc/link.c b/net/tipc/link.c index 23bcc11..082c3b5 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -1921,7 +1921,7 @@ static struct tipc_node *tipc_link_find_owner(const char *link_name, { struct tipc_link *l_ptr; struct tipc_node *n_ptr; - struct tipc_node *found_node = 0; + struct tipc_node *found_node = NULL; int i; *bearer_id = 0; -- cgit v0.10.2 From 87897931c83038af4931990af5b9365b41829921 Mon Sep 17 00:00:00 2001 From: Alex Gartrell Date: Thu, 25 Dec 2014 23:05:03 -0800 Subject: tun: Fixed unsigned/signed comparison Validated that this was actually using the unsigned comparison with gdb. Signed-off-by: Alex Gartrell Signed-off-by: David S. Miller diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 8c8dc16..df5e948 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1501,7 +1501,7 @@ static int tun_recvmsg(struct kiocb *iocb, struct socket *sock, goto out; } ret = tun_do_read(tun, tfile, &m->msg_iter, flags & MSG_DONTWAIT); - if (ret > total_len) { + if (ret > (ssize_t)total_len) { m->msg_flags |= MSG_TRUNC; ret = flags & MSG_TRUNC ? ret : total_len; } -- cgit v0.10.2 From 957f094f221f81e457133b1f4c4d95ffa49ff731 Mon Sep 17 00:00:00 2001 From: Alex Gartrell Date: Thu, 25 Dec 2014 23:22:49 -0800 Subject: tun: return proper error code from tun_do_read Instead of -1 with EAGAIN, read on a O_NONBLOCK tun fd will return 0. This fixes this by properly returning the error code from __skb_recv_datagram. Signed-off-by: Alex Gartrell Signed-off-by: David S. Miller diff --git a/drivers/net/tun.c b/drivers/net/tun.c index df5e948..c0df872 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1380,7 +1380,7 @@ static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile, skb = __skb_recv_datagram(tfile->socket.sk, noblock ? MSG_DONTWAIT : 0, &peeked, &off, &err); if (!skb) - return 0; + return err; ret = tun_put_user(tun, tfile, skb, to); if (unlikely(ret < 0)) -- cgit v0.10.2 From 33f72e6f0c67f673fd0c63a8182dbd9ffb8cf50b Mon Sep 17 00:00:00 2001 From: Bill Hong Date: Sat, 27 Dec 2014 10:12:39 -0800 Subject: l2tp : multicast notification to the registered listeners Previously l2tp module did not provide any means for the user space to get notified when tunnels/sessions are added/modified/deleted. This change contains the following - create a multicast group for the listeners to register. - notify the registered listeners when the tunnels/sessions are created/modified/deleted. Signed-off-by: Bill Hong Reviewed-by: Stephen Hemminger Reviewed-by: Sven-Thorsten Dietrich Signed-off-by: David S. Miller diff --git a/include/uapi/linux/l2tp.h b/include/uapi/linux/l2tp.h index 21caa26..347ef22 100644 --- a/include/uapi/linux/l2tp.h +++ b/include/uapi/linux/l2tp.h @@ -178,5 +178,6 @@ enum l2tp_seqmode { */ #define L2TP_GENL_NAME "l2tp" #define L2TP_GENL_VERSION 0x1 +#define L2TP_GENL_MCGROUP "l2tp" #endif /* _UAPI_LINUX_L2TP_H_ */ diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index 0ac907a..6b16598 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -40,6 +40,18 @@ static struct genl_family l2tp_nl_family = { .netnsok = true, }; +static const struct genl_multicast_group l2tp_multicast_group[] = { + { + .name = L2TP_GENL_MCGROUP, + }, +}; + +static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, + int flags, struct l2tp_tunnel *tunnel, u8 cmd); +static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, + int flags, struct l2tp_session *session, + u8 cmd); + /* Accessed under genl lock */ static const struct l2tp_nl_cmd_ops *l2tp_nl_cmd_ops[__L2TP_PWTYPE_MAX]; @@ -97,6 +109,52 @@ out: return ret; } +static int l2tp_tunnel_notify(struct genl_family *family, + struct genl_info *info, + struct l2tp_tunnel *tunnel, + u8 cmd) +{ + struct sk_buff *msg; + int ret; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq, + NLM_F_ACK, tunnel, cmd); + + if (ret >= 0) + return genlmsg_multicast_allns(family, msg, 0, 0, GFP_ATOMIC); + + nlmsg_free(msg); + + return ret; +} + +static int l2tp_session_notify(struct genl_family *family, + struct genl_info *info, + struct l2tp_session *session, + u8 cmd) +{ + struct sk_buff *msg; + int ret; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq, + NLM_F_ACK, session, cmd); + + if (ret >= 0) + return genlmsg_multicast_allns(family, msg, 0, 0, GFP_ATOMIC); + + nlmsg_free(msg); + + return ret; +} + static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info) { u32 tunnel_id; @@ -188,6 +246,9 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info break; } + if (ret >= 0) + ret = l2tp_tunnel_notify(&l2tp_nl_family, info, + tunnel, L2TP_CMD_TUNNEL_CREATE); out: return ret; } @@ -211,6 +272,9 @@ static int l2tp_nl_cmd_tunnel_delete(struct sk_buff *skb, struct genl_info *info goto out; } + l2tp_tunnel_notify(&l2tp_nl_family, info, + tunnel, L2TP_CMD_TUNNEL_DELETE); + (void) l2tp_tunnel_delete(tunnel); out: @@ -239,12 +303,15 @@ static int l2tp_nl_cmd_tunnel_modify(struct sk_buff *skb, struct genl_info *info if (info->attrs[L2TP_ATTR_DEBUG]) tunnel->debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]); + ret = l2tp_tunnel_notify(&l2tp_nl_family, info, + tunnel, L2TP_CMD_TUNNEL_MODIFY); + out: return ret; } static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int flags, - struct l2tp_tunnel *tunnel) + struct l2tp_tunnel *tunnel, u8 cmd) { void *hdr; struct nlattr *nest; @@ -254,8 +321,7 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int fla struct ipv6_pinfo *np = NULL; #endif - hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, - L2TP_CMD_TUNNEL_GET); + hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, cmd); if (!hdr) return -EMSGSIZE; @@ -359,7 +425,7 @@ static int l2tp_nl_cmd_tunnel_get(struct sk_buff *skb, struct genl_info *info) } ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq, - NLM_F_ACK, tunnel); + NLM_F_ACK, tunnel, L2TP_CMD_TUNNEL_GET); if (ret < 0) goto err_out; @@ -385,7 +451,7 @@ static int l2tp_nl_cmd_tunnel_dump(struct sk_buff *skb, struct netlink_callback if (l2tp_nl_tunnel_send(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, - tunnel) <= 0) + tunnel, L2TP_CMD_TUNNEL_GET) <= 0) goto out; ti++; @@ -539,6 +605,13 @@ static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *inf ret = (*l2tp_nl_cmd_ops[cfg.pw_type]->session_create)(net, tunnel_id, session_id, peer_session_id, &cfg); + if (ret >= 0) { + session = l2tp_session_find(net, tunnel, session_id); + if (session) + ret = l2tp_session_notify(&l2tp_nl_family, info, session, + L2TP_CMD_SESSION_CREATE); + } + out: return ret; } @@ -555,6 +628,9 @@ static int l2tp_nl_cmd_session_delete(struct sk_buff *skb, struct genl_info *inf goto out; } + l2tp_session_notify(&l2tp_nl_family, info, + session, L2TP_CMD_SESSION_DELETE); + pw_type = session->pwtype; if (pw_type < __L2TP_PWTYPE_MAX) if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete) @@ -601,12 +677,15 @@ static int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *inf if (info->attrs[L2TP_ATTR_MRU]) session->mru = nla_get_u16(info->attrs[L2TP_ATTR_MRU]); + ret = l2tp_session_notify(&l2tp_nl_family, info, + session, L2TP_CMD_SESSION_MODIFY); + out: return ret; } static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, int flags, - struct l2tp_session *session) + struct l2tp_session *session, u8 cmd) { void *hdr; struct nlattr *nest; @@ -615,7 +694,7 @@ static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, int fl sk = tunnel->sock; - hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, L2TP_CMD_SESSION_GET); + hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, cmd); if (!hdr) return -EMSGSIZE; @@ -699,7 +778,7 @@ static int l2tp_nl_cmd_session_get(struct sk_buff *skb, struct genl_info *info) } ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq, - 0, session); + 0, session, L2TP_CMD_SESSION_GET); if (ret < 0) goto err_out; @@ -737,7 +816,7 @@ static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, - session) <= 0) + session, L2TP_CMD_SESSION_GET) <= 0) break; si++; @@ -896,7 +975,9 @@ EXPORT_SYMBOL_GPL(l2tp_nl_unregister_ops); static int l2tp_nl_init(void) { pr_info("L2TP netlink interface\n"); - return genl_register_family_with_ops(&l2tp_nl_family, l2tp_nl_ops); + return genl_register_family_with_ops_groups(&l2tp_nl_family, + l2tp_nl_ops, + l2tp_multicast_group); } static void l2tp_nl_cleanup(void) -- cgit v0.10.2 From bec94d430f2c97159e21e38c0f1fa4da3710d5e1 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Sat, 27 Dec 2014 10:01:42 -0800 Subject: gre: allow live address change The GRE tap device supports Ethernet over GRE, but doesn't care about the source address of the tunnel, therefore it can be changed without bring device down. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 4f4bf5b..942576e 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -673,6 +673,7 @@ static bool ipgre_netlink_encap_parms(struct nlattr *data[], static int gre_tap_init(struct net_device *dev) { __gre_tunnel_init(dev); + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; return ip_tunnel_init(dev); } -- cgit v0.10.2 From 8274a97aa4c694ad0d7b31b283a89dcca140e62b Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 31 Dec 2014 10:55:29 -0800 Subject: fib_trie: Update usage stats to be percpu instead of global variables The trie usage stats were currently being shared by all threads that were calling fib_table_lookup. As a result when multiple threads were performing lookups simultaneously the trie would begin to cache bounce between those threads. In order to prevent this I have updated the usage stats to use a set of percpu variables. By doing this we should be able to avoid the cache bouncing and still make use of these stats. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 23104a3..6689020 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -67,7 +67,7 @@ static int __net_init fib4_rules_init(struct net *net) return 0; fail: - kfree(local_table); + fib_free_table(local_table); return -ENOMEM; } #else diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 18bcaf2..d3dbb48 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -153,7 +153,7 @@ struct trie_stat { struct trie { struct rt_trie_node __rcu *trie; #ifdef CONFIG_IP_FIB_TRIE_STATS - struct trie_use_stats stats; + struct trie_use_stats __percpu *stats; #endif }; @@ -631,7 +631,7 @@ static struct rt_trie_node *resize(struct trie *t, struct tnode *tn) if (IS_ERR(tn)) { tn = old_tn; #ifdef CONFIG_IP_FIB_TRIE_STATS - t->stats.resize_node_skipped++; + this_cpu_inc(t->stats->resize_node_skipped); #endif break; } @@ -658,7 +658,7 @@ static struct rt_trie_node *resize(struct trie *t, struct tnode *tn) if (IS_ERR(tn)) { tn = old_tn; #ifdef CONFIG_IP_FIB_TRIE_STATS - t->stats.resize_node_skipped++; + this_cpu_inc(t->stats->resize_node_skipped); #endif break; } @@ -1357,7 +1357,7 @@ static int check_leaf(struct fib_table *tb, struct trie *t, struct leaf *l, err = fib_props[fa->fa_type].error; if (err) { #ifdef CONFIG_IP_FIB_TRIE_STATS - t->stats.semantic_match_passed++; + this_cpu_inc(t->stats->semantic_match_passed); #endif return err; } @@ -1372,7 +1372,7 @@ static int check_leaf(struct fib_table *tb, struct trie *t, struct leaf *l, continue; #ifdef CONFIG_IP_FIB_TRIE_STATS - t->stats.semantic_match_passed++; + this_cpu_inc(t->stats->semantic_match_passed); #endif res->prefixlen = li->plen; res->nh_sel = nhsel; @@ -1388,7 +1388,7 @@ static int check_leaf(struct fib_table *tb, struct trie *t, struct leaf *l, } #ifdef CONFIG_IP_FIB_TRIE_STATS - t->stats.semantic_match_miss++; + this_cpu_inc(t->stats->semantic_match_miss); #endif } @@ -1399,6 +1399,9 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, struct fib_result *res, int fib_flags) { struct trie *t = (struct trie *) tb->tb_data; +#ifdef CONFIG_IP_FIB_TRIE_STATS + struct trie_use_stats __percpu *stats = t->stats; +#endif int ret; struct rt_trie_node *n; struct tnode *pn; @@ -1417,7 +1420,7 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, goto failed; #ifdef CONFIG_IP_FIB_TRIE_STATS - t->stats.gets++; + this_cpu_inc(stats->gets); #endif /* Just a leaf? */ @@ -1441,7 +1444,7 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, if (n == NULL) { #ifdef CONFIG_IP_FIB_TRIE_STATS - t->stats.null_node_hit++; + this_cpu_inc(stats->null_node_hit); #endif goto backtrace; } @@ -1576,7 +1579,7 @@ backtrace: chopped_off = 0; #ifdef CONFIG_IP_FIB_TRIE_STATS - t->stats.backtrack++; + this_cpu_inc(stats->backtrack); #endif goto backtrace; } @@ -1830,6 +1833,11 @@ int fib_table_flush(struct fib_table *tb) void fib_free_table(struct fib_table *tb) { +#ifdef CONFIG_IP_FIB_TRIE_STATS + struct trie *t = (struct trie *)tb->tb_data; + + free_percpu(t->stats); +#endif /* CONFIG_IP_FIB_TRIE_STATS */ kfree(tb); } @@ -1973,7 +1981,14 @@ struct fib_table *fib_trie_table(u32 id) tb->tb_num_default = 0; t = (struct trie *) tb->tb_data; - memset(t, 0, sizeof(*t)); + RCU_INIT_POINTER(t->trie, NULL); +#ifdef CONFIG_IP_FIB_TRIE_STATS + t->stats = alloc_percpu(struct trie_use_stats); + if (!t->stats) { + kfree(tb); + tb = NULL; + } +#endif return tb; } @@ -2139,18 +2154,31 @@ static void trie_show_stats(struct seq_file *seq, struct trie_stat *stat) #ifdef CONFIG_IP_FIB_TRIE_STATS static void trie_show_usage(struct seq_file *seq, - const struct trie_use_stats *stats) + const struct trie_use_stats __percpu *stats) { + struct trie_use_stats s = { 0 }; + int cpu; + + /* loop through all of the CPUs and gather up the stats */ + for_each_possible_cpu(cpu) { + const struct trie_use_stats *pcpu = per_cpu_ptr(stats, cpu); + + s.gets += pcpu->gets; + s.backtrack += pcpu->backtrack; + s.semantic_match_passed += pcpu->semantic_match_passed; + s.semantic_match_miss += pcpu->semantic_match_miss; + s.null_node_hit += pcpu->null_node_hit; + s.resize_node_skipped += pcpu->resize_node_skipped; + } + seq_printf(seq, "\nCounters:\n---------\n"); - seq_printf(seq, "gets = %u\n", stats->gets); - seq_printf(seq, "backtracks = %u\n", stats->backtrack); + seq_printf(seq, "gets = %u\n", s.gets); + seq_printf(seq, "backtracks = %u\n", s.backtrack); seq_printf(seq, "semantic match passed = %u\n", - stats->semantic_match_passed); - seq_printf(seq, "semantic match miss = %u\n", - stats->semantic_match_miss); - seq_printf(seq, "null node hit= %u\n", stats->null_node_hit); - seq_printf(seq, "skipped node resize = %u\n\n", - stats->resize_node_skipped); + s.semantic_match_passed); + seq_printf(seq, "semantic match miss = %u\n", s.semantic_match_miss); + seq_printf(seq, "null node hit= %u\n", s.null_node_hit); + seq_printf(seq, "skipped node resize = %u\n\n", s.resize_node_skipped); } #endif /* CONFIG_IP_FIB_TRIE_STATS */ @@ -2191,7 +2219,7 @@ static int fib_triestat_seq_show(struct seq_file *seq, void *v) trie_collect_stats(t, &stat); trie_show_stats(seq, &stat); #ifdef CONFIG_IP_FIB_TRIE_STATS - trie_show_usage(seq, &t->stats); + trie_show_usage(seq, t->stats); #endif } } -- cgit v0.10.2 From 64c9b6fb26ebcc82e36438d4084f2258f29dbadf Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 31 Dec 2014 10:55:35 -0800 Subject: fib_trie: Make leaf and tnode more uniform This change makes some fundamental changes to the way leaves and tnodes are constructed. The big differences are: 1. Leaves now populate pos and bits indicating their full key size. 2. Trie nodes now mask out their lower bits to be consistent with the leaf 3. Both structures have been reordered so that rt_trie_node now consisists of a much larger region including the pos, bits, and rcu portions of the tnode structure. On 32b systems this will result in the leaf being 4B larger as the pos and bits values were added to a hole created by the key as it was only 4B in length. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index d3dbb48..2b72207 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -87,24 +87,38 @@ typedef unsigned int t_key; -#define T_TNODE 0 -#define T_LEAF 1 -#define NODE_TYPE_MASK 0x1UL -#define NODE_TYPE(node) ((node)->parent & NODE_TYPE_MASK) +#define IS_TNODE(n) ((n)->bits) +#define IS_LEAF(n) (!(n)->bits) -#define IS_TNODE(n) (!(n->parent & T_LEAF)) -#define IS_LEAF(n) (n->parent & T_LEAF) +struct tnode { + t_key key; + unsigned char bits; /* 2log(KEYLENGTH) bits needed */ + unsigned char pos; /* 2log(KEYLENGTH) bits needed */ + struct tnode __rcu *parent; + union { + struct rcu_head rcu; + struct tnode *tnode_free; + }; + unsigned int full_children; /* KEYLENGTH bits needed */ + unsigned int empty_children; /* KEYLENGTH bits needed */ + struct rt_trie_node __rcu *child[0]; +}; struct rt_trie_node { - unsigned long parent; t_key key; + unsigned char bits; + unsigned char pos; + struct tnode __rcu *parent; + struct rcu_head rcu; }; struct leaf { - unsigned long parent; t_key key; - struct hlist_head list; + unsigned char bits; + unsigned char pos; + struct tnode __rcu *parent; struct rcu_head rcu; + struct hlist_head list; }; struct leaf_info { @@ -115,20 +129,6 @@ struct leaf_info { struct rcu_head rcu; }; -struct tnode { - unsigned long parent; - t_key key; - unsigned char pos; /* 2log(KEYLENGTH) bits needed */ - unsigned char bits; /* 2log(KEYLENGTH) bits needed */ - unsigned int full_children; /* KEYLENGTH bits needed */ - unsigned int empty_children; /* KEYLENGTH bits needed */ - union { - struct rcu_head rcu; - struct tnode *tnode_free; - }; - struct rt_trie_node __rcu *child[0]; -}; - #ifdef CONFIG_IP_FIB_TRIE_STATS struct trie_use_stats { unsigned int gets; @@ -176,38 +176,27 @@ static const int sync_pages = 128; static struct kmem_cache *fn_alias_kmem __read_mostly; static struct kmem_cache *trie_leaf_kmem __read_mostly; -/* - * caller must hold RTNL - */ -static inline struct tnode *node_parent(const struct rt_trie_node *node) -{ - unsigned long parent; +/* caller must hold RTNL */ +#define node_parent(n) rtnl_dereference((n)->parent) - parent = rcu_dereference_index_check(node->parent, lockdep_rtnl_is_held()); +/* caller must hold RCU read lock or RTNL */ +#define node_parent_rcu(n) rcu_dereference_rtnl((n)->parent) - return (struct tnode *)(parent & ~NODE_TYPE_MASK); -} - -/* - * caller must hold RCU read lock or RTNL - */ -static inline struct tnode *node_parent_rcu(const struct rt_trie_node *node) +/* wrapper for rcu_assign_pointer */ +static inline void node_set_parent(struct rt_trie_node *node, struct tnode *ptr) { - unsigned long parent; - - parent = rcu_dereference_index_check(node->parent, rcu_read_lock_held() || - lockdep_rtnl_is_held()); - - return (struct tnode *)(parent & ~NODE_TYPE_MASK); + if (node) + rcu_assign_pointer(node->parent, ptr); } -/* Same as rcu_assign_pointer - * but that macro() assumes that value is a pointer. +#define NODE_INIT_PARENT(n, p) RCU_INIT_POINTER((n)->parent, p) + +/* This provides us with the number of children in this node, in the case of a + * leaf this will return 0 meaning none of the children are accessible. */ -static inline void node_set_parent(struct rt_trie_node *node, struct tnode *ptr) +static inline int tnode_child_length(const struct tnode *tn) { - smp_wmb(); - node->parent = (unsigned long)ptr | NODE_TYPE(node); + return (1ul << tn->bits) & ~(1ul); } /* @@ -215,7 +204,7 @@ static inline void node_set_parent(struct rt_trie_node *node, struct tnode *ptr) */ static inline struct rt_trie_node *tnode_get_child(const struct tnode *tn, unsigned int i) { - BUG_ON(i >= 1U << tn->bits); + BUG_ON(i >= tnode_child_length(tn)); return rtnl_dereference(tn->child[i]); } @@ -225,16 +214,11 @@ static inline struct rt_trie_node *tnode_get_child(const struct tnode *tn, unsig */ static inline struct rt_trie_node *tnode_get_child_rcu(const struct tnode *tn, unsigned int i) { - BUG_ON(i >= 1U << tn->bits); + BUG_ON(i >= tnode_child_length(tn)); return rcu_dereference_rtnl(tn->child[i]); } -static inline int tnode_child_length(const struct tnode *tn) -{ - return 1 << tn->bits; -} - static inline t_key mask_pfx(t_key k, unsigned int l) { return (l == 0) ? 0 : k >> (KEYLENGTH-l) << (KEYLENGTH-l); @@ -336,11 +320,6 @@ static inline int tkey_mismatch(t_key a, int offset, t_key b) */ -static inline void check_tnode(const struct tnode *tn) -{ - WARN_ON(tn && tn->pos+tn->bits > 32); -} - static const int halve_threshold = 25; static const int inflate_threshold = 50; static const int halve_threshold_root = 15; @@ -426,11 +405,20 @@ static void tnode_free_flush(void) } } -static struct leaf *leaf_new(void) +static struct leaf *leaf_new(t_key key) { struct leaf *l = kmem_cache_alloc(trie_leaf_kmem, GFP_KERNEL); if (l) { - l->parent = T_LEAF; + l->parent = NULL; + /* set key and pos to reflect full key value + * any trailing zeros in the key should be ignored + * as the nodes are searched + */ + l->key = key; + l->pos = KEYLENGTH; + /* set bits to 0 indicating we are not a tnode */ + l->bits = 0; + INIT_HLIST_HEAD(&l->list); } return l; @@ -451,12 +439,16 @@ static struct tnode *tnode_new(t_key key, int pos, int bits) { size_t sz = sizeof(struct tnode) + (sizeof(struct rt_trie_node *) << bits); struct tnode *tn = tnode_alloc(sz); + unsigned int shift = pos + bits; + + /* verify bits and pos their msb bits clear and values are valid */ + BUG_ON(!bits || (shift > KEYLENGTH)); if (tn) { - tn->parent = T_TNODE; + tn->parent = NULL; tn->pos = pos; tn->bits = bits; - tn->key = key; + tn->key = mask_pfx(key, pos); tn->full_children = 0; tn->empty_children = 1<pos == tn->pos + tn->bits; + return n && IS_TNODE(n) && (n->pos == (tn->pos + tn->bits)); } static inline void put_child(struct tnode *tn, int i, @@ -514,8 +503,7 @@ static void tnode_put_child_reorg(struct tnode *tn, int i, struct rt_trie_node * else if (!wasfull && isfull) tn->full_children++; - if (n) - node_set_parent(n, tn); + node_set_parent(n, tn); rcu_assign_pointer(tn->child[i], n); } @@ -523,7 +511,7 @@ static void tnode_put_child_reorg(struct tnode *tn, int i, struct rt_trie_node * #define MAX_WORK 10 static struct rt_trie_node *resize(struct trie *t, struct tnode *tn) { - int i; + struct rt_trie_node *n = NULL; struct tnode *old_tn; int inflate_threshold_use; int halve_threshold_use; @@ -536,12 +524,11 @@ static struct rt_trie_node *resize(struct trie *t, struct tnode *tn) tn, inflate_threshold, halve_threshold); /* No children */ - if (tn->empty_children == tnode_child_length(tn)) { - tnode_free_safe(tn); - return NULL; - } + if (tn->empty_children > (tnode_child_length(tn) - 1)) + goto no_children; + /* One child */ - if (tn->empty_children == tnode_child_length(tn) - 1) + if (tn->empty_children == (tnode_child_length(tn) - 1)) goto one_child; /* * Double as long as the resulting node has a number of @@ -607,11 +594,9 @@ static struct rt_trie_node *resize(struct trie *t, struct tnode *tn) * */ - check_tnode(tn); - /* Keep root node larger */ - if (!node_parent((struct rt_trie_node *)tn)) { + if (!node_parent(tn)) { inflate_threshold_use = inflate_threshold_root; halve_threshold_use = halve_threshold_root; } else { @@ -637,8 +622,6 @@ static struct rt_trie_node *resize(struct trie *t, struct tnode *tn) } } - check_tnode(tn); - /* Return if at least one inflate is run */ if (max_work != MAX_WORK) return (struct rt_trie_node *) tn; @@ -666,21 +649,16 @@ static struct rt_trie_node *resize(struct trie *t, struct tnode *tn) /* Only one child remains */ - if (tn->empty_children == tnode_child_length(tn) - 1) { + if (tn->empty_children == (tnode_child_length(tn) - 1)) { + unsigned long i; one_child: - for (i = 0; i < tnode_child_length(tn); i++) { - struct rt_trie_node *n; - - n = rtnl_dereference(tn->child[i]); - if (!n) - continue; - - /* compress one level */ - - node_set_parent(n, NULL); - tnode_free_safe(tn); - return n; - } + for (i = tnode_child_length(tn); !n && i;) + n = tnode_get_child(tn, --i); +no_children: + /* compress one level */ + node_set_parent(n, NULL); + tnode_free_safe(tn); + return n; } return (struct rt_trie_node *) tn; } @@ -760,8 +738,7 @@ static struct tnode *inflate(struct trie *t, struct tnode *tn) /* A leaf or an internal node with skipped bits */ - if (IS_LEAF(node) || ((struct tnode *) node)->pos > - tn->pos + tn->bits - 1) { + if (IS_LEAF(node) || (node->pos > (tn->pos + tn->bits - 1))) { put_child(tn, tkey_extract_bits(node->key, oldtnode->pos, oldtnode->bits + 1), node); @@ -958,11 +935,9 @@ fib_find_node(struct trie *t, u32 key) pos = 0; n = rcu_dereference_rtnl(t->trie); - while (n != NULL && NODE_TYPE(n) == T_TNODE) { + while (n && IS_TNODE(n)) { tn = (struct tnode *) n; - check_tnode(tn); - if (tkey_sub_equals(tn->key, pos, tn->pos-pos, key)) { pos = tn->pos + tn->bits; n = tnode_get_child_rcu(tn, @@ -988,7 +963,7 @@ static void trie_rebalance(struct trie *t, struct tnode *tn) key = tn->key; - while (tn != NULL && (tp = node_parent((struct rt_trie_node *)tn)) != NULL) { + while (tn != NULL && (tp = node_parent(tn)) != NULL) { cindex = tkey_extract_bits(key, tp->pos, tp->bits); wasfull = tnode_full(tp, tnode_get_child(tp, cindex)); tn = (struct tnode *)resize(t, tn); @@ -996,7 +971,7 @@ static void trie_rebalance(struct trie *t, struct tnode *tn) tnode_put_child_reorg(tp, cindex, (struct rt_trie_node *)tn, wasfull); - tp = node_parent((struct rt_trie_node *) tn); + tp = node_parent(tn); if (!tp) rcu_assign_pointer(t->trie, (struct rt_trie_node *)tn); @@ -1048,11 +1023,9 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) * If it doesn't, we need to replace it with a T_TNODE. */ - while (n != NULL && NODE_TYPE(n) == T_TNODE) { + while (n && IS_TNODE(n)) { tn = (struct tnode *) n; - check_tnode(tn); - if (tkey_sub_equals(tn->key, pos, tn->pos-pos, key)) { tp = tn; pos = tn->pos + tn->bits; @@ -1087,12 +1060,11 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) insert_leaf_info(&l->list, li); goto done; } - l = leaf_new(); + l = leaf_new(key); if (!l) return NULL; - l->key = key; li = leaf_info_new(plen); if (!li) { @@ -1569,7 +1541,7 @@ backtrace: if (chopped_off <= pn->bits) { cindex &= ~(1 << (chopped_off-1)); } else { - struct tnode *parent = node_parent_rcu((struct rt_trie_node *) pn); + struct tnode *parent = node_parent_rcu(pn); if (!parent) goto failed; @@ -1597,7 +1569,7 @@ EXPORT_SYMBOL_GPL(fib_table_lookup); */ static void trie_leaf_remove(struct trie *t, struct leaf *l) { - struct tnode *tp = node_parent((struct rt_trie_node *) l); + struct tnode *tp = node_parent(l); pr_debug("entering trie_leaf_remove(%p)\n", l); @@ -2374,7 +2346,7 @@ static int fib_trie_seq_show(struct seq_file *seq, void *v) if (IS_TNODE(n)) { struct tnode *tn = (struct tnode *) n; - __be32 prf = htonl(mask_pfx(tn->key, tn->pos)); + __be32 prf = htonl(tn->key); seq_indent(seq, iter->depth-1); seq_printf(seq, " +-- %pI4/%d %d %d %d\n", -- cgit v0.10.2 From 37fd30f2da573c2625ed29561e5b8eb7b0258860 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 31 Dec 2014 10:55:41 -0800 Subject: fib_trie: Merge tnode_free and leaf_free into node_free Both the leaf and the tnode had an rcu_head in them, but they had them in slightly different places. Since we now have them in the same spot and know that any node with bits == 0 is a leaf and the rest are either vmalloc or kmalloc tnodes depending on the value of bits it makes it easy to combine the functions and reduce overhead. In addition I have taken advantage of the rcu_head pointer to go ahead and put together a simple linked list instead of using the tnode pointer as this way we can merge either type of structure for freeing. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 2b72207..d1a9907 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -95,15 +95,17 @@ struct tnode { unsigned char bits; /* 2log(KEYLENGTH) bits needed */ unsigned char pos; /* 2log(KEYLENGTH) bits needed */ struct tnode __rcu *parent; - union { - struct rcu_head rcu; - struct tnode *tnode_free; - }; + struct rcu_head rcu; + /* everything above this comment must be the same as rt_trie_node */ unsigned int full_children; /* KEYLENGTH bits needed */ unsigned int empty_children; /* KEYLENGTH bits needed */ struct rt_trie_node __rcu *child[0]; }; +/* This struct represents the shared bits between tnode and leaf. If any + * ordering is changed here is must also be updated in tnode and leaf as + * well. + */ struct rt_trie_node { t_key key; unsigned char bits; @@ -118,6 +120,7 @@ struct leaf { unsigned char pos; struct tnode __rcu *parent; struct rcu_head rcu; + /* everything above this comment must be the same as rt_trie_node */ struct hlist_head list; }; @@ -163,7 +166,7 @@ static struct rt_trie_node *resize(struct trie *t, struct tnode *tn); static struct tnode *inflate(struct trie *t, struct tnode *tn); static struct tnode *halve(struct trie *t, struct tnode *tn); /* tnodes to free after resize(); protected by RTNL */ -static struct tnode *tnode_free_head; +static struct callback_head *tnode_free_head; static size_t tnode_free_size; /* @@ -336,17 +339,23 @@ static inline void alias_free_mem_rcu(struct fib_alias *fa) call_rcu(&fa->rcu, __alias_free_mem); } -static void __leaf_free_rcu(struct rcu_head *head) -{ - struct leaf *l = container_of(head, struct leaf, rcu); - kmem_cache_free(trie_leaf_kmem, l); -} +#define TNODE_KMALLOC_MAX \ + ilog2((PAGE_SIZE - sizeof(struct tnode)) / sizeof(struct rt_trie_node *)) -static inline void free_leaf(struct leaf *l) +static void __node_free_rcu(struct rcu_head *head) { - call_rcu(&l->rcu, __leaf_free_rcu); + struct rt_trie_node *n = container_of(head, struct rt_trie_node, rcu); + + if (IS_LEAF(n)) + kmem_cache_free(trie_leaf_kmem, n); + else if (n->bits <= TNODE_KMALLOC_MAX) + kfree(n); + else + vfree(n); } +#define node_free(n) call_rcu(&n->rcu, __node_free_rcu) + static inline void free_leaf_info(struct leaf_info *leaf) { kfree_rcu(leaf, rcu); @@ -360,43 +369,24 @@ static struct tnode *tnode_alloc(size_t size) return vzalloc(size); } -static void __tnode_free_rcu(struct rcu_head *head) -{ - struct tnode *tn = container_of(head, struct tnode, rcu); - size_t size = sizeof(struct tnode) + - (sizeof(struct rt_trie_node *) << tn->bits); - - if (size <= PAGE_SIZE) - kfree(tn); - else - vfree(tn); -} - -static inline void tnode_free(struct tnode *tn) -{ - if (IS_LEAF(tn)) - free_leaf((struct leaf *) tn); - else - call_rcu(&tn->rcu, __tnode_free_rcu); -} - static void tnode_free_safe(struct tnode *tn) { BUG_ON(IS_LEAF(tn)); - tn->tnode_free = tnode_free_head; - tnode_free_head = tn; - tnode_free_size += sizeof(struct tnode) + - (sizeof(struct rt_trie_node *) << tn->bits); + tn->rcu.next = tnode_free_head; + tnode_free_head = &tn->rcu; } static void tnode_free_flush(void) { - struct tnode *tn; + struct callback_head *head; + + while ((head = tnode_free_head)) { + struct tnode *tn = container_of(head, struct tnode, rcu); + + tnode_free_head = head->next; + tnode_free_size += offsetof(struct tnode, child[1 << tn->bits]); - while ((tn = tnode_free_head)) { - tnode_free_head = tn->tnode_free; - tn->tnode_free = NULL; - tnode_free(tn); + node_free(tn); } if (tnode_free_size >= PAGE_SIZE * sync_pages) { @@ -437,7 +427,7 @@ static struct leaf_info *leaf_info_new(int plen) static struct tnode *tnode_new(t_key key, int pos, int bits) { - size_t sz = sizeof(struct tnode) + (sizeof(struct rt_trie_node *) << bits); + size_t sz = offsetof(struct tnode, child[1 << bits]); struct tnode *tn = tnode_alloc(sz); unsigned int shift = pos + bits; @@ -666,15 +656,15 @@ no_children: static void tnode_clean_free(struct tnode *tn) { + struct rt_trie_node *tofree; int i; - struct tnode *tofree; for (i = 0; i < tnode_child_length(tn); i++) { - tofree = (struct tnode *)rtnl_dereference(tn->child[i]); + tofree = rtnl_dereference(tn->child[i]); if (tofree) - tnode_free(tofree); + node_free(tofree); } - tnode_free(tn); + node_free(tn); } static struct tnode *inflate(struct trie *t, struct tnode *tn) @@ -717,7 +707,7 @@ static struct tnode *inflate(struct trie *t, struct tnode *tn) inode->bits - 1); if (!right) { - tnode_free(left); + node_free(left); goto nomem; } @@ -1068,7 +1058,7 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) li = leaf_info_new(plen); if (!li) { - free_leaf(l); + node_free(l); return NULL; } @@ -1100,7 +1090,7 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) if (!tn) { free_leaf_info(li); - free_leaf(l); + node_free(l); return NULL; } @@ -1580,7 +1570,7 @@ static void trie_leaf_remove(struct trie *t, struct leaf *l) } else RCU_INIT_POINTER(t->trie, NULL); - free_leaf(l); + node_free(l); } /* -- cgit v0.10.2 From adaf981685b7b17d773d17f4ee6a128a3a6b6dc8 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 31 Dec 2014 10:55:47 -0800 Subject: fib_trie: Merge leaf into tnode This change makes it so that leaf and tnode are the same struct. As a result there is no need for rt_trie_node anymore since everyting can be merged into tnode. On 32b systems this results in the leaf being 4 bytes larger, however I don't know if that is really an issue as this and an eariler patch that added bits & pos have increased the size from 20 to 28. If I am not mistaken slub/slab allocate on power of 2 sizes so 20 was likely being rounded up to 32 anyway. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index d1a9907..94c929f 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -96,32 +96,16 @@ struct tnode { unsigned char pos; /* 2log(KEYLENGTH) bits needed */ struct tnode __rcu *parent; struct rcu_head rcu; - /* everything above this comment must be the same as rt_trie_node */ - unsigned int full_children; /* KEYLENGTH bits needed */ - unsigned int empty_children; /* KEYLENGTH bits needed */ - struct rt_trie_node __rcu *child[0]; -}; - -/* This struct represents the shared bits between tnode and leaf. If any - * ordering is changed here is must also be updated in tnode and leaf as - * well. - */ -struct rt_trie_node { - t_key key; - unsigned char bits; - unsigned char pos; - struct tnode __rcu *parent; - struct rcu_head rcu; -}; - -struct leaf { - t_key key; - unsigned char bits; - unsigned char pos; - struct tnode __rcu *parent; - struct rcu_head rcu; - /* everything above this comment must be the same as rt_trie_node */ - struct hlist_head list; + union { + /* The fields in this struct are valid if bits > 0 (TNODE) */ + struct { + unsigned int full_children; /* KEYLENGTH bits needed */ + unsigned int empty_children; /* KEYLENGTH bits needed */ + struct tnode __rcu *child[0]; + }; + /* This list pointer if valid if bits == 0 (LEAF) */ + struct hlist_head list; + }; }; struct leaf_info { @@ -154,15 +138,15 @@ struct trie_stat { }; struct trie { - struct rt_trie_node __rcu *trie; + struct tnode __rcu *trie; #ifdef CONFIG_IP_FIB_TRIE_STATS struct trie_use_stats __percpu *stats; #endif }; -static void tnode_put_child_reorg(struct tnode *tn, int i, struct rt_trie_node *n, +static void tnode_put_child_reorg(struct tnode *tn, int i, struct tnode *n, int wasfull); -static struct rt_trie_node *resize(struct trie *t, struct tnode *tn); +static struct tnode *resize(struct trie *t, struct tnode *tn); static struct tnode *inflate(struct trie *t, struct tnode *tn); static struct tnode *halve(struct trie *t, struct tnode *tn); /* tnodes to free after resize(); protected by RTNL */ @@ -186,10 +170,10 @@ static struct kmem_cache *trie_leaf_kmem __read_mostly; #define node_parent_rcu(n) rcu_dereference_rtnl((n)->parent) /* wrapper for rcu_assign_pointer */ -static inline void node_set_parent(struct rt_trie_node *node, struct tnode *ptr) +static inline void node_set_parent(struct tnode *n, struct tnode *tp) { - if (node) - rcu_assign_pointer(node->parent, ptr); + if (n) + rcu_assign_pointer(n->parent, tp); } #define NODE_INIT_PARENT(n, p) RCU_INIT_POINTER((n)->parent, p) @@ -205,7 +189,7 @@ static inline int tnode_child_length(const struct tnode *tn) /* * caller must hold RTNL */ -static inline struct rt_trie_node *tnode_get_child(const struct tnode *tn, unsigned int i) +static inline struct tnode *tnode_get_child(const struct tnode *tn, unsigned int i) { BUG_ON(i >= tnode_child_length(tn)); @@ -215,7 +199,7 @@ static inline struct rt_trie_node *tnode_get_child(const struct tnode *tn, unsig /* * caller must hold RCU read lock or RTNL */ -static inline struct rt_trie_node *tnode_get_child_rcu(const struct tnode *tn, unsigned int i) +static inline struct tnode *tnode_get_child_rcu(const struct tnode *tn, unsigned int i) { BUG_ON(i >= tnode_child_length(tn)); @@ -340,11 +324,11 @@ static inline void alias_free_mem_rcu(struct fib_alias *fa) } #define TNODE_KMALLOC_MAX \ - ilog2((PAGE_SIZE - sizeof(struct tnode)) / sizeof(struct rt_trie_node *)) + ilog2((PAGE_SIZE - sizeof(struct tnode)) / sizeof(struct tnode *)) static void __node_free_rcu(struct rcu_head *head) { - struct rt_trie_node *n = container_of(head, struct rt_trie_node, rcu); + struct tnode *n = container_of(head, struct tnode, rcu); if (IS_LEAF(n)) kmem_cache_free(trie_leaf_kmem, n); @@ -395,9 +379,9 @@ static void tnode_free_flush(void) } } -static struct leaf *leaf_new(t_key key) +static struct tnode *leaf_new(t_key key) { - struct leaf *l = kmem_cache_alloc(trie_leaf_kmem, GFP_KERNEL); + struct tnode *l = kmem_cache_alloc(trie_leaf_kmem, GFP_KERNEL); if (l) { l->parent = NULL; /* set key and pos to reflect full key value @@ -444,7 +428,7 @@ static struct tnode *tnode_new(t_key key, int pos, int bits) } pr_debug("AT %p s=%zu %zu\n", tn, sizeof(struct tnode), - sizeof(struct rt_trie_node *) << bits); + sizeof(struct tnode *) << bits); return tn; } @@ -453,13 +437,13 @@ static struct tnode *tnode_new(t_key key, int pos, int bits) * and no bits are skipped. See discussion in dyntree paper p. 6 */ -static inline int tnode_full(const struct tnode *tn, const struct rt_trie_node *n) +static inline int tnode_full(const struct tnode *tn, const struct tnode *n) { return n && IS_TNODE(n) && (n->pos == (tn->pos + tn->bits)); } static inline void put_child(struct tnode *tn, int i, - struct rt_trie_node *n) + struct tnode *n) { tnode_put_child_reorg(tn, i, n, -1); } @@ -469,10 +453,10 @@ static inline void put_child(struct tnode *tn, int i, * Update the value of full_children and empty_children. */ -static void tnode_put_child_reorg(struct tnode *tn, int i, struct rt_trie_node *n, +static void tnode_put_child_reorg(struct tnode *tn, int i, struct tnode *n, int wasfull) { - struct rt_trie_node *chi = rtnl_dereference(tn->child[i]); + struct tnode *chi = rtnl_dereference(tn->child[i]); int isfull; BUG_ON(i >= 1<bits); @@ -499,10 +483,9 @@ static void tnode_put_child_reorg(struct tnode *tn, int i, struct rt_trie_node * } #define MAX_WORK 10 -static struct rt_trie_node *resize(struct trie *t, struct tnode *tn) +static struct tnode *resize(struct trie *t, struct tnode *tn) { - struct rt_trie_node *n = NULL; - struct tnode *old_tn; + struct tnode *old_tn, *n = NULL; int inflate_threshold_use; int halve_threshold_use; int max_work; @@ -614,7 +597,7 @@ static struct rt_trie_node *resize(struct trie *t, struct tnode *tn) /* Return if at least one inflate is run */ if (max_work != MAX_WORK) - return (struct rt_trie_node *) tn; + return tn; /* * Halve as long as the number of empty children in this @@ -650,13 +633,13 @@ no_children: tnode_free_safe(tn); return n; } - return (struct rt_trie_node *) tn; + return tn; } static void tnode_clean_free(struct tnode *tn) { - struct rt_trie_node *tofree; + struct tnode *tofree; int i; for (i = 0; i < tnode_child_length(tn); i++) { @@ -667,10 +650,10 @@ static void tnode_clean_free(struct tnode *tn) node_free(tn); } -static struct tnode *inflate(struct trie *t, struct tnode *tn) +static struct tnode *inflate(struct trie *t, struct tnode *oldtnode) { - struct tnode *oldtnode = tn; - int olen = tnode_child_length(tn); + int olen = tnode_child_length(oldtnode); + struct tnode *tn; int i; pr_debug("In inflate\n"); @@ -690,11 +673,8 @@ static struct tnode *inflate(struct trie *t, struct tnode *tn) for (i = 0; i < olen; i++) { struct tnode *inode; - inode = (struct tnode *) tnode_get_child(oldtnode, i); - if (inode && - IS_TNODE(inode) && - inode->pos == oldtnode->pos + oldtnode->bits && - inode->bits > 1) { + inode = tnode_get_child(oldtnode, i); + if (tnode_full(oldtnode, inode) && inode->bits > 1) { struct tnode *left, *right; t_key m = ~0U << (KEYLENGTH - 1) >> inode->pos; @@ -711,33 +691,29 @@ static struct tnode *inflate(struct trie *t, struct tnode *tn) goto nomem; } - put_child(tn, 2*i, (struct rt_trie_node *) left); - put_child(tn, 2*i+1, (struct rt_trie_node *) right); + put_child(tn, 2*i, left); + put_child(tn, 2*i+1, right); } } for (i = 0; i < olen; i++) { - struct tnode *inode; - struct rt_trie_node *node = tnode_get_child(oldtnode, i); + struct tnode *inode = tnode_get_child(oldtnode, i); struct tnode *left, *right; int size, j; /* An empty child */ - if (node == NULL) + if (inode == NULL) continue; /* A leaf or an internal node with skipped bits */ - - if (IS_LEAF(node) || (node->pos > (tn->pos + tn->bits - 1))) { + if (!tnode_full(oldtnode, inode)) { put_child(tn, - tkey_extract_bits(node->key, oldtnode->pos, oldtnode->bits + 1), - node); + tkey_extract_bits(inode->key, tn->pos, tn->bits), + inode); continue; } /* An internal node with two children */ - inode = (struct tnode *) node; - if (inode->bits == 1) { put_child(tn, 2*i, rtnl_dereference(inode->child[0])); put_child(tn, 2*i+1, rtnl_dereference(inode->child[1])); @@ -769,12 +745,12 @@ static struct tnode *inflate(struct trie *t, struct tnode *tn) * bit to zero. */ - left = (struct tnode *) tnode_get_child(tn, 2*i); + left = tnode_get_child(tn, 2*i); put_child(tn, 2*i, NULL); BUG_ON(!left); - right = (struct tnode *) tnode_get_child(tn, 2*i+1); + right = tnode_get_child(tn, 2*i+1); put_child(tn, 2*i+1, NULL); BUG_ON(!right); @@ -796,12 +772,11 @@ nomem: return ERR_PTR(-ENOMEM); } -static struct tnode *halve(struct trie *t, struct tnode *tn) +static struct tnode *halve(struct trie *t, struct tnode *oldtnode) { - struct tnode *oldtnode = tn; - struct rt_trie_node *left, *right; + int olen = tnode_child_length(oldtnode); + struct tnode *tn, *left, *right; int i; - int olen = tnode_child_length(tn); pr_debug("In halve\n"); @@ -830,7 +805,7 @@ static struct tnode *halve(struct trie *t, struct tnode *tn) if (!newn) goto nomem; - put_child(tn, i/2, (struct rt_trie_node *)newn); + put_child(tn, i/2, newn); } } @@ -855,7 +830,7 @@ static struct tnode *halve(struct trie *t, struct tnode *tn) } /* Two nonempty children */ - newBinNode = (struct tnode *) tnode_get_child(tn, i/2); + newBinNode = tnode_get_child(tn, i/2); put_child(tn, i/2, NULL); put_child(newBinNode, 0, left); put_child(newBinNode, 1, right); @@ -871,7 +846,7 @@ nomem: /* readside must use rcu_read_lock currently dump routines via get_fa_head and dump */ -static struct leaf_info *find_leaf_info(struct leaf *l, int plen) +static struct leaf_info *find_leaf_info(struct tnode *l, int plen) { struct hlist_head *head = &l->list; struct leaf_info *li; @@ -883,7 +858,7 @@ static struct leaf_info *find_leaf_info(struct leaf *l, int plen) return NULL; } -static inline struct list_head *get_fa_head(struct leaf *l, int plen) +static inline struct list_head *get_fa_head(struct tnode *l, int plen) { struct leaf_info *li = find_leaf_info(l, plen); @@ -915,32 +890,25 @@ static void insert_leaf_info(struct hlist_head *head, struct leaf_info *new) /* rcu_read_lock needs to be hold by caller from readside */ -static struct leaf * -fib_find_node(struct trie *t, u32 key) +static struct tnode *fib_find_node(struct trie *t, u32 key) { - int pos; - struct tnode *tn; - struct rt_trie_node *n; - - pos = 0; - n = rcu_dereference_rtnl(t->trie); + struct tnode *n = rcu_dereference_rtnl(t->trie); + int pos = 0; while (n && IS_TNODE(n)) { - tn = (struct tnode *) n; - - if (tkey_sub_equals(tn->key, pos, tn->pos-pos, key)) { - pos = tn->pos + tn->bits; - n = tnode_get_child_rcu(tn, + if (tkey_sub_equals(n->key, pos, n->pos-pos, key)) { + pos = n->pos + n->bits; + n = tnode_get_child_rcu(n, tkey_extract_bits(key, - tn->pos, - tn->bits)); + n->pos, + n->bits)); } else break; } /* Case we have found a leaf. Compare prefixes */ if (n != NULL && IS_LEAF(n) && tkey_equals(key, n->key)) - return (struct leaf *)n; + return n; return NULL; } @@ -956,14 +924,13 @@ static void trie_rebalance(struct trie *t, struct tnode *tn) while (tn != NULL && (tp = node_parent(tn)) != NULL) { cindex = tkey_extract_bits(key, tp->pos, tp->bits); wasfull = tnode_full(tp, tnode_get_child(tp, cindex)); - tn = (struct tnode *)resize(t, tn); + tn = resize(t, tn); - tnode_put_child_reorg(tp, cindex, - (struct rt_trie_node *)tn, wasfull); + tnode_put_child_reorg(tp, cindex, tn, wasfull); tp = node_parent(tn); if (!tp) - rcu_assign_pointer(t->trie, (struct rt_trie_node *)tn); + rcu_assign_pointer(t->trie, tn); tnode_free_flush(); if (!tp) @@ -973,9 +940,9 @@ static void trie_rebalance(struct trie *t, struct tnode *tn) /* Handle last (top) tnode */ if (IS_TNODE(tn)) - tn = (struct tnode *)resize(t, tn); + tn = resize(t, tn); - rcu_assign_pointer(t->trie, (struct rt_trie_node *)tn); + rcu_assign_pointer(t->trie, tn); tnode_free_flush(); } @@ -985,8 +952,8 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) { int pos, newpos; struct tnode *tp = NULL, *tn = NULL; - struct rt_trie_node *n; - struct leaf *l; + struct tnode *n; + struct tnode *l; int missbit; struct list_head *fa_head = NULL; struct leaf_info *li; @@ -1014,17 +981,15 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) */ while (n && IS_TNODE(n)) { - tn = (struct tnode *) n; - - if (tkey_sub_equals(tn->key, pos, tn->pos-pos, key)) { - tp = tn; - pos = tn->pos + tn->bits; - n = tnode_get_child(tn, + if (tkey_sub_equals(n->key, pos, n->pos-pos, key)) { + tp = n; + pos = n->pos + n->bits; + n = tnode_get_child(n, tkey_extract_bits(key, - tn->pos, - tn->bits)); + n->pos, + n->bits)); - BUG_ON(n && node_parent(n) != tn); + BUG_ON(n && node_parent(n) != tp); } else break; } @@ -1040,14 +1005,13 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) /* Case 1: n is a leaf. Compare prefixes */ if (n != NULL && IS_LEAF(n) && tkey_equals(key, n->key)) { - l = (struct leaf *) n; li = leaf_info_new(plen); if (!li) return NULL; fa_head = &li->falh; - insert_leaf_info(&l->list, li); + insert_leaf_info(&n->list, li); goto done; } l = leaf_new(key); @@ -1068,10 +1032,10 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) if (t->trie && n == NULL) { /* Case 2: n is NULL, and will just insert a new leaf */ - node_set_parent((struct rt_trie_node *)l, tp); + node_set_parent(l, tp); cindex = tkey_extract_bits(key, tp->pos, tp->bits); - put_child(tp, cindex, (struct rt_trie_node *)l); + put_child(tp, cindex, l); } else { /* Case 3: n is a LEAF or a TNODE and the key doesn't match. */ /* @@ -1094,17 +1058,17 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) return NULL; } - node_set_parent((struct rt_trie_node *)tn, tp); + node_set_parent(tn, tp); missbit = tkey_extract_bits(key, newpos, 1); - put_child(tn, missbit, (struct rt_trie_node *)l); + put_child(tn, missbit, l); put_child(tn, 1-missbit, n); if (tp) { cindex = tkey_extract_bits(key, tp->pos, tp->bits); - put_child(tp, cindex, (struct rt_trie_node *)tn); + put_child(tp, cindex, tn); } else { - rcu_assign_pointer(t->trie, (struct rt_trie_node *)tn); + rcu_assign_pointer(t->trie, tn); } tp = tn; @@ -1134,7 +1098,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) u8 tos = cfg->fc_tos; u32 key, mask; int err; - struct leaf *l; + struct tnode *l; if (plen > 32) return -EINVAL; @@ -1292,7 +1256,7 @@ err: } /* should be called with rcu_read_lock */ -static int check_leaf(struct fib_table *tb, struct trie *t, struct leaf *l, +static int check_leaf(struct fib_table *tb, struct trie *t, struct tnode *l, t_key key, const struct flowi4 *flp, struct fib_result *res, int fib_flags) { @@ -1365,7 +1329,7 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, struct trie_use_stats __percpu *stats = t->stats; #endif int ret; - struct rt_trie_node *n; + struct tnode *n; struct tnode *pn; unsigned int pos, bits; t_key key = ntohl(flp->daddr); @@ -1387,11 +1351,11 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, /* Just a leaf? */ if (IS_LEAF(n)) { - ret = check_leaf(tb, t, (struct leaf *)n, key, flp, res, fib_flags); + ret = check_leaf(tb, t, n, key, flp, res, fib_flags); goto found; } - pn = (struct tnode *) n; + pn = n; chopped_off = 0; while (pn) { @@ -1412,13 +1376,13 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, } if (IS_LEAF(n)) { - ret = check_leaf(tb, t, (struct leaf *)n, key, flp, res, fib_flags); + ret = check_leaf(tb, t, n, key, flp, res, fib_flags); if (ret > 0) goto backtrace; goto found; } - cn = (struct tnode *)n; + cn = n; /* * It's a tnode, and we can do some extra checks here if we @@ -1506,7 +1470,7 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, current_prefix_length = mp; } - pn = (struct tnode *)n; /* Descend */ + pn = n; /* Descend */ chopped_off = 0; continue; @@ -1557,7 +1521,7 @@ EXPORT_SYMBOL_GPL(fib_table_lookup); /* * Remove the leaf and return parent. */ -static void trie_leaf_remove(struct trie *t, struct leaf *l) +static void trie_leaf_remove(struct trie *t, struct tnode *l) { struct tnode *tp = node_parent(l); @@ -1584,7 +1548,7 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg) u8 tos = cfg->fc_tos; struct fib_alias *fa, *fa_to_delete; struct list_head *fa_head; - struct leaf *l; + struct tnode *l; struct leaf_info *li; if (plen > 32) @@ -1682,7 +1646,7 @@ static int trie_flush_list(struct list_head *head) return found; } -static int trie_flush_leaf(struct leaf *l) +static int trie_flush_leaf(struct tnode *l) { int found = 0; struct hlist_head *lih = &l->list; @@ -1704,7 +1668,7 @@ static int trie_flush_leaf(struct leaf *l) * Scan for the next right leaf starting at node p->child[idx] * Since we have back pointer, no recursion necessary. */ -static struct leaf *leaf_walk_rcu(struct tnode *p, struct rt_trie_node *c) +static struct tnode *leaf_walk_rcu(struct tnode *p, struct tnode *c) { do { t_key idx; @@ -1720,47 +1684,46 @@ static struct leaf *leaf_walk_rcu(struct tnode *p, struct rt_trie_node *c) continue; if (IS_LEAF(c)) - return (struct leaf *) c; + return c; /* Rescan start scanning in new node */ - p = (struct tnode *) c; + p = c; idx = 0; } /* Node empty, walk back up to parent */ - c = (struct rt_trie_node *) p; + c = p; } while ((p = node_parent_rcu(c)) != NULL); return NULL; /* Root of trie */ } -static struct leaf *trie_firstleaf(struct trie *t) +static struct tnode *trie_firstleaf(struct trie *t) { - struct tnode *n = (struct tnode *)rcu_dereference_rtnl(t->trie); + struct tnode *n = rcu_dereference_rtnl(t->trie); if (!n) return NULL; if (IS_LEAF(n)) /* trie is just a leaf */ - return (struct leaf *) n; + return n; return leaf_walk_rcu(n, NULL); } -static struct leaf *trie_nextleaf(struct leaf *l) +static struct tnode *trie_nextleaf(struct tnode *l) { - struct rt_trie_node *c = (struct rt_trie_node *) l; - struct tnode *p = node_parent_rcu(c); + struct tnode *p = node_parent_rcu(l); if (!p) return NULL; /* trie with just one leaf */ - return leaf_walk_rcu(p, c); + return leaf_walk_rcu(p, l); } -static struct leaf *trie_leafindex(struct trie *t, int index) +static struct tnode *trie_leafindex(struct trie *t, int index) { - struct leaf *l = trie_firstleaf(t); + struct tnode *l = trie_firstleaf(t); while (l && index-- > 0) l = trie_nextleaf(l); @@ -1775,7 +1738,7 @@ static struct leaf *trie_leafindex(struct trie *t, int index) int fib_table_flush(struct fib_table *tb) { struct trie *t = (struct trie *) tb->tb_data; - struct leaf *l, *ll = NULL; + struct tnode *l, *ll = NULL; int found = 0; for (l = trie_firstleaf(t); l; l = trie_nextleaf(l)) { @@ -1840,7 +1803,7 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah, return skb->len; } -static int fn_trie_dump_leaf(struct leaf *l, struct fib_table *tb, +static int fn_trie_dump_leaf(struct tnode *l, struct fib_table *tb, struct sk_buff *skb, struct netlink_callback *cb) { struct leaf_info *li; @@ -1876,7 +1839,7 @@ static int fn_trie_dump_leaf(struct leaf *l, struct fib_table *tb, int fib_table_dump(struct fib_table *tb, struct sk_buff *skb, struct netlink_callback *cb) { - struct leaf *l; + struct tnode *l; struct trie *t = (struct trie *) tb->tb_data; t_key key = cb->args[2]; int count = cb->args[3]; @@ -1922,7 +1885,7 @@ void __init fib_trie_init(void) 0, SLAB_PANIC, NULL); trie_leaf_kmem = kmem_cache_create("ip_fib_trie", - max(sizeof(struct leaf), + max(sizeof(struct tnode), sizeof(struct leaf_info)), 0, SLAB_PANIC, NULL); } @@ -1965,7 +1928,7 @@ struct fib_trie_iter { unsigned int depth; }; -static struct rt_trie_node *fib_trie_get_next(struct fib_trie_iter *iter) +static struct tnode *fib_trie_get_next(struct fib_trie_iter *iter) { struct tnode *tn = iter->tnode; unsigned int cindex = iter->index; @@ -1979,7 +1942,7 @@ static struct rt_trie_node *fib_trie_get_next(struct fib_trie_iter *iter) iter->tnode, iter->index, iter->depth); rescan: while (cindex < (1<bits)) { - struct rt_trie_node *n = tnode_get_child_rcu(tn, cindex); + struct tnode *n = tnode_get_child_rcu(tn, cindex); if (n) { if (IS_LEAF(n)) { @@ -1987,7 +1950,7 @@ rescan: iter->index = cindex + 1; } else { /* push down one level */ - iter->tnode = (struct tnode *) n; + iter->tnode = n; iter->index = 0; ++iter->depth; } @@ -1998,7 +1961,7 @@ rescan: } /* Current node exhausted, pop back up */ - p = node_parent_rcu((struct rt_trie_node *)tn); + p = node_parent_rcu(tn); if (p) { cindex = tkey_extract_bits(tn->key, p->pos, p->bits)+1; tn = p; @@ -2010,10 +1973,10 @@ rescan: return NULL; } -static struct rt_trie_node *fib_trie_get_first(struct fib_trie_iter *iter, +static struct tnode *fib_trie_get_first(struct fib_trie_iter *iter, struct trie *t) { - struct rt_trie_node *n; + struct tnode *n; if (!t) return NULL; @@ -2023,7 +1986,7 @@ static struct rt_trie_node *fib_trie_get_first(struct fib_trie_iter *iter, return NULL; if (IS_TNODE(n)) { - iter->tnode = (struct tnode *) n; + iter->tnode = n; iter->index = 0; iter->depth = 1; } else { @@ -2037,7 +2000,7 @@ static struct rt_trie_node *fib_trie_get_first(struct fib_trie_iter *iter, static void trie_collect_stats(struct trie *t, struct trie_stat *s) { - struct rt_trie_node *n; + struct tnode *n; struct fib_trie_iter iter; memset(s, 0, sizeof(*s)); @@ -2045,7 +2008,6 @@ static void trie_collect_stats(struct trie *t, struct trie_stat *s) rcu_read_lock(); for (n = fib_trie_get_first(&iter, t); n; n = fib_trie_get_next(&iter)) { if (IS_LEAF(n)) { - struct leaf *l = (struct leaf *)n; struct leaf_info *li; s->leaves++; @@ -2053,18 +2015,17 @@ static void trie_collect_stats(struct trie *t, struct trie_stat *s) if (iter.depth > s->maxdepth) s->maxdepth = iter.depth; - hlist_for_each_entry_rcu(li, &l->list, hlist) + hlist_for_each_entry_rcu(li, &n->list, hlist) ++s->prefixes; } else { - const struct tnode *tn = (const struct tnode *) n; int i; s->tnodes++; - if (tn->bits < MAX_STAT_DEPTH) - s->nodesizes[tn->bits]++; + if (n->bits < MAX_STAT_DEPTH) + s->nodesizes[n->bits]++; - for (i = 0; i < (1<bits); i++) - if (!tn->child[i]) + for (i = 0; i < tnode_child_length(n); i++) + if (!rcu_access_pointer(n->child[i])) s->nullpointers++; } } @@ -2088,7 +2049,7 @@ static void trie_show_stats(struct seq_file *seq, struct trie_stat *stat) seq_printf(seq, "\tMax depth: %u\n", stat->maxdepth); seq_printf(seq, "\tLeaves: %u\n", stat->leaves); - bytes = sizeof(struct leaf) * stat->leaves; + bytes = sizeof(struct tnode) * stat->leaves; seq_printf(seq, "\tPrefixes: %u\n", stat->prefixes); bytes += sizeof(struct leaf_info) * stat->prefixes; @@ -2109,7 +2070,7 @@ static void trie_show_stats(struct seq_file *seq, struct trie_stat *stat) seq_putc(seq, '\n'); seq_printf(seq, "\tPointers: %u\n", pointers); - bytes += sizeof(struct rt_trie_node *) * pointers; + bytes += sizeof(struct tnode *) * pointers; seq_printf(seq, "Null ptrs: %u\n", stat->nullpointers); seq_printf(seq, "Total size: %u kB\n", (bytes + 1023) / 1024); } @@ -2163,7 +2124,7 @@ static int fib_triestat_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "Basic info: size of leaf:" " %Zd bytes, size of tnode: %Zd bytes.\n", - sizeof(struct leaf), sizeof(struct tnode)); + sizeof(struct tnode), sizeof(struct tnode)); for (h = 0; h < FIB_TABLE_HASHSZ; h++) { struct hlist_head *head = &net->ipv4.fib_table_hash[h]; @@ -2202,7 +2163,7 @@ static const struct file_operations fib_triestat_fops = { .release = single_release_net, }; -static struct rt_trie_node *fib_trie_get_idx(struct seq_file *seq, loff_t pos) +static struct tnode *fib_trie_get_idx(struct seq_file *seq, loff_t pos) { struct fib_trie_iter *iter = seq->private; struct net *net = seq_file_net(seq); @@ -2214,7 +2175,7 @@ static struct rt_trie_node *fib_trie_get_idx(struct seq_file *seq, loff_t pos) struct fib_table *tb; hlist_for_each_entry_rcu(tb, head, tb_hlist) { - struct rt_trie_node *n; + struct tnode *n; for (n = fib_trie_get_first(iter, (struct trie *) tb->tb_data); @@ -2243,7 +2204,7 @@ static void *fib_trie_seq_next(struct seq_file *seq, void *v, loff_t *pos) struct fib_table *tb = iter->tb; struct hlist_node *tb_node; unsigned int h; - struct rt_trie_node *n; + struct tnode *n; ++*pos; /* next node in same table */ @@ -2329,29 +2290,26 @@ static inline const char *rtn_type(char *buf, size_t len, unsigned int t) static int fib_trie_seq_show(struct seq_file *seq, void *v) { const struct fib_trie_iter *iter = seq->private; - struct rt_trie_node *n = v; + struct tnode *n = v; if (!node_parent_rcu(n)) fib_table_print(seq, iter->tb); if (IS_TNODE(n)) { - struct tnode *tn = (struct tnode *) n; - __be32 prf = htonl(tn->key); + __be32 prf = htonl(n->key); - seq_indent(seq, iter->depth-1); + seq_indent(seq, iter->depth - 1); seq_printf(seq, " +-- %pI4/%d %d %d %d\n", - &prf, tn->pos, tn->bits, tn->full_children, - tn->empty_children); - + &prf, n->pos, n->bits, n->full_children, + n->empty_children); } else { - struct leaf *l = (struct leaf *) n; struct leaf_info *li; - __be32 val = htonl(l->key); + __be32 val = htonl(n->key); seq_indent(seq, iter->depth); seq_printf(seq, " |-- %pI4\n", &val); - hlist_for_each_entry_rcu(li, &l->list, hlist) { + hlist_for_each_entry_rcu(li, &n->list, hlist) { struct fib_alias *fa; list_for_each_entry_rcu(fa, &li->falh, fa_list) { @@ -2401,9 +2359,9 @@ struct fib_route_iter { t_key key; }; -static struct leaf *fib_route_get_idx(struct fib_route_iter *iter, loff_t pos) +static struct tnode *fib_route_get_idx(struct fib_route_iter *iter, loff_t pos) { - struct leaf *l = NULL; + struct tnode *l = NULL; struct trie *t = iter->main_trie; /* use cache location of last found key */ @@ -2448,7 +2406,7 @@ static void *fib_route_seq_start(struct seq_file *seq, loff_t *pos) static void *fib_route_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct fib_route_iter *iter = seq->private; - struct leaf *l = v; + struct tnode *l = v; ++*pos; if (v == SEQ_START_TOKEN) { @@ -2494,7 +2452,7 @@ static unsigned int fib_flag_trans(int type, __be32 mask, const struct fib_info */ static int fib_route_seq_show(struct seq_file *seq, void *v) { - struct leaf *l = v; + struct tnode *l = v; struct leaf_info *li; if (v == SEQ_START_TOKEN) { -- cgit v0.10.2 From 9f9e636d4f89f788c5cf9c6a5357501c0d405fcb Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 31 Dec 2014 10:55:54 -0800 Subject: fib_trie: Optimize fib_table_lookup to avoid wasting time on loops/variables This patch is meant to reduce the complexity of fib_table_lookup by reducing the number of variables to the bare minimum while still keeping the same if not improved functionality versus the original. Most of this change was started off by the desire to rid the function of chopped_off and current_prefix_length as they actually added very little to the function since they only applied when computing the cindex. I was able to replace them mostly with just a check for the prefix match. As long as the prefix between the key and the node being tested was the same we know we can search the tnode fully versus just testing cindex 0. The second portion of the change ended up being a massive reordering. Originally the calls to check_leaf were up near the start of the loop, and the backtracing and descending into lower levels of tnodes was later. This didn't make much sense as the structure of the tree means the leaves are always the last thing to be tested. As such I reordered things so that we instead have a loop that will delve into the tree and only exit when we have either found a leaf or we have exhausted the tree. The advantage of rearranging things like this is that we can fully inline check_leaf since there is now only one reference to it in the function. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 94c929f..3fe4dd9 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -90,6 +90,9 @@ typedef unsigned int t_key; #define IS_TNODE(n) ((n)->bits) #define IS_LEAF(n) (!(n)->bits) +#define get_shift(_kv) (KEYLENGTH - (_kv)->pos - (_kv)->bits) +#define get_index(_key, _kv) (((_key) ^ (_kv)->key) >> get_shift(_kv)) + struct tnode { t_key key; unsigned char bits; /* 2log(KEYLENGTH) bits needed */ @@ -1281,7 +1284,7 @@ static int check_leaf(struct fib_table *tb, struct trie *t, struct tnode *l, continue; fib_alias_accessed(fa); err = fib_props[fa->fa_type].error; - if (err) { + if (unlikely(err < 0)) { #ifdef CONFIG_IP_FIB_TRIE_STATS this_cpu_inc(t->stats->semantic_match_passed); #endif @@ -1303,7 +1306,7 @@ static int check_leaf(struct fib_table *tb, struct trie *t, struct tnode *l, res->prefixlen = li->plen; res->nh_sel = nhsel; res->type = fa->fa_type; - res->scope = fa->fa_info->fib_scope; + res->scope = fi->fib_scope; res->fi = fi; res->table = tb; res->fa_head = &li->falh; @@ -1321,23 +1324,24 @@ static int check_leaf(struct fib_table *tb, struct trie *t, struct tnode *l, return 1; } +static inline t_key prefix_mismatch(t_key key, struct tnode *n) +{ + t_key prefix = n->key; + + return (key ^ prefix) & (prefix | -prefix); +} + int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, struct fib_result *res, int fib_flags) { - struct trie *t = (struct trie *) tb->tb_data; + struct trie *t = (struct trie *)tb->tb_data; #ifdef CONFIG_IP_FIB_TRIE_STATS struct trie_use_stats __percpu *stats = t->stats; #endif - int ret; - struct tnode *n; - struct tnode *pn; - unsigned int pos, bits; - t_key key = ntohl(flp->daddr); - unsigned int chopped_off; - t_key cindex = 0; - unsigned int current_prefix_length = KEYLENGTH; - struct tnode *cn; - t_key pref_mismatch; + const t_key key = ntohl(flp->daddr); + struct tnode *n, *pn; + t_key cindex; + int ret = 1; rcu_read_lock(); @@ -1349,170 +1353,102 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, this_cpu_inc(stats->gets); #endif - /* Just a leaf? */ - if (IS_LEAF(n)) { - ret = check_leaf(tb, t, n, key, flp, res, fib_flags); - goto found; - } - pn = n; - chopped_off = 0; - - while (pn) { - pos = pn->pos; - bits = pn->bits; - - if (!chopped_off) - cindex = tkey_extract_bits(mask_pfx(key, current_prefix_length), - pos, bits); - - n = tnode_get_child_rcu(pn, cindex); - - if (n == NULL) { -#ifdef CONFIG_IP_FIB_TRIE_STATS - this_cpu_inc(stats->null_node_hit); -#endif - goto backtrace; - } + cindex = 0; + + /* Step 1: Travel to the longest prefix match in the trie */ + for (;;) { + unsigned long index = get_index(key, n); + + /* This bit of code is a bit tricky but it combines multiple + * checks into a single check. The prefix consists of the + * prefix plus zeros for the "bits" in the prefix. The index + * is the difference between the key and this value. From + * this we can actually derive several pieces of data. + * if !(index >> bits) + * we know the value is child index + * else + * we have a mismatch in skip bits and failed + */ + if (index >> n->bits) + break; - if (IS_LEAF(n)) { - ret = check_leaf(tb, t, n, key, flp, res, fib_flags); - if (ret > 0) - goto backtrace; + /* we have found a leaf. Prefixes have already been compared */ + if (IS_LEAF(n)) goto found; - } - cn = n; - - /* - * It's a tnode, and we can do some extra checks here if we - * like, to avoid descending into a dead-end branch. - * This tnode is in the parent's child array at index - * key[p_pos..p_pos+p_bits] but potentially with some bits - * chopped off, so in reality the index may be just a - * subprefix, padded with zero at the end. - * We can also take a look at any skipped bits in this - * tnode - everything up to p_pos is supposed to be ok, - * and the non-chopped bits of the index (se previous - * paragraph) are also guaranteed ok, but the rest is - * considered unknown. - * - * The skipped bits are key[pos+bits..cn->pos]. - */ - - /* If current_prefix_length < pos+bits, we are already doing - * actual prefix matching, which means everything from - * pos+(bits-chopped_off) onward must be zero along some - * branch of this subtree - otherwise there is *no* valid - * prefix present. Here we can only check the skipped - * bits. Remember, since we have already indexed into the - * parent's child array, we know that the bits we chopped of - * *are* zero. + /* only record pn and cindex if we are going to be chopping + * bits later. Otherwise we are just wasting cycles. */ - - /* NOTA BENE: Checking only skipped bits - for the new node here */ - - if (current_prefix_length < pos+bits) { - if (tkey_extract_bits(cn->key, current_prefix_length, - cn->pos - current_prefix_length) - || !(cn->child[0])) - goto backtrace; + if (index) { + pn = n; + cindex = index; } - /* - * If chopped_off=0, the index is fully validated and we - * only need to look at the skipped bits for this, the new, - * tnode. What we actually want to do is to find out if - * these skipped bits match our key perfectly, or if we will - * have to count on finding a matching prefix further down, - * because if we do, we would like to have some way of - * verifying the existence of such a prefix at this point. - */ + n = rcu_dereference(n->child[index]); + if (unlikely(!n)) + goto backtrace; + } - /* The only thing we can do at this point is to verify that - * any such matching prefix can indeed be a prefix to our - * key, and if the bits in the node we are inspecting that - * do not match our key are not ZERO, this cannot be true. - * Thus, find out where there is a mismatch (before cn->pos) - * and verify that all the mismatching bits are zero in the - * new tnode's key. - */ + /* Step 2: Sort out leaves and begin backtracing for longest prefix */ + for (;;) { + /* record the pointer where our next node pointer is stored */ + struct tnode __rcu **cptr = n->child; - /* - * Note: We aren't very concerned about the piece of - * the key that precede pn->pos+pn->bits, since these - * have already been checked. The bits after cn->pos - * aren't checked since these are by definition - * "unknown" at this point. Thus, what we want to see - * is if we are about to enter the "prefix matching" - * state, and in that case verify that the skipped - * bits that will prevail throughout this subtree are - * zero, as they have to be if we are to find a - * matching prefix. + /* This test verifies that none of the bits that differ + * between the key and the prefix exist in the region of + * the lsb and higher in the prefix. */ + if (unlikely(prefix_mismatch(key, n))) + goto backtrace; - pref_mismatch = mask_pfx(cn->key ^ key, cn->pos); + /* exit out and process leaf */ + if (unlikely(IS_LEAF(n))) + break; - /* - * In short: If skipped bits in this node do not match - * the search key, enter the "prefix matching" - * state.directly. + /* Don't bother recording parent info. Since we are in + * prefix match mode we will have to come back to wherever + * we started this traversal anyway */ - if (pref_mismatch) { - /* fls(x) = __fls(x) + 1 */ - int mp = KEYLENGTH - __fls(pref_mismatch) - 1; - - if (tkey_extract_bits(cn->key, mp, cn->pos - mp) != 0) - goto backtrace; - - if (current_prefix_length >= cn->pos) - current_prefix_length = mp; - } - - pn = n; /* Descend */ - chopped_off = 0; - continue; + while ((n = rcu_dereference(*cptr)) == NULL) { backtrace: - chopped_off++; - - /* As zero don't change the child key (cindex) */ - while ((chopped_off <= pn->bits) - && !(cindex & (1<<(chopped_off-1)))) - chopped_off++; - - /* Decrease current_... with bits chopped off */ - if (current_prefix_length > pn->pos + pn->bits - chopped_off) - current_prefix_length = pn->pos + pn->bits - - chopped_off; - - /* - * Either we do the actual chop off according or if we have - * chopped off all bits in this tnode walk up to our parent. - */ - - if (chopped_off <= pn->bits) { - cindex &= ~(1 << (chopped_off-1)); - } else { - struct tnode *parent = node_parent_rcu(pn); - if (!parent) - goto failed; - - /* Get Child's index */ - cindex = tkey_extract_bits(pn->key, parent->pos, parent->bits); - pn = parent; - chopped_off = 0; - #ifdef CONFIG_IP_FIB_TRIE_STATS - this_cpu_inc(stats->backtrack); + if (!n) + this_cpu_inc(stats->null_node_hit); #endif - goto backtrace; + /* If we are at cindex 0 there are no more bits for + * us to strip at this level so we must ascend back + * up one level to see if there are any more bits to + * be stripped there. + */ + while (!cindex) { + t_key pkey = pn->key; + + pn = node_parent_rcu(pn); + if (unlikely(!pn)) + goto failed; +#ifdef CONFIG_IP_FIB_TRIE_STATS + this_cpu_inc(stats->backtrack); +#endif + /* Get Child's index */ + cindex = get_index(pkey, pn); + } + + /* strip the least significant bit from the cindex */ + cindex &= cindex - 1; + + /* grab pointer for next child node */ + cptr = &pn->child[cindex]; } } -failed: - ret = 1; + found: + /* Step 3: Process the leaf, if that fails fall back to backtracing */ + ret = check_leaf(tb, t, n, key, flp, res, fib_flags); + if (unlikely(ret > 0)) + goto backtrace; +failed: rcu_read_unlock(); return ret; } -- cgit v0.10.2 From 939afb0657dd8c8f9486d172d6bb62fc902e2f23 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 31 Dec 2014 10:56:00 -0800 Subject: fib_trie: Optimize fib_find_node This patch makes use of the same features I made use of for fib_table_lookup to streamline fib_find_node. The resultant code should be smaller and run faster than the original. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 3fe4dd9..ac04f31 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -892,28 +892,34 @@ static void insert_leaf_info(struct hlist_head *head, struct leaf_info *new) } /* rcu_read_lock needs to be hold by caller from readside */ - static struct tnode *fib_find_node(struct trie *t, u32 key) { struct tnode *n = rcu_dereference_rtnl(t->trie); - int pos = 0; - while (n && IS_TNODE(n)) { - if (tkey_sub_equals(n->key, pos, n->pos-pos, key)) { - pos = n->pos + n->bits; - n = tnode_get_child_rcu(n, - tkey_extract_bits(key, - n->pos, - n->bits)); - } else + while (n) { + unsigned long index = get_index(key, n); + + /* This bit of code is a bit tricky but it combines multiple + * checks into a single check. The prefix consists of the + * prefix plus zeros for the bits in the cindex. The index + * is the difference between the key and this value. From + * this we can actually derive several pieces of data. + * if !(index >> bits) + * we know the value is cindex + * else + * we have a mismatch in skip bits and failed + */ + if (index >> n->bits) + return NULL; + + /* we have found a leaf. Prefixes have already been compared */ + if (IS_LEAF(n)) break; - } - /* Case we have found a leaf. Compare prefixes */ - if (n != NULL && IS_LEAF(n) && tkey_equals(key, n->key)) - return n; + n = rcu_dereference_rtnl(n->child[index]); + } - return NULL; + return n; } static void trie_rebalance(struct trie *t, struct tnode *tn) -- cgit v0.10.2 From 836a0123c98c91df71678ce63c0d42770996582a Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 31 Dec 2014 10:56:06 -0800 Subject: fib_trie: Optimize fib_table_insert This patch updates the fib_table_insert function to take advantage of the changes made to improve the performance of fib_table_lookup. As a result the code should be smaller and run faster then the original. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index ac04f31..8a147c3 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -222,31 +222,6 @@ static inline t_key tkey_extract_bits(t_key a, unsigned int offset, unsigned int return 0; } -static inline int tkey_equals(t_key a, t_key b) -{ - return a == b; -} - -static inline int tkey_sub_equals(t_key a, int offset, int bits, t_key b) -{ - if (bits == 0 || offset >= KEYLENGTH) - return 1; - bits = bits > KEYLENGTH ? KEYLENGTH : bits; - return ((a ^ b) << offset) >> (KEYLENGTH - bits) == 0; -} - -static inline int tkey_mismatch(t_key a, int offset, t_key b) -{ - t_key diff = a ^ b; - int i = offset; - - if (!diff) - return 0; - while ((diff << i) >> (KEYLENGTH-1) == 0) - i++; - return i; -} - /* To understand this stuff, an understanding of keys and all their bits is necessary. Every node in the trie has a key associated with it, but not @@ -485,6 +460,15 @@ static void tnode_put_child_reorg(struct tnode *tn, int i, struct tnode *n, rcu_assign_pointer(tn->child[i], n); } +static void put_child_root(struct tnode *tp, struct trie *t, + t_key key, struct tnode *n) +{ + if (tp) + put_child(tp, get_index(key, tp), n); + else + rcu_assign_pointer(t->trie, n); +} + #define MAX_WORK 10 static struct tnode *resize(struct trie *t, struct tnode *tn) { @@ -959,138 +943,100 @@ static void trie_rebalance(struct trie *t, struct tnode *tn) static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) { - int pos, newpos; - struct tnode *tp = NULL, *tn = NULL; - struct tnode *n; - struct tnode *l; - int missbit; struct list_head *fa_head = NULL; + struct tnode *l, *n, *tp = NULL; struct leaf_info *li; - t_key cindex; - pos = 0; + li = leaf_info_new(plen); + if (!li) + return NULL; + fa_head = &li->falh; + n = rtnl_dereference(t->trie); /* If we point to NULL, stop. Either the tree is empty and we should * just put a new leaf in if, or we have reached an empty child slot, * and we should just put our new leaf in that. - * If we point to a T_TNODE, check if it matches our key. Note that - * a T_TNODE might be skipping any number of bits - its 'pos' need - * not be the parent's 'pos'+'bits'! - * - * If it does match the current key, get pos/bits from it, extract - * the index from our key, push the T_TNODE and walk the tree. - * - * If it doesn't, we have to replace it with a new T_TNODE. * - * If we point to a T_LEAF, it might or might not have the same key - * as we do. If it does, just change the value, update the T_LEAF's - * value, and return it. - * If it doesn't, we need to replace it with a T_TNODE. + * If we hit a node with a key that does't match then we should stop + * and create a new tnode to replace that node and insert ourselves + * and the other node into the new tnode. */ + while (n) { + unsigned long index = get_index(key, n); - while (n && IS_TNODE(n)) { - if (tkey_sub_equals(n->key, pos, n->pos-pos, key)) { - tp = n; - pos = n->pos + n->bits; - n = tnode_get_child(n, - tkey_extract_bits(key, - n->pos, - n->bits)); - - BUG_ON(n && node_parent(n) != tp); - } else + /* This bit of code is a bit tricky but it combines multiple + * checks into a single check. The prefix consists of the + * prefix plus zeros for the "bits" in the prefix. The index + * is the difference between the key and this value. From + * this we can actually derive several pieces of data. + * if !(index >> bits) + * we know the value is child index + * else + * we have a mismatch in skip bits and failed + */ + if (index >> n->bits) break; - } - - /* - * n ----> NULL, LEAF or TNODE - * - * tp is n's (parent) ----> NULL or TNODE - */ - - BUG_ON(tp && IS_LEAF(tp)); - - /* Case 1: n is a leaf. Compare prefixes */ - - if (n != NULL && IS_LEAF(n) && tkey_equals(key, n->key)) { - li = leaf_info_new(plen); - if (!li) - return NULL; + /* we have found a leaf. Prefixes have already been compared */ + if (IS_LEAF(n)) { + /* Case 1: n is a leaf, and prefixes match*/ + insert_leaf_info(&n->list, li); + return fa_head; + } - fa_head = &li->falh; - insert_leaf_info(&n->list, li); - goto done; + tp = n; + n = rcu_dereference_rtnl(n->child[index]); } - l = leaf_new(key); - - if (!l) - return NULL; - - li = leaf_info_new(plen); - if (!li) { - node_free(l); + l = leaf_new(key); + if (!l) { + free_leaf_info(li); return NULL; } - fa_head = &li->falh; insert_leaf_info(&l->list, li); - if (t->trie && n == NULL) { - /* Case 2: n is NULL, and will just insert a new leaf */ - - node_set_parent(l, tp); - - cindex = tkey_extract_bits(key, tp->pos, tp->bits); - put_child(tp, cindex, l); - } else { - /* Case 3: n is a LEAF or a TNODE and the key doesn't match. */ - /* - * Add a new tnode here - * first tnode need some special handling - */ + /* Case 2: n is a LEAF or a TNODE and the key doesn't match. + * + * Add a new tnode here + * first tnode need some special handling + * leaves us in position for handling as case 3 + */ + if (n) { + struct tnode *tn; + int newpos; - if (n) { - pos = tp ? tp->pos+tp->bits : 0; - newpos = tkey_mismatch(key, pos, n->key); - tn = tnode_new(n->key, newpos, 1); - } else { - newpos = 0; - tn = tnode_new(key, newpos, 1); /* First tnode */ - } + newpos = KEYLENGTH - __fls(n->key ^ key) - 1; + tn = tnode_new(key, newpos, 1); if (!tn) { free_leaf_info(li); node_free(l); return NULL; } - node_set_parent(tn, tp); - - missbit = tkey_extract_bits(key, newpos, 1); - put_child(tn, missbit, l); - put_child(tn, 1-missbit, n); + /* initialize routes out of node */ + NODE_INIT_PARENT(tn, tp); + put_child(tn, get_index(key, tn) ^ 1, n); - if (tp) { - cindex = tkey_extract_bits(key, tp->pos, tp->bits); - put_child(tp, cindex, tn); - } else { - rcu_assign_pointer(t->trie, tn); - } + /* start adding routes into the node */ + put_child_root(tp, t, key, tn); + node_set_parent(n, tn); + /* parent now has a NULL spot where the leaf can go */ tp = tn; } - if (tp && tp->pos + tp->bits > 32) - pr_warn("fib_trie tp=%p pos=%d, bits=%d, key=%0x plen=%d\n", - tp, tp->pos, tp->bits, key, plen); - - /* Rebalance the trie */ + /* Case 3: n is NULL, and will just insert a new leaf */ + if (tp) { + NODE_INIT_PARENT(l, tp); + put_child(tp, get_index(key, tp), l); + trie_rebalance(t, tp); + } else { + rcu_assign_pointer(t->trie, l); + } - trie_rebalance(t, tp); -done: return fa_head; } @@ -1470,11 +1416,11 @@ static void trie_leaf_remove(struct trie *t, struct tnode *l) pr_debug("entering trie_leaf_remove(%p)\n", l); if (tp) { - t_key cindex = tkey_extract_bits(l->key, tp->pos, tp->bits); - put_child(tp, cindex, NULL); + put_child(tp, get_index(l->key, tp), NULL); trie_rebalance(t, tp); - } else + } else { RCU_INIT_POINTER(t->trie, NULL); + } node_free(l); } -- cgit v0.10.2 From e9b44019d41a5cf41de43e895a5ed38b19a59b5b Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 31 Dec 2014 10:56:12 -0800 Subject: fib_trie: Update meaning of pos to represent unchecked bits This change moves the pos value to the other side of the "bits" field. By doing this it actually simplifies a significant amount of code in the trie. For example when halving a tree we know that the bit lost exists at oldnode->pos, and if we inflate the tree the new bit being add is at tn->pos. Previously to find those bits you would have to subtract pos and bits from the keylength or start with a value of (1 << 31) and then shift that. There are a number of spots throughout the code that benefit from this. In the case of the hot-path searches the main advantage is that we can drop 2 or more operations from the search path as we no longer need to compute the value for the index to be shifted by and can instead just use the raw pos value. In addition the tkey_extract_bits is now defunct and can be replaced by get_index since the two operations were doing the same thing, but now get_index does it much more quickly as it is only an xor and shift versus a pair of shifts and a subtraction. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 8a147c3..9d67548 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -90,8 +90,7 @@ typedef unsigned int t_key; #define IS_TNODE(n) ((n)->bits) #define IS_LEAF(n) (!(n)->bits) -#define get_shift(_kv) (KEYLENGTH - (_kv)->pos - (_kv)->bits) -#define get_index(_key, _kv) (((_key) ^ (_kv)->key) >> get_shift(_kv)) +#define get_index(_key, _kv) (((_key) ^ (_kv)->key) >> (_kv)->pos) struct tnode { t_key key; @@ -209,81 +208,64 @@ static inline struct tnode *tnode_get_child_rcu(const struct tnode *tn, unsigned return rcu_dereference_rtnl(tn->child[i]); } -static inline t_key mask_pfx(t_key k, unsigned int l) -{ - return (l == 0) ? 0 : k >> (KEYLENGTH-l) << (KEYLENGTH-l); -} - -static inline t_key tkey_extract_bits(t_key a, unsigned int offset, unsigned int bits) -{ - if (offset < KEYLENGTH) - return ((t_key)(a << offset)) >> (KEYLENGTH - bits); - else - return 0; -} - -/* - To understand this stuff, an understanding of keys and all their bits is - necessary. Every node in the trie has a key associated with it, but not - all of the bits in that key are significant. - - Consider a node 'n' and its parent 'tp'. - - If n is a leaf, every bit in its key is significant. Its presence is - necessitated by path compression, since during a tree traversal (when - searching for a leaf - unless we are doing an insertion) we will completely - ignore all skipped bits we encounter. Thus we need to verify, at the end of - a potentially successful search, that we have indeed been walking the - correct key path. - - Note that we can never "miss" the correct key in the tree if present by - following the wrong path. Path compression ensures that segments of the key - that are the same for all keys with a given prefix are skipped, but the - skipped part *is* identical for each node in the subtrie below the skipped - bit! trie_insert() in this implementation takes care of that - note the - call to tkey_sub_equals() in trie_insert(). - - if n is an internal node - a 'tnode' here, the various parts of its key - have many different meanings. - - Example: - _________________________________________________________________ - | i | i | i | i | i | i | i | N | N | N | S | S | S | S | S | C | - ----------------------------------------------------------------- - 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - - _________________________________________________________________ - | C | C | C | u | u | u | u | u | u | u | u | u | u | u | u | u | - ----------------------------------------------------------------- - 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 - - tp->pos = 7 - tp->bits = 3 - n->pos = 15 - n->bits = 4 - - First, let's just ignore the bits that come before the parent tp, that is - the bits from 0 to (tp->pos-1). They are *known* but at this point we do - not use them for anything. - - The bits from (tp->pos) to (tp->pos + tp->bits - 1) - "N", above - are the - index into the parent's child array. That is, they will be used to find - 'n' among tp's children. - - The bits from (tp->pos + tp->bits) to (n->pos - 1) - "S" - are skipped bits - for the node n. - - All the bits we have seen so far are significant to the node n. The rest - of the bits are really not needed or indeed known in n->key. - - The bits from (n->pos) to (n->pos + n->bits - 1) - "C" - are the index into - n's child array, and will of course be different for each child. - - - The rest of the bits, from (n->pos + n->bits) onward, are completely unknown - at this point. - -*/ +/* To understand this stuff, an understanding of keys and all their bits is + * necessary. Every node in the trie has a key associated with it, but not + * all of the bits in that key are significant. + * + * Consider a node 'n' and its parent 'tp'. + * + * If n is a leaf, every bit in its key is significant. Its presence is + * necessitated by path compression, since during a tree traversal (when + * searching for a leaf - unless we are doing an insertion) we will completely + * ignore all skipped bits we encounter. Thus we need to verify, at the end of + * a potentially successful search, that we have indeed been walking the + * correct key path. + * + * Note that we can never "miss" the correct key in the tree if present by + * following the wrong path. Path compression ensures that segments of the key + * that are the same for all keys with a given prefix are skipped, but the + * skipped part *is* identical for each node in the subtrie below the skipped + * bit! trie_insert() in this implementation takes care of that. + * + * if n is an internal node - a 'tnode' here, the various parts of its key + * have many different meanings. + * + * Example: + * _________________________________________________________________ + * | i | i | i | i | i | i | i | N | N | N | S | S | S | S | S | C | + * ----------------------------------------------------------------- + * 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 + * + * _________________________________________________________________ + * | C | C | C | u | u | u | u | u | u | u | u | u | u | u | u | u | + * ----------------------------------------------------------------- + * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + * + * tp->pos = 22 + * tp->bits = 3 + * n->pos = 13 + * n->bits = 4 + * + * First, let's just ignore the bits that come before the parent tp, that is + * the bits from (tp->pos + tp->bits) to 31. They are *known* but at this + * point we do not use them for anything. + * + * The bits from (tp->pos) to (tp->pos + tp->bits - 1) - "N", above - are the + * index into the parent's child array. That is, they will be used to find + * 'n' among tp's children. + * + * The bits from (n->pos + n->bits) to (tn->pos - 1) - "S" - are skipped bits + * for the node n. + * + * All the bits we have seen so far are significant to the node n. The rest + * of the bits are really not needed or indeed known in n->key. + * + * The bits from (n->pos) to (n->pos + n->bits - 1) - "C" - are the index into + * n's child array, and will of course be different for each child. + * + * The rest of the bits, from 0 to (n->pos + n->bits), are completely unknown + * at this point. + */ static const int halve_threshold = 25; static const int inflate_threshold = 50; @@ -367,7 +349,7 @@ static struct tnode *leaf_new(t_key key) * as the nodes are searched */ l->key = key; - l->pos = KEYLENGTH; + l->pos = 0; /* set bits to 0 indicating we are not a tnode */ l->bits = 0; @@ -400,7 +382,7 @@ static struct tnode *tnode_new(t_key key, int pos, int bits) tn->parent = NULL; tn->pos = pos; tn->bits = bits; - tn->key = mask_pfx(key, pos); + tn->key = (shift < KEYLENGTH) ? (key >> shift) << shift : 0; tn->full_children = 0; tn->empty_children = 1<pos == (tn->pos + tn->bits)); + return n && ((n->pos + n->bits) == tn->pos) && IS_TNODE(n); } static inline void put_child(struct tnode *tn, int i, @@ -641,11 +621,12 @@ static struct tnode *inflate(struct trie *t, struct tnode *oldtnode) { int olen = tnode_child_length(oldtnode); struct tnode *tn; + t_key m; int i; pr_debug("In inflate\n"); - tn = tnode_new(oldtnode->key, oldtnode->pos, oldtnode->bits + 1); + tn = tnode_new(oldtnode->key, oldtnode->pos - 1, oldtnode->bits + 1); if (!tn) return ERR_PTR(-ENOMEM); @@ -656,21 +637,18 @@ static struct tnode *inflate(struct trie *t, struct tnode *oldtnode) * fails. In case of failure we return the oldnode and inflate * of tnode is ignored. */ + for (i = 0, m = 1u << tn->pos; i < olen; i++) { + struct tnode *inode = tnode_get_child(oldtnode, i); - for (i = 0; i < olen; i++) { - struct tnode *inode; - - inode = tnode_get_child(oldtnode, i); - if (tnode_full(oldtnode, inode) && inode->bits > 1) { + if (tnode_full(oldtnode, inode) && (inode->bits > 1)) { struct tnode *left, *right; - t_key m = ~0U << (KEYLENGTH - 1) >> inode->pos; - left = tnode_new(inode->key&(~m), inode->pos + 1, + left = tnode_new(inode->key & ~m, inode->pos, inode->bits - 1); if (!left) goto nomem; - right = tnode_new(inode->key|m, inode->pos + 1, + right = tnode_new(inode->key | m, inode->pos, inode->bits - 1); if (!right) { @@ -694,9 +672,7 @@ static struct tnode *inflate(struct trie *t, struct tnode *oldtnode) /* A leaf or an internal node with skipped bits */ if (!tnode_full(oldtnode, inode)) { - put_child(tn, - tkey_extract_bits(inode->key, tn->pos, tn->bits), - inode); + put_child(tn, get_index(inode->key, tn), inode); continue; } @@ -767,7 +743,7 @@ static struct tnode *halve(struct trie *t, struct tnode *oldtnode) pr_debug("In halve\n"); - tn = tnode_new(oldtnode->key, oldtnode->pos, oldtnode->bits - 1); + tn = tnode_new(oldtnode->key, oldtnode->pos + 1, oldtnode->bits - 1); if (!tn) return ERR_PTR(-ENOMEM); @@ -787,7 +763,7 @@ static struct tnode *halve(struct trie *t, struct tnode *oldtnode) if (left && right) { struct tnode *newn; - newn = tnode_new(left->key, tn->pos + tn->bits, 1); + newn = tnode_new(left->key, oldtnode->pos, 1); if (!newn) goto nomem; @@ -915,7 +891,7 @@ static void trie_rebalance(struct trie *t, struct tnode *tn) key = tn->key; while (tn != NULL && (tp = node_parent(tn)) != NULL) { - cindex = tkey_extract_bits(key, tp->pos, tp->bits); + cindex = get_index(key, tp); wasfull = tnode_full(tp, tnode_get_child(tp, cindex)); tn = resize(t, tn); @@ -1005,11 +981,8 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) */ if (n) { struct tnode *tn; - int newpos; - - newpos = KEYLENGTH - __fls(n->key ^ key) - 1; - tn = tnode_new(key, newpos, 1); + tn = tnode_new(key, __fls(key ^ n->key), 1); if (!tn) { free_leaf_info(li); node_free(l); @@ -1559,12 +1532,7 @@ static int trie_flush_leaf(struct tnode *l) static struct tnode *leaf_walk_rcu(struct tnode *p, struct tnode *c) { do { - t_key idx; - - if (c) - idx = tkey_extract_bits(c->key, p->pos, p->bits) + 1; - else - idx = 0; + t_key idx = c ? idx = get_index(c->key, p) + 1 : 0; while (idx < 1u << p->bits) { c = tnode_get_child_rcu(p, idx++); @@ -1851,7 +1819,7 @@ rescan: /* Current node exhausted, pop back up */ p = node_parent_rcu(tn); if (p) { - cindex = tkey_extract_bits(tn->key, p->pos, p->bits)+1; + cindex = get_index(tn->key, p) + 1; tn = p; --iter->depth; goto rescan; @@ -2186,10 +2154,10 @@ static int fib_trie_seq_show(struct seq_file *seq, void *v) if (IS_TNODE(n)) { __be32 prf = htonl(n->key); - seq_indent(seq, iter->depth - 1); - seq_printf(seq, " +-- %pI4/%d %d %d %d\n", - &prf, n->pos, n->bits, n->full_children, - n->empty_children); + seq_indent(seq, iter->depth-1); + seq_printf(seq, " +-- %pI4/%zu %u %u %u\n", + &prf, KEYLENGTH - n->pos - n->bits, n->bits, + n->full_children, n->empty_children); } else { struct leaf_info *li; __be32 val = htonl(n->key); -- cgit v0.10.2 From 98293e8d2f51976d5f790400859a517c0f7cb598 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 31 Dec 2014 10:56:18 -0800 Subject: fib_trie: Use unsigned long for anything dealing with a shift by bits This change makes it so that anything that can be shifted by, or compared to a value shifted by bits is updated to be an unsigned long. This is mostly a precaution against an insanely huge address space that somehow starts coming close to the 2^32 root node size which would require something like 1.5 billion addresses. I chose unsigned long instead of unsigned long long since I do not believe it is possible to allocate a 32 bit tnode on a 32 bit system as the memory consumed would be 16GB + 28B which exceeds the addressible space for any one process. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 9d67548..28a3065 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -146,8 +146,8 @@ struct trie { #endif }; -static void tnode_put_child_reorg(struct tnode *tn, int i, struct tnode *n, - int wasfull); +static void tnode_put_child_reorg(struct tnode *tn, unsigned long i, + struct tnode *n, int wasfull); static struct tnode *resize(struct trie *t, struct tnode *tn); static struct tnode *inflate(struct trie *t, struct tnode *tn); static struct tnode *halve(struct trie *t, struct tnode *tn); @@ -183,25 +183,23 @@ static inline void node_set_parent(struct tnode *n, struct tnode *tp) /* This provides us with the number of children in this node, in the case of a * leaf this will return 0 meaning none of the children are accessible. */ -static inline int tnode_child_length(const struct tnode *tn) +static inline unsigned long tnode_child_length(const struct tnode *tn) { return (1ul << tn->bits) & ~(1ul); } -/* - * caller must hold RTNL - */ -static inline struct tnode *tnode_get_child(const struct tnode *tn, unsigned int i) +/* caller must hold RTNL */ +static inline struct tnode *tnode_get_child(const struct tnode *tn, + unsigned long i) { BUG_ON(i >= tnode_child_length(tn)); return rtnl_dereference(tn->child[i]); } -/* - * caller must hold RCU read lock or RTNL - */ -static inline struct tnode *tnode_get_child_rcu(const struct tnode *tn, unsigned int i) +/* caller must hold RCU read lock or RTNL */ +static inline struct tnode *tnode_get_child_rcu(const struct tnode *tn, + unsigned long i) { BUG_ON(i >= tnode_child_length(tn)); @@ -400,7 +398,7 @@ static inline int tnode_full(const struct tnode *tn, const struct tnode *n) return n && ((n->pos + n->bits) == tn->pos) && IS_TNODE(n); } -static inline void put_child(struct tnode *tn, int i, +static inline void put_child(struct tnode *tn, unsigned long i, struct tnode *n) { tnode_put_child_reorg(tn, i, n, -1); @@ -411,13 +409,13 @@ static inline void put_child(struct tnode *tn, int i, * Update the value of full_children and empty_children. */ -static void tnode_put_child_reorg(struct tnode *tn, int i, struct tnode *n, - int wasfull) +static void tnode_put_child_reorg(struct tnode *tn, unsigned long i, + struct tnode *n, int wasfull) { struct tnode *chi = rtnl_dereference(tn->child[i]); int isfull; - BUG_ON(i >= 1<bits); + BUG_ON(i >= tnode_child_length(tn)); /* update emptyChildren */ if (n == NULL && chi != NULL) @@ -607,10 +605,10 @@ no_children: static void tnode_clean_free(struct tnode *tn) { struct tnode *tofree; - int i; + unsigned long i; for (i = 0; i < tnode_child_length(tn); i++) { - tofree = rtnl_dereference(tn->child[i]); + tofree = tnode_get_child(tn, i); if (tofree) node_free(tofree); } @@ -619,10 +617,10 @@ static void tnode_clean_free(struct tnode *tn) static struct tnode *inflate(struct trie *t, struct tnode *oldtnode) { - int olen = tnode_child_length(oldtnode); + unsigned long olen = tnode_child_length(oldtnode); struct tnode *tn; + unsigned long i; t_key m; - int i; pr_debug("In inflate\n"); @@ -664,7 +662,7 @@ static struct tnode *inflate(struct trie *t, struct tnode *oldtnode) for (i = 0; i < olen; i++) { struct tnode *inode = tnode_get_child(oldtnode, i); struct tnode *left, *right; - int size, j; + unsigned long size, j; /* An empty child */ if (inode == NULL) @@ -737,7 +735,7 @@ nomem: static struct tnode *halve(struct trie *t, struct tnode *oldtnode) { - int olen = tnode_child_length(oldtnode); + unsigned long olen = tnode_child_length(oldtnode); struct tnode *tn, *left, *right; int i; @@ -1532,9 +1530,9 @@ static int trie_flush_leaf(struct tnode *l) static struct tnode *leaf_walk_rcu(struct tnode *p, struct tnode *c) { do { - t_key idx = c ? idx = get_index(c->key, p) + 1 : 0; + unsigned long idx = c ? idx = get_index(c->key, p) + 1 : 0; - while (idx < 1u << p->bits) { + while (idx < tnode_child_length(p)) { c = tnode_get_child_rcu(p, idx++); if (!c) continue; @@ -1786,8 +1784,8 @@ struct fib_trie_iter { static struct tnode *fib_trie_get_next(struct fib_trie_iter *iter) { + unsigned long cindex = iter->index; struct tnode *tn = iter->tnode; - unsigned int cindex = iter->index; struct tnode *p; /* A single entry routing table */ @@ -1797,7 +1795,7 @@ static struct tnode *fib_trie_get_next(struct fib_trie_iter *iter) pr_debug("get_next iter={node=%p index=%d depth=%d}\n", iter->tnode, iter->index, iter->depth); rescan: - while (cindex < (1<bits)) { + while (cindex < tnode_child_length(tn)) { struct tnode *n = tnode_get_child_rcu(tn, cindex); if (n) { @@ -1874,15 +1872,16 @@ static void trie_collect_stats(struct trie *t, struct trie_stat *s) hlist_for_each_entry_rcu(li, &n->list, hlist) ++s->prefixes; } else { - int i; + unsigned long i; s->tnodes++; if (n->bits < MAX_STAT_DEPTH) s->nodesizes[n->bits]++; - for (i = 0; i < tnode_child_length(n); i++) + for (i = 0; i < tnode_child_length(n); i++) { if (!rcu_access_pointer(n->child[i])) s->nullpointers++; + } } } rcu_read_unlock(); -- cgit v0.10.2 From 345e9b54268ae065520a7252c182d22ef4591718 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 31 Dec 2014 10:56:24 -0800 Subject: fib_trie: Push rcu_read_lock/unlock to callers This change is to start cleaning up some of the rcu_read_lock/unlock handling. I realized while reviewing the code there are several spots that I don't believe are being handled correctly or are masking warnings by locally calling rcu_read_lock/unlock instead of calling them at the correct level. A common example is a call to fib_get_table followed by fib_table_lookup. The rcu_read_lock/unlock ought to wrap both but there are several spots where they were not wrapped. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 09a819e..5bd120e 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -222,16 +222,19 @@ static inline struct fib_table *fib_new_table(struct net *net, u32 id) static inline int fib_lookup(struct net *net, const struct flowi4 *flp, struct fib_result *res) { - struct fib_table *table; + int err = -ENETUNREACH; + + rcu_read_lock(); + + if (!fib_table_lookup(fib_get_table(net, RT_TABLE_LOCAL), flp, res, + FIB_LOOKUP_NOREF) || + !fib_table_lookup(fib_get_table(net, RT_TABLE_MAIN), flp, res, + FIB_LOOKUP_NOREF)) + err = 0; - table = fib_get_table(net, RT_TABLE_LOCAL); - if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF)) - return 0; + rcu_read_unlock(); - table = fib_get_table(net, RT_TABLE_MAIN); - if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF)) - return 0; - return -ENETUNREACH; + return err; } #else /* CONFIG_IP_MULTIPLE_TABLES */ @@ -247,20 +250,25 @@ static inline int fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res) { if (!net->ipv4.fib_has_custom_rules) { + int err = -ENETUNREACH; + + rcu_read_lock(); + res->tclassid = 0; - if (net->ipv4.fib_local && - !fib_table_lookup(net->ipv4.fib_local, flp, res, - FIB_LOOKUP_NOREF)) - return 0; - if (net->ipv4.fib_main && - !fib_table_lookup(net->ipv4.fib_main, flp, res, - FIB_LOOKUP_NOREF)) - return 0; - if (net->ipv4.fib_default && - !fib_table_lookup(net->ipv4.fib_default, flp, res, - FIB_LOOKUP_NOREF)) - return 0; - return -ENETUNREACH; + if ((net->ipv4.fib_local && + !fib_table_lookup(net->ipv4.fib_local, flp, res, + FIB_LOOKUP_NOREF)) || + (net->ipv4.fib_main && + !fib_table_lookup(net->ipv4.fib_main, flp, res, + FIB_LOOKUP_NOREF)) || + (net->ipv4.fib_default && + !fib_table_lookup(net->ipv4.fib_default, flp, res, + FIB_LOOKUP_NOREF))) + err = 0; + + rcu_read_unlock(); + + return err; } return __fib_lookup(net, flp, res); } diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 6689020..57be71d 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -109,6 +109,7 @@ struct fib_table *fib_new_table(struct net *net, u32 id) return tb; } +/* caller must hold either rtnl or rcu read lock */ struct fib_table *fib_get_table(struct net *net, u32 id) { struct fib_table *tb; @@ -119,15 +120,11 @@ struct fib_table *fib_get_table(struct net *net, u32 id) id = RT_TABLE_MAIN; h = id & (FIB_TABLE_HASHSZ - 1); - rcu_read_lock(); head = &net->ipv4.fib_table_hash[h]; hlist_for_each_entry_rcu(tb, head, tb_hlist) { - if (tb->tb_id == id) { - rcu_read_unlock(); + if (tb->tb_id == id) return tb; - } } - rcu_read_unlock(); return NULL; } #endif /* CONFIG_IP_MULTIPLE_TABLES */ @@ -167,16 +164,18 @@ static inline unsigned int __inet_dev_addr_type(struct net *net, if (ipv4_is_multicast(addr)) return RTN_MULTICAST; + rcu_read_lock(); + local_table = fib_get_table(net, RT_TABLE_LOCAL); if (local_table) { ret = RTN_UNICAST; - rcu_read_lock(); if (!fib_table_lookup(local_table, &fl4, &res, FIB_LOOKUP_NOREF)) { if (!dev || dev == res.fi->fib_dev) ret = res.type; } - rcu_read_unlock(); } + + rcu_read_unlock(); return ret; } @@ -919,7 +918,7 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim) #undef BRD1_OK } -static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb) +static void nl_fib_lookup(struct net *net, struct fib_result_nl *frn) { struct fib_result res; @@ -929,6 +928,11 @@ static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb) .flowi4_tos = frn->fl_tos, .flowi4_scope = frn->fl_scope, }; + struct fib_table *tb; + + rcu_read_lock(); + + tb = fib_get_table(net, frn->tb_id_in); frn->err = -ENOENT; if (tb) { @@ -945,6 +949,8 @@ static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb) } local_bh_enable(); } + + rcu_read_unlock(); } static void nl_fib_input(struct sk_buff *skb) @@ -952,7 +958,6 @@ static void nl_fib_input(struct sk_buff *skb) struct net *net; struct fib_result_nl *frn; struct nlmsghdr *nlh; - struct fib_table *tb; u32 portid; net = sock_net(skb->sk); @@ -967,9 +972,7 @@ static void nl_fib_input(struct sk_buff *skb) nlh = nlmsg_hdr(skb); frn = (struct fib_result_nl *) nlmsg_data(nlh); - tb = fib_get_table(net, frn->tb_id_in); - - nl_fib_lookup(frn, tb); + nl_fib_lookup(net, frn); portid = NETLINK_CB(skb).portid; /* netlink portid */ NETLINK_CB(skb).portid = 0; /* from kernel */ diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 8f7bd56..d3db718 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -81,27 +81,25 @@ static int fib4_rule_action(struct fib_rule *rule, struct flowi *flp, break; case FR_ACT_UNREACHABLE: - err = -ENETUNREACH; - goto errout; + return -ENETUNREACH; case FR_ACT_PROHIBIT: - err = -EACCES; - goto errout; + return -EACCES; case FR_ACT_BLACKHOLE: default: - err = -EINVAL; - goto errout; + return -EINVAL; } + rcu_read_lock(); + tbl = fib_get_table(rule->fr_net, rule->table); - if (!tbl) - goto errout; + if (tbl) + err = fib_table_lookup(tbl, &flp->u.ip4, + (struct fib_result *)arg->result, + arg->flags); - err = fib_table_lookup(tbl, &flp->u.ip4, (struct fib_result *) arg->result, arg->flags); - if (err > 0) - err = -EAGAIN; -errout: + rcu_read_unlock(); return err; } diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 28a3065..987b06d 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1181,72 +1181,6 @@ err: return err; } -/* should be called with rcu_read_lock */ -static int check_leaf(struct fib_table *tb, struct trie *t, struct tnode *l, - t_key key, const struct flowi4 *flp, - struct fib_result *res, int fib_flags) -{ - struct leaf_info *li; - struct hlist_head *hhead = &l->list; - - hlist_for_each_entry_rcu(li, hhead, hlist) { - struct fib_alias *fa; - - if (l->key != (key & li->mask_plen)) - continue; - - list_for_each_entry_rcu(fa, &li->falh, fa_list) { - struct fib_info *fi = fa->fa_info; - int nhsel, err; - - if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos) - continue; - if (fi->fib_dead) - continue; - if (fa->fa_info->fib_scope < flp->flowi4_scope) - continue; - fib_alias_accessed(fa); - err = fib_props[fa->fa_type].error; - if (unlikely(err < 0)) { -#ifdef CONFIG_IP_FIB_TRIE_STATS - this_cpu_inc(t->stats->semantic_match_passed); -#endif - return err; - } - if (fi->fib_flags & RTNH_F_DEAD) - continue; - for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) { - const struct fib_nh *nh = &fi->fib_nh[nhsel]; - - if (nh->nh_flags & RTNH_F_DEAD) - continue; - if (flp->flowi4_oif && flp->flowi4_oif != nh->nh_oif) - continue; - -#ifdef CONFIG_IP_FIB_TRIE_STATS - this_cpu_inc(t->stats->semantic_match_passed); -#endif - res->prefixlen = li->plen; - res->nh_sel = nhsel; - res->type = fa->fa_type; - res->scope = fi->fib_scope; - res->fi = fi; - res->table = tb; - res->fa_head = &li->falh; - if (!(fib_flags & FIB_LOOKUP_NOREF)) - atomic_inc(&fi->fib_clntref); - return 0; - } - } - -#ifdef CONFIG_IP_FIB_TRIE_STATS - this_cpu_inc(t->stats->semantic_match_miss); -#endif - } - - return 1; -} - static inline t_key prefix_mismatch(t_key key, struct tnode *n) { t_key prefix = n->key; @@ -1254,6 +1188,7 @@ static inline t_key prefix_mismatch(t_key key, struct tnode *n) return (key ^ prefix) & (prefix | -prefix); } +/* should be called with rcu_read_lock */ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, struct fib_result *res, int fib_flags) { @@ -1263,14 +1198,12 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, #endif const t_key key = ntohl(flp->daddr); struct tnode *n, *pn; + struct leaf_info *li; t_key cindex; - int ret = 1; - - rcu_read_lock(); n = rcu_dereference(t->trie); if (!n) - goto failed; + return -EAGAIN; #ifdef CONFIG_IP_FIB_TRIE_STATS this_cpu_inc(stats->gets); @@ -1350,7 +1283,7 @@ backtrace: pn = node_parent_rcu(pn); if (unlikely(!pn)) - goto failed; + return -EAGAIN; #ifdef CONFIG_IP_FIB_TRIE_STATS this_cpu_inc(stats->backtrack); #endif @@ -1368,12 +1301,62 @@ backtrace: found: /* Step 3: Process the leaf, if that fails fall back to backtracing */ - ret = check_leaf(tb, t, n, key, flp, res, fib_flags); - if (unlikely(ret > 0)) - goto backtrace; -failed: - rcu_read_unlock(); - return ret; + hlist_for_each_entry_rcu(li, &n->list, hlist) { + struct fib_alias *fa; + + if ((key ^ n->key) & li->mask_plen) + continue; + + list_for_each_entry_rcu(fa, &li->falh, fa_list) { + struct fib_info *fi = fa->fa_info; + int nhsel, err; + + if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos) + continue; + if (fi->fib_dead) + continue; + if (fa->fa_info->fib_scope < flp->flowi4_scope) + continue; + fib_alias_accessed(fa); + err = fib_props[fa->fa_type].error; + if (unlikely(err < 0)) { +#ifdef CONFIG_IP_FIB_TRIE_STATS + this_cpu_inc(stats->semantic_match_passed); +#endif + return err; + } + if (fi->fib_flags & RTNH_F_DEAD) + continue; + for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) { + const struct fib_nh *nh = &fi->fib_nh[nhsel]; + + if (nh->nh_flags & RTNH_F_DEAD) + continue; + if (flp->flowi4_oif && flp->flowi4_oif != nh->nh_oif) + continue; + + if (!(fib_flags & FIB_LOOKUP_NOREF)) + atomic_inc(&fi->fib_clntref); + + res->prefixlen = li->plen; + res->nh_sel = nhsel; + res->type = fa->fa_type; + res->scope = fi->fib_scope; + res->fi = fi; + res->table = tb; + res->fa_head = &li->falh; +#ifdef CONFIG_IP_FIB_TRIE_STATS + this_cpu_inc(stats->semantic_match_passed); +#endif + return err; + } + } + +#ifdef CONFIG_IP_FIB_TRIE_STATS + this_cpu_inc(stats->semantic_match_miss); +#endif + } + goto backtrace; } EXPORT_SYMBOL_GPL(fib_table_lookup); -- cgit v0.10.2 From cf3637bb8f07fb3b6791708f1d5118afb5446061 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 31 Dec 2014 10:56:31 -0800 Subject: fib_trie: Move resize to after inflate/halve This change consists of a cut/paste of resize to behind inflate and halve so that I could remove the two function prototypes. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 987b06d..d044fa3 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -149,8 +149,6 @@ struct trie { static void tnode_put_child_reorg(struct tnode *tn, unsigned long i, struct tnode *n, int wasfull); static struct tnode *resize(struct trie *t, struct tnode *tn); -static struct tnode *inflate(struct trie *t, struct tnode *tn); -static struct tnode *halve(struct trie *t, struct tnode *tn); /* tnodes to free after resize(); protected by RTNL */ static struct callback_head *tnode_free_head; static size_t tnode_free_size; @@ -447,161 +445,6 @@ static void put_child_root(struct tnode *tp, struct trie *t, rcu_assign_pointer(t->trie, n); } -#define MAX_WORK 10 -static struct tnode *resize(struct trie *t, struct tnode *tn) -{ - struct tnode *old_tn, *n = NULL; - int inflate_threshold_use; - int halve_threshold_use; - int max_work; - - if (!tn) - return NULL; - - pr_debug("In tnode_resize %p inflate_threshold=%d threshold=%d\n", - tn, inflate_threshold, halve_threshold); - - /* No children */ - if (tn->empty_children > (tnode_child_length(tn) - 1)) - goto no_children; - - /* One child */ - if (tn->empty_children == (tnode_child_length(tn) - 1)) - goto one_child; - /* - * Double as long as the resulting node has a number of - * nonempty nodes that are above the threshold. - */ - - /* - * From "Implementing a dynamic compressed trie" by Stefan Nilsson of - * the Helsinki University of Technology and Matti Tikkanen of Nokia - * Telecommunications, page 6: - * "A node is doubled if the ratio of non-empty children to all - * children in the *doubled* node is at least 'high'." - * - * 'high' in this instance is the variable 'inflate_threshold'. It - * is expressed as a percentage, so we multiply it with - * tnode_child_length() and instead of multiplying by 2 (since the - * child array will be doubled by inflate()) and multiplying - * the left-hand side by 100 (to handle the percentage thing) we - * multiply the left-hand side by 50. - * - * The left-hand side may look a bit weird: tnode_child_length(tn) - * - tn->empty_children is of course the number of non-null children - * in the current node. tn->full_children is the number of "full" - * children, that is non-null tnodes with a skip value of 0. - * All of those will be doubled in the resulting inflated tnode, so - * we just count them one extra time here. - * - * A clearer way to write this would be: - * - * to_be_doubled = tn->full_children; - * not_to_be_doubled = tnode_child_length(tn) - tn->empty_children - - * tn->full_children; - * - * new_child_length = tnode_child_length(tn) * 2; - * - * new_fill_factor = 100 * (not_to_be_doubled + 2*to_be_doubled) / - * new_child_length; - * if (new_fill_factor >= inflate_threshold) - * - * ...and so on, tho it would mess up the while () loop. - * - * anyway, - * 100 * (not_to_be_doubled + 2*to_be_doubled) / new_child_length >= - * inflate_threshold - * - * avoid a division: - * 100 * (not_to_be_doubled + 2*to_be_doubled) >= - * inflate_threshold * new_child_length - * - * expand not_to_be_doubled and to_be_doubled, and shorten: - * 100 * (tnode_child_length(tn) - tn->empty_children + - * tn->full_children) >= inflate_threshold * new_child_length - * - * expand new_child_length: - * 100 * (tnode_child_length(tn) - tn->empty_children + - * tn->full_children) >= - * inflate_threshold * tnode_child_length(tn) * 2 - * - * shorten again: - * 50 * (tn->full_children + tnode_child_length(tn) - - * tn->empty_children) >= inflate_threshold * - * tnode_child_length(tn) - * - */ - - /* Keep root node larger */ - - if (!node_parent(tn)) { - inflate_threshold_use = inflate_threshold_root; - halve_threshold_use = halve_threshold_root; - } else { - inflate_threshold_use = inflate_threshold; - halve_threshold_use = halve_threshold; - } - - max_work = MAX_WORK; - while ((tn->full_children > 0 && max_work-- && - 50 * (tn->full_children + tnode_child_length(tn) - - tn->empty_children) - >= inflate_threshold_use * tnode_child_length(tn))) { - - old_tn = tn; - tn = inflate(t, tn); - - if (IS_ERR(tn)) { - tn = old_tn; -#ifdef CONFIG_IP_FIB_TRIE_STATS - this_cpu_inc(t->stats->resize_node_skipped); -#endif - break; - } - } - - /* Return if at least one inflate is run */ - if (max_work != MAX_WORK) - return tn; - - /* - * Halve as long as the number of empty children in this - * node is above threshold. - */ - - max_work = MAX_WORK; - while (tn->bits > 1 && max_work-- && - 100 * (tnode_child_length(tn) - tn->empty_children) < - halve_threshold_use * tnode_child_length(tn)) { - - old_tn = tn; - tn = halve(t, tn); - if (IS_ERR(tn)) { - tn = old_tn; -#ifdef CONFIG_IP_FIB_TRIE_STATS - this_cpu_inc(t->stats->resize_node_skipped); -#endif - break; - } - } - - - /* Only one child remains */ - if (tn->empty_children == (tnode_child_length(tn) - 1)) { - unsigned long i; -one_child: - for (i = tnode_child_length(tn); !n && i;) - n = tnode_get_child(tn, --i); -no_children: - /* compress one level */ - node_set_parent(n, NULL); - tnode_free_safe(tn); - return n; - } - return tn; -} - - static void tnode_clean_free(struct tnode *tn) { struct tnode *tofree; @@ -804,6 +647,160 @@ nomem: return ERR_PTR(-ENOMEM); } +#define MAX_WORK 10 +static struct tnode *resize(struct trie *t, struct tnode *tn) +{ + struct tnode *old_tn, *n = NULL; + int inflate_threshold_use; + int halve_threshold_use; + int max_work; + + if (!tn) + return NULL; + + pr_debug("In tnode_resize %p inflate_threshold=%d threshold=%d\n", + tn, inflate_threshold, halve_threshold); + + /* No children */ + if (tn->empty_children > (tnode_child_length(tn) - 1)) + goto no_children; + + /* One child */ + if (tn->empty_children == (tnode_child_length(tn) - 1)) + goto one_child; + /* + * Double as long as the resulting node has a number of + * nonempty nodes that are above the threshold. + */ + + /* + * From "Implementing a dynamic compressed trie" by Stefan Nilsson of + * the Helsinki University of Technology and Matti Tikkanen of Nokia + * Telecommunications, page 6: + * "A node is doubled if the ratio of non-empty children to all + * children in the *doubled* node is at least 'high'." + * + * 'high' in this instance is the variable 'inflate_threshold'. It + * is expressed as a percentage, so we multiply it with + * tnode_child_length() and instead of multiplying by 2 (since the + * child array will be doubled by inflate()) and multiplying + * the left-hand side by 100 (to handle the percentage thing) we + * multiply the left-hand side by 50. + * + * The left-hand side may look a bit weird: tnode_child_length(tn) + * - tn->empty_children is of course the number of non-null children + * in the current node. tn->full_children is the number of "full" + * children, that is non-null tnodes with a skip value of 0. + * All of those will be doubled in the resulting inflated tnode, so + * we just count them one extra time here. + * + * A clearer way to write this would be: + * + * to_be_doubled = tn->full_children; + * not_to_be_doubled = tnode_child_length(tn) - tn->empty_children - + * tn->full_children; + * + * new_child_length = tnode_child_length(tn) * 2; + * + * new_fill_factor = 100 * (not_to_be_doubled + 2*to_be_doubled) / + * new_child_length; + * if (new_fill_factor >= inflate_threshold) + * + * ...and so on, tho it would mess up the while () loop. + * + * anyway, + * 100 * (not_to_be_doubled + 2*to_be_doubled) / new_child_length >= + * inflate_threshold + * + * avoid a division: + * 100 * (not_to_be_doubled + 2*to_be_doubled) >= + * inflate_threshold * new_child_length + * + * expand not_to_be_doubled and to_be_doubled, and shorten: + * 100 * (tnode_child_length(tn) - tn->empty_children + + * tn->full_children) >= inflate_threshold * new_child_length + * + * expand new_child_length: + * 100 * (tnode_child_length(tn) - tn->empty_children + + * tn->full_children) >= + * inflate_threshold * tnode_child_length(tn) * 2 + * + * shorten again: + * 50 * (tn->full_children + tnode_child_length(tn) - + * tn->empty_children) >= inflate_threshold * + * tnode_child_length(tn) + * + */ + + /* Keep root node larger */ + + if (!node_parent(tn)) { + inflate_threshold_use = inflate_threshold_root; + halve_threshold_use = halve_threshold_root; + } else { + inflate_threshold_use = inflate_threshold; + halve_threshold_use = halve_threshold; + } + + max_work = MAX_WORK; + while ((tn->full_children > 0 && max_work-- && + 50 * (tn->full_children + tnode_child_length(tn) + - tn->empty_children) + >= inflate_threshold_use * tnode_child_length(tn))) { + + old_tn = tn; + tn = inflate(t, tn); + + if (IS_ERR(tn)) { + tn = old_tn; +#ifdef CONFIG_IP_FIB_TRIE_STATS + this_cpu_inc(t->stats->resize_node_skipped); +#endif + break; + } + } + + /* Return if at least one inflate is run */ + if (max_work != MAX_WORK) + return tn; + + /* + * Halve as long as the number of empty children in this + * node is above threshold. + */ + + max_work = MAX_WORK; + while (tn->bits > 1 && max_work-- && + 100 * (tnode_child_length(tn) - tn->empty_children) < + halve_threshold_use * tnode_child_length(tn)) { + + old_tn = tn; + tn = halve(t, tn); + if (IS_ERR(tn)) { + tn = old_tn; +#ifdef CONFIG_IP_FIB_TRIE_STATS + this_cpu_inc(t->stats->resize_node_skipped); +#endif + break; + } + } + + + /* Only one child remains */ + if (tn->empty_children == (tnode_child_length(tn) - 1)) { + unsigned long i; +one_child: + for (i = tnode_child_length(tn); !n && i;) + n = tnode_get_child(tn, --i); +no_children: + /* compress one level */ + node_set_parent(n, NULL); + tnode_free_safe(tn); + return n; + } + return tn; +} + /* readside must use rcu_read_lock currently dump routines via get_fa_head and dump */ -- cgit v0.10.2 From f05a48198bf742efd9d36553c51e98a648dbe807 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 31 Dec 2014 10:56:37 -0800 Subject: fib_trie: Add functions should_inflate and should_halve This change pulls the logic for if we should inflate/halve the nodes out into separate functions. It also addresses what I believe is a bug where 1 full node is all that is needed to keep a node from ever being halved. Simple script to reproduce the issue: modprobe dummy; ifconfig dummy0 up for i in `seq 0 255`; do ifconfig dummy0:$i 10.0.${i}.1/24 up; done ifconfig dummy0:256 10.0.255.33/16 up for i in `seq 0 254`; do ifconfig dummy0:$i down; done Results from /proc/net/fib_triestat Before: Local: Aver depth: 3.00 Max depth: 4 Leaves: 17 Prefixes: 18 Internal nodes: 11 1: 8 2: 2 10: 1 Pointers: 1048 Null ptrs: 1021 Total size: 11 kB After: Local: Aver depth: 3.41 Max depth: 5 Leaves: 17 Prefixes: 18 Internal nodes: 12 1: 8 2: 3 3: 1 Pointers: 36 Null ptrs: 8 Total size: 3 kB Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index d044fa3..58e1224 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -647,12 +647,94 @@ nomem: return ERR_PTR(-ENOMEM); } +/* From "Implementing a dynamic compressed trie" by Stefan Nilsson of + * the Helsinki University of Technology and Matti Tikkanen of Nokia + * Telecommunications, page 6: + * "A node is doubled if the ratio of non-empty children to all + * children in the *doubled* node is at least 'high'." + * + * 'high' in this instance is the variable 'inflate_threshold'. It + * is expressed as a percentage, so we multiply it with + * tnode_child_length() and instead of multiplying by 2 (since the + * child array will be doubled by inflate()) and multiplying + * the left-hand side by 100 (to handle the percentage thing) we + * multiply the left-hand side by 50. + * + * The left-hand side may look a bit weird: tnode_child_length(tn) + * - tn->empty_children is of course the number of non-null children + * in the current node. tn->full_children is the number of "full" + * children, that is non-null tnodes with a skip value of 0. + * All of those will be doubled in the resulting inflated tnode, so + * we just count them one extra time here. + * + * A clearer way to write this would be: + * + * to_be_doubled = tn->full_children; + * not_to_be_doubled = tnode_child_length(tn) - tn->empty_children - + * tn->full_children; + * + * new_child_length = tnode_child_length(tn) * 2; + * + * new_fill_factor = 100 * (not_to_be_doubled + 2*to_be_doubled) / + * new_child_length; + * if (new_fill_factor >= inflate_threshold) + * + * ...and so on, tho it would mess up the while () loop. + * + * anyway, + * 100 * (not_to_be_doubled + 2*to_be_doubled) / new_child_length >= + * inflate_threshold + * + * avoid a division: + * 100 * (not_to_be_doubled + 2*to_be_doubled) >= + * inflate_threshold * new_child_length + * + * expand not_to_be_doubled and to_be_doubled, and shorten: + * 100 * (tnode_child_length(tn) - tn->empty_children + + * tn->full_children) >= inflate_threshold * new_child_length + * + * expand new_child_length: + * 100 * (tnode_child_length(tn) - tn->empty_children + + * tn->full_children) >= + * inflate_threshold * tnode_child_length(tn) * 2 + * + * shorten again: + * 50 * (tn->full_children + tnode_child_length(tn) - + * tn->empty_children) >= inflate_threshold * + * tnode_child_length(tn) + * + */ +static bool should_inflate(const struct tnode *tn) +{ + unsigned long used = tnode_child_length(tn); + unsigned long threshold = used; + + /* Keep root node larger */ + threshold *= node_parent(tn) ? inflate_threshold : + inflate_threshold_root; + used += tn->full_children; + used -= tn->empty_children; + + return tn->pos && ((50 * used) >= threshold); +} + +static bool should_halve(const struct tnode *tn) +{ + unsigned long used = tnode_child_length(tn); + unsigned long threshold = used; + + /* Keep root node larger */ + threshold *= node_parent(tn) ? halve_threshold : + halve_threshold_root; + used -= tn->empty_children; + + return (tn->bits > 1) && ((100 * used) < threshold); +} + #define MAX_WORK 10 static struct tnode *resize(struct trie *t, struct tnode *tn) { struct tnode *old_tn, *n = NULL; - int inflate_threshold_use; - int halve_threshold_use; int max_work; if (!tn) @@ -668,86 +750,12 @@ static struct tnode *resize(struct trie *t, struct tnode *tn) /* One child */ if (tn->empty_children == (tnode_child_length(tn) - 1)) goto one_child; - /* - * Double as long as the resulting node has a number of - * nonempty nodes that are above the threshold. - */ - /* - * From "Implementing a dynamic compressed trie" by Stefan Nilsson of - * the Helsinki University of Technology and Matti Tikkanen of Nokia - * Telecommunications, page 6: - * "A node is doubled if the ratio of non-empty children to all - * children in the *doubled* node is at least 'high'." - * - * 'high' in this instance is the variable 'inflate_threshold'. It - * is expressed as a percentage, so we multiply it with - * tnode_child_length() and instead of multiplying by 2 (since the - * child array will be doubled by inflate()) and multiplying - * the left-hand side by 100 (to handle the percentage thing) we - * multiply the left-hand side by 50. - * - * The left-hand side may look a bit weird: tnode_child_length(tn) - * - tn->empty_children is of course the number of non-null children - * in the current node. tn->full_children is the number of "full" - * children, that is non-null tnodes with a skip value of 0. - * All of those will be doubled in the resulting inflated tnode, so - * we just count them one extra time here. - * - * A clearer way to write this would be: - * - * to_be_doubled = tn->full_children; - * not_to_be_doubled = tnode_child_length(tn) - tn->empty_children - - * tn->full_children; - * - * new_child_length = tnode_child_length(tn) * 2; - * - * new_fill_factor = 100 * (not_to_be_doubled + 2*to_be_doubled) / - * new_child_length; - * if (new_fill_factor >= inflate_threshold) - * - * ...and so on, tho it would mess up the while () loop. - * - * anyway, - * 100 * (not_to_be_doubled + 2*to_be_doubled) / new_child_length >= - * inflate_threshold - * - * avoid a division: - * 100 * (not_to_be_doubled + 2*to_be_doubled) >= - * inflate_threshold * new_child_length - * - * expand not_to_be_doubled and to_be_doubled, and shorten: - * 100 * (tnode_child_length(tn) - tn->empty_children + - * tn->full_children) >= inflate_threshold * new_child_length - * - * expand new_child_length: - * 100 * (tnode_child_length(tn) - tn->empty_children + - * tn->full_children) >= - * inflate_threshold * tnode_child_length(tn) * 2 - * - * shorten again: - * 50 * (tn->full_children + tnode_child_length(tn) - - * tn->empty_children) >= inflate_threshold * - * tnode_child_length(tn) - * + /* Double as long as the resulting node has a number of + * nonempty nodes that are above the threshold. */ - - /* Keep root node larger */ - - if (!node_parent(tn)) { - inflate_threshold_use = inflate_threshold_root; - halve_threshold_use = halve_threshold_root; - } else { - inflate_threshold_use = inflate_threshold; - halve_threshold_use = halve_threshold; - } - max_work = MAX_WORK; - while ((tn->full_children > 0 && max_work-- && - 50 * (tn->full_children + tnode_child_length(tn) - - tn->empty_children) - >= inflate_threshold_use * tnode_child_length(tn))) { - + while (should_inflate(tn) && max_work--) { old_tn = tn; tn = inflate(t, tn); @@ -764,16 +772,11 @@ static struct tnode *resize(struct trie *t, struct tnode *tn) if (max_work != MAX_WORK) return tn; - /* - * Halve as long as the number of empty children in this + /* Halve as long as the number of empty children in this * node is above threshold. */ - max_work = MAX_WORK; - while (tn->bits > 1 && max_work-- && - 100 * (tnode_child_length(tn) - tn->empty_children) < - halve_threshold_use * tnode_child_length(tn)) { - + while (should_halve(tn) && max_work--) { old_tn = tn; tn = halve(t, tn); if (IS_ERR(tn)) { -- cgit v0.10.2 From ff181ed8768fa57fbda40fa97914f27fe275129b Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 31 Dec 2014 10:56:43 -0800 Subject: fib_trie: Push assignment of child to parent down into inflate/halve This change makes it so that the assignment of the tnode to the parent is handled directly within whatever function is currently handling the node be it inflate, halve, or resize. By doing this we can avoid some of the need to set NULL pointers in the tree while we are resizing the subnodes. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 58e1224..7fbd2c5 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -146,9 +146,7 @@ struct trie { #endif }; -static void tnode_put_child_reorg(struct tnode *tn, unsigned long i, - struct tnode *n, int wasfull); -static struct tnode *resize(struct trie *t, struct tnode *tn); +static void resize(struct trie *t, struct tnode *tn); /* tnodes to free after resize(); protected by RTNL */ static struct callback_head *tnode_free_head; static size_t tnode_free_size; @@ -396,22 +394,13 @@ static inline int tnode_full(const struct tnode *tn, const struct tnode *n) return n && ((n->pos + n->bits) == tn->pos) && IS_TNODE(n); } -static inline void put_child(struct tnode *tn, unsigned long i, - struct tnode *n) -{ - tnode_put_child_reorg(tn, i, n, -1); -} - - /* - * Add a child at position i overwriting the old value. - * Update the value of full_children and empty_children. - */ - -static void tnode_put_child_reorg(struct tnode *tn, unsigned long i, - struct tnode *n, int wasfull) +/* Add a child at position i overwriting the old value. + * Update the value of full_children and empty_children. + */ +static void put_child(struct tnode *tn, unsigned long i, struct tnode *n) { struct tnode *chi = rtnl_dereference(tn->child[i]); - int isfull; + int isfull, wasfull; BUG_ON(i >= tnode_child_length(tn)); @@ -422,10 +411,9 @@ static void tnode_put_child_reorg(struct tnode *tn, unsigned long i, tn->empty_children--; /* update fullChildren */ - if (wasfull == -1) - wasfull = tnode_full(tn, chi); - + wasfull = tnode_full(tn, chi); isfull = tnode_full(tn, n); + if (wasfull && !isfull) tn->full_children--; else if (!wasfull && isfull) @@ -458,9 +446,10 @@ static void tnode_clean_free(struct tnode *tn) node_free(tn); } -static struct tnode *inflate(struct trie *t, struct tnode *oldtnode) +static int inflate(struct trie *t, struct tnode *oldtnode) { unsigned long olen = tnode_child_length(oldtnode); + struct tnode *tp = node_parent(oldtnode); struct tnode *tn; unsigned long i; t_key m; @@ -468,9 +457,8 @@ static struct tnode *inflate(struct trie *t, struct tnode *oldtnode) pr_debug("In inflate\n"); tn = tnode_new(oldtnode->key, oldtnode->pos - 1, oldtnode->bits + 1); - if (!tn) - return ERR_PTR(-ENOMEM); + return -ENOMEM; /* * Preallocate and store tnodes before the actual work so we @@ -564,30 +552,36 @@ static struct tnode *inflate(struct trie *t, struct tnode *oldtnode) put_child(left, j, rtnl_dereference(inode->child[j])); put_child(right, j, rtnl_dereference(inode->child[j + size])); } - put_child(tn, 2*i, resize(t, left)); - put_child(tn, 2*i+1, resize(t, right)); + + put_child(tn, 2 * i, left); + put_child(tn, 2 * i + 1, right); tnode_free_safe(inode); + + resize(t, left); + resize(t, right); } + + put_child_root(tp, t, tn->key, tn); tnode_free_safe(oldtnode); - return tn; + return 0; nomem: tnode_clean_free(tn); - return ERR_PTR(-ENOMEM); + return -ENOMEM; } -static struct tnode *halve(struct trie *t, struct tnode *oldtnode) +static int halve(struct trie *t, struct tnode *oldtnode) { unsigned long olen = tnode_child_length(oldtnode); + struct tnode *tp = node_parent(oldtnode); struct tnode *tn, *left, *right; int i; pr_debug("In halve\n"); tn = tnode_new(oldtnode->key, oldtnode->pos + 1, oldtnode->bits - 1); - if (!tn) - return ERR_PTR(-ENOMEM); + return -ENOMEM; /* * Preallocate and store tnodes before the actual work so we @@ -606,8 +600,10 @@ static struct tnode *halve(struct trie *t, struct tnode *oldtnode) newn = tnode_new(left->key, oldtnode->pos, 1); - if (!newn) - goto nomem; + if (!newn) { + tnode_clean_free(tn); + return -ENOMEM; + } put_child(tn, i/2, newn); } @@ -635,16 +631,18 @@ static struct tnode *halve(struct trie *t, struct tnode *oldtnode) /* Two nonempty children */ newBinNode = tnode_get_child(tn, i/2); - put_child(tn, i/2, NULL); put_child(newBinNode, 0, left); put_child(newBinNode, 1, right); - put_child(tn, i/2, resize(t, newBinNode)); + + put_child(tn, i / 2, newBinNode); + + resize(t, newBinNode); } + + put_child_root(tp, t, tn->key, tn); tnode_free_safe(oldtnode); - return tn; -nomem: - tnode_clean_free(tn); - return ERR_PTR(-ENOMEM); + + return 0; } /* From "Implementing a dynamic compressed trie" by Stefan Nilsson of @@ -704,45 +702,48 @@ nomem: * tnode_child_length(tn) * */ -static bool should_inflate(const struct tnode *tn) +static bool should_inflate(const struct tnode *tp, const struct tnode *tn) { unsigned long used = tnode_child_length(tn); unsigned long threshold = used; /* Keep root node larger */ - threshold *= node_parent(tn) ? inflate_threshold : - inflate_threshold_root; + threshold *= tp ? inflate_threshold : inflate_threshold_root; used += tn->full_children; used -= tn->empty_children; return tn->pos && ((50 * used) >= threshold); } -static bool should_halve(const struct tnode *tn) +static bool should_halve(const struct tnode *tp, const struct tnode *tn) { unsigned long used = tnode_child_length(tn); unsigned long threshold = used; /* Keep root node larger */ - threshold *= node_parent(tn) ? halve_threshold : - halve_threshold_root; + threshold *= tp ? halve_threshold : halve_threshold_root; used -= tn->empty_children; return (tn->bits > 1) && ((100 * used) < threshold); } #define MAX_WORK 10 -static struct tnode *resize(struct trie *t, struct tnode *tn) +static void resize(struct trie *t, struct tnode *tn) { - struct tnode *old_tn, *n = NULL; + struct tnode *tp = node_parent(tn), *n = NULL; + struct tnode __rcu **cptr; int max_work; - if (!tn) - return NULL; - pr_debug("In tnode_resize %p inflate_threshold=%d threshold=%d\n", tn, inflate_threshold, halve_threshold); + /* track the tnode via the pointer from the parent instead of + * doing it ourselves. This way we can let RCU fully do its + * thing without us interfering + */ + cptr = tp ? &tp->child[get_index(tn->key, tp)] : &t->trie; + BUG_ON(tn != rtnl_dereference(*cptr)); + /* No children */ if (tn->empty_children > (tnode_child_length(tn) - 1)) goto no_children; @@ -755,39 +756,35 @@ static struct tnode *resize(struct trie *t, struct tnode *tn) * nonempty nodes that are above the threshold. */ max_work = MAX_WORK; - while (should_inflate(tn) && max_work--) { - old_tn = tn; - tn = inflate(t, tn); - - if (IS_ERR(tn)) { - tn = old_tn; + while (should_inflate(tp, tn) && max_work--) { + if (inflate(t, tn)) { #ifdef CONFIG_IP_FIB_TRIE_STATS this_cpu_inc(t->stats->resize_node_skipped); #endif break; } + + tn = rtnl_dereference(*cptr); } /* Return if at least one inflate is run */ if (max_work != MAX_WORK) - return tn; + return; /* Halve as long as the number of empty children in this * node is above threshold. */ max_work = MAX_WORK; - while (should_halve(tn) && max_work--) { - old_tn = tn; - tn = halve(t, tn); - if (IS_ERR(tn)) { - tn = old_tn; + while (should_halve(tp, tn) && max_work--) { + if (halve(t, tn)) { #ifdef CONFIG_IP_FIB_TRIE_STATS this_cpu_inc(t->stats->resize_node_skipped); #endif break; } - } + tn = rtnl_dereference(*cptr); + } /* Only one child remains */ if (tn->empty_children == (tnode_child_length(tn) - 1)) { @@ -797,11 +794,12 @@ one_child: n = tnode_get_child(tn, --i); no_children: /* compress one level */ - node_set_parent(n, NULL); + put_child_root(tp, t, tn->key, n); + node_set_parent(n, tp); + + /* drop dead node */ tnode_free_safe(tn); - return n; } - return tn; } /* readside must use rcu_read_lock currently dump routines @@ -882,34 +880,19 @@ static struct tnode *fib_find_node(struct trie *t, u32 key) static void trie_rebalance(struct trie *t, struct tnode *tn) { - int wasfull; - t_key cindex, key; struct tnode *tp; - key = tn->key; - - while (tn != NULL && (tp = node_parent(tn)) != NULL) { - cindex = get_index(key, tp); - wasfull = tnode_full(tp, tnode_get_child(tp, cindex)); - tn = resize(t, tn); - - tnode_put_child_reorg(tp, cindex, tn, wasfull); - - tp = node_parent(tn); - if (!tp) - rcu_assign_pointer(t->trie, tn); + while ((tp = node_parent(tn)) != NULL) { + resize(t, tn); tnode_free_flush(); - if (!tp) - break; tn = tp; } /* Handle last (top) tnode */ if (IS_TNODE(tn)) - tn = resize(t, tn); + resize(t, tn); - rcu_assign_pointer(t->trie, tn); tnode_free_flush(); } -- cgit v0.10.2 From fc86a93b46d707c6e245b15127409340eaf6ae47 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 31 Dec 2014 10:56:49 -0800 Subject: fib_trie: Push tnode flushing down to inflate/halve This change pushes the tnode freeing down into the inflate and halve functions. It makes more sense here as we have a better grasp of what is going on and when a given cluster of nodes is ready to be freed. I believe this may address a bug in the freeing logic as well. For some reason if the freelist got to a certain size we would call synchronize_rcu(). I'm assuming that what they meant to do is call synchronize_rcu() after they had handed off that much memory via call_rcu(). As such that is what I have updated the behavior to be. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 7fbd2c5..485983e 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -147,8 +147,6 @@ struct trie { }; static void resize(struct trie *t, struct tnode *tn); -/* tnodes to free after resize(); protected by RTNL */ -static struct callback_head *tnode_free_head; static size_t tnode_free_size; /* @@ -307,32 +305,6 @@ static struct tnode *tnode_alloc(size_t size) return vzalloc(size); } -static void tnode_free_safe(struct tnode *tn) -{ - BUG_ON(IS_LEAF(tn)); - tn->rcu.next = tnode_free_head; - tnode_free_head = &tn->rcu; -} - -static void tnode_free_flush(void) -{ - struct callback_head *head; - - while ((head = tnode_free_head)) { - struct tnode *tn = container_of(head, struct tnode, rcu); - - tnode_free_head = head->next; - tnode_free_size += offsetof(struct tnode, child[1 << tn->bits]); - - node_free(tn); - } - - if (tnode_free_size >= PAGE_SIZE * sync_pages) { - tnode_free_size = 0; - synchronize_rcu(); - } -} - static struct tnode *leaf_new(t_key key) { struct tnode *l = kmem_cache_alloc(trie_leaf_kmem, GFP_KERNEL); @@ -433,17 +405,33 @@ static void put_child_root(struct tnode *tp, struct trie *t, rcu_assign_pointer(t->trie, n); } -static void tnode_clean_free(struct tnode *tn) +static inline void tnode_free_init(struct tnode *tn) { - struct tnode *tofree; - unsigned long i; + tn->rcu.next = NULL; +} + +static inline void tnode_free_append(struct tnode *tn, struct tnode *n) +{ + n->rcu.next = tn->rcu.next; + tn->rcu.next = &n->rcu; +} - for (i = 0; i < tnode_child_length(tn); i++) { - tofree = tnode_get_child(tn, i); - if (tofree) - node_free(tofree); +static void tnode_free(struct tnode *tn) +{ + struct callback_head *head = &tn->rcu; + + while (head) { + head = head->next; + tnode_free_size += offsetof(struct tnode, child[1 << tn->bits]); + node_free(tn); + + tn = container_of(head, struct tnode, rcu); + } + + if (tnode_free_size >= PAGE_SIZE * sync_pages) { + tnode_free_size = 0; + synchronize_rcu(); } - node_free(tn); } static int inflate(struct trie *t, struct tnode *oldtnode) @@ -476,20 +464,23 @@ static int inflate(struct trie *t, struct tnode *oldtnode) inode->bits - 1); if (!left) goto nomem; + tnode_free_append(tn, left); right = tnode_new(inode->key | m, inode->pos, inode->bits - 1); - if (!right) { - node_free(left); + if (!right) goto nomem; - } + tnode_free_append(tn, right); put_child(tn, 2*i, left); put_child(tn, 2*i+1, right); } } + /* prepare oldtnode to be freed */ + tnode_free_init(oldtnode); + for (i = 0; i < olen; i++) { struct tnode *inode = tnode_get_child(oldtnode, i); struct tnode *left, *right; @@ -505,12 +496,13 @@ static int inflate(struct trie *t, struct tnode *oldtnode) continue; } + /* drop the node in the old tnode free list */ + tnode_free_append(oldtnode, inode); + /* An internal node with two children */ if (inode->bits == 1) { put_child(tn, 2*i, rtnl_dereference(inode->child[0])); put_child(tn, 2*i+1, rtnl_dereference(inode->child[1])); - - tnode_free_safe(inode); continue; } @@ -556,17 +548,19 @@ static int inflate(struct trie *t, struct tnode *oldtnode) put_child(tn, 2 * i, left); put_child(tn, 2 * i + 1, right); - tnode_free_safe(inode); - + /* resize child nodes */ resize(t, left); resize(t, right); } put_child_root(tp, t, tn->key, tn); - tnode_free_safe(oldtnode); + + /* we completed without error, prepare to free old node */ + tnode_free(oldtnode); return 0; nomem: - tnode_clean_free(tn); + /* all pointers should be clean so we are done */ + tnode_free(tn); return -ENOMEM; } @@ -599,17 +593,20 @@ static int halve(struct trie *t, struct tnode *oldtnode) struct tnode *newn; newn = tnode_new(left->key, oldtnode->pos, 1); - if (!newn) { - tnode_clean_free(tn); + tnode_free(tn); return -ENOMEM; } + tnode_free_append(tn, newn); put_child(tn, i/2, newn); } } + /* prepare oldtnode to be freed */ + tnode_free_init(oldtnode); + for (i = 0; i < olen; i += 2) { struct tnode *newBinNode; @@ -636,11 +633,14 @@ static int halve(struct trie *t, struct tnode *oldtnode) put_child(tn, i / 2, newBinNode); + /* resize child node */ resize(t, newBinNode); } put_child_root(tp, t, tn->key, tn); - tnode_free_safe(oldtnode); + + /* all pointers should be clean so we are done */ + tnode_free(oldtnode); return 0; } @@ -798,7 +798,8 @@ no_children: node_set_parent(n, tp); /* drop dead node */ - tnode_free_safe(tn); + tnode_free_init(tn); + tnode_free(tn); } } @@ -884,16 +885,12 @@ static void trie_rebalance(struct trie *t, struct tnode *tn) while ((tp = node_parent(tn)) != NULL) { resize(t, tn); - - tnode_free_flush(); tn = tp; } /* Handle last (top) tnode */ if (IS_TNODE(tn)) resize(t, tn); - - tnode_free_flush(); } /* only used from updater-side */ -- cgit v0.10.2 From 12c081a5c82ef64a90906247cecfa5422962e387 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 31 Dec 2014 10:56:55 -0800 Subject: fib_trie: inflate/halve nodes in a more RCU friendly way This change pulls the node_set_parent functionality out of put_child_reorg and instead leaves that to the function to take care of as well. By doing this we can fully construct the new cluster of tnodes and all of the pointers out of it before we start routing pointers into it. I am suspecting this will likely fix some concurency issues though I don't have a good test to show as such. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 485983e..0c88df0 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -391,8 +391,6 @@ static void put_child(struct tnode *tn, unsigned long i, struct tnode *n) else if (!wasfull && isfull) tn->full_children++; - node_set_parent(n, tn); - rcu_assign_pointer(tn->child[i], n); } @@ -436,10 +434,8 @@ static void tnode_free(struct tnode *tn) static int inflate(struct trie *t, struct tnode *oldtnode) { - unsigned long olen = tnode_child_length(oldtnode); - struct tnode *tp = node_parent(oldtnode); - struct tnode *tn; - unsigned long i; + struct tnode *inode, *node0, *node1, *tn, *tp; + unsigned long i, j, k; t_key m; pr_debug("In inflate\n"); @@ -448,43 +444,13 @@ static int inflate(struct trie *t, struct tnode *oldtnode) if (!tn) return -ENOMEM; - /* - * Preallocate and store tnodes before the actual work so we - * don't get into an inconsistent state if memory allocation - * fails. In case of failure we return the oldnode and inflate - * of tnode is ignored. + /* Assemble all of the pointers in our cluster, in this case that + * represents all of the pointers out of our allocated nodes that + * point to existing tnodes and the links between our allocated + * nodes. */ - for (i = 0, m = 1u << tn->pos; i < olen; i++) { - struct tnode *inode = tnode_get_child(oldtnode, i); - - if (tnode_full(oldtnode, inode) && (inode->bits > 1)) { - struct tnode *left, *right; - - left = tnode_new(inode->key & ~m, inode->pos, - inode->bits - 1); - if (!left) - goto nomem; - tnode_free_append(tn, left); - - right = tnode_new(inode->key | m, inode->pos, - inode->bits - 1); - - if (!right) - goto nomem; - tnode_free_append(tn, right); - - put_child(tn, 2*i, left); - put_child(tn, 2*i+1, right); - } - } - - /* prepare oldtnode to be freed */ - tnode_free_init(oldtnode); - - for (i = 0; i < olen; i++) { - struct tnode *inode = tnode_get_child(oldtnode, i); - struct tnode *left, *right; - unsigned long size, j; + for (i = tnode_child_length(oldtnode), m = 1u << tn->pos; i;) { + inode = tnode_get_child(oldtnode, --i); /* An empty child */ if (inode == NULL) @@ -496,65 +462,99 @@ static int inflate(struct trie *t, struct tnode *oldtnode) continue; } - /* drop the node in the old tnode free list */ - tnode_free_append(oldtnode, inode); - /* An internal node with two children */ if (inode->bits == 1) { - put_child(tn, 2*i, rtnl_dereference(inode->child[0])); - put_child(tn, 2*i+1, rtnl_dereference(inode->child[1])); + put_child(tn, 2 * i + 1, tnode_get_child(inode, 1)); + put_child(tn, 2 * i, tnode_get_child(inode, 0)); continue; } - /* An internal node with more than two children */ - /* We will replace this node 'inode' with two new - * ones, 'left' and 'right', each with half of the + * ones, 'node0' and 'node1', each with half of the * original children. The two new nodes will have * a position one bit further down the key and this * means that the "significant" part of their keys * (see the discussion near the top of this file) * will differ by one bit, which will be "0" in - * left's key and "1" in right's key. Since we are + * node0's key and "1" in node1's key. Since we are * moving the key position by one step, the bit that * we are moving away from - the bit at position - * (inode->pos) - is the one that will differ between - * left and right. So... we synthesize that bit in the - * two new keys. - * The mask 'm' below will be a single "one" bit at - * the position (inode->pos) + * (tn->pos) - is the one that will differ between + * node0 and node1. So... we synthesize that bit in the + * two new keys. */ + node1 = tnode_new(inode->key | m, inode->pos, inode->bits - 1); + if (!node1) + goto nomem; + tnode_free_append(tn, node1); + + node0 = tnode_new(inode->key & ~m, inode->pos, inode->bits - 1); + if (!node0) + goto nomem; + tnode_free_append(tn, node0); + + /* populate child pointers in new nodes */ + for (k = tnode_child_length(inode), j = k / 2; j;) { + put_child(node1, --j, tnode_get_child(inode, --k)); + put_child(node0, j, tnode_get_child(inode, j)); + put_child(node1, --j, tnode_get_child(inode, --k)); + put_child(node0, j, tnode_get_child(inode, j)); + } - /* Use the old key, but set the new significant - * bit to zero. - */ + /* link new nodes to parent */ + NODE_INIT_PARENT(node1, tn); + NODE_INIT_PARENT(node0, tn); + + /* link parent to nodes */ + put_child(tn, 2 * i + 1, node1); + put_child(tn, 2 * i, node0); + } - left = tnode_get_child(tn, 2*i); - put_child(tn, 2*i, NULL); + /* setup the parent pointer into and out of this node */ + tp = node_parent(oldtnode); + NODE_INIT_PARENT(tn, tp); + put_child_root(tp, t, tn->key, tn); + + /* prepare oldtnode to be freed */ + tnode_free_init(oldtnode); - BUG_ON(!left); + /* update all child nodes parent pointers to route to us */ + for (i = tnode_child_length(oldtnode); i;) { + inode = tnode_get_child(oldtnode, --i); + + /* A leaf or an internal node with skipped bits */ + if (!tnode_full(oldtnode, inode)) { + node_set_parent(inode, tn); + continue; + } - right = tnode_get_child(tn, 2*i+1); - put_child(tn, 2*i+1, NULL); + /* drop the node in the old tnode free list */ + tnode_free_append(oldtnode, inode); - BUG_ON(!right); + /* fetch new nodes */ + node1 = tnode_get_child(tn, 2 * i + 1); + node0 = tnode_get_child(tn, 2 * i); - size = tnode_child_length(left); - for (j = 0; j < size; j++) { - put_child(left, j, rtnl_dereference(inode->child[j])); - put_child(right, j, rtnl_dereference(inode->child[j + size])); + /* bits == 1 then node0 and node1 represent inode's children */ + if (inode->bits == 1) { + node_set_parent(node1, tn); + node_set_parent(node0, tn); + continue; } - put_child(tn, 2 * i, left); - put_child(tn, 2 * i + 1, right); + /* update parent pointers in child node's children */ + for (k = tnode_child_length(inode), j = k / 2; j;) { + node_set_parent(tnode_get_child(inode, --k), node1); + node_set_parent(tnode_get_child(inode, --j), node0); + node_set_parent(tnode_get_child(inode, --k), node1); + node_set_parent(tnode_get_child(inode, --j), node0); + } /* resize child nodes */ - resize(t, left); - resize(t, right); + resize(t, node1); + resize(t, node0); } - put_child_root(tp, t, tn->key, tn); - /* we completed without error, prepare to free old node */ tnode_free(oldtnode); return 0; @@ -566,10 +566,8 @@ nomem: static int halve(struct trie *t, struct tnode *oldtnode) { - unsigned long olen = tnode_child_length(oldtnode); - struct tnode *tp = node_parent(oldtnode); - struct tnode *tn, *left, *right; - int i; + struct tnode *tn, *tp, *inode, *node0, *node1; + unsigned long i; pr_debug("In halve\n"); @@ -577,68 +575,64 @@ static int halve(struct trie *t, struct tnode *oldtnode) if (!tn) return -ENOMEM; - /* - * Preallocate and store tnodes before the actual work so we - * don't get into an inconsistent state if memory allocation - * fails. In case of failure we return the oldnode and halve - * of tnode is ignored. + /* Assemble all of the pointers in our cluster, in this case that + * represents all of the pointers out of our allocated nodes that + * point to existing tnodes and the links between our allocated + * nodes. */ + for (i = tnode_child_length(oldtnode); i;) { + node1 = tnode_get_child(oldtnode, --i); + node0 = tnode_get_child(oldtnode, --i); - for (i = 0; i < olen; i += 2) { - left = tnode_get_child(oldtnode, i); - right = tnode_get_child(oldtnode, i+1); + /* At least one of the children is empty */ + if (!node1 || !node0) { + put_child(tn, i / 2, node1 ? : node0); + continue; + } /* Two nonempty children */ - if (left && right) { - struct tnode *newn; - - newn = tnode_new(left->key, oldtnode->pos, 1); - if (!newn) { - tnode_free(tn); - return -ENOMEM; - } - tnode_free_append(tn, newn); - - put_child(tn, i/2, newn); + inode = tnode_new(node0->key, oldtnode->pos, 1); + if (!inode) { + tnode_free(tn); + return -ENOMEM; } + tnode_free_append(tn, inode); + /* initialize pointers out of node */ + put_child(inode, 1, node1); + put_child(inode, 0, node0); + NODE_INIT_PARENT(inode, tn); + + /* link parent to node */ + put_child(tn, i / 2, inode); } + /* setup the parent pointer out of and back into this node */ + tp = node_parent(oldtnode); + NODE_INIT_PARENT(tn, tp); + put_child_root(tp, t, tn->key, tn); + /* prepare oldtnode to be freed */ tnode_free_init(oldtnode); - for (i = 0; i < olen; i += 2) { - struct tnode *newBinNode; - - left = tnode_get_child(oldtnode, i); - right = tnode_get_child(oldtnode, i+1); - - /* At least one of the children is empty */ - if (left == NULL) { - if (right == NULL) /* Both are empty */ - continue; - put_child(tn, i/2, right); - continue; - } + /* update all of the child parent pointers */ + for (i = tnode_child_length(tn); i;) { + inode = tnode_get_child(tn, --i); - if (right == NULL) { - put_child(tn, i/2, left); + /* only new tnodes will be considered "full" nodes */ + if (!tnode_full(tn, inode)) { + node_set_parent(inode, tn); continue; } /* Two nonempty children */ - newBinNode = tnode_get_child(tn, i/2); - put_child(newBinNode, 0, left); - put_child(newBinNode, 1, right); - - put_child(tn, i / 2, newBinNode); + node_set_parent(tnode_get_child(inode, 1), inode); + node_set_parent(tnode_get_child(inode, 0), inode); /* resize child node */ - resize(t, newBinNode); + resize(t, inode); } - put_child_root(tp, t, tn->key, tn); - /* all pointers should be clean so we are done */ tnode_free(oldtnode); -- cgit v0.10.2 From 21d1f11db0e2f20a549ad8322879507850544670 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 31 Dec 2014 10:57:02 -0800 Subject: fib_trie: Remove checks for index >= tnode_child_length from tnode_get_child For some reason the compiler doesn't seem to understand that when we are in a loop that runs from tnode_child_length - 1 to 0 we don't expect the value of tn->bits to change. As such every call to tnode_get_child was rerunning tnode_chile_length which ended up consuming quite a bit of space in the resultant assembly code. I have gone though and verified that in all cases where tnode_get_child is used we are either winding though a fixed loop from tnode_child_length - 1 to 0, or are in a fastpath case where we are verifying the value by either checking for any remaining bits after shifting index by bits and testing for leaf, or by using tnode_child_length. size net/ipv4/fib_trie.o Before: text data bss dec hex filename 15506 376 8 15890 3e12 net/ipv4/fib_trie.o After: text data bss dec hex filename 14827 376 8 15211 3b6b net/ipv4/fib_trie.o Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 0c88df0..87fc9a4 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -186,8 +186,6 @@ static inline unsigned long tnode_child_length(const struct tnode *tn) static inline struct tnode *tnode_get_child(const struct tnode *tn, unsigned long i) { - BUG_ON(i >= tnode_child_length(tn)); - return rtnl_dereference(tn->child[i]); } @@ -195,8 +193,6 @@ static inline struct tnode *tnode_get_child(const struct tnode *tn, static inline struct tnode *tnode_get_child_rcu(const struct tnode *tn, unsigned long i) { - BUG_ON(i >= tnode_child_length(tn)); - return rcu_dereference_rtnl(tn->child[i]); } @@ -371,7 +367,7 @@ static inline int tnode_full(const struct tnode *tn, const struct tnode *n) */ static void put_child(struct tnode *tn, unsigned long i, struct tnode *n) { - struct tnode *chi = rtnl_dereference(tn->child[i]); + struct tnode *chi = tnode_get_child(tn, i); int isfull, wasfull; BUG_ON(i >= tnode_child_length(tn)); @@ -867,7 +863,7 @@ static struct tnode *fib_find_node(struct trie *t, u32 key) if (IS_LEAF(n)) break; - n = rcu_dereference_rtnl(n->child[index]); + n = tnode_get_child_rcu(n, index); } return n; @@ -934,7 +930,7 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) } tp = n; - n = rcu_dereference_rtnl(n->child[index]); + n = tnode_get_child_rcu(n, index); } l = leaf_new(key); @@ -1215,7 +1211,7 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, cindex = index; } - n = rcu_dereference(n->child[index]); + n = tnode_get_child_rcu(n, index); if (unlikely(!n)) goto backtrace; } @@ -1835,7 +1831,7 @@ static void trie_collect_stats(struct trie *t, struct trie_stat *s) if (n->bits < MAX_STAT_DEPTH) s->nodesizes[n->bits]++; - for (i = 0; i < tnode_child_length(n); i++) { + for (i = tnode_child_length(n); i--;) { if (!rcu_access_pointer(n->child[i])) s->nullpointers++; } -- cgit v0.10.2 From 5405afd1a30620de8601436bae739c67c0bc7324 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 31 Dec 2014 10:57:08 -0800 Subject: fib_trie: Add tracking value for suffix length This change adds a tracking value for the maximum suffix length of all prefixes stored in any given tnode. With this value we can determine if we need to backtrace or not based on if the suffix is greater than the pos value. By doing this we can reduce the CPU overhead for lookups in the local table as many of the prefixes there are 32b long and have a suffix length of 0 meaning we can immediately backtrace to the root node without needing to test any of the nodes between it and where we ended up. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 87fc9a4..281e5e0 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -96,6 +96,7 @@ struct tnode { t_key key; unsigned char bits; /* 2log(KEYLENGTH) bits needed */ unsigned char pos; /* 2log(KEYLENGTH) bits needed */ + unsigned char slen; struct tnode __rcu *parent; struct rcu_head rcu; union { @@ -311,6 +312,7 @@ static struct tnode *leaf_new(t_key key) * as the nodes are searched */ l->key = key; + l->slen = 0; l->pos = 0; /* set bits to 0 indicating we are not a tnode */ l->bits = 0; @@ -342,6 +344,7 @@ static struct tnode *tnode_new(t_key key, int pos, int bits) if (tn) { tn->parent = NULL; + tn->slen = pos; tn->pos = pos; tn->bits = bits; tn->key = (shift < KEYLENGTH) ? (key >> shift) << shift : 0; @@ -387,6 +390,9 @@ static void put_child(struct tnode *tn, unsigned long i, struct tnode *n) else if (!wasfull && isfull) tn->full_children++; + if (n && (tn->slen < n->slen)) + tn->slen = n->slen; + rcu_assign_pointer(tn->child[i], n); } @@ -635,6 +641,41 @@ static int halve(struct trie *t, struct tnode *oldtnode) return 0; } +static unsigned char update_suffix(struct tnode *tn) +{ + unsigned char slen = tn->pos; + unsigned long stride, i; + + /* search though the list of children looking for nodes that might + * have a suffix greater than the one we currently have. This is + * why we start with a stride of 2 since a stride of 1 would + * represent the nodes with suffix length equal to tn->pos + */ + for (i = 0, stride = 0x2ul ; i < tnode_child_length(tn); i += stride) { + struct tnode *n = tnode_get_child(tn, i); + + if (!n || (n->slen <= slen)) + continue; + + /* update stride and slen based on new value */ + stride <<= (n->slen - slen); + slen = n->slen; + i &= ~(stride - 1); + + /* if slen covers all but the last bit we can stop here + * there will be nothing longer than that since only node + * 0 and 1 << (bits - 1) could have that as their suffix + * length. + */ + if ((slen + 1) >= (tn->pos + tn->bits)) + break; + } + + tn->slen = slen; + + return slen; +} + /* From "Implementing a dynamic compressed trie" by Stefan Nilsson of * the Helsinki University of Technology and Matti Tikkanen of Nokia * Telecommunications, page 6: @@ -790,6 +831,19 @@ no_children: /* drop dead node */ tnode_free_init(tn); tnode_free(tn); + return; + } + + /* Return if at least one deflate was run */ + if (max_work != MAX_WORK) + return; + + /* push the suffix length to the parent node */ + if (tn->slen > tn->pos) { + unsigned char slen = update_suffix(tn); + + if (tp && (slen > tp->slen)) + tp->slen = slen; } } @@ -818,8 +872,58 @@ static inline struct list_head *get_fa_head(struct tnode *l, int plen) return &li->falh; } -static void insert_leaf_info(struct hlist_head *head, struct leaf_info *new) +static void leaf_pull_suffix(struct tnode *l) +{ + struct tnode *tp = node_parent(l); + + while (tp && (tp->slen > tp->pos) && (tp->slen > l->slen)) { + if (update_suffix(tp) > l->slen) + break; + tp = node_parent(tp); + } +} + +static void leaf_push_suffix(struct tnode *l) { + struct tnode *tn = node_parent(l); + + /* if this is a new leaf then tn will be NULL and we can sort + * out parent suffix lengths as a part of trie_rebalance + */ + while (tn && (tn->slen < l->slen)) { + tn->slen = l->slen; + tn = node_parent(tn); + } +} + +static void remove_leaf_info(struct tnode *l, struct leaf_info *old) +{ + struct hlist_node *prev; + + /* record the location of the pointer to this object */ + prev = rtnl_dereference(hlist_pprev_rcu(&old->hlist)); + + /* remove the leaf info from the list */ + hlist_del_rcu(&old->hlist); + + /* if we emptied the list this leaf will be freed and we can sort + * out parent suffix lengths as a part of trie_rebalance + */ + if (hlist_empty(&l->list)) + return; + + /* if we removed the tail then we need to update slen */ + if (!rcu_access_pointer(hlist_next_rcu(prev))) { + struct leaf_info *li = hlist_entry(prev, typeof(*li), hlist); + + l->slen = KEYLENGTH - li->plen; + leaf_pull_suffix(l); + } +} + +static void insert_leaf_info(struct tnode *l, struct leaf_info *new) +{ + struct hlist_head *head = &l->list; struct leaf_info *li = NULL, *last = NULL; if (hlist_empty(head)) { @@ -836,6 +940,12 @@ static void insert_leaf_info(struct hlist_head *head, struct leaf_info *new) else hlist_add_before_rcu(&new->hlist, &li->hlist); } + + /* if we added to the tail node then we need to update slen */ + if (!rcu_access_pointer(hlist_next_rcu(&new->hlist))) { + l->slen = KEYLENGTH - new->plen; + leaf_push_suffix(l); + } } /* rcu_read_lock needs to be hold by caller from readside */ @@ -925,7 +1035,7 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) /* we have found a leaf. Prefixes have already been compared */ if (IS_LEAF(n)) { /* Case 1: n is a leaf, and prefixes match*/ - insert_leaf_info(&n->list, li); + insert_leaf_info(n, li); return fa_head; } @@ -939,7 +1049,7 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) return NULL; } - insert_leaf_info(&l->list, li); + insert_leaf_info(l, li); /* Case 2: n is a LEAF or a TNODE and the key doesn't match. * @@ -1206,7 +1316,7 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, /* only record pn and cindex if we are going to be chopping * bits later. Otherwise we are just wasting cycles. */ - if (index) { + if (n->slen > n->pos) { pn = n; cindex = index; } @@ -1225,7 +1335,7 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, * between the key and the prefix exist in the region of * the lsb and higher in the prefix. */ - if (unlikely(prefix_mismatch(key, n))) + if (unlikely(prefix_mismatch(key, n)) || (n->slen == n->pos)) goto backtrace; /* exit out and process leaf */ @@ -1425,7 +1535,7 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg) tb->tb_num_default--; if (list_empty(fa_head)) { - hlist_del_rcu(&li->hlist); + remove_leaf_info(l, li); free_leaf_info(li); } -- cgit v0.10.2 From 54da5083b71f01596ac3112685920fdde4540685 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 31 Dec 2014 18:32:25 -0500 Subject: e1000e: Include clocksource.h to get CLOCKSOURCE_MASK. Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index e14fd85..2537d36a 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -43,6 +43,7 @@ #include #include #include +#include #include "e1000.h" -- cgit v0.10.2 From 9aacfb2023c67c8e797bf1f9ba5e095c5a09d296 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 31 Dec 2014 18:32:40 -0500 Subject: igb_ptp: Include clocksource.h to get CLOCKSOURCE_MASK. Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index 1d27f2d..8baf3fd 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "igb.h" -- cgit v0.10.2 From 7ad269ea1a2b7ddc8ae26794162c21bad2d9b2e5 Mon Sep 17 00:00:00 2001 From: Roger Chen Date: Mon, 29 Dec 2014 17:43:36 +0800 Subject: GMAC: add driver for Rockchip RK3288 SoCs integrated GMAC This driver is based on stmmac driver. changes since v2: - use tab instead of space for macros - use HIWORD_UPDATE macro for GMAC_CLK_RX_DL_CFG and GMAC_CLK_TX_DL_CFG - remove drive-strength setting in the driver and set it in the pinctrl settings - use dev_err instead of pr_err - remove clock names's macros, just use the real name of the clock - use devm_clk_get() instead of clk_get() - remove clk_set_parent(bsp_priv->clk_mac, bsp_priv->clk_mac_pll) - remove gpio setting for LDO, just use regulator API - remove phy reset using gpio in the glue layer, it has been handled in the stmmac driver - remove handling phy interrupt (mii interrupt) changes since v1: - use BIT() to set register - combine two remap_write() operations into one for the same register - use macros for register value setting - remove grf fail check in rk_gmac_setup() and save all the check in set_rgmii_speed() - remove .tx_coe=1 in rk_gmac_data Signed-off-by: Roger Chen Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index ac4d562..73c2715 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -6,7 +6,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \ obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o stmmac-platform-objs:= stmmac_platform.o dwmac-meson.o dwmac-sunxi.o \ - dwmac-sti.o dwmac-socfpga.o + dwmac-sti.o dwmac-socfpga.o dwmac-rk.o obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o stmmac-pci-objs:= stmmac_pci.o diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c new file mode 100644 index 0000000..35f9b86 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -0,0 +1,459 @@ +/** + * dwmac-rk.c - Rockchip RK3288 DWMAC specific glue layer + * + * Copyright (C) 2014 Chen-Zhi (Roger Chen) + * + * Chen-Zhi (Roger Chen) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rk_priv_data { + struct platform_device *pdev; + int phy_iface; + char regulator[32]; + + bool clk_enabled; + bool clock_input; + + struct clk *clk_mac; + struct clk *clk_mac_pll; + struct clk *gmac_clkin; + struct clk *mac_clk_rx; + struct clk *mac_clk_tx; + struct clk *clk_mac_ref; + struct clk *clk_mac_refout; + struct clk *aclk_mac; + struct clk *pclk_mac; + + int tx_delay; + int rx_delay; + + struct regmap *grf; +}; + +#define HIWORD_UPDATE(val, mask, shift) \ + ((val) << (shift) | (mask) << ((shift) + 16)) + +#define GRF_BIT(nr) (BIT(nr) | BIT(nr+16)) +#define GRF_CLR_BIT(nr) (BIT(nr+16)) + +#define RK3288_GRF_SOC_CON1 0x0248 +#define RK3288_GRF_SOC_CON3 0x0250 +#define RK3288_GRF_GPIO3D_E 0x01ec +#define RK3288_GRF_GPIO4A_E 0x01f0 +#define RK3288_GRF_GPIO4B_E 0x01f4 + +/*RK3288_GRF_SOC_CON1*/ +#define GMAC_PHY_INTF_SEL_RGMII (GRF_BIT(6) | GRF_CLR_BIT(7) | GRF_CLR_BIT(8)) +#define GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(6) | GRF_CLR_BIT(7) | GRF_BIT(8)) +#define GMAC_FLOW_CTRL GRF_BIT(9) +#define GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(9) +#define GMAC_SPEED_10M GRF_CLR_BIT(10) +#define GMAC_SPEED_100M GRF_BIT(10) +#define GMAC_RMII_CLK_25M GRF_BIT(11) +#define GMAC_RMII_CLK_2_5M GRF_CLR_BIT(11) +#define GMAC_CLK_125M (GRF_CLR_BIT(12) | GRF_CLR_BIT(13)) +#define GMAC_CLK_25M (GRF_BIT(12) | GRF_BIT(13)) +#define GMAC_CLK_2_5M (GRF_CLR_BIT(12) | GRF_BIT(13)) +#define GMAC_RMII_MODE GRF_BIT(14) +#define GMAC_RMII_MODE_CLR GRF_CLR_BIT(14) + +/*RK3288_GRF_SOC_CON3*/ +#define GMAC_TXCLK_DLY_ENABLE GRF_BIT(14) +#define GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(14) +#define GMAC_RXCLK_DLY_ENABLE GRF_BIT(15) +#define GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15) +#define GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 7) +#define GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) + +static void set_to_rgmii(struct rk_priv_data *bsp_priv, + int tx_delay, int rx_delay) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); + return; + } + + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, + GMAC_PHY_INTF_SEL_RGMII | GMAC_RMII_MODE_CLR); + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3, + GMAC_RXCLK_DLY_ENABLE | GMAC_TXCLK_DLY_ENABLE | + GMAC_CLK_RX_DL_CFG(rx_delay) | + GMAC_CLK_TX_DL_CFG(tx_delay)); +} + +static void set_to_rmii(struct rk_priv_data *bsp_priv) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); + return; + } + + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, + GMAC_PHY_INTF_SEL_RMII | GMAC_RMII_MODE); +} + +static void set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); + return; + } + + if (speed == 10) + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_2_5M); + else if (speed == 100) + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_25M); + else if (speed == 1000) + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_125M); + else + dev_err(dev, "unknown speed value for RGMII! speed=%d", speed); +} + +static void set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); + return; + } + + if (speed == 10) { + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, + GMAC_RMII_CLK_2_5M | GMAC_SPEED_10M); + } else if (speed == 100) { + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, + GMAC_RMII_CLK_25M | GMAC_SPEED_100M); + } else { + dev_err(dev, "unknown speed value for RMII! speed=%d", speed); + } +} + +static int gmac_clk_init(struct rk_priv_data *bsp_priv) +{ + struct device *dev = &bsp_priv->pdev->dev; + + bsp_priv->clk_enabled = false; + + bsp_priv->mac_clk_rx = devm_clk_get(dev, "mac_clk_rx"); + if (IS_ERR(bsp_priv->mac_clk_rx)) + dev_err(dev, "%s: cannot get clock %s\n", + __func__, "mac_clk_rx"); + + bsp_priv->mac_clk_tx = devm_clk_get(dev, "mac_clk_tx"); + if (IS_ERR(bsp_priv->mac_clk_tx)) + dev_err(dev, "%s: cannot get clock %s\n", + __func__, "mac_clk_tx"); + + bsp_priv->aclk_mac = devm_clk_get(dev, "aclk_mac"); + if (IS_ERR(bsp_priv->aclk_mac)) + dev_err(dev, "%s: cannot get clock %s\n", + __func__, "aclk_mac"); + + bsp_priv->pclk_mac = devm_clk_get(dev, "pclk_mac"); + if (IS_ERR(bsp_priv->pclk_mac)) + dev_err(dev, "%s: cannot get clock %s\n", + __func__, "pclk_mac"); + + bsp_priv->clk_mac = devm_clk_get(dev, "stmmaceth"); + if (IS_ERR(bsp_priv->clk_mac)) + dev_err(dev, "%s: cannot get clock %s\n", + __func__, "stmmaceth"); + + if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) { + bsp_priv->clk_mac_ref = devm_clk_get(dev, "clk_mac_ref"); + if (IS_ERR(bsp_priv->clk_mac_ref)) + dev_err(dev, "%s: cannot get clock %s\n", + __func__, "clk_mac_ref"); + + if (!bsp_priv->clock_input) { + bsp_priv->clk_mac_refout = + devm_clk_get(dev, "clk_mac_refout"); + if (IS_ERR(bsp_priv->clk_mac_refout)) + dev_err(dev, "%s: cannot get clock %s\n", + __func__, "clk_mac_refout"); + } + } + + if (bsp_priv->clock_input) { + dev_info(dev, "%s: clock input from PHY\n", __func__); + } else { + if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) + clk_set_rate(bsp_priv->clk_mac_pll, 50000000); + } + + return 0; +} + +static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable) +{ + int phy_iface = phy_iface = bsp_priv->phy_iface; + + if (enable) { + if (!bsp_priv->clk_enabled) { + if (phy_iface == PHY_INTERFACE_MODE_RMII) { + if (!IS_ERR(bsp_priv->mac_clk_rx)) + clk_prepare_enable( + bsp_priv->mac_clk_rx); + + if (!IS_ERR(bsp_priv->clk_mac_ref)) + clk_prepare_enable( + bsp_priv->clk_mac_ref); + + if (!IS_ERR(bsp_priv->clk_mac_refout)) + clk_prepare_enable( + bsp_priv->clk_mac_refout); + } + + if (!IS_ERR(bsp_priv->aclk_mac)) + clk_prepare_enable(bsp_priv->aclk_mac); + + if (!IS_ERR(bsp_priv->pclk_mac)) + clk_prepare_enable(bsp_priv->pclk_mac); + + if (!IS_ERR(bsp_priv->mac_clk_tx)) + clk_prepare_enable(bsp_priv->mac_clk_tx); + + /** + * if (!IS_ERR(bsp_priv->clk_mac)) + * clk_prepare_enable(bsp_priv->clk_mac); + */ + mdelay(5); + bsp_priv->clk_enabled = true; + } + } else { + if (bsp_priv->clk_enabled) { + if (phy_iface == PHY_INTERFACE_MODE_RMII) { + if (!IS_ERR(bsp_priv->mac_clk_rx)) + clk_disable_unprepare( + bsp_priv->mac_clk_rx); + + if (!IS_ERR(bsp_priv->clk_mac_ref)) + clk_disable_unprepare( + bsp_priv->clk_mac_ref); + + if (!IS_ERR(bsp_priv->clk_mac_refout)) + clk_disable_unprepare( + bsp_priv->clk_mac_refout); + } + + if (!IS_ERR(bsp_priv->aclk_mac)) + clk_disable_unprepare(bsp_priv->aclk_mac); + + if (!IS_ERR(bsp_priv->pclk_mac)) + clk_disable_unprepare(bsp_priv->pclk_mac); + + if (!IS_ERR(bsp_priv->mac_clk_tx)) + clk_disable_unprepare(bsp_priv->mac_clk_tx); + /** + * if (!IS_ERR(bsp_priv->clk_mac)) + * clk_disable_unprepare(bsp_priv->clk_mac); + */ + bsp_priv->clk_enabled = false; + } + } + + return 0; +} + +static int phy_power_on(struct rk_priv_data *bsp_priv, bool enable) +{ + struct regulator *ldo; + char *ldostr = bsp_priv->regulator; + int ret; + struct device *dev = &bsp_priv->pdev->dev; + + if (!ldostr) { + dev_err(dev, "%s: no ldo found\n", __func__); + return -1; + } + + ldo = regulator_get(NULL, ldostr); + if (!ldo) { + dev_err(dev, "\n%s get ldo %s failed\n", __func__, ldostr); + } else { + if (enable) { + if (!regulator_is_enabled(ldo)) { + regulator_set_voltage(ldo, 3300000, 3300000); + ret = regulator_enable(ldo); + if (ret != 0) + dev_err(dev, "%s: fail to enable %s\n", + __func__, ldostr); + else + dev_info(dev, "turn on ldo done.\n"); + } else { + dev_warn(dev, "%s is enabled before enable", + ldostr); + } + } else { + if (regulator_is_enabled(ldo)) { + ret = regulator_disable(ldo); + if (ret != 0) + dev_err(dev, "%s: fail to disable %s\n", + __func__, ldostr); + else + dev_info(dev, "turn off ldo done.\n"); + } else { + dev_warn(dev, "%s is disabled before disable", + ldostr); + } + } + regulator_put(ldo); + } + + return 0; +} + +static void *rk_gmac_setup(struct platform_device *pdev) +{ + struct rk_priv_data *bsp_priv; + struct device *dev = &pdev->dev; + int ret; + const char *strings = NULL; + int value; + + bsp_priv = devm_kzalloc(dev, sizeof(*bsp_priv), GFP_KERNEL); + if (!bsp_priv) + return ERR_PTR(-ENOMEM); + + bsp_priv->phy_iface = of_get_phy_mode(dev->of_node); + + ret = of_property_read_string(dev->of_node, "phy_regulator", &strings); + if (ret) { + dev_warn(dev, "%s: Can not read property: phy_regulator.\n", + __func__); + } else { + dev_info(dev, "%s: PHY power controlled by regulator(%s).\n", + __func__, strings); + strcpy(bsp_priv->regulator, strings); + } + + ret = of_property_read_string(dev->of_node, "clock_in_out", &strings); + if (ret) { + dev_err(dev, "%s: Can not read property: clock_in_out.\n", + __func__); + bsp_priv->clock_input = true; + } else { + dev_info(dev, "%s: clock input or output? (%s).\n", + __func__, strings); + if (!strcmp(strings, "input")) + bsp_priv->clock_input = true; + else + bsp_priv->clock_input = false; + } + + ret = of_property_read_u32(dev->of_node, "tx_delay", &value); + if (ret) { + bsp_priv->tx_delay = 0x30; + dev_err(dev, "%s: Can not read property: tx_delay.", __func__); + dev_err(dev, "%s: set tx_delay to 0x%x\n", + __func__, bsp_priv->tx_delay); + } else { + dev_info(dev, "%s: TX delay(0x%x).\n", __func__, value); + bsp_priv->tx_delay = value; + } + + ret = of_property_read_u32(dev->of_node, "rx_delay", &value); + if (ret) { + bsp_priv->rx_delay = 0x10; + dev_err(dev, "%s: Can not read property: rx_delay.", __func__); + dev_err(dev, "%s: set rx_delay to 0x%x\n", + __func__, bsp_priv->rx_delay); + } else { + dev_info(dev, "%s: RX delay(0x%x).\n", __func__, value); + bsp_priv->rx_delay = value; + } + + bsp_priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node, + "rockchip,grf"); + bsp_priv->pdev = pdev; + + /*rmii or rgmii*/ + if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII) { + dev_info(dev, "%s: init for RGMII\n", __func__); + set_to_rgmii(bsp_priv, bsp_priv->tx_delay, bsp_priv->rx_delay); + } else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) { + dev_info(dev, "%s: init for RMII\n", __func__); + set_to_rmii(bsp_priv); + } else { + dev_err(dev, "%s: NO interface defined!\n", __func__); + } + + gmac_clk_init(bsp_priv); + + return bsp_priv; +} + +static int rk_gmac_init(struct platform_device *pdev, void *priv) +{ + struct rk_priv_data *bsp_priv = priv; + int ret; + + ret = phy_power_on(bsp_priv, true); + if (ret) + return ret; + + ret = gmac_clk_enable(bsp_priv, true); + if (ret) + return ret; + + return 0; +} + +static void rk_gmac_exit(struct platform_device *pdev, void *priv) +{ + struct rk_priv_data *gmac = priv; + + phy_power_on(gmac, false); + gmac_clk_enable(gmac, false); +} + +static void rk_fix_speed(void *priv, unsigned int speed) +{ + struct rk_priv_data *bsp_priv = priv; + struct device *dev = &bsp_priv->pdev->dev; + + if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII) + set_rgmii_speed(bsp_priv, speed); + else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) + set_rmii_speed(bsp_priv, speed); + else + dev_err(dev, "unsupported interface %d", bsp_priv->phy_iface); +} + +const struct stmmac_of_data rk3288_gmac_data = { + .has_gmac = 1, + .fix_mac_speed = rk_fix_speed, + .setup = rk_gmac_setup, + .init = rk_gmac_init, + .exit = rk_gmac_exit, +}; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 3039de2..879e29f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -33,6 +33,7 @@ static const struct of_device_id stmmac_dt_ids[] = { /* SoC specific glue layers should come before generic bindings */ + { .compatible = "rockchip,rk3288-gmac", .data = &rk3288_gmac_data}, { .compatible = "amlogic,meson6-dwmac", .data = &meson6_dwmac_data}, { .compatible = "allwinner,sun7i-a20-gmac", .data = &sun7i_gmac_data}, { .compatible = "st,stih415-dwmac", .data = &stih4xx_dwmac_data}, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h index 25dd1f7..093eb99 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h @@ -24,5 +24,6 @@ extern const struct stmmac_of_data sun7i_gmac_data; extern const struct stmmac_of_data stih4xx_dwmac_data; extern const struct stmmac_of_data stid127_dwmac_data; extern const struct stmmac_of_data socfpga_gmac_data; +extern const struct stmmac_of_data rk3288_gmac_data; #endif /* __STMMAC_PLATFORM_H__ */ -- cgit v0.10.2 From 3cf8e53a48f67ccdbc527860e852eef135971d98 Mon Sep 17 00:00:00 2001 From: Roger Chen Date: Mon, 29 Dec 2014 17:43:55 +0800 Subject: GMAC: define clock ID used for GMAC changes since v2: 1. remove SCLK_MAC_PLL Signed-off-by: Roger Chen Signed-off-by: David S. Miller diff --git a/include/dt-bindings/clock/rk3288-cru.h b/include/dt-bindings/clock/rk3288-cru.h index f60ce72..1c34c24 100644 --- a/include/dt-bindings/clock/rk3288-cru.h +++ b/include/dt-bindings/clock/rk3288-cru.h @@ -81,6 +81,9 @@ #define SCLK_SDIO1_SAMPLE 120 #define SCLK_EMMC_SAMPLE 121 +#define SCLK_MAC 151 +#define SCLK_MACREF_OUT 152 + #define DCLK_VOP0 190 #define DCLK_VOP1 191 -- cgit v0.10.2 From 7f186025c7d1032c3e8e14af9e8e635ee1c22d46 Mon Sep 17 00:00:00 2001 From: Roger Chen Date: Mon, 29 Dec 2014 17:44:07 +0800 Subject: GMAC: modify CRU config for Rockchip RK3288 SoCs integrated GMAC modify CRU config for GMAC driver changes since v2: 1. remove SCLK_MAC_PLL Signed-off-by: Roger Chen Signed-off-by: David S. Miller diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index ac6be7c..40d267f 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c @@ -190,7 +190,7 @@ PNAME(mux_uart2_p) = { "uart2_src", "uart2_frac", "xin24m" }; PNAME(mux_uart3_p) = { "uart3_src", "uart3_frac", "xin24m" }; PNAME(mux_uart4_p) = { "uart4_src", "uart4_frac", "xin24m" }; PNAME(mux_cif_out_p) = { "cif_src", "xin24m" }; -PNAME(mux_macref_p) = { "mac_src", "ext_gmac" }; +PNAME(mux_mac_p) = { "mac_pll_src", "ext_gmac" }; PNAME(mux_hsadcout_p) = { "hsadc_src", "ext_hsadc" }; PNAME(mux_edp_24m_p) = { "ext_edp_24m", "xin24m" }; PNAME(mux_tspout_p) = { "cpll", "gpll", "npll", "xin27m" }; @@ -575,18 +575,18 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { MUX(SCLK_UART4, "sclk_uart4", mux_uart4_p, 0, RK3288_CLKSEL_CON(3), 8, 2, MFLAGS), - COMPOSITE(0, "mac_src", mux_pll_src_npll_cpll_gpll_p, 0, + COMPOSITE(0, "mac_pll_src", mux_pll_src_npll_cpll_gpll_p, 0, RK3288_CLKSEL_CON(21), 0, 2, MFLAGS, 8, 5, DFLAGS, RK3288_CLKGATE_CON(2), 5, GFLAGS), - MUX(0, "macref", mux_macref_p, 0, + MUX(SCLK_MAC, "mac_clk", mux_mac_p, 0, RK3288_CLKSEL_CON(21), 4, 1, MFLAGS), - GATE(0, "sclk_macref_out", "macref", 0, + GATE(SCLK_MACREF_OUT, "sclk_macref_out", "mac_clk", 0, RK3288_CLKGATE_CON(5), 3, GFLAGS), - GATE(SCLK_MACREF, "sclk_macref", "macref", 0, + GATE(SCLK_MACREF, "sclk_macref", "mac_clk", 0, RK3288_CLKGATE_CON(5), 2, GFLAGS), - GATE(SCLK_MAC_RX, "sclk_mac_rx", "macref", 0, + GATE(SCLK_MAC_RX, "sclk_mac_rx", "mac_clk", 0, RK3288_CLKGATE_CON(5), 0, GFLAGS), - GATE(SCLK_MAC_TX, "sclk_mac_tx", "macref", 0, + GATE(SCLK_MAC_TX, "sclk_mac_tx", "mac_clk", 0, RK3288_CLKGATE_CON(5), 1, GFLAGS), COMPOSITE(0, "hsadc_src", mux_pll_src_cpll_gpll_p, 0, -- cgit v0.10.2 From 3d3fb74afc9b511013966ec0b1f8fd60d55b886b Mon Sep 17 00:00:00 2001 From: Roger Chen Date: Mon, 29 Dec 2014 17:44:16 +0800 Subject: ARM: dts: rockchip: add gmac info for rk3288 add gmac info in rk3288.dtsi for GMAC driver changes since v2: 1. add drive-strength in the pinctrl settings Signed-off-by: Roger Chen Signed-off-by: David S. Miller diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi index fd19f00..910dcad 100644 --- a/arch/arm/boot/dts/rk3288.dtsi +++ b/arch/arm/boot/dts/rk3288.dtsi @@ -380,6 +380,22 @@ status = "disabled"; }; + gmac: ethernet@ff290000 { + compatible = "rockchip,rk3288-gmac"; + reg = <0xff290000 0x10000>; + interrupts = ; + interrupt-names = "macirq"; + rockchip,grf = <&grf>; + clocks = <&cru SCLK_MAC>, + <&cru SCLK_MAC_RX>, <&cru SCLK_MAC_TX>, + <&cru SCLK_MACREF>, <&cru SCLK_MACREF_OUT>, + <&cru ACLK_GMAC>, <&cru PCLK_GMAC>; + clock-names = "stmmaceth", + "mac_clk_rx", "mac_clk_tx", + "clk_mac_ref", "clk_mac_refout", + "aclk_mac", "pclk_mac"; + }; + usb_host0_ehci: usb@ff500000 { compatible = "generic-ehci"; reg = <0xff500000 0x100>; @@ -725,6 +741,11 @@ bias-disable; }; + pcfg_pull_none_12ma: pcfg-pull-none-12ma { + bias-disable; + drive-strength = <12>; + }; + i2c0 { i2c0_xfer: i2c0-xfer { rockchip,pins = <0 15 RK_FUNC_1 &pcfg_pull_none>, @@ -1068,5 +1089,38 @@ rockchip,pins = <7 23 3 &pcfg_pull_none>; }; }; + + gmac { + rgmii_pins: rgmii-pins { + rockchip,pins = <3 30 3 &pcfg_pull_none>, + <3 31 3 &pcfg_pull_none>, + <3 26 3 &pcfg_pull_none>, + <3 27 3 &pcfg_pull_none>, + <3 28 3 &pcfg_pull_none_12ma>, + <3 29 3 &pcfg_pull_none_12ma>, + <3 24 3 &pcfg_pull_none_12ma>, + <3 25 3 &pcfg_pull_none_12ma>, + <4 0 3 &pcfg_pull_none>, + <4 5 3 &pcfg_pull_none>, + <4 6 3 &pcfg_pull_none>, + <4 9 3 &pcfg_pull_none_12ma>, + <4 4 3 &pcfg_pull_none_12ma>, + <4 1 3 &pcfg_pull_none>, + <4 3 3 &pcfg_pull_none>; + }; + + rmii_pins: rmii-pins { + rockchip,pins = <3 30 3 &pcfg_pull_none>, + <3 31 3 &pcfg_pull_none>, + <3 28 3 &pcfg_pull_none>, + <3 29 3 &pcfg_pull_none>, + <4 0 3 &pcfg_pull_none>, + <4 5 3 &pcfg_pull_none>, + <4 4 3 &pcfg_pull_none>, + <4 1 3 &pcfg_pull_none>, + <4 2 3 &pcfg_pull_none>, + <4 3 3 &pcfg_pull_none>; + }; + }; }; }; -- cgit v0.10.2 From e35e47ac521b69bc9c5c0654463278161ec92d72 Mon Sep 17 00:00:00 2001 From: Roger Chen Date: Mon, 29 Dec 2014 17:44:25 +0800 Subject: ARM: dts: rockchip: enable gmac on RK3288 evb board enable gmac in rk3288-evb-rk808.dts changes since v2: 1. add fixed regulator for PHY 2. remove power-gpio, reset-gpio, phyirq-gpio, pmu_regulator setting 3. add "snps,reset-gpio", "snps,reset-active-low;" "snps,reset-delays-us" Signed-off-by: Roger Chen Signed-off-by: David S. Miller diff --git a/arch/arm/boot/dts/rk3288-evb-rk808.dts b/arch/arm/boot/dts/rk3288-evb-rk808.dts index d8c775e6..831a7aa 100644 --- a/arch/arm/boot/dts/rk3288-evb-rk808.dts +++ b/arch/arm/boot/dts/rk3288-evb-rk808.dts @@ -15,6 +15,13 @@ / { compatible = "rockchip,rk3288-evb-rk808", "rockchip,rk3288"; + + ext_gmac: external-gmac-clock { + compatible = "fixed-clock"; + clock-frequency = <125000000>; + clock-output-names = "ext_gmac"; + #clock-cells = <0>; + }; }; &cpu0 { @@ -152,3 +159,19 @@ }; }; }; + +&gmac { + phy_regulator = "vcc_phy"; + phy-mode = "rgmii"; + clock_in_out = "input"; + snps,reset-gpio = <&gpio4 7 0>; + snps,reset-active-low; + snps,reset-delays-us = <0 10000 1000000>; + assigned-clocks = <&cru SCLK_MAC>; + assigned-clock-parents = <&ext_gmac>; + pinctrl-names = "default"; + pinctrl-0 = <&rgmii_pins>; + tx_delay = <0x30>; + rx_delay = <0x10>; + status = "ok"; +}; diff --git a/arch/arm/boot/dts/rk3288-evb.dtsi b/arch/arm/boot/dts/rk3288-evb.dtsi index 3e067dd..048cb17 100644 --- a/arch/arm/boot/dts/rk3288-evb.dtsi +++ b/arch/arm/boot/dts/rk3288-evb.dtsi @@ -90,6 +90,17 @@ regulator-always-on; regulator-boot-on; }; + + vcc_phy: vcc-phy-regulator { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio0 6 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <ð_phy_pwr>; + regulator-name = "vcc_phy"; + regulator-always-on; + regulator-boot-on; + }; }; &emmc { @@ -178,6 +189,12 @@ rockchip,pins = <0 14 RK_FUNC_GPIO &pcfg_pull_none>; }; }; + + eth_phy { + eth_phy_pwr: eth-phy-pwr { + rockchip,pins = <0 6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; }; &usb_host0_ehci { -- cgit v0.10.2 From 53a8393037b251e9c2af3b11859a923ced88c0c7 Mon Sep 17 00:00:00 2001 From: Roger Chen Date: Mon, 29 Dec 2014 17:44:34 +0800 Subject: GMAC: add document for Rockchip RK3288 GMAC The document descripts how to add properties for GMAC in device tree. change since v2: 1. remove power-gpio, reset-gpio, phyirq-gpio, pmu_regulator setting 2. add "snps,reset-gpio", "snps,reset-active-low;" "snps,reset-delays-us" Signed-off-by: Roger Chen Signed-off-by: David S. Miller diff --git a/Documentation/devicetree/bindings/net/rockchip-dwmac.txt b/Documentation/devicetree/bindings/net/rockchip-dwmac.txt new file mode 100644 index 0000000..2362dcd --- /dev/null +++ b/Documentation/devicetree/bindings/net/rockchip-dwmac.txt @@ -0,0 +1,67 @@ +Rockchip SoC RK3288 10/100/1000 Ethernet driver(GMAC) + +The device node has following properties. + +Required properties: + - compatible: Can be "rockchip,rk3288-gmac". + - reg: addresses and length of the register sets for the device. + - interrupts: Should contain the GMAC interrupts. + - interrupt-names: Should contain the interrupt names "macirq". + - rockchip,grf: phandle to the syscon grf used to control speed and mode. + - clocks: <&cru SCLK_MAC>: clock selector for main clock, from PLL or PHY. + <&cru SCLK_MAC_PLL>: PLL clock for SCLK_MAC + <&cru SCLK_MAC_RX>: clock gate for RX + <&cru SCLK_MAC_TX>: clock gate for TX + <&cru SCLK_MACREF>: clock gate for RMII referce clock + <&cru SCLK_MACREF_OUT> clock gate for RMII reference clock output + <&cru ACLK_GMAC>: AXI clock gate for GMAC + <&cru PCLK_GMAC>: APB clock gate for GMAC + - clock-names: One name for each entry in the clocks property. + - phy-mode: See ethernet.txt file in the same directory. + - pinctrl-names: Names corresponding to the numbered pinctrl states. + - pinctrl-0: pin-control mode. can be <&rgmii_pins> or <&rmii_pins>. + - clock_in_out: For RGMII, it must be "input", means main clock(125MHz) + is not sourced from SoC's PLL, but input from PHY; For RMII, "input" means + PHY provides the reference clock(50MHz), "output" means GMAC provides the + reference clock. + - snps,reset-gpio gpio number for phy reset. + - snps,reset-active-low boolean flag to indicate if phy reset is active low. + - assigned-clocks: main clock, should be <&cru SCLK_MAC>; + - assigned-clock-parents = parent of main clock. + can be <&ext_gmac> or <&cru SCLK_MAC_PLL>. + +Optional properties: + - tx_delay: Delay value for TXD timing. Range value is 0~0x7F, 0x30 as default. + - rx_delay: Delay value for RXD timing. Range value is 0~0x7F, 0x10 as default. + +Example: + +gmac: ethernet@ff290000 { + compatible = "rockchip,rk3288-gmac"; + reg = <0xff290000 0x10000>; + interrupts = ; + interrupt-names = "macirq"; + rockchip,grf = <&grf>; + clocks = <&cru SCLK_MAC>, + <&cru SCLK_MAC_RX>, <&cru SCLK_MAC_TX>, + <&cru SCLK_MACREF>, <&cru SCLK_MACREF_OUT>, + <&cru ACLK_GMAC>, <&cru PCLK_GMAC>; + clock-names = "stmmaceth", + "mac_clk_rx", "mac_clk_tx", + "clk_mac_ref", "clk_mac_refout", + "aclk_mac", "pclk_mac"; + phy-mode = "rgmii"; + pinctrl-names = "default"; + pinctrl-0 = <&rgmii_pins /*&rmii_pins*/>; + + clock_in_out = "input"; + snps,reset-gpio = <&gpio4 7 0>; + snps,reset-active-low; + + assigned-clocks = <&cru SCLK_MAC>; + assigned-clock-parents = <&ext_gmac>; + tx_delay = <0x30>; + rx_delay = <0x10>; + + status = "ok"; +}; -- cgit v0.10.2 From 801a55911432f582c8ab82c895d2821dc02b70e3 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Fri, 2 Jan 2015 06:11:16 +0100 Subject: x86: init_mem_mapping(): use capital BIOS in comment Use capital BIOS in comment. Its cleaner, and allows diference between BIOS and BIOs. Signed-off-by: Pavel Machek Signed-off-by: Jiri Kosina diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 66dba36..452f904 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -582,7 +582,7 @@ void __init init_mem_mapping(void) * * * On x86, access has to be given to the first megabyte of ram because that area - * contains bios code and data regions used by X and dosemu and similar apps. + * contains BIOS code and data regions used by X and dosemu and similar apps. * Access has to be given to non-kernel-ram areas as well, these contain the PCI * mmio resources as well as potential bios/acpi data regions. */ -- cgit v0.10.2 From 9ed03783d37c0566673c790aa1ff3f1d02129514 Mon Sep 17 00:00:00 2001 From: Jeremiah Mahler Date: Sun, 21 Dec 2014 05:30:04 -0800 Subject: doc: usbmon: fix spelling s/unpriviledged/unprivileged/ Signed-off-by: Jeremiah Mahler Signed-off-by: Jiri Kosina diff --git a/Documentation/usb/usbmon.txt b/Documentation/usb/usbmon.txt index c42bb9c..7587d84 100644 --- a/Documentation/usb/usbmon.txt +++ b/Documentation/usb/usbmon.txt @@ -231,7 +231,7 @@ number. Number zero (/dev/usbmon0) is special and means "all buses". Note that specific naming policy is set by your Linux distribution. If you create /dev/usbmon0 by hand, make sure that it is owned by root -and has mode 0600. Otherwise, unpriviledged users will be able to snoop +and has mode 0600. Otherwise, unprivileged users will be able to snoop keyboard traffic. The following ioctl calls are available, with MON_IOC_MAGIC 0x92: -- cgit v0.10.2 From 231821d4c3fa72de8fd9981e608323529ac9ad70 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Mon, 15 Dec 2014 12:04:16 +0900 Subject: dynamic_debug: fix comment Signed-off-by: Masatake YAMATO Signed-off-by: Jiri Kosina diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index dfba055..96bc6a4 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -641,7 +641,7 @@ static __init int ddebug_setup_query(char *str) __setup("ddebug_query=", ddebug_setup_query); /* - * File_ops->write method for /dynamic_debug/conrol. Gathers the + * File_ops->write method for /dynamic_debug/control. Gathers the * command text from userspace, parses and executes it. */ #define USER_BUF_PAGE 4096 -- cgit v0.10.2 From 775c503f65679c46647c9414b218738b7062b215 Mon Sep 17 00:00:00 2001 From: Alexander Kuleshov Date: Sun, 28 Dec 2014 12:44:45 +0600 Subject: mpc85xx_edac: Fix a typo in comments s/kenel/kernel/g [ Boris: massage commit message a bit ] Signed-off-by: Alexander Kuleshov Cc: Johannes Thumshirn Link: http://lkml.kernel.org/r/1419749085-7128-1-git-send-email-kuleshovmail@gmail.com Signed-off-by: Borislav Petkov diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c index ffb1a9a..1fa76a5 100644 --- a/drivers/edac/mpc85xx_edac.c +++ b/drivers/edac/mpc85xx_edac.c @@ -1,5 +1,5 @@ /* - * Freescale MPC85xx Memory Controller kenel module + * Freescale MPC85xx Memory Controller kernel module * * Parts Copyrighted (c) 2013 by Freescale Semiconductor, Inc. * diff --git a/drivers/edac/mpc85xx_edac.h b/drivers/edac/mpc85xx_edac.h index 8c62564..4498baf 100644 --- a/drivers/edac/mpc85xx_edac.h +++ b/drivers/edac/mpc85xx_edac.h @@ -1,5 +1,5 @@ /* - * Freescale MPC85xx Memory Controller kenel module + * Freescale MPC85xx Memory Controller kernel module * Author: Dave Jiang * * 2006-2007 (c) MontaVista Software, Inc. This file is licensed under -- cgit v0.10.2 From e9dd86267f2571a9649399c9e268266ca5aa6152 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:35 +0100 Subject: ALSA: ml403-ac97cr: Remove always NULL parameter snd_ml403_ac97cr_pcm() takes a pointer to a pointer of a PCM where if this parameter is provided the newly allocated PCM is stored. All callers pass NULL though, so remove the parameter. This makes the code a bit cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c index bcca825..ec01de1 100644 --- a/sound/drivers/ml403-ac97cr.c +++ b/sound/drivers/ml403-ac97cr.c @@ -1238,14 +1238,11 @@ snd_ml403_ac97cr_mixer(struct snd_ml403_ac97cr *ml403_ac97cr) } static int -snd_ml403_ac97cr_pcm(struct snd_ml403_ac97cr *ml403_ac97cr, int device, - struct snd_pcm **rpcm) +snd_ml403_ac97cr_pcm(struct snd_ml403_ac97cr *ml403_ac97cr, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; err = snd_pcm_new(ml403_ac97cr->card, "ML403AC97CR/1", device, 1, 1, &pcm); if (err < 0) @@ -1263,8 +1260,6 @@ snd_ml403_ac97cr_pcm(struct snd_ml403_ac97cr *ml403_ac97cr, int device, snd_dma_continuous_data(GFP_KERNEL), 64 * 1024, 128 * 1024); - if (rpcm) - *rpcm = pcm; return 0; } @@ -1298,7 +1293,7 @@ static int snd_ml403_ac97cr_probe(struct platform_device *pfdev) return err; } PDEBUG(INIT_INFO, "probe(): mixer done\n"); - err = snd_ml403_ac97cr_pcm(ml403_ac97cr, 0, NULL); + err = snd_ml403_ac97cr_pcm(ml403_ac97cr, 0); if (err < 0) { snd_card_free(card); return err; -- cgit v0.10.2 From 7f605418837add961466dc66f7370332d44c1a92 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:36 +0100 Subject: ALSA: ad1816a: Remove always NULL parameters snd_ad1816a_pcm() and snd_ad1816a_timer() take a pointer to a pointer of a PCM/timer where if this parameter is provided the newly allocated object is stored. All callers pass NULL though, so remove the parameter. This makes the code a bit cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/include/sound/ad1816a.h b/include/sound/ad1816a.h index abdf609..f2d3a6d 100644 --- a/include/sound/ad1816a.h +++ b/include/sound/ad1816a.h @@ -170,10 +170,9 @@ extern int snd_ad1816a_create(struct snd_card *card, unsigned long port, int irq, int dma1, int dma2, struct snd_ad1816a *chip); -extern int snd_ad1816a_pcm(struct snd_ad1816a *chip, int device, struct snd_pcm **rpcm); +extern int snd_ad1816a_pcm(struct snd_ad1816a *chip, int device); extern int snd_ad1816a_mixer(struct snd_ad1816a *chip); -extern int snd_ad1816a_timer(struct snd_ad1816a *chip, int device, - struct snd_timer **rtimer); +extern int snd_ad1816a_timer(struct snd_ad1816a *chip, int device); #ifdef CONFIG_PM extern void snd_ad1816a_suspend(struct snd_ad1816a *chip); extern void snd_ad1816a_resume(struct snd_ad1816a *chip); diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c index f481a41..7692265 100644 --- a/sound/isa/ad1816a/ad1816a.c +++ b/sound/isa/ad1816a/ad1816a.c @@ -142,7 +142,6 @@ static int snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard, struct snd_card *card; struct snd_ad1816a *chip; struct snd_opl3 *opl3; - struct snd_timer *timer; error = snd_card_new(&pcard->card->dev, index[dev], id[dev], THIS_MODULE, @@ -172,7 +171,7 @@ static int snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard, sprintf(card->longname, "%s, SS at 0x%lx, irq %d, dma %d&%d", card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]); - if ((error = snd_ad1816a_pcm(chip, 0, NULL)) < 0) { + if ((error = snd_ad1816a_pcm(chip, 0)) < 0) { snd_card_free(card); return error; } @@ -182,7 +181,7 @@ static int snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard, return error; } - error = snd_ad1816a_timer(chip, 0, &timer); + error = snd_ad1816a_timer(chip, 0); if (error < 0) { snd_card_free(card); return error; diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c index 01a0798..5f99102 100644 --- a/sound/isa/ad1816a/ad1816a_lib.c +++ b/sound/isa/ad1816a/ad1816a_lib.c @@ -675,7 +675,7 @@ static struct snd_pcm_ops snd_ad1816a_capture_ops = { .pointer = snd_ad1816a_capture_pointer, }; -int snd_ad1816a_pcm(struct snd_ad1816a *chip, int device, struct snd_pcm **rpcm) +int snd_ad1816a_pcm(struct snd_ad1816a *chip, int device) { int error; struct snd_pcm *pcm; @@ -697,13 +697,10 @@ int snd_ad1816a_pcm(struct snd_ad1816a *chip, int device, struct snd_pcm **rpcm) 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); chip->pcm = pcm; - if (rpcm) - *rpcm = pcm; return 0; } -int snd_ad1816a_timer(struct snd_ad1816a *chip, int device, - struct snd_timer **rtimer) +int snd_ad1816a_timer(struct snd_ad1816a *chip, int device) { struct snd_timer *timer; struct snd_timer_id tid; @@ -720,8 +717,6 @@ int snd_ad1816a_timer(struct snd_ad1816a *chip, int device, timer->private_data = chip; chip->timer = timer; timer->hw = snd_ad1816a_timer_table; - if (rtimer) - *rtimer = timer; return 0; } -- cgit v0.10.2 From 4b8ab88518726197c8659bc9aca96c6af01dab57 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:37 +0100 Subject: ALSA: es1688: Remove almost always NULL parameter snd_es1688_pcm() takes a pointer to a pointer of a PCM where if this parameter is provided the newly allocated PCM is stored. This PCM is also available from the pcm field of the snd_es1688 struct that got passed to the same function. This patch updates all callers which passed a pointer to use that field instead and then removes the parameter from the function. This makes the code a bit shorter and cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/include/sound/es1688.h b/include/sound/es1688.h index 1d636a2..b34f23a 100644 --- a/include/sound/es1688.h +++ b/include/sound/es1688.h @@ -115,8 +115,7 @@ int snd_es1688_create(struct snd_card *card, int mpu_irq, int dma8, unsigned short hardware); -int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, int device, - struct snd_pcm **rpcm); +int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, int device); int snd_es1688_mixer(struct snd_card *card, struct snd_es1688 *chip); int snd_es1688_reset(struct snd_es1688 *chip); diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c index 76001fe0..1901c2b 100644 --- a/sound/isa/es1688/es1688.c +++ b/sound/isa/es1688/es1688.c @@ -138,10 +138,9 @@ static int snd_es1688_probe(struct snd_card *card, unsigned int n) { struct snd_es1688 *chip = card->private_data; struct snd_opl3 *opl3; - struct snd_pcm *pcm; int error; - error = snd_es1688_pcm(card, chip, 0, &pcm); + error = snd_es1688_pcm(card, chip, 0); if (error < 0) return error; @@ -150,9 +149,9 @@ static int snd_es1688_probe(struct snd_card *card, unsigned int n) return error; strlcpy(card->driver, "ES1688", sizeof(card->driver)); - strlcpy(card->shortname, pcm->name, sizeof(card->shortname)); + strlcpy(card->shortname, chip->pcm->name, sizeof(card->shortname)); snprintf(card->longname, sizeof(card->longname), - "%s at 0x%lx, irq %i, dma %i", pcm->name, chip->port, + "%s at 0x%lx, irq %i, dma %i", chip->pcm->name, chip->port, chip->irq, chip->dma8); if (fm_port[n] == SNDRV_AUTO_PORT) diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c index b545014..52aac84 100644 --- a/sound/isa/es1688/es1688_lib.c +++ b/sound/isa/es1688/es1688_lib.c @@ -728,8 +728,7 @@ static struct snd_pcm_ops snd_es1688_capture_ops = { .pointer = snd_es1688_capture_pointer, }; -int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, - int device, struct snd_pcm **rpcm) +int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, int device) { struct snd_pcm *pcm; int err; @@ -749,9 +748,6 @@ int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(), 64*1024, 64*1024); - - if (rpcm) - *rpcm = pcm; return 0; } diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c index 28a1693..acc65528 100644 --- a/sound/isa/gus/gusextreme.c +++ b/sound/isa/gus/gusextreme.c @@ -284,7 +284,7 @@ static int snd_gusextreme_probe(struct device *dev, unsigned int n) } gus->codec_flag = 1; - error = snd_es1688_pcm(card, es1688, 0, NULL); + error = snd_es1688_pcm(card, es1688, 0); if (error < 0) goto out; -- cgit v0.10.2 From 1a2515ac94bbb1c409dea2e4a7b011592569b239 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:38 +0100 Subject: ALSA: es18xx: Remove always NULL parameter snd_es18xx_pcm() takes a pointer to a pointer of a PCM where if this parameter is provided the newly allocated PCM is stored. All callers pass NULL though, so remove the parameter. This makes the code a bit cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c index b481bb8..6cc2d2b 100644 --- a/sound/isa/es18xx.c +++ b/sound/isa/es18xx.c @@ -1687,16 +1687,13 @@ static struct snd_pcm_ops snd_es18xx_capture_ops = { .pointer = snd_es18xx_capture_pointer, }; -static int snd_es18xx_pcm(struct snd_card *card, int device, - struct snd_pcm **rpcm) +static int snd_es18xx_pcm(struct snd_card *card, int device) { struct snd_es18xx *chip = card->private_data; struct snd_pcm *pcm; char str[16]; int err; - if (rpcm) - *rpcm = NULL; sprintf(str, "ES%x", chip->version); if (chip->caps & ES18XX_PCM2) err = snd_pcm_new(card, str, device, 2, 1, &pcm); @@ -1722,9 +1719,6 @@ static int snd_es18xx_pcm(struct snd_card *card, int device, snd_dma_isa_data(), 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); - - if (rpcm) - *rpcm = pcm; return 0; } @@ -2154,7 +2148,7 @@ static int snd_audiodrive_probe(struct snd_card *card, int dev) chip->port, irq[dev], dma1[dev]); - err = snd_es18xx_pcm(card, 0, NULL); + err = snd_es18xx_pcm(card, 0); if (err < 0) return err; -- cgit v0.10.2 From db5abb3c499e553a8afedee1417d6a6308dda7bd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:39 +0100 Subject: ALSA: gus: Remove always NULL parameters snd_gf1_pcm_new() and snd_gf1_rawmidi_new() take a pointer to a pointer of a PCM/MIDI where if this parameter is provided the newly allocated object is stored. All callers pass NULL though, so remove the parameter. This makes the code a bit cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/include/sound/gus.h b/include/sound/gus.h index 42905d8..06bbdc2 100644 --- a/include/sound/gus.h +++ b/include/sound/gus.h @@ -591,7 +591,7 @@ int snd_gf1_new_mixer(struct snd_gus_card * gus); /* gus_pcm.c */ -int snd_gf1_pcm_new(struct snd_gus_card * gus, int pcm_dev, int control_index, struct snd_pcm ** rpcm); +int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index); #ifdef CONFIG_SND_DEBUG extern void snd_gf1_print_voice_registers(struct snd_gus_card * gus); @@ -620,7 +620,7 @@ void snd_gus_irq_profile_init(struct snd_gus_card *gus); /* gus_uart.c */ -int snd_gf1_rawmidi_new(struct snd_gus_card * gus, int device, struct snd_rawmidi **rrawmidi); +int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device); /* gus_dram.c */ int snd_gus_dram_write(struct snd_gus_card *gus, char __user *ptr, diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c index 2dcf45b..25f6788 100644 --- a/sound/isa/gus/gus_pcm.c +++ b/sound/isa/gus/gus_pcm.c @@ -849,7 +849,7 @@ static struct snd_pcm_ops snd_gf1_pcm_capture_ops = { .pointer = snd_gf1_pcm_capture_pointer, }; -int snd_gf1_pcm_new(struct snd_gus_card * gus, int pcm_dev, int control_index, struct snd_pcm ** rpcm) +int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index) { struct snd_card *card; struct snd_kcontrol *kctl; @@ -857,8 +857,6 @@ int snd_gf1_pcm_new(struct snd_gus_card * gus, int pcm_dev, int control_index, s struct snd_pcm_substream *substream; int capture, err; - if (rpcm) - *rpcm = NULL; card = gus->card; capture = !gus->interwave && !gus->ess_flag && !gus->ace_flag ? 1 : 0; err = snd_pcm_new(card, @@ -903,8 +901,6 @@ int snd_gf1_pcm_new(struct snd_gus_card * gus, int pcm_dev, int control_index, s return err; kctl->id.index = control_index; - if (rpcm) - *rpcm = pcm; return 0; } diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c index 21cc42e..3992912 100644 --- a/sound/isa/gus/gus_uart.c +++ b/sound/isa/gus/gus_uart.c @@ -241,13 +241,11 @@ static struct snd_rawmidi_ops snd_gf1_uart_input = .trigger = snd_gf1_uart_input_trigger, }; -int snd_gf1_rawmidi_new(struct snd_gus_card * gus, int device, struct snd_rawmidi ** rrawmidi) +int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device) { struct snd_rawmidi *rmidi; int err; - if (rrawmidi) - *rrawmidi = NULL; if ((err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi)) < 0) return err; strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1"); @@ -256,7 +254,5 @@ int snd_gf1_rawmidi_new(struct snd_gus_card * gus, int device, struct snd_rawmid rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; rmidi->private_data = gus; gus->midi_uart = rmidi; - if (rrawmidi) - *rrawmidi = rmidi; return err; } diff --git a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c index 7ce29ff..f001971 100644 --- a/sound/isa/gus/gusclassic.c +++ b/sound/isa/gus/gusclassic.c @@ -181,12 +181,12 @@ static int snd_gusclassic_probe(struct device *dev, unsigned int n) if (error < 0) goto out; - error = snd_gf1_pcm_new(gus, 0, 0, NULL); + error = snd_gf1_pcm_new(gus, 0, 0); if (error < 0) goto out; if (!gus->ace_flag) { - error = snd_gf1_rawmidi_new(gus, 0, NULL); + error = snd_gf1_rawmidi_new(gus, 0); if (error < 0) goto out; } diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c index acc65528..693d95f 100644 --- a/sound/isa/gus/gusextreme.c +++ b/sound/isa/gus/gusextreme.c @@ -295,7 +295,7 @@ static int snd_gusextreme_probe(struct device *dev, unsigned int n) snd_component_add(card, "ES1688"); if (pcm_channels[n] > 0) { - error = snd_gf1_pcm_new(gus, 1, 1, NULL); + error = snd_gf1_pcm_new(gus, 1, 1); if (error < 0) goto out; } diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c index 39df36c..b2b3734 100644 --- a/sound/isa/gus/gusmax.c +++ b/sound/isa/gus/gusmax.c @@ -322,14 +322,14 @@ static int snd_gusmax_probe(struct device *pdev, unsigned int dev) goto _err; if (pcm_channels[dev] > 0) { - if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) + if ((err = snd_gf1_pcm_new(gus, 1, 1)) < 0) goto _err; } err = snd_gusmax_mixer(wss); if (err < 0) goto _err; - err = snd_gf1_rawmidi_new(gus, 0, NULL); + err = snd_gf1_rawmidi_new(gus, 0); if (err < 0) goto _err; diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c index ad55e5c..8ea54a7 100644 --- a/sound/isa/gus/interwave.c +++ b/sound/isa/gus/interwave.c @@ -711,7 +711,7 @@ static int snd_interwave_probe(struct snd_card *card, int dev) return err; if (pcm_channels[dev] > 0) { - err = snd_gf1_pcm_new(gus, 1, 1, NULL); + err = snd_gf1_pcm_new(gus, 1, 1); if (err < 0) return err; } @@ -740,7 +740,7 @@ static int snd_interwave_probe(struct snd_card *card, int dev) #endif gus->uart_enable = midi[dev]; - if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) + if ((err = snd_gf1_rawmidi_new(gus, 0)) < 0) return err; #ifndef SNDRV_STB -- cgit v0.10.2 From f6be4e624aa26699a9a89bc24089e88a227141c1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:40 +0100 Subject: ALSA: msnd: Remove always NULL parameter snd_msnd_pcm() takes a pointer to a pointer of a PCM where if this parameter is provided the newly allocated PCM is stored. All callers pass NULL though, so remove the parameter. This makes the code a bit cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/isa/msnd/msnd.c b/sound/isa/msnd/msnd.c index 1cee18f..835d4aa 100644 --- a/sound/isa/msnd/msnd.c +++ b/sound/isa/msnd/msnd.c @@ -679,8 +679,7 @@ static struct snd_pcm_ops snd_msnd_capture_ops = { }; -int snd_msnd_pcm(struct snd_card *card, int device, - struct snd_pcm **rpcm) +int snd_msnd_pcm(struct snd_card *card, int device) { struct snd_msnd *chip = card->private_data; struct snd_pcm *pcm; @@ -696,9 +695,6 @@ int snd_msnd_pcm(struct snd_card *card, int device, pcm->private_data = chip; strcpy(pcm->name, "Hurricane"); - - if (rpcm) - *rpcm = pcm; return 0; } EXPORT_SYMBOL(snd_msnd_pcm); diff --git a/sound/isa/msnd/msnd.h b/sound/isa/msnd/msnd.h index dbac3a4..5f3c7dc 100644 --- a/sound/isa/msnd/msnd.h +++ b/sound/isa/msnd/msnd.h @@ -297,7 +297,7 @@ int snd_msnd_disable_irq(struct snd_msnd *chip); void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file); int snd_msnd_DAPQ(struct snd_msnd *chip, int start); int snd_msnd_DARQ(struct snd_msnd *chip, int start); -int snd_msnd_pcm(struct snd_card *card, int device, struct snd_pcm **rpcm); +int snd_msnd_pcm(struct snd_card *card, int device); int snd_msndmidi_new(struct snd_card *card, int device); void snd_msndmidi_input_read(void *mpu); diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c index 5016bf9..65b3682 100644 --- a/sound/isa/msnd/msnd_pinnacle.c +++ b/sound/isa/msnd/msnd_pinnacle.c @@ -582,7 +582,7 @@ static int snd_msnd_attach(struct snd_card *card) if (err < 0) goto err_release_region; - err = snd_msnd_pcm(card, 0, NULL); + err = snd_msnd_pcm(card, 0); if (err < 0) { printk(KERN_ERR LOGNAME ": error creating new PCM device\n"); goto err_release_region; -- cgit v0.10.2 From 8c77629996cbabee1d5b6d9b0d1e97cbca197ba1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:41 +0100 Subject: ALSA: sb8: Remove always NULL parameters snd_sb8dsp_pcm() and snd_sb8dsp_midi() take a pointer to a pointer of a PCM/MIDI where if this parameter is provided the newly allocated object is stored. All callers pass NULL though, so remove the parameter. This makes the code a bit cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/include/sound/sb.h b/include/sound/sb.h index ba39603..13438ff 100644 --- a/include/sound/sb.h +++ b/include/sound/sb.h @@ -308,7 +308,7 @@ void snd_sbmixer_resume(struct snd_sb *chip); #endif /* sb8_init.c */ -int snd_sb8dsp_pcm(struct snd_sb *chip, int device, struct snd_pcm ** rpcm); +int snd_sb8dsp_pcm(struct snd_sb *chip, int device); /* sb8.c */ irqreturn_t snd_sb8dsp_interrupt(struct snd_sb *chip); int snd_sb8_playback_open(struct snd_pcm_substream *substream); @@ -317,7 +317,7 @@ int snd_sb8_playback_close(struct snd_pcm_substream *substream); int snd_sb8_capture_close(struct snd_pcm_substream *substream); /* midi8.c */ irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip); -int snd_sb8dsp_midi(struct snd_sb *chip, int device, struct snd_rawmidi ** rrawmidi); +int snd_sb8dsp_midi(struct snd_sb *chip, int device); /* sb16_init.c */ int snd_sb16dsp_pcm(struct snd_sb *chip, int device, struct snd_pcm ** rpcm); diff --git a/sound/isa/sb/jazz16.c b/sound/isa/sb/jazz16.c index 90d2eba..6b4884d 100644 --- a/sound/isa/sb/jazz16.c +++ b/sound/isa/sb/jazz16.c @@ -297,7 +297,7 @@ static int snd_jazz16_probe(struct device *devptr, unsigned int dev) "Media Vision Jazz16 at 0x%lx, irq %d, dma8 %d, dma16 %d", port[dev], xirq, xdma8, xdma16); - err = snd_sb8dsp_pcm(chip, 0, NULL); + err = snd_sb8dsp_pcm(chip, 0); if (err < 0) goto err_free; err = snd_sbmixer_new(chip); diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c index 6c32b3a..b8e2391 100644 --- a/sound/isa/sb/sb8.c +++ b/sound/isa/sb/sb8.c @@ -157,7 +157,7 @@ static int snd_sb8_probe(struct device *pdev, unsigned int dev) goto _err; } - if ((err = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) + if ((err = snd_sb8dsp_pcm(chip, 0)) < 0) goto _err; if ((err = snd_sbmixer_new(chip)) < 0) @@ -182,7 +182,7 @@ static int snd_sb8_probe(struct device *pdev, unsigned int dev) goto _err; } - if ((err = snd_sb8dsp_midi(chip, 0, NULL)) < 0) + if ((err = snd_sb8dsp_midi(chip, 0)) < 0) goto _err; strcpy(card->driver, chip->hardware == SB_HW_PRO ? "SB Pro" : "SB8"); diff --git a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c index 24d4121..d4d8b0e 100644 --- a/sound/isa/sb/sb8_main.c +++ b/sound/isa/sb/sb8_main.c @@ -594,15 +594,13 @@ static struct snd_pcm_ops snd_sb8_capture_ops = { .pointer = snd_sb8_capture_pointer, }; -int snd_sb8dsp_pcm(struct snd_sb *chip, int device, struct snd_pcm ** rpcm) +int snd_sb8dsp_pcm(struct snd_sb *chip, int device) { struct snd_card *card = chip->card; struct snd_pcm *pcm; int err; size_t max_prealloc = 64 * 1024; - if (rpcm) - *rpcm = NULL; if ((err = snd_pcm_new(card, "SB8 DSP", device, 1, 1, &pcm)) < 0) return err; sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff); @@ -618,8 +616,6 @@ int snd_sb8dsp_pcm(struct snd_sb *chip, int device, struct snd_pcm ** rpcm) snd_dma_isa_data(), 64*1024, max_prealloc); - if (rpcm) - *rpcm = pcm; return 0; } diff --git a/sound/isa/sb/sb8_midi.c b/sound/isa/sb/sb8_midi.c index 988a8b7..ffc71f0 100644 --- a/sound/isa/sb/sb8_midi.c +++ b/sound/isa/sb/sb8_midi.c @@ -263,13 +263,11 @@ static struct snd_rawmidi_ops snd_sb8dsp_midi_input = .trigger = snd_sb8dsp_midi_input_trigger, }; -int snd_sb8dsp_midi(struct snd_sb *chip, int device, struct snd_rawmidi ** rrawmidi) +int snd_sb8dsp_midi(struct snd_sb *chip, int device) { struct snd_rawmidi *rmidi; int err; - if (rrawmidi) - *rrawmidi = NULL; if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0) return err; strcpy(rmidi->name, "SB8 MIDI"); @@ -280,7 +278,5 @@ int snd_sb8dsp_midi(struct snd_sb *chip, int device, struct snd_rawmidi ** rrawm rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; rmidi->private_data = chip; chip->rmidi = rmidi; - if (rrawmidi) - *rrawmidi = rmidi; return 0; } -- cgit v0.10.2 From 92533f188862fbefe357ada4e4af67b8e730e680 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:42 +0100 Subject: ASoC: sb16: Simplify snd_sb16dsp_pcm() All callers of snd_sb16dsp_pcm() always pass the pcm field of the first parameter as the last parameter. Simplify the function by moving this inside the function itself. This makes the code a bit shorter and cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/include/sound/sb.h b/include/sound/sb.h index 13438ff..33e2d11 100644 --- a/include/sound/sb.h +++ b/include/sound/sb.h @@ -320,7 +320,7 @@ irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip); int snd_sb8dsp_midi(struct snd_sb *chip, int device); /* sb16_init.c */ -int snd_sb16dsp_pcm(struct snd_sb *chip, int device, struct snd_pcm ** rpcm); +int snd_sb16dsp_pcm(struct snd_sb *chip, int device); const struct snd_pcm_ops *snd_sb16dsp_get_pcm_ops(int direction); int snd_sb16dsp_configure(struct snd_sb *chip); /* sb16.c */ diff --git a/sound/isa/als100.c b/sound/isa/als100.c index 32d0152..bc9ea30 100644 --- a/sound/isa/als100.c +++ b/sound/isa/als100.c @@ -233,7 +233,7 @@ static int snd_card_als100_probe(int dev, irq[dev], dma8[dev], dma16[dev]); } - if ((error = snd_sb16dsp_pcm(chip, 0, &chip->pcm)) < 0) { + if ((error = snd_sb16dsp_pcm(chip, 0)) < 0) { snd_card_free(card); return error; } diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c index 3f69454..4a7d7c8 100644 --- a/sound/isa/sb/sb16.c +++ b/sound/isa/sb/sb16.c @@ -374,7 +374,7 @@ static int snd_sb16_probe(struct snd_card *card, int dev) if (! is_isapnp_selected(dev) && (err = snd_sb16dsp_configure(chip)) < 0) return err; - if ((err = snd_sb16dsp_pcm(chip, 0, &chip->pcm)) < 0) + if ((err = snd_sb16dsp_pcm(chip, 0)) < 0) return err; strcpy(card->driver, diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c index 72b10f4..63d11b7 100644 --- a/sound/isa/sb/sb16_main.c +++ b/sound/isa/sb/sb16_main.c @@ -860,19 +860,18 @@ static struct snd_pcm_ops snd_sb16_capture_ops = { .pointer = snd_sb16_capture_pointer, }; -int snd_sb16dsp_pcm(struct snd_sb * chip, int device, struct snd_pcm ** rpcm) +int snd_sb16dsp_pcm(struct snd_sb *chip, int device) { struct snd_card *card = chip->card; struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; if ((err = snd_pcm_new(card, "SB16 DSP", device, 1, 1, &pcm)) < 0) return err; sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff); pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; pcm->private_data = chip; + chip->pcm = pcm; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb16_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb16_capture_ops); @@ -885,9 +884,6 @@ int snd_sb16dsp_pcm(struct snd_sb * chip, int device, struct snd_pcm ** rpcm) snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(), 64*1024, 128*1024); - - if (rpcm) - *rpcm = pcm; return 0; } diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c index b102550..0a8cf94 100644 --- a/sound/pci/cs5530.c +++ b/sound/pci/cs5530.c @@ -223,7 +223,7 @@ static int snd_cs5530_create(struct snd_card *card, return err; } - err = snd_sb16dsp_pcm(chip->sb, 0, &chip->sb->pcm); + err = snd_sb16dsp_pcm(chip->sb, 0); if (err < 0) { dev_err(card->dev, "Could not create PCM\n"); snd_cs5530_free(chip); -- cgit v0.10.2 From fa60c065694a81e534c61809ab7dd419366e9335 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:43 +0100 Subject: ALSA: wss: Remove (almost) always NULL parameters Most callers of snd_wss_pcm(), snd_wss_timer() and snd_cs4236_pcm() pass NULL as the last parameter, some callers pass a pointer but never use it after the function has been called and only a few callers pass a pointer and actually use it. The later is only the case for snd_wss_pcm() for snd_cs4236_pcm() and it is possible to get the same PCM object by accessing the pcm field of the snd_wss struct that was passed as the first parameter. This function removes the last parameters from the functions mentioned above and updates the callers which used it to use chip->pcm instead. This allows us to slightly simplify the functions since they don't have to check and set the last parameter anymore which makes the code slightly shorter and cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/include/sound/wss.h b/include/sound/wss.h index 0c7f034..1823e3a 100644 --- a/include/sound/wss.h +++ b/include/sound/wss.h @@ -154,8 +154,8 @@ int snd_wss_create(struct snd_card *card, unsigned short hardware, unsigned short hwshare, struct snd_wss **rchip); -int snd_wss_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm); -int snd_wss_timer(struct snd_wss *chip, int device, struct snd_timer **rtimer); +int snd_wss_pcm(struct snd_wss *chip, int device); +int snd_wss_timer(struct snd_wss *chip, int device); int snd_wss_mixer(struct snd_wss *chip); const struct snd_pcm_ops *snd_wss_get_pcm_ops(int direction); @@ -167,7 +167,7 @@ int snd_cs4236_create(struct snd_card *card, unsigned short hardware, unsigned short hwshare, struct snd_wss **rchip); -int snd_cs4236_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm); +int snd_cs4236_pcm(struct snd_wss *chip, int device); int snd_cs4236_mixer(struct snd_wss *chip); /* diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c index 093f22a..f159da4e 100644 --- a/sound/isa/ad1848/ad1848.c +++ b/sound/isa/ad1848/ad1848.c @@ -88,7 +88,6 @@ static int snd_ad1848_probe(struct device *dev, unsigned int n) { struct snd_card *card; struct snd_wss *chip; - struct snd_pcm *pcm; int error; error = snd_card_new(dev, index[n], id[n], THIS_MODULE, 0, &card); @@ -103,7 +102,7 @@ static int snd_ad1848_probe(struct device *dev, unsigned int n) card->private_data = chip; - error = snd_wss_pcm(chip, 0, &pcm); + error = snd_wss_pcm(chip, 0); if (error < 0) goto out; @@ -112,10 +111,10 @@ static int snd_ad1848_probe(struct device *dev, unsigned int n) goto out; strcpy(card->driver, "AD1848"); - strcpy(card->shortname, pcm->name); + strcpy(card->shortname, chip->pcm->name); sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", - pcm->name, chip->port, irq[n], dma1[n]); + chip->pcm->name, chip->port, irq[n], dma1[n]); if (thinkpad[n]) strcat(card->longname, " [Thinkpad]"); diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c index 0ea75fc..b8e768e 100644 --- a/sound/isa/azt2320.c +++ b/sound/isa/azt2320.c @@ -215,7 +215,7 @@ static int snd_card_azt2320_probe(int dev, sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i", card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]); - error = snd_wss_pcm(chip, 0, NULL); + error = snd_wss_pcm(chip, 0); if (error < 0) { snd_card_free(card); return error; @@ -225,7 +225,7 @@ static int snd_card_azt2320_probe(int dev, snd_card_free(card); return error; } - error = snd_wss_timer(chip, 0, NULL); + error = snd_wss_timer(chip, 0); if (error < 0) { snd_card_free(card); return error; diff --git a/sound/isa/cmi8328.c b/sound/isa/cmi8328.c index 4778852..2c89d95 100644 --- a/sound/isa/cmi8328.c +++ b/sound/isa/cmi8328.c @@ -307,7 +307,7 @@ static int snd_cmi8328_probe(struct device *pdev, unsigned int ndev) if (err < 0) goto error; - err = snd_wss_pcm(cmi->wss, 0, NULL); + err = snd_wss_pcm(cmi->wss, 0); if (err < 0) goto error; @@ -318,7 +318,7 @@ static int snd_cmi8328_probe(struct device *pdev, unsigned int ndev) if (err < 0) goto error; - if (snd_wss_timer(cmi->wss, 0, NULL) < 0) + if (snd_wss_timer(cmi->wss, 0) < 0) snd_printk(KERN_WARNING "error initializing WSS timer\n"); if (mpuport[ndev] == SNDRV_AUTO_PORT) { diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c index 7dba07a..282cd75 100644 --- a/sound/isa/cs423x/cs4231.c +++ b/sound/isa/cs423x/cs4231.c @@ -92,7 +92,6 @@ static int snd_cs4231_probe(struct device *dev, unsigned int n) { struct snd_card *card; struct snd_wss *chip; - struct snd_pcm *pcm; int error; error = snd_card_new(dev, index[n], id[n], THIS_MODULE, 0, &card); @@ -106,15 +105,15 @@ static int snd_cs4231_probe(struct device *dev, unsigned int n) card->private_data = chip; - error = snd_wss_pcm(chip, 0, &pcm); + error = snd_wss_pcm(chip, 0); if (error < 0) goto out; strcpy(card->driver, "CS4231"); - strcpy(card->shortname, pcm->name); + strcpy(card->shortname, chip->pcm->name); sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", - pcm->name, chip->port, irq[n], dma1[n]); + chip->pcm->name, chip->port, irq[n], dma1[n]); if (dma2[n] >= 0) sprintf(card->longname + strlen(card->longname), "&%d", dma2[n]); @@ -122,7 +121,7 @@ static int snd_cs4231_probe(struct device *dev, unsigned int n) if (error < 0) goto out; - error = snd_wss_timer(chip, 0, NULL); + error = snd_wss_timer(chip, 0); if (error < 0) goto out; diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c index 750f51c..9d7582c 100644 --- a/sound/isa/cs423x/cs4236.c +++ b/sound/isa/cs423x/cs4236.c @@ -382,7 +382,6 @@ static int snd_cs423x_card_new(struct device *pdev, int dev, static int snd_cs423x_probe(struct snd_card *card, int dev) { struct snd_card_cs4236 *acard; - struct snd_pcm *pcm; struct snd_wss *chip; struct snd_opl3 *opl3; int err; @@ -404,7 +403,7 @@ static int snd_cs423x_probe(struct snd_card *card, int dev) acard->chip = chip; if (chip->hardware & WSS_HW_CS4236B_MASK) { - err = snd_cs4236_pcm(chip, 0, &pcm); + err = snd_cs4236_pcm(chip, 0); if (err < 0) return err; @@ -412,7 +411,7 @@ static int snd_cs423x_probe(struct snd_card *card, int dev) if (err < 0) return err; } else { - err = snd_wss_pcm(chip, 0, &pcm); + err = snd_wss_pcm(chip, 0); if (err < 0) return err; @@ -420,17 +419,17 @@ static int snd_cs423x_probe(struct snd_card *card, int dev) if (err < 0) return err; } - strcpy(card->driver, pcm->name); - strcpy(card->shortname, pcm->name); + strcpy(card->driver, chip->pcm->name); + strcpy(card->shortname, chip->pcm->name); sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i", - pcm->name, + chip->pcm->name, chip->port, irq[dev], dma1[dev]); if (dma2[dev] >= 0) sprintf(card->longname + strlen(card->longname), "&%d", dma2[dev]); - err = snd_wss_timer(chip, 0, NULL); + err = snd_wss_timer(chip, 0); if (err < 0) return err; diff --git a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c index c5adca3..add7ffc 100644 --- a/sound/isa/cs423x/cs4236_lib.c +++ b/sound/isa/cs423x/cs4236_lib.c @@ -376,17 +376,14 @@ int snd_cs4236_create(struct snd_card *card, return 0; } -int snd_cs4236_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm) +int snd_cs4236_pcm(struct snd_wss *chip, int device) { - struct snd_pcm *pcm; int err; - err = snd_wss_pcm(chip, device, &pcm); + err = snd_wss_pcm(chip, device); if (err < 0) return err; - pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX; - if (rpcm) - *rpcm = pcm; + chip->pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX; return 0; } diff --git a/sound/isa/galaxy/galaxy.c b/sound/isa/galaxy/galaxy.c index 1eb2b1e..3227884 100644 --- a/sound/isa/galaxy/galaxy.c +++ b/sound/isa/galaxy/galaxy.c @@ -569,7 +569,7 @@ static int snd_galaxy_probe(struct device *dev, unsigned int n) if (err < 0) goto error; - err = snd_wss_pcm(chip, 0, NULL); + err = snd_wss_pcm(chip, 0); if (err < 0) goto error; @@ -577,7 +577,7 @@ static int snd_galaxy_probe(struct device *dev, unsigned int n) if (err < 0) goto error; - err = snd_wss_timer(chip, 0, NULL); + err = snd_wss_timer(chip, 0); if (err < 0) goto error; diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c index b2b3734..8216e8d 100644 --- a/sound/isa/gus/gusmax.c +++ b/sound/isa/gus/gusmax.c @@ -309,7 +309,7 @@ static int snd_gusmax_probe(struct device *pdev, unsigned int dev) if (err < 0) goto _err; - err = snd_wss_pcm(wss, 0, NULL); + err = snd_wss_pcm(wss, 0); if (err < 0) goto _err; @@ -317,7 +317,7 @@ static int snd_gusmax_probe(struct device *pdev, unsigned int dev) if (err < 0) goto _err; - err = snd_wss_timer(wss, 2, NULL); + err = snd_wss_timer(wss, 2); if (err < 0) goto _err; diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c index 8ea54a7..70d0040 100644 --- a/sound/isa/gus/interwave.c +++ b/sound/isa/gus/interwave.c @@ -647,7 +647,6 @@ static int snd_interwave_probe(struct snd_card *card, int dev) #ifdef SNDRV_STB struct snd_i2c_bus *i2c_bus; #endif - struct snd_pcm *pcm; char *str; int err; @@ -695,14 +694,15 @@ static int snd_interwave_probe(struct snd_card *card, int dev) if (err < 0) return err; - err = snd_wss_pcm(wss, 0, &pcm); + err = snd_wss_pcm(wss, 0); if (err < 0) return err; - sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A'); - strcat(pcm->name, " (codec)"); + sprintf(wss->pcm->name + strlen(wss->pcm->name), " rev %c", + gus->revision + 'A'); + strcat(wss->pcm->name, " (codec)"); - err = snd_wss_timer(wss, 2, NULL); + err = snd_wss_timer(wss, 2); if (err < 0) return err; diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c index a219bc3..d7aff52 100644 --- a/sound/isa/opl3sa2.c +++ b/sound/isa/opl3sa2.c @@ -684,7 +684,7 @@ static int snd_opl3sa2_probe(struct snd_card *card, int dev) return err; } chip->wss = wss; - err = snd_wss_pcm(wss, 0, NULL); + err = snd_wss_pcm(wss, 0); if (err < 0) return err; err = snd_wss_mixer(wss); @@ -693,7 +693,7 @@ static int snd_opl3sa2_probe(struct snd_card *card, int dev) err = snd_opl3sa2_mixer(card); if (err < 0) return err; - err = snd_wss_timer(wss, 0, NULL); + err = snd_wss_timer(wss, 0); if (err < 0) return err; if (fm_port[dev] >= 0x340 && fm_port[dev] < 0x400) { diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c index c2ca681..5464528 100644 --- a/sound/isa/opti9xx/miro.c +++ b/sound/isa/opti9xx/miro.c @@ -1270,8 +1270,6 @@ static int snd_miro_probe(struct snd_card *card) int error; struct snd_miro *miro = card->private_data; struct snd_wss *codec; - struct snd_timer *timer; - struct snd_pcm *pcm; struct snd_rawmidi *rmidi; if (!miro->res_mc_base) { @@ -1310,7 +1308,7 @@ static int snd_miro_probe(struct snd_card *card) if (error < 0) return error; - error = snd_wss_pcm(codec, 0, &pcm); + error = snd_wss_pcm(codec, 0); if (error < 0) return error; @@ -1318,11 +1316,11 @@ static int snd_miro_probe(struct snd_card *card) if (error < 0) return error; - error = snd_wss_timer(codec, 0, &timer); + error = snd_wss_timer(codec, 0); if (error < 0) return error; - miro->pcm = pcm; + miro->pcm = codec->pcm; error = snd_miro_mixer(card, miro); if (error < 0) @@ -1356,8 +1354,8 @@ static int snd_miro_probe(struct snd_card *card) strcpy(card->driver, "miro"); sprintf(card->longname, "%s: OPTi%s, %s at 0x%lx, irq %d, dma %d&%d", - card->shortname, miro->name, pcm->name, miro->wss_base + 4, - miro->irq, miro->dma1, miro->dma2); + card->shortname, miro->name, codec->pcm->name, + miro->wss_base + 4, miro->irq, miro->dma1, miro->dma2); if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT) rmidi = NULL; diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c index c9b5828..840831f 100644 --- a/sound/isa/opti9xx/opti92x-ad1848.c +++ b/sound/isa/opti9xx/opti92x-ad1848.c @@ -820,10 +820,6 @@ static int snd_opti9xx_probe(struct snd_card *card) int xdma2; struct snd_opti9xx *chip = card->private_data; struct snd_wss *codec; -#ifdef CS4231 - struct snd_timer *timer; -#endif - struct snd_pcm *pcm; struct snd_rawmidi *rmidi; struct snd_hwdep *synth; @@ -855,7 +851,7 @@ static int snd_opti9xx_probe(struct snd_card *card) if (error < 0) return error; chip->codec = codec; - error = snd_wss_pcm(codec, 0, &pcm); + error = snd_wss_pcm(codec, 0); if (error < 0) return error; error = snd_wss_mixer(codec); @@ -867,7 +863,7 @@ static int snd_opti9xx_probe(struct snd_card *card) return error; #endif #ifdef CS4231 - error = snd_wss_timer(codec, 0, &timer); + error = snd_wss_timer(codec, 0); if (error < 0) return error; #endif @@ -884,11 +880,12 @@ static int snd_opti9xx_probe(struct snd_card *card) sprintf(card->shortname, "OPTi %s", card->driver); #if defined(CS4231) || defined(OPTi93X) sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d", - card->shortname, pcm->name, + card->shortname, codec->pcm->name, chip->wss_base + 4, irq, dma1, xdma2); #else sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d", - card->shortname, pcm->name, chip->wss_base + 4, irq, dma1); + card->shortname, codec->pcm->name, chip->wss_base + 4, irq, + dma1); #endif /* CS4231 || OPTi93X */ if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT) diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c index 15a152e..51cfa76 100644 --- a/sound/isa/sc6000.c +++ b/sound/isa/sc6000.c @@ -625,7 +625,7 @@ static int snd_sc6000_probe(struct device *devptr, unsigned int dev) if (err < 0) goto err_unmap2; - err = snd_wss_pcm(chip, 0, NULL); + err = snd_wss_pcm(chip, 0); if (err < 0) { snd_printk(KERN_ERR PFX "error creating new WSS PCM device\n"); diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c index 44405df..018ab14 100644 --- a/sound/isa/sscape.c +++ b/sound/isa/sscape.c @@ -877,7 +877,6 @@ static int create_ad1845(struct snd_card *card, unsigned port, codec_type, WSS_HWSHARE_DMA1, &chip); if (!err) { unsigned long flags; - struct snd_pcm *pcm; if (sscape->type != SSCAPE_VIVO) { /* @@ -893,7 +892,7 @@ static int create_ad1845(struct snd_card *card, unsigned port, } - err = snd_wss_pcm(chip, 0, &pcm); + err = snd_wss_pcm(chip, 0); if (err < 0) { snd_printk(KERN_ERR "sscape: No PCM device " "for AD1845 chip\n"); @@ -907,7 +906,7 @@ static int create_ad1845(struct snd_card *card, unsigned port, goto _error; } if (chip->hardware != WSS_HW_AD1848) { - err = snd_wss_timer(chip, 0, NULL); + err = snd_wss_timer(chip, 0); if (err < 0) { snd_printk(KERN_ERR "sscape: No timer device " "for AD1845 chip\n"); diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c index bfbf38c..a0987a5 100644 --- a/sound/isa/wavefront/wavefront.c +++ b/sound/isa/wavefront/wavefront.c @@ -380,11 +380,11 @@ snd_wavefront_probe (struct snd_card *card, int dev) return err; } - err = snd_wss_pcm(chip, 0, NULL); + err = snd_wss_pcm(chip, 0); if (err < 0) return err; - err = snd_wss_timer(chip, 0, NULL); + err = snd_wss_timer(chip, 0); if (err < 0) return err; diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c index 347bb1b..6530d32 100644 --- a/sound/isa/wss/wss_lib.c +++ b/sound/isa/wss/wss_lib.c @@ -1923,7 +1923,7 @@ static struct snd_pcm_ops snd_wss_capture_ops = { .pointer = snd_wss_capture_pointer, }; -int snd_wss_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm) +int snd_wss_pcm(struct snd_wss *chip, int device) { struct snd_pcm *pcm; int err; @@ -1949,8 +1949,6 @@ int snd_wss_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm) 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); chip->pcm = pcm; - if (rpcm) - *rpcm = pcm; return 0; } EXPORT_SYMBOL(snd_wss_pcm); @@ -1961,7 +1959,7 @@ static void snd_wss_timer_free(struct snd_timer *timer) chip->timer = NULL; } -int snd_wss_timer(struct snd_wss *chip, int device, struct snd_timer **rtimer) +int snd_wss_timer(struct snd_wss *chip, int device) { struct snd_timer *timer; struct snd_timer_id tid; @@ -1980,8 +1978,6 @@ int snd_wss_timer(struct snd_wss *chip, int device, struct snd_timer **rtimer) timer->private_free = snd_wss_timer_free; timer->hw = snd_wss_timer_table; chip->timer = timer; - if (rtimer) - *rtimer = timer; return 0; } EXPORT_SYMBOL(snd_wss_timer); -- cgit v0.10.2 From a08800940f72bb5502a8b6afb675231cef0b3a62 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:44 +0100 Subject: ASLA: ad1889: Remove always NULL parameter snd_ad1889_pcm_init() takes a pointer to a pointer of a PCM where if this parameter is provided the newly allocated PCM is stored. All callers pass NULL though, so remove the parameter. This makes the code a bit cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index 1610c38..547ee30 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -623,14 +623,11 @@ snd_ad1889_interrupt(int irq, void *dev_id) } static int -snd_ad1889_pcm_init(struct snd_ad1889 *chip, int device, struct snd_pcm **rpcm) +snd_ad1889_pcm_init(struct snd_ad1889 *chip, int device) { int err; struct snd_pcm *pcm; - if (rpcm) - *rpcm = NULL; - err = snd_pcm_new(chip->card, chip->card->driver, device, 1, 1, &pcm); if (err < 0) return err; @@ -658,9 +655,6 @@ snd_ad1889_pcm_init(struct snd_ad1889 *chip, int device, struct snd_pcm **rpcm) return err; } - if (rpcm) - *rpcm = pcm; - return 0; } @@ -1016,7 +1010,7 @@ snd_ad1889_probe(struct pci_dev *pci, if (err < 0) goto free_and_ret; - err = snd_ad1889_pcm_init(chip, 0, NULL); + err = snd_ad1889_pcm_init(chip, 0); if (err < 0) goto free_and_ret; -- cgit v0.10.2 From d18132aa15d2bf7a5f227b590175b73aa3591a91 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:45 +0100 Subject: ALSA: asihpi: Remove always NULL parameter snd_asihpi_hpi_new() takes a pointer to a pointer of a hwdep where if this parameter is provided the newly allocated hwdep is stored. All callers pass NULL though, so remove the parameter. This makes the code a bit cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index e9273fb..7a55fef 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -2832,14 +2832,11 @@ static int snd_asihpi_hpi_ioctl(struct snd_hwdep *hw, struct file *file, /* results in /dev/snd/hwC#D0 file for each card with index # also /proc/asound/hwdep will contain '#-00: asihpi (HPI) for each card' */ -static int snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi, - int device, struct snd_hwdep **rhwdep) +static int snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi, int device) { struct snd_hwdep *hw; int err; - if (rhwdep) - *rhwdep = NULL; err = snd_hwdep_new(asihpi->card, "HPI", device, &hw); if (err < 0) return err; @@ -2849,8 +2846,6 @@ static int snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi, hw->ops.ioctl = snd_asihpi_hpi_ioctl; hw->ops.release = snd_asihpi_hpi_release; hw->private_data = asihpi; - if (rhwdep) - *rhwdep = hw; return 0; } @@ -2993,7 +2988,7 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, /* always create, can be enabled or disabled dynamically by enable_hwdep module param*/ - snd_asihpi_hpi_new(asihpi, 0, NULL); + snd_asihpi_hpi_new(asihpi, 0); strcpy(card->driver, "ASIHPI"); -- cgit v0.10.2 From 3e4f4776f427ddeeba6972f34cca04df478a67b4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:46 +0100 Subject: ALSA: cs4281: Remove always NULL parameters snd_cs4281_pcm() and snd_cs4281_midi() take a pointer to a pointer of a PCM/MIDI object where if this parameter is provided the newly allocated object is stored. All callers pass NULL though, so remove the parameter. This makes the code a bit shorter and cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 4c49b5c..05a4337 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -973,14 +973,11 @@ static struct snd_pcm_ops snd_cs4281_capture_ops = { .pointer = snd_cs4281_pointer, }; -static int snd_cs4281_pcm(struct cs4281 *chip, int device, - struct snd_pcm **rpcm) +static int snd_cs4281_pcm(struct cs4281 *chip, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; err = snd_pcm_new(chip->card, "CS4281", device, 1, 1, &pcm); if (err < 0) return err; @@ -996,8 +993,6 @@ static int snd_cs4281_pcm(struct cs4281 *chip, int device, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 512*1024); - if (rpcm) - *rpcm = pcm; return 0; } @@ -1788,14 +1783,11 @@ static struct snd_rawmidi_ops snd_cs4281_midi_input = .trigger = snd_cs4281_midi_input_trigger, }; -static int snd_cs4281_midi(struct cs4281 *chip, int device, - struct snd_rawmidi **rrawmidi) +static int snd_cs4281_midi(struct cs4281 *chip, int device) { struct snd_rawmidi *rmidi; int err; - if (rrawmidi) - *rrawmidi = NULL; if ((err = snd_rawmidi_new(chip->card, "CS4281", device, 1, 1, &rmidi)) < 0) return err; strcpy(rmidi->name, "CS4281"); @@ -1804,8 +1796,6 @@ static int snd_cs4281_midi(struct cs4281 *chip, int device, rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; rmidi->private_data = chip; chip->rmidi = rmidi; - if (rrawmidi) - *rrawmidi = rmidi; return 0; } @@ -1941,11 +1931,11 @@ static int snd_cs4281_probe(struct pci_dev *pci, snd_card_free(card); return err; } - if ((err = snd_cs4281_pcm(chip, 0, NULL)) < 0) { + if ((err = snd_cs4281_pcm(chip, 0)) < 0) { snd_card_free(card); return err; } - if ((err = snd_cs4281_midi(chip, 0, NULL)) < 0) { + if ((err = snd_cs4281_midi(chip, 0)) < 0) { snd_card_free(card); return err; } -- cgit v0.10.2 From 72134c4d9e97bcde376a8746510408da3fe039cc Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:47 +0100 Subject: ALSA: cs46xx: Remove always NULL parameters The various PCM and MIDI allocation functions in this driver take a pointer to a pointer of a PCM/MIDI object where if this parameter is provided the newly allocated object is stored. All callers pass NULL though, so remove the parameter. This makes the code a bit shorter and cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index 6a6858c..655fbea 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -100,16 +100,16 @@ static int snd_card_cs46xx_probe(struct pci_dev *pci, } card->private_data = chip; chip->accept_valid = mmap_valid[dev]; - if ((err = snd_cs46xx_pcm(chip, 0, NULL)) < 0) { + if ((err = snd_cs46xx_pcm(chip, 0)) < 0) { snd_card_free(card); return err; } #ifdef CONFIG_SND_CS46XX_NEW_DSP - if ((err = snd_cs46xx_pcm_rear(chip,1, NULL)) < 0) { + if ((err = snd_cs46xx_pcm_rear(chip, 1)) < 0) { snd_card_free(card); return err; } - if ((err = snd_cs46xx_pcm_iec958(chip,2,NULL)) < 0) { + if ((err = snd_cs46xx_pcm_iec958(chip, 2)) < 0) { snd_card_free(card); return err; } @@ -120,13 +120,13 @@ static int snd_card_cs46xx_probe(struct pci_dev *pci, } #ifdef CONFIG_SND_CS46XX_NEW_DSP if (chip->nr_ac97_codecs ==2) { - if ((err = snd_cs46xx_pcm_center_lfe(chip,3,NULL)) < 0) { + if ((err = snd_cs46xx_pcm_center_lfe(chip, 3)) < 0) { snd_card_free(card); return err; } } #endif - if ((err = snd_cs46xx_midi(chip, 0, NULL)) < 0) { + if ((err = snd_cs46xx_midi(chip, 0)) < 0) { snd_card_free(card); return err; } diff --git a/sound/pci/cs46xx/cs46xx.h b/sound/pci/cs46xx/cs46xx.h index c49a082..9c9f89a 100644 --- a/sound/pci/cs46xx/cs46xx.h +++ b/sound/pci/cs46xx/cs46xx.h @@ -1737,12 +1737,12 @@ int snd_cs46xx_create(struct snd_card *card, struct snd_cs46xx **rcodec); extern const struct dev_pm_ops snd_cs46xx_pm; -int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); -int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); -int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); -int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); +int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device); +int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device); +int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device); +int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device); int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device); -int snd_cs46xx_midi(struct snd_cs46xx *chip, int device, struct snd_rawmidi **rmidi); +int snd_cs46xx_midi(struct snd_cs46xx *chip, int device); int snd_cs46xx_start_dsp(struct snd_cs46xx *chip); int snd_cs46xx_gameport(struct snd_cs46xx *chip); diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 32b44f2..dfec84e 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -1778,13 +1778,11 @@ static struct snd_pcm_ops snd_cs46xx_capture_indirect_ops = { #define MAX_PLAYBACK_CHANNELS 1 #endif -int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm) +int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; if ((err = snd_pcm_new(chip->card, "CS46xx", device, MAX_PLAYBACK_CHANNELS, 1, &pcm)) < 0) return err; @@ -1801,23 +1799,16 @@ int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm) snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 256*1024); - if (rpcm) - *rpcm = pcm; - return 0; } #ifdef CONFIG_SND_CS46XX_NEW_DSP -int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device, - struct snd_pcm **rpcm) +int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; - if ((err = snd_pcm_new(chip->card, "CS46xx - Rear", device, MAX_PLAYBACK_CHANNELS, 0, &pcm)) < 0) return err; @@ -1833,21 +1824,14 @@ int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 256*1024); - if (rpcm) - *rpcm = pcm; - return 0; } -int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device, - struct snd_pcm **rpcm) +int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; - if ((err = snd_pcm_new(chip->card, "CS46xx - Center LFE", device, MAX_PLAYBACK_CHANNELS, 0, &pcm)) < 0) return err; @@ -1863,21 +1847,14 @@ int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 256*1024); - if (rpcm) - *rpcm = pcm; - return 0; } -int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device, - struct snd_pcm **rpcm) +int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; - if ((err = snd_pcm_new(chip->card, "CS46xx - IEC958", device, 1, 0, &pcm)) < 0) return err; @@ -1893,9 +1870,6 @@ int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 256*1024); - if (rpcm) - *rpcm = pcm; - return 0; } #endif @@ -2724,13 +2698,11 @@ static struct snd_rawmidi_ops snd_cs46xx_midi_input = .trigger = snd_cs46xx_midi_input_trigger, }; -int snd_cs46xx_midi(struct snd_cs46xx *chip, int device, struct snd_rawmidi **rrawmidi) +int snd_cs46xx_midi(struct snd_cs46xx *chip, int device) { struct snd_rawmidi *rmidi; int err; - if (rrawmidi) - *rrawmidi = NULL; if ((err = snd_rawmidi_new(chip->card, "CS46XX", device, 1, 1, &rmidi)) < 0) return err; strcpy(rmidi->name, "CS46XX"); @@ -2739,8 +2711,6 @@ int snd_cs46xx_midi(struct snd_cs46xx *chip, int device, struct snd_rawmidi **rr rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; rmidi->private_data = chip; chip->rmidi = rmidi; - if (rrawmidi) - *rrawmidi = NULL; return 0; } -- cgit v0.10.2 From 50b8d94e1819768e8eccae4d24698a17b8940776 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:48 +0100 Subject: ALSA: ens1370: Remove always NULL parameters The various PCM and MIDI allocation functions in this driver take a pointer to a pointer of a PCM/MIDI object where if this parameter is provided the newly allocated object is stored. All callers pass NULL though, so remove the parameter. This makes the code a bit shorter and cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index d94cb3c..bb1f4d2 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1268,14 +1268,11 @@ static const struct snd_pcm_chmap_elem surround_map[] = { { } }; -static int snd_ensoniq_pcm(struct ensoniq *ensoniq, int device, - struct snd_pcm **rpcm) +static int snd_ensoniq_pcm(struct ensoniq *ensoniq, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; err = snd_pcm_new(ensoniq->card, CHIP_NAME "/1", device, 1, 1, &pcm); if (err < 0) return err; @@ -1302,22 +1299,14 @@ static int snd_ensoniq_pcm(struct ensoniq *ensoniq, int device, err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, snd_pcm_std_chmaps, 2, 0, NULL); #endif - if (err < 0) - return err; - - if (rpcm) - *rpcm = pcm; - return 0; + return err; } -static int snd_ensoniq_pcm2(struct ensoniq *ensoniq, int device, - struct snd_pcm **rpcm) +static int snd_ensoniq_pcm2(struct ensoniq *ensoniq, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; err = snd_pcm_new(ensoniq->card, CHIP_NAME "/2", device, 1, 0, &pcm); if (err < 0) return err; @@ -1342,12 +1331,7 @@ static int snd_ensoniq_pcm2(struct ensoniq *ensoniq, int device, err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, surround_map, 2, 0, NULL); #endif - if (err < 0) - return err; - - if (rpcm) - *rpcm = pcm; - return 0; + return err; } /* @@ -2362,14 +2346,11 @@ static struct snd_rawmidi_ops snd_ensoniq_midi_input = .trigger = snd_ensoniq_midi_input_trigger, }; -static int snd_ensoniq_midi(struct ensoniq *ensoniq, int device, - struct snd_rawmidi **rrawmidi) +static int snd_ensoniq_midi(struct ensoniq *ensoniq, int device) { struct snd_rawmidi *rmidi; int err; - if (rrawmidi) - *rrawmidi = NULL; if ((err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi)) < 0) return err; strcpy(rmidi->name, CHIP_NAME); @@ -2379,8 +2360,6 @@ static int snd_ensoniq_midi(struct ensoniq *ensoniq, int device, SNDRV_RAWMIDI_INFO_DUPLEX; rmidi->private_data = ensoniq; ensoniq->rmidi = rmidi; - if (rrawmidi) - *rrawmidi = rmidi; return 0; } @@ -2462,15 +2441,15 @@ static int snd_audiopci_probe(struct pci_dev *pci, return err; } #endif - if ((err = snd_ensoniq_pcm(ensoniq, 0, NULL)) < 0) { + if ((err = snd_ensoniq_pcm(ensoniq, 0)) < 0) { snd_card_free(card); return err; } - if ((err = snd_ensoniq_pcm2(ensoniq, 1, NULL)) < 0) { + if ((err = snd_ensoniq_pcm2(ensoniq, 1)) < 0) { snd_card_free(card); return err; } - if ((err = snd_ensoniq_midi(ensoniq, 0, NULL)) < 0) { + if ((err = snd_ensoniq_midi(ensoniq, 0)) < 0) { snd_card_free(card); return err; } -- cgit v0.10.2 From bb814c396af777c31ed168ce57f620967dd133f1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:49 +0100 Subject: ALSA: emu10k1: Remove always NULL parameters The various PCM and hwdep allocation functions in this driver take a pointer to a pointer of a PCM/hwdep where if this parameter is provided the newly allocated object is stored. All callers pass NULL though, so remove the parameter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index c46908c..7f6ab6c 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1809,17 +1809,17 @@ int snd_emu10k1_create(struct snd_card *card, uint subsystem, struct snd_emu10k1 ** remu); -int snd_emu10k1_pcm(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm); -int snd_emu10k1_pcm_mic(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm); -int snd_emu10k1_pcm_efx(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm); -int snd_p16v_pcm(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm); +int snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device); +int snd_emu10k1_pcm_mic(struct snd_emu10k1 *emu, int device); +int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device); +int snd_p16v_pcm(struct snd_emu10k1 *emu, int device); int snd_p16v_free(struct snd_emu10k1 * emu); int snd_p16v_mixer(struct snd_emu10k1 * emu); -int snd_emu10k1_pcm_multi(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm); -int snd_emu10k1_fx8010_pcm(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm); +int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device); +int snd_emu10k1_fx8010_pcm(struct snd_emu10k1 *emu, int device); int snd_emu10k1_mixer(struct snd_emu10k1 * emu, int pcm_device, int multi_device); int snd_emu10k1_timer(struct snd_emu10k1 * emu, int device); -int snd_emu10k1_fx8010_new(struct snd_emu10k1 *emu, int device, struct snd_hwdep ** rhwdep); +int snd_emu10k1_fx8010_new(struct snd_emu10k1 *emu, int device); irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id); diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 4c17163..a3c00a6 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -132,11 +132,11 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci, goto error; card->private_data = emu; emu->delay_pcm_irq = delay_pcm_irq[dev] & 0x1f; - if ((err = snd_emu10k1_pcm(emu, 0, NULL)) < 0) + if ((err = snd_emu10k1_pcm(emu, 0)) < 0) goto error; - if ((err = snd_emu10k1_pcm_mic(emu, 1, NULL)) < 0) + if ((err = snd_emu10k1_pcm_mic(emu, 1)) < 0) goto error; - if ((err = snd_emu10k1_pcm_efx(emu, 2, NULL)) < 0) + if ((err = snd_emu10k1_pcm_efx(emu, 2)) < 0) goto error; /* This stores the periods table. */ if (emu->card_capabilities->ca0151_chip) { /* P16V */ @@ -151,10 +151,10 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci, if ((err = snd_emu10k1_timer(emu, 0)) < 0) goto error; - if ((err = snd_emu10k1_pcm_multi(emu, 3, NULL)) < 0) + if ((err = snd_emu10k1_pcm_multi(emu, 3)) < 0) goto error; if (emu->card_capabilities->ca0151_chip) { /* P16V */ - if ((err = snd_p16v_pcm(emu, 4, NULL)) < 0) + if ((err = snd_p16v_pcm(emu, 4)) < 0) goto error; } if (emu->audigy) { @@ -164,7 +164,7 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci, if ((err = snd_emu10k1_midi(emu)) < 0) goto error; } - if ((err = snd_emu10k1_fx8010_new(emu, 0, NULL)) < 0) + if ((err = snd_emu10k1_fx8010_new(emu, 0)) < 0) goto error; #ifdef ENABLE_SYNTH if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 15933f9..6d1b98d 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -847,15 +847,13 @@ static const struct snd_pcm_chmap_elem clfe_map[] = { { } }; -static int snd_emu10k1x_pcm(struct emu10k1x *emu, int device, struct snd_pcm **rpcm) +static int snd_emu10k1x_pcm(struct emu10k1x *emu, int device) { struct snd_pcm *pcm; const struct snd_pcm_chmap_elem *map = NULL; int err; int capture = 0; - if (rpcm) - *rpcm = NULL; if (device == 0) capture = 1; @@ -896,15 +894,8 @@ static int snd_emu10k1x_pcm(struct emu10k1x *emu, int device, struct snd_pcm **r snd_dma_pci_data(emu->pci), 32*1024, 32*1024); - err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, 2, + return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, 2, 1 << 2, NULL); - if (err < 0) - return err; - - if (rpcm) - *rpcm = pcm; - - return 0; } static int snd_emu10k1x_create(struct snd_card *card, @@ -1583,15 +1574,15 @@ static int snd_emu10k1x_probe(struct pci_dev *pci, return err; } - if ((err = snd_emu10k1x_pcm(chip, 0, NULL)) < 0) { + if ((err = snd_emu10k1x_pcm(chip, 0)) < 0) { snd_card_free(card); return err; } - if ((err = snd_emu10k1x_pcm(chip, 1, NULL)) < 0) { + if ((err = snd_emu10k1x_pcm(chip, 1)) < 0) { snd_card_free(card); return err; } - if ((err = snd_emu10k1x_pcm(chip, 2, NULL)) < 0) { + if ((err = snd_emu10k1x_pcm(chip, 2)) < 0) { snd_card_free(card); return err; } diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index eb5c0ab..56fc47b 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -2641,14 +2641,11 @@ static int snd_emu10k1_fx8010_release(struct snd_hwdep * hw, struct file *file) return 0; } -int snd_emu10k1_fx8010_new(struct snd_emu10k1 *emu, int device, - struct snd_hwdep **rhwdep) +int snd_emu10k1_fx8010_new(struct snd_emu10k1 *emu, int device) { struct snd_hwdep *hw; int err; - if (rhwdep) - *rhwdep = NULL; if ((err = snd_hwdep_new(emu->card, "FX8010", device, &hw)) < 0) return err; strcpy(hw->name, "EMU10K1 (FX8010)"); @@ -2657,8 +2654,6 @@ int snd_emu10k1_fx8010_new(struct snd_emu10k1 *emu, int device, hw->ops.ioctl = snd_emu10k1_fx8010_ioctl; hw->ops.release = snd_emu10k1_fx8010_release; hw->private_data = emu; - if (rhwdep) - *rhwdep = hw; return 0; } diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index f82481b..0dc0738 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -1400,15 +1400,12 @@ static struct snd_pcm_ops snd_emu10k1_efx_playback_ops = { .page = snd_pcm_sgbuf_ops_page, }; -int snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device, struct snd_pcm **rpcm) +int snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device) { struct snd_pcm *pcm; struct snd_pcm_substream *substream; int err; - if (rpcm) - *rpcm = NULL; - if ((err = snd_pcm_new(emu->card, "emu10k1", device, 32, 1, &pcm)) < 0) return err; @@ -1429,22 +1426,15 @@ int snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device, struct snd_pcm **rpcm) for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024); - if (rpcm) - *rpcm = pcm; - return 0; } -int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device, - struct snd_pcm **rpcm) +int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device) { struct snd_pcm *pcm; struct snd_pcm_substream *substream; int err; - if (rpcm) - *rpcm = NULL; - if ((err = snd_pcm_new(emu->card, "emu10k1", device, 1, 0, &pcm)) < 0) return err; @@ -1461,9 +1451,6 @@ int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device, if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0) return err; - if (rpcm) - *rpcm = pcm; - return 0; } @@ -1479,15 +1466,11 @@ static struct snd_pcm_ops snd_emu10k1_capture_mic_ops = { .pointer = snd_emu10k1_capture_pointer, }; -int snd_emu10k1_pcm_mic(struct snd_emu10k1 *emu, int device, - struct snd_pcm **rpcm) +int snd_emu10k1_pcm_mic(struct snd_emu10k1 *emu, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; - if ((err = snd_pcm_new(emu->card, "emu10k1 mic", device, 0, 1, &pcm)) < 0) return err; @@ -1501,8 +1484,6 @@ int snd_emu10k1_pcm_mic(struct snd_emu10k1 *emu, int device, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024); - if (rpcm) - *rpcm = pcm; return 0; } @@ -1822,16 +1803,12 @@ static struct snd_pcm_ops snd_emu10k1_fx8010_playback_ops = { .ack = snd_emu10k1_fx8010_playback_transfer, }; -int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device, - struct snd_pcm **rpcm) +int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device) { struct snd_pcm *pcm; struct snd_kcontrol *kctl; int err; - if (rpcm) - *rpcm = NULL; - if ((err = snd_pcm_new(emu->card, "emu10k1 efx", device, 8, 1, &pcm)) < 0) return err; @@ -1843,8 +1820,6 @@ int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device, pcm->info_flags = 0; strcpy(pcm->name, "Multichannel Capture/PT Playback"); emu->pcm_efx = pcm; - if (rpcm) - *rpcm = pcm; /* EFX capture - record the "FXBUS2" channels, by default we connect the EXTINs * to these diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index 7ef3898..b672755 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -640,7 +640,7 @@ int snd_p16v_free(struct snd_emu10k1 *chip) return 0; } -int snd_p16v_pcm(struct snd_emu10k1 *emu, int device, struct snd_pcm **rpcm) +int snd_p16v_pcm(struct snd_emu10k1 *emu, int device) { struct snd_pcm *pcm; struct snd_pcm_substream *substream; @@ -649,8 +649,6 @@ int snd_p16v_pcm(struct snd_emu10k1 *emu, int device, struct snd_pcm **rpcm) /* dev_dbg(emu->card->dev, "snd_p16v_pcm called. device=%d\n", device); */ emu->p16v_device_offset = device; - if (rpcm) - *rpcm = NULL; if ((err = snd_pcm_new(emu->card, "p16v", device, 1, capture, &pcm)) < 0) return err; @@ -694,9 +692,6 @@ int snd_p16v_pcm(struct snd_emu10k1 *emu, int device, struct snd_pcm **rpcm) */ } - if (rpcm) - *rpcm = pcm; - return 0; } -- cgit v0.10.2 From 483337f903dd39a10d4d6617cf6f30feeead061c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:50 +0100 Subject: ALSA: fm801: Remove always NULL parameter snd_fm801_pcm() takes a pointer to a pointer of a PCM where if this parameter is provided the newly allocated PCM is stored. All callers pass NULL though, so remove the parameter. This makes the code a bit cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index d167aff..9a2122f 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -704,13 +704,11 @@ static struct snd_pcm_ops snd_fm801_capture_ops = { .pointer = snd_fm801_capture_pointer, }; -static int snd_fm801_pcm(struct fm801 *chip, int device, struct snd_pcm **rpcm) +static int snd_fm801_pcm(struct fm801 *chip, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; if ((err = snd_pcm_new(chip->card, "FM801", device, 1, 1, &pcm)) < 0) return err; @@ -726,16 +724,10 @@ static int snd_fm801_pcm(struct fm801 *chip, int device, struct snd_pcm **rpcm) snd_dma_pci_data(chip->pci), chip->multichannel ? 128*1024 : 64*1024, 128*1024); - err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, snd_pcm_alt_chmaps, chip->multichannel ? 6 : 2, 0, NULL); - if (err < 0) - return err; - - if (rpcm) - *rpcm = pcm; - return 0; } /* @@ -1340,7 +1332,7 @@ static int snd_card_fm801_probe(struct pci_dev *pci, if (chip->tea575x_tuner & TUNER_ONLY) goto __fm801_tuner_only; - if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) { + if ((err = snd_fm801_pcm(chip, 0)) < 0) { snd_card_free(card); return err; } -- cgit v0.10.2 From 08a4c10b58d6403ca9fe8c872e22ecad47924fec Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:51 +0100 Subject: ALSA: ice1712: Remove always NULL parameters The various PCM allocation functions in this driver take a pointer to a pointer of a PCM where if this parameter is provided the newly allocated PCM is stored. All callers pass NULL though, so remove the parameter. This makes the code a bit cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index b039b46..a2ee2982 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -880,13 +880,11 @@ static struct snd_pcm_ops snd_ice1712_capture_ops = { .pointer = snd_ice1712_capture_pointer, }; -static int snd_ice1712_pcm(struct snd_ice1712 *ice, int device, struct snd_pcm **rpcm) +static int snd_ice1712_pcm(struct snd_ice1712 *ice, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; err = snd_pcm_new(ice->card, "ICE1712 consumer", device, 1, 1, &pcm); if (err < 0) return err; @@ -902,22 +900,17 @@ static int snd_ice1712_pcm(struct snd_ice1712 *ice, int device, struct snd_pcm * snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(ice->pci), 64*1024, 64*1024); - if (rpcm) - *rpcm = pcm; - dev_warn(ice->card->dev, "Consumer PCM code does not work well at the moment --jk\n"); return 0; } -static int snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device, struct snd_pcm **rpcm) +static int snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; err = snd_pcm_new(ice->card, "ICE1712 consumer (DS)", device, 6, 0, &pcm); if (err < 0) return err; @@ -932,9 +925,6 @@ static int snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device, struct snd_pc snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(ice->pci), 64*1024, 128*1024); - if (rpcm) - *rpcm = pcm; - return 0; } @@ -1260,13 +1250,11 @@ static struct snd_pcm_ops snd_ice1712_capture_pro_ops = { .pointer = snd_ice1712_capture_pro_pointer, }; -static int snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device, struct snd_pcm **rpcm) +static int snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; err = snd_pcm_new(ice->card, "ICE1712 multi", device, 1, 1, &pcm); if (err < 0) return err; @@ -1282,8 +1270,6 @@ static int snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device, struct snd snd_dma_pci_data(ice->pci), 256*1024, 256*1024); ice->pcm_pro = pcm; - if (rpcm) - *rpcm = pcm; if (ice->cs8427) { /* assign channels to iec958 */ @@ -2691,14 +2677,14 @@ static int snd_ice1712_probe(struct pci_dev *pci, c = &no_matched; __found: - err = snd_ice1712_pcm_profi(ice, pcm_dev++, NULL); + err = snd_ice1712_pcm_profi(ice, pcm_dev++); if (err < 0) { snd_card_free(card); return err; } if (ice_has_con_ac97(ice)) { - err = snd_ice1712_pcm(ice, pcm_dev++, NULL); + err = snd_ice1712_pcm(ice, pcm_dev++); if (err < 0) { snd_card_free(card); return err; @@ -2726,7 +2712,7 @@ static int snd_ice1712_probe(struct pci_dev *pci, } if (ice_has_con_ac97(ice)) { - err = snd_ice1712_pcm_ds(ice, pcm_dev++, NULL); + err = snd_ice1712_pcm_ds(ice, pcm_dev++); if (err < 0) { snd_card_free(card); return err; -- cgit v0.10.2 From 15aded4c924ae149a38e639f421f5313125eea89 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:52 +0100 Subject: ALSA: riptide: Remove always NULL parameter snd_riptide_pcm() takes a pointer to a pointer of a PCM where if this parameter is provided the newly allocated PCM is stored. All callers pass NULL though, so remove the parameter. This makes the code a bit cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 6abc2ac..121f2c0 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -1706,14 +1706,11 @@ static struct snd_pcm_ops snd_riptide_capture_ops = { .pointer = snd_riptide_pointer, }; -static int -snd_riptide_pcm(struct snd_riptide *chip, int device, struct snd_pcm **rpcm) +static int snd_riptide_pcm(struct snd_riptide *chip, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; if ((err = snd_pcm_new(chip->card, "RIPTIDE", device, PLAYBACK_SUBSTREAMS, 1, &pcm)) < 0) @@ -1729,8 +1726,6 @@ snd_riptide_pcm(struct snd_riptide *chip, int device, struct snd_pcm **rpcm) snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(chip->pci), 64 * 1024, 128 * 1024); - if (rpcm) - *rpcm = pcm; return 0; } @@ -2092,7 +2087,7 @@ snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) if (err < 0) goto error; card->private_data = chip; - err = snd_riptide_pcm(chip, 0, NULL); + err = snd_riptide_pcm(chip, 0); if (err < 0) goto error; err = snd_riptide_mixer(chip); -- cgit v0.10.2 From 0f33991dd2811cb5458b1a2bc19c6aa6ff64c00b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:53 +0100 Subject: ALSA: sonicvibes: Remove always NULL parameter snd_sonicvibes_pcm() take a pointer to a pointer of a PCM where if this parameter is provided the newly allocated PCM is stored. All callers pass NULL though, so remove the parameter. This makes the code a bit cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 313a732..34cee5c 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -880,8 +880,7 @@ static struct snd_pcm_ops snd_sonicvibes_capture_ops = { .pointer = snd_sonicvibes_capture_pointer, }; -static int snd_sonicvibes_pcm(struct sonicvibes *sonic, int device, - struct snd_pcm **rpcm) +static int snd_sonicvibes_pcm(struct sonicvibes *sonic, int device) { struct snd_pcm *pcm; int err; @@ -902,8 +901,6 @@ static int snd_sonicvibes_pcm(struct sonicvibes *sonic, int device, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(sonic->pci), 64*1024, 128*1024); - if (rpcm) - *rpcm = pcm; return 0; } @@ -1491,7 +1488,7 @@ static int snd_sonic_probe(struct pci_dev *pci, (unsigned long long)pci_resource_start(pci, 1), sonic->irq); - if ((err = snd_sonicvibes_pcm(sonic, 0, NULL)) < 0) { + if ((err = snd_sonicvibes_pcm(sonic, 0)) < 0) { snd_card_free(card); return err; } -- cgit v0.10.2 From 1b16416fda21a8c5704bce809e0d7387bbfb097d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:54 +0100 Subject: ALSA: trident: Remove always NULL parameters The various PCM allocation functions in this driver take a pointer to a pointer of a PCM where if this parameter is provided the newly allocated PCM is stored. All callers pass NULL though, so remove the parameter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c index a54cd68..cedf13b 100644 --- a/sound/pci/trident/trident.c +++ b/sound/pci/trident/trident.c @@ -127,21 +127,21 @@ static int snd_trident_probe(struct pci_dev *pci, sprintf(card->longname, "%s PCI Audio at 0x%lx, irq %d", card->shortname, trident->port, trident->irq); - if ((err = snd_trident_pcm(trident, pcm_dev++, NULL)) < 0) { + if ((err = snd_trident_pcm(trident, pcm_dev++)) < 0) { snd_card_free(card); return err; } switch (trident->device) { case TRIDENT_DEVICE_ID_DX: case TRIDENT_DEVICE_ID_NX: - if ((err = snd_trident_foldback_pcm(trident, pcm_dev++, NULL)) < 0) { + if ((err = snd_trident_foldback_pcm(trident, pcm_dev++)) < 0) { snd_card_free(card); return err; } break; } if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) { - if ((err = snd_trident_spdif_pcm(trident, pcm_dev++, NULL)) < 0) { + if ((err = snd_trident_spdif_pcm(trident, pcm_dev++)) < 0) { snd_card_free(card); return err; } diff --git a/sound/pci/trident/trident.h b/sound/pci/trident/trident.h index 5f110eb..9624e59 100644 --- a/sound/pci/trident/trident.h +++ b/sound/pci/trident/trident.h @@ -420,9 +420,9 @@ int snd_trident_create(struct snd_card *card, struct snd_trident ** rtrident); int snd_trident_create_gameport(struct snd_trident *trident); -int snd_trident_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm); -int snd_trident_foldback_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm); -int snd_trident_spdif_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm); +int snd_trident_pcm(struct snd_trident *trident, int device); +int snd_trident_foldback_pcm(struct snd_trident *trident, int device); +int snd_trident_spdif_pcm(struct snd_trident *trident, int device); int snd_trident_attach_synthesizer(struct snd_trident * trident); struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, int client, int port); diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 57cd757..c380c99 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -2172,14 +2172,11 @@ static struct snd_pcm_ops snd_trident_spdif_7018_ops = { ---------------------------------------------------------------------------*/ -int snd_trident_pcm(struct snd_trident *trident, - int device, struct snd_pcm **rpcm) +int snd_trident_pcm(struct snd_trident *trident, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, trident->ChanPCM, 1, &pcm)) < 0) return err; @@ -2214,8 +2211,6 @@ int snd_trident_pcm(struct snd_trident *trident, snd_dma_pci_data(trident->pci), 64*1024, 128*1024); } - if (rpcm) - *rpcm = pcm; return 0; } @@ -2230,16 +2225,13 @@ int snd_trident_pcm(struct snd_trident *trident, ---------------------------------------------------------------------------*/ -int snd_trident_foldback_pcm(struct snd_trident *trident, - int device, struct snd_pcm **rpcm) +int snd_trident_foldback_pcm(struct snd_trident *trident, int device) { struct snd_pcm *foldback; int err; int num_chan = 3; struct snd_pcm_substream *substream; - if (rpcm) - *rpcm = NULL; if (trident->device == TRIDENT_DEVICE_ID_NX) num_chan = 4; if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, 0, num_chan, &foldback)) < 0) @@ -2271,8 +2263,6 @@ int snd_trident_foldback_pcm(struct snd_trident *trident, snd_pcm_lib_preallocate_pages_for_all(foldback, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), 64*1024, 128*1024); - if (rpcm) - *rpcm = foldback; return 0; } @@ -2287,14 +2277,11 @@ int snd_trident_foldback_pcm(struct snd_trident *trident, ---------------------------------------------------------------------------*/ -int snd_trident_spdif_pcm(struct snd_trident *trident, - int device, struct snd_pcm **rpcm) +int snd_trident_spdif_pcm(struct snd_trident *trident, int device) { struct snd_pcm *spdif; int err; - if (rpcm) - *rpcm = NULL; if ((err = snd_pcm_new(trident->card, "trident_dx_nx IEC958", device, 1, 0, &spdif)) < 0) return err; @@ -2310,8 +2297,6 @@ int snd_trident_spdif_pcm(struct snd_trident *trident, snd_pcm_lib_preallocate_pages_for_all(spdif, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), 64*1024, 128*1024); - if (rpcm) - *rpcm = spdif; return 0; } -- cgit v0.10.2 From 38c4718121804317e00a3a905612d17485f754c0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 12:24:55 +0100 Subject: ALSA: ymfpci: Remove always NULL parameters The various PCM allocation functions in this driver take a pointer to a pointer of a PCM where if this parameter is provided the newly allocated PCM is stored. All callers pass NULL though, so remove the parameter. This makes the code a bit shorter and cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index 47a1923..812e27a 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -283,11 +283,11 @@ static int snd_card_ymfpci_probe(struct pci_dev *pci, card->shortname, chip->reg_area_phys, chip->irq); - if ((err = snd_ymfpci_pcm(chip, 0, NULL)) < 0) { + if ((err = snd_ymfpci_pcm(chip, 0)) < 0) { snd_card_free(card); return err; } - if ((err = snd_ymfpci_pcm_spdif(chip, 1, NULL)) < 0) { + if ((err = snd_ymfpci_pcm_spdif(chip, 1)) < 0) { snd_card_free(card); return err; } @@ -297,12 +297,12 @@ static int snd_card_ymfpci_probe(struct pci_dev *pci, return err; } if (chip->ac97->ext_id & AC97_EI_SDAC) { - err = snd_ymfpci_pcm_4ch(chip, 2, NULL); + err = snd_ymfpci_pcm_4ch(chip, 2); if (err < 0) { snd_card_free(card); return err; } - err = snd_ymfpci_pcm2(chip, 3, NULL); + err = snd_ymfpci_pcm2(chip, 3); if (err < 0) { snd_card_free(card); return err; diff --git a/sound/pci/ymfpci/ymfpci.h b/sound/pci/ymfpci/ymfpci.h index 4631a23..149d4cb 100644 --- a/sound/pci/ymfpci/ymfpci.h +++ b/sound/pci/ymfpci/ymfpci.h @@ -379,10 +379,10 @@ void snd_ymfpci_free_gameport(struct snd_ymfpci *chip); extern const struct dev_pm_ops snd_ymfpci_pm; -int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); -int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); -int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); -int snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); +int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device); +int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device); +int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device); +int snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device); int snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch); int snd_ymfpci_timer(struct snd_ymfpci *chip, int device); diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 81c916a..f5581a9 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -1145,13 +1145,11 @@ static struct snd_pcm_ops snd_ymfpci_capture_rec_ops = { .pointer = snd_ymfpci_capture_pointer, }; -int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm) +int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; if ((err = snd_pcm_new(chip->card, "YMFPCI", device, 32, 1, &pcm)) < 0) return err; pcm->private_data = chip; @@ -1167,14 +1165,8 @@ int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm) snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 256*1024); - err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, snd_pcm_std_chmaps, 2, 0, NULL); - if (err < 0) - return err; - - if (rpcm) - *rpcm = pcm; - return 0; } static struct snd_pcm_ops snd_ymfpci_capture_ac97_ops = { @@ -1188,13 +1180,11 @@ static struct snd_pcm_ops snd_ymfpci_capture_ac97_ops = { .pointer = snd_ymfpci_capture_pointer, }; -int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm) +int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; if ((err = snd_pcm_new(chip->card, "YMFPCI - PCM2", device, 0, 1, &pcm)) < 0) return err; pcm->private_data = chip; @@ -1210,8 +1200,6 @@ int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm) snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 256*1024); - if (rpcm) - *rpcm = pcm; return 0; } @@ -1226,14 +1214,11 @@ static struct snd_pcm_ops snd_ymfpci_playback_spdif_ops = { .pointer = snd_ymfpci_playback_pointer, }; -int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device, - struct snd_pcm **rpcm) +int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; if ((err = snd_pcm_new(chip->card, "YMFPCI - IEC958", device, 1, 0, &pcm)) < 0) return err; pcm->private_data = chip; @@ -1248,8 +1233,6 @@ int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 256*1024); - if (rpcm) - *rpcm = pcm; return 0; } @@ -1272,14 +1255,11 @@ static const struct snd_pcm_chmap_elem surround_map[] = { { } }; -int snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device, - struct snd_pcm **rpcm) +int snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device) { struct snd_pcm *pcm; int err; - if (rpcm) - *rpcm = NULL; if ((err = snd_pcm_new(chip->card, "YMFPCI - Rear", device, 1, 0, &pcm)) < 0) return err; pcm->private_data = chip; @@ -1294,14 +1274,8 @@ int snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 256*1024); - err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, surround_map, 2, 0, NULL); - if (err < 0) - return err; - - if (rpcm) - *rpcm = pcm; - return 0; } static int snd_ymfpci_spdif_default_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -- cgit v0.10.2 From ef394acb45e232e940367053896708375da1ff92 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 15:03:42 +0100 Subject: ALSA: oxygen: Remove unnecessary snd_pcm_lib_preallocate_free_for_all() The ALSA core takes care that all preallocated memory is freed when the PCM itself is freed. There is no need to do this manually in the driver. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index 0282824..af22a74 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -676,11 +676,6 @@ static struct snd_pcm_ops oxygen_ac97_ops = { .pointer = oxygen_pointer, }; -static void oxygen_pcm_free(struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - int oxygen_pcm_init(struct oxygen *chip) { struct snd_pcm *pcm; @@ -705,7 +700,6 @@ int oxygen_pcm_init(struct oxygen *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &oxygen_rec_b_ops); pcm->private_data = chip; - pcm->private_free = oxygen_pcm_free; strcpy(pcm->name, "Multichannel"); if (outs) snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, @@ -734,7 +728,6 @@ int oxygen_pcm_init(struct oxygen *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &oxygen_rec_c_ops); pcm->private_data = chip; - pcm->private_free = oxygen_pcm_free; strcpy(pcm->name, "Digital"); snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), @@ -765,7 +758,6 @@ int oxygen_pcm_init(struct oxygen *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &oxygen_rec_b_ops); pcm->private_data = chip; - pcm->private_free = oxygen_pcm_free; strcpy(pcm->name, outs ? "Front Panel" : "Analog 2"); snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), -- cgit v0.10.2 From a14e7eb59bb7ce10766d4778d382ca52b235147f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 15:03:43 +0100 Subject: ALSA: ps3: Remove unnecessary snd_pcm_lib_preallocate_free_for_all() The ALSA core takes care that all preallocated memory is freed when the card (and the PCM) itself is freed. There is no need to do this manually in the driver. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c index 58f292a..3682425 100644 --- a/sound/ppc/snd_ps3.c +++ b/sound/ppc/snd_ps3.c @@ -1044,7 +1044,7 @@ static int snd_ps3_driver_probe(struct ps3_system_bus_device *dev) if (!the_card.null_buffer_start_vaddr) { pr_info("%s: nullbuffer alloc failed\n", __func__); ret = -ENOMEM; - goto clean_preallocate; + goto clean_card; } pr_debug("%s: null vaddr=%p dma=%#llx\n", __func__, the_card.null_buffer_start_vaddr, @@ -1066,8 +1066,6 @@ clean_dma_map: PAGE_SIZE, the_card.null_buffer_start_vaddr, the_card.null_buffer_start_dma_addr); -clean_preallocate: - snd_pcm_lib_preallocate_free_for_all(the_card.pcm); clean_card: snd_card_free(the_card.card); clean_irq: -- cgit v0.10.2 From 48e08d0fb265b007ebbb29a72297ff7e40938969 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Tue, 11 Nov 2014 12:49:41 -0800 Subject: x86, entry: Switch stacks on a paranoid entry from userspace This causes all non-NMI, non-double-fault kernel entries from userspace to run on the normal kernel stack. Double-fault is exempt to minimize confusion if we double-fault directly from userspace due to a bad kernel stack. This is, suprisingly, simpler and shorter than the current code. It removes the IMO rather frightening paranoid_userspace path, and it make sync_regs much simpler. There is no risk of stack overflow due to this change -- the kernel stack that we switch to is empty. This will also enable us to create non-atomic sections within machine checks from userspace, which will simplify memory failure handling. It will also allow the upcoming fsgsbase code to be simplified, because it doesn't need to worry about usergs when scheduling in paranoid_exit, as that code no longer exists. Cc: Oleg Nesterov Cc: Andi Kleen Cc: Tony Luck Acked-by: Borislav Petkov Signed-off-by: Andy Lutomirski diff --git a/Documentation/x86/entry_64.txt b/Documentation/x86/entry_64.txt index 4a1c5c2..9132b86 100644 --- a/Documentation/x86/entry_64.txt +++ b/Documentation/x86/entry_64.txt @@ -78,9 +78,6 @@ The expensive (paranoid) way is to read back the MSR_GS_BASE value xorl %ebx,%ebx 1: ret -and the whole paranoid non-paranoid macro complexity is about whether -to suffer that RDMSR cost. - If we are at an interrupt or user-trap/gate-alike boundary then we can use the faster check: the stack will be a reliable indicator of whether SWAPGS was already done: if we see that we are a secondary @@ -93,6 +90,15 @@ which might have triggered right after a normal entry wrote CS to the stack but before we executed SWAPGS, then the only safe way to check for GS is the slower method: the RDMSR. -So we try only to mark those entry methods 'paranoid' that absolutely -need the more expensive check for the GS base - and we generate all -'normal' entry points with the regular (faster) entry macros. +Therefore, super-atomic entries (except NMI, which is handled separately) +must use idtentry with paranoid=1 to handle gsbase correctly. This +triggers three main behavior changes: + + - Interrupt entry will use the slower gsbase check. + - Interrupt entry from user mode will switch off the IST stack. + - Interrupt exit to kernel mode will not attempt to reschedule. + +We try to only use IST entries and the paranoid entry code for vectors +that absolutely need the more expensive check for the GS base - and we +generate all 'normal' entry points with the regular (faster) paranoid=0 +variant. diff --git a/Documentation/x86/x86_64/kernel-stacks b/Documentation/x86/x86_64/kernel-stacks index a01eec5..e3c8a49 100644 --- a/Documentation/x86/x86_64/kernel-stacks +++ b/Documentation/x86/x86_64/kernel-stacks @@ -40,9 +40,11 @@ An IST is selected by a non-zero value in the IST field of an interrupt-gate descriptor. When an interrupt occurs and the hardware loads such a descriptor, the hardware automatically sets the new stack pointer based on the IST value, then invokes the interrupt handler. If -software wants to allow nested IST interrupts then the handler must -adjust the IST values on entry to and exit from the interrupt handler. -(This is occasionally done, e.g. for debug exceptions.) +the interrupt came from user mode, then the interrupt handler prologue +will switch back to the per-thread stack. If software wants to allow +nested IST interrupts then the handler must adjust the IST values on +entry to and exit from the interrupt handler. (This is occasionally +done, e.g. for debug exceptions.) Events with different IST codes (i.e. with different stacks) can be nested. For example, a debug interrupt can safely be interrupted by an diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index 9ebaf63..931f32f 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -1048,6 +1048,11 @@ ENTRY(\sym) CFI_ADJUST_CFA_OFFSET ORIG_RAX-R15 .if \paranoid + .if \paranoid == 1 + CFI_REMEMBER_STATE + testl $3, CS(%rsp) /* If coming from userspace, switch */ + jnz 1f /* stacks. */ + .endif call save_paranoid .else call error_entry @@ -1088,6 +1093,36 @@ ENTRY(\sym) jmp error_exit /* %ebx: no swapgs flag */ .endif + .if \paranoid == 1 + CFI_RESTORE_STATE + /* + * Paranoid entry from userspace. Switch stacks and treat it + * as a normal entry. This means that paranoid handlers + * run in real process context if user_mode(regs). + */ +1: + call error_entry + + DEFAULT_FRAME 0 + + movq %rsp,%rdi /* pt_regs pointer */ + call sync_regs + movq %rax,%rsp /* switch stack */ + + movq %rsp,%rdi /* pt_regs pointer */ + + .if \has_error_code + movq ORIG_RAX(%rsp),%rsi /* get error code */ + movq $-1,ORIG_RAX(%rsp) /* no syscall to restart */ + .else + xorl %esi,%esi /* no error code */ + .endif + + call \do_sym + + jmp error_exit /* %ebx: no swapgs flag */ + .endif + CFI_ENDPROC END(\sym) .endm @@ -1108,7 +1143,7 @@ idtentry overflow do_overflow has_error_code=0 idtentry bounds do_bounds has_error_code=0 idtentry invalid_op do_invalid_op has_error_code=0 idtentry device_not_available do_device_not_available has_error_code=0 -idtentry double_fault do_double_fault has_error_code=1 paranoid=1 +idtentry double_fault do_double_fault has_error_code=1 paranoid=2 idtentry coprocessor_segment_overrun do_coprocessor_segment_overrun has_error_code=0 idtentry invalid_TSS do_invalid_TSS has_error_code=1 idtentry segment_not_present do_segment_not_present has_error_code=1 @@ -1289,16 +1324,14 @@ idtentry machine_check has_error_code=0 paranoid=1 do_sym=*machine_check_vector( #endif /* - * "Paranoid" exit path from exception stack. - * Paranoid because this is used by NMIs and cannot take - * any kernel state for granted. - * We don't do kernel preemption checks here, because only - * NMI should be common and it does not enable IRQs and - * cannot get reschedule ticks. + * "Paranoid" exit path from exception stack. This is invoked + * only on return from non-NMI IST interrupts that came + * from kernel space. * - * "trace" is 0 for the NMI handler only, because irq-tracing - * is fundamentally NMI-unsafe. (we cannot change the soft and - * hard flags at once, atomically) + * We may be returning to very strange contexts (e.g. very early + * in syscall entry), so checking for preemption here would + * be complicated. Fortunately, we there's no good reason + * to try to handle preemption here. */ /* ebx: no swapgs flag */ @@ -1308,43 +1341,14 @@ ENTRY(paranoid_exit) TRACE_IRQS_OFF_DEBUG testl %ebx,%ebx /* swapgs needed? */ jnz paranoid_restore - testl $3,CS(%rsp) - jnz paranoid_userspace -paranoid_swapgs: TRACE_IRQS_IRETQ 0 SWAPGS_UNSAFE_STACK RESTORE_ALL 8 - jmp irq_return + INTERRUPT_RETURN paranoid_restore: TRACE_IRQS_IRETQ_DEBUG 0 RESTORE_ALL 8 - jmp irq_return -paranoid_userspace: - GET_THREAD_INFO(%rcx) - movl TI_flags(%rcx),%ebx - andl $_TIF_WORK_MASK,%ebx - jz paranoid_swapgs - movq %rsp,%rdi /* &pt_regs */ - call sync_regs - movq %rax,%rsp /* switch stack for scheduling */ - testl $_TIF_NEED_RESCHED,%ebx - jnz paranoid_schedule - movl %ebx,%edx /* arg3: thread flags */ - TRACE_IRQS_ON - ENABLE_INTERRUPTS(CLBR_NONE) - xorl %esi,%esi /* arg2: oldset */ - movq %rsp,%rdi /* arg1: &pt_regs */ - call do_notify_resume - DISABLE_INTERRUPTS(CLBR_NONE) - TRACE_IRQS_OFF - jmp paranoid_userspace -paranoid_schedule: - TRACE_IRQS_ON - ENABLE_INTERRUPTS(CLBR_ANY) - SCHEDULE_USER - DISABLE_INTERRUPTS(CLBR_ANY) - TRACE_IRQS_OFF - jmp paranoid_userspace + INTERRUPT_RETURN CFI_ENDPROC END(paranoid_exit) diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 88900e2..28f3e5f 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -466,27 +466,14 @@ NOKPROBE_SYMBOL(do_int3); #ifdef CONFIG_X86_64 /* - * Help handler running on IST stack to switch back to user stack - * for scheduling or signal handling. The actual stack switch is done in - * entry.S + * Help handler running on IST stack to switch off the IST stack if the + * interrupted code was in user mode. The actual stack switch is done in + * entry_64.S */ asmlinkage __visible notrace struct pt_regs *sync_regs(struct pt_regs *eregs) { - struct pt_regs *regs = eregs; - /* Did already sync */ - if (eregs == (struct pt_regs *)eregs->sp) - ; - /* Exception from user space */ - else if (user_mode(eregs)) - regs = task_pt_regs(current); - /* - * Exception from kernel and interrupts are enabled. Move to - * kernel process stack. - */ - else if (eregs->flags & X86_EFLAGS_IF) - regs = (struct pt_regs *)(eregs->sp -= sizeof(struct pt_regs)); - if (eregs != regs) - *regs = *eregs; + struct pt_regs *regs = task_pt_regs(current); + *regs = *eregs; return regs; } NOKPROBE_SYMBOL(sync_regs); -- cgit v0.10.2 From 959274753857efe9c5f1ba35fe727f51e9aa128d Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Wed, 19 Nov 2014 17:41:09 -0800 Subject: x86, traps: Track entry into and exit from IST context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We currently pretend that IST context is like standard exception context, but this is incorrect. IST entries from userspace are like standard exceptions except that they use per-cpu stacks, so they are atomic. IST entries from kernel space are like NMIs from RCU's perspective -- they are not quiescent states even if they interrupted the kernel during a quiescent state. Add and use ist_enter and ist_exit to track IST context. Even though x86_32 has no IST stacks, we track these interrupts the same way. This fixes two issues: - Scheduling from an IST interrupt handler will now warn. It would previously appear to work as long as we got lucky and nothing overwrote the stack frame. (I don't know of any bugs in this that would trigger the warning, but it's good to be on the safe side.) - RCU handling in IST context was dangerous. As far as I know, only machine checks were likely to trigger this, but it's good to be on the safe side. Note that the machine check handlers appears to have been missing any context tracking at all before this patch. Cc: "Paul E. McKenney" Cc: Josh Triplett Cc: Frédéric Weisbecker Signed-off-by: Andy Lutomirski diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h index 707adc6..3cf525e 100644 --- a/arch/x86/include/asm/traps.h +++ b/arch/x86/include/asm/traps.h @@ -1,6 +1,7 @@ #ifndef _ASM_X86_TRAPS_H #define _ASM_X86_TRAPS_H +#include #include #include @@ -110,6 +111,9 @@ asmlinkage void smp_thermal_interrupt(void); asmlinkage void mce_threshold_interrupt(void); #endif +extern enum ctx_state ist_enter(struct pt_regs *regs); +extern void ist_exit(struct pt_regs *regs, enum ctx_state prev_state); + /* Interrupts/Exceptions */ enum { X86_TRAP_DE = 0, /* 0, Divide-by-zero */ diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index d2c6116..800d423 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -43,6 +43,7 @@ #include #include +#include #include #include @@ -1063,6 +1064,7 @@ void do_machine_check(struct pt_regs *regs, long error_code) { struct mca_config *cfg = &mca_cfg; struct mce m, *final; + enum ctx_state prev_state; int i; int worst = 0; int severity; @@ -1085,6 +1087,8 @@ void do_machine_check(struct pt_regs *regs, long error_code) DECLARE_BITMAP(valid_banks, MAX_NR_BANKS); char *msg = "Unknown"; + prev_state = ist_enter(regs); + this_cpu_inc(mce_exception_count); if (!cfg->banks) @@ -1216,6 +1220,7 @@ void do_machine_check(struct pt_regs *regs, long error_code) mce_wrmsrl(MSR_IA32_MCG_STATUS, 0); out: sync_core(); + ist_exit(regs, prev_state); } EXPORT_SYMBOL_GPL(do_machine_check); diff --git a/arch/x86/kernel/cpu/mcheck/p5.c b/arch/x86/kernel/cpu/mcheck/p5.c index a304298..ec2663a 100644 --- a/arch/x86/kernel/cpu/mcheck/p5.c +++ b/arch/x86/kernel/cpu/mcheck/p5.c @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -17,8 +18,11 @@ int mce_p5_enabled __read_mostly; /* Machine check handler for Pentium class Intel CPUs: */ static void pentium_machine_check(struct pt_regs *regs, long error_code) { + enum ctx_state prev_state; u32 loaddr, hi, lotype; + prev_state = ist_enter(regs); + rdmsr(MSR_IA32_P5_MC_ADDR, loaddr, hi); rdmsr(MSR_IA32_P5_MC_TYPE, lotype, hi); @@ -33,6 +37,8 @@ static void pentium_machine_check(struct pt_regs *regs, long error_code) } add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); + + ist_exit(regs, prev_state); } /* Set up machine check reporting for processors with Intel style MCE: */ diff --git a/arch/x86/kernel/cpu/mcheck/winchip.c b/arch/x86/kernel/cpu/mcheck/winchip.c index 7dc5564..bd5d46a 100644 --- a/arch/x86/kernel/cpu/mcheck/winchip.c +++ b/arch/x86/kernel/cpu/mcheck/winchip.c @@ -7,14 +7,19 @@ #include #include +#include #include #include /* Machine check handler for WinChip C6: */ static void winchip_machine_check(struct pt_regs *regs, long error_code) { + enum ctx_state prev_state = ist_enter(regs); + printk(KERN_EMERG "CPU0: Machine Check Exception.\n"); add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); + + ist_exit(regs, prev_state); } /* Set up machine check reporting on the Winchip C6 series */ diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 28f3e5f..b3a9d24 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -108,6 +108,39 @@ static inline void preempt_conditional_cli(struct pt_regs *regs) preempt_count_dec(); } +enum ctx_state ist_enter(struct pt_regs *regs) +{ + /* + * We are atomic because we're on the IST stack (or we're on x86_32, + * in which case we still shouldn't schedule. + */ + preempt_count_add(HARDIRQ_OFFSET); + + if (user_mode_vm(regs)) { + /* Other than that, we're just an exception. */ + return exception_enter(); + } else { + /* + * We might have interrupted pretty much anything. In + * fact, if we're a machine check, we can even interrupt + * NMI processing. We don't want in_nmi() to return true, + * but we need to notify RCU. + */ + rcu_nmi_enter(); + return IN_KERNEL; /* the value is irrelevant. */ + } +} + +void ist_exit(struct pt_regs *regs, enum ctx_state prev_state) +{ + preempt_count_sub(HARDIRQ_OFFSET); + + if (user_mode_vm(regs)) + return exception_exit(prev_state); + else + rcu_nmi_exit(); +} + static nokprobe_inline int do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str, struct pt_regs *regs, long error_code) @@ -251,6 +284,8 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) * end up promoting it to a doublefault. In that case, modify * the stack to make it look like we just entered the #GP * handler from user space, similar to bad_iret. + * + * No need for ist_enter here because we don't use RCU. */ if (((long)regs->sp >> PGDIR_SHIFT) == ESPFIX_PGD_ENTRY && regs->cs == __KERNEL_CS && @@ -263,12 +298,12 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) normal_regs->orig_ax = 0; /* Missing (lost) #GP error code */ regs->ip = (unsigned long)general_protection; regs->sp = (unsigned long)&normal_regs->orig_ax; + return; } #endif - exception_enter(); - /* Return not checked because double check cannot be ignored */ + ist_enter(regs); /* Discard prev_state because we won't return. */ notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV); tsk->thread.error_code = error_code; @@ -434,7 +469,7 @@ dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code) if (poke_int3_handler(regs)) return; - prev_state = exception_enter(); + prev_state = ist_enter(regs); #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP, SIGTRAP) == NOTIFY_STOP) @@ -460,7 +495,7 @@ dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code) preempt_conditional_cli(regs); debug_stack_usage_dec(); exit: - exception_exit(prev_state); + ist_exit(regs, prev_state); } NOKPROBE_SYMBOL(do_int3); @@ -541,7 +576,7 @@ dotraplinkage void do_debug(struct pt_regs *regs, long error_code) unsigned long dr6; int si_code; - prev_state = exception_enter(); + prev_state = ist_enter(regs); get_debugreg(dr6, 6); @@ -616,7 +651,7 @@ dotraplinkage void do_debug(struct pt_regs *regs, long error_code) debug_stack_usage_dec(); exit: - exception_exit(prev_state); + ist_exit(regs, prev_state); } NOKPROBE_SYMBOL(do_debug); -- cgit v0.10.2 From 83653c16da91112236292871b820cb8b367220e3 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Thu, 13 Nov 2014 15:57:07 -0800 Subject: x86: Clean up current_stack_pointer There's no good reason for it to be a macro, and x86_64 will want to use it, so it should be in a header. Acked-by: Borislav Petkov Signed-off-by: Andy Lutomirski diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index 547e344..8b13b0fb 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -170,6 +170,17 @@ static inline struct thread_info *current_thread_info(void) return ti; } +static inline unsigned long current_stack_pointer(void) +{ + unsigned long sp; +#ifdef CONFIG_X86_64 + asm("mov %%rsp,%0" : "=g" (sp)); +#else + asm("mov %%esp,%0" : "=g" (sp)); +#endif + return sp; +} + #else /* !__ASSEMBLY__ */ /* how to get the thread information struct from ASM */ diff --git a/arch/x86/kernel/irq_32.c b/arch/x86/kernel/irq_32.c index 63ce838..28d28f5 100644 --- a/arch/x86/kernel/irq_32.c +++ b/arch/x86/kernel/irq_32.c @@ -69,16 +69,9 @@ static void call_on_stack(void *func, void *stack) : "memory", "cc", "edx", "ecx", "eax"); } -/* how to get the current stack pointer from C */ -#define current_stack_pointer ({ \ - unsigned long sp; \ - asm("mov %%esp,%0" : "=g" (sp)); \ - sp; \ -}) - static inline void *current_stack(void) { - return (void *)(current_stack_pointer & ~(THREAD_SIZE - 1)); + return (void *)(current_stack_pointer() & ~(THREAD_SIZE - 1)); } static inline int @@ -103,7 +96,7 @@ execute_on_irq_stack(int overflow, struct irq_desc *desc, int irq) /* Save the next esp at the bottom of the stack */ prev_esp = (u32 *)irqstk; - *prev_esp = current_stack_pointer; + *prev_esp = current_stack_pointer(); if (unlikely(overflow)) call_on_stack(print_stack_overflow, isp); @@ -156,7 +149,7 @@ void do_softirq_own_stack(void) /* Push the previous esp onto the stack */ prev_esp = (u32 *)irqstk; - *prev_esp = current_stack_pointer; + *prev_esp = current_stack_pointer(); call_on_stack(__do_softirq, isp); } -- cgit v0.10.2 From bced35b65aefe53a6f77a9ed0ce1aea86e9d65a2 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Wed, 19 Nov 2014 17:59:41 -0800 Subject: x86, traps: Add ist_begin_non_atomic and ist_end_non_atomic In some IST handlers, if the interrupt came from user mode, we can safely enable preemption. Add helpers to do it safely. This is intended to be used my the memory failure code in do_machine_check. Acked-by: Borislav Petkov Signed-off-by: Andy Lutomirski diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h index 3cf525e..4e49d7d 100644 --- a/arch/x86/include/asm/traps.h +++ b/arch/x86/include/asm/traps.h @@ -113,6 +113,8 @@ asmlinkage void mce_threshold_interrupt(void); extern enum ctx_state ist_enter(struct pt_regs *regs); extern void ist_exit(struct pt_regs *regs, enum ctx_state prev_state); +extern void ist_begin_non_atomic(struct pt_regs *regs); +extern void ist_end_non_atomic(void); /* Interrupts/Exceptions */ enum { diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index b3a9d24..7176f84 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -141,6 +141,44 @@ void ist_exit(struct pt_regs *regs, enum ctx_state prev_state) rcu_nmi_exit(); } +/** + * ist_begin_non_atomic() - begin a non-atomic section in an IST exception + * @regs: regs passed to the IST exception handler + * + * IST exception handlers normally cannot schedule. As a special + * exception, if the exception interrupted userspace code (i.e. + * user_mode_vm(regs) would return true) and the exception was not + * a double fault, it can be safe to schedule. ist_begin_non_atomic() + * begins a non-atomic section within an ist_enter()/ist_exit() region. + * Callers are responsible for enabling interrupts themselves inside + * the non-atomic section, and callers must call is_end_non_atomic() + * before ist_exit(). + */ +void ist_begin_non_atomic(struct pt_regs *regs) +{ + BUG_ON(!user_mode_vm(regs)); + + /* + * Sanity check: we need to be on the normal thread stack. This + * will catch asm bugs and any attempt to use ist_preempt_enable + * from double_fault. + */ + BUG_ON(((current_stack_pointer() ^ this_cpu_read_stable(kernel_stack)) + & ~(THREAD_SIZE - 1)) != 0); + + preempt_count_sub(HARDIRQ_OFFSET); +} + +/** + * ist_end_non_atomic() - begin a non-atomic section in an IST exception + * + * Ends a non-atomic section started with ist_begin_non_atomic(). + */ +void ist_end_non_atomic(void) +{ + preempt_count_add(HARDIRQ_OFFSET); +} + static nokprobe_inline int do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str, struct pt_regs *regs, long error_code) -- cgit v0.10.2 From cef27f971e04785bc1c711a74a9e0d81bfd0b150 Mon Sep 17 00:00:00 2001 From: Shaohui Xie Date: Tue, 30 Dec 2014 16:27:33 +0800 Subject: net/fsl: remove reset from xgmac_mdio Since the reset is just clock setting, individual mdio reset is not available. Signed-off-by: Shaohui Xie Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/freescale/xgmac_mdio.c b/drivers/net/ethernet/freescale/xgmac_mdio.c index 6e7db66..90adba1 100644 --- a/drivers/net/ethernet/freescale/xgmac_mdio.c +++ b/drivers/net/ethernet/freescale/xgmac_mdio.c @@ -174,24 +174,6 @@ static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum) return value; } -/* Reset the MIIM registers, and wait for the bus to free */ -static int xgmac_mdio_reset(struct mii_bus *bus) -{ - struct tgec_mdio_controller __iomem *regs = bus->priv; - int ret; - - mutex_lock(&bus->mdio_lock); - - /* Setup the MII Mgmt clock speed */ - out_be32(®s->mdio_stat, MDIO_STAT_CLKDIV(100)); - - ret = xgmac_wait_until_free(&bus->dev, regs); - - mutex_unlock(&bus->mdio_lock); - - return ret; -} - static int xgmac_mdio_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -212,7 +194,6 @@ static int xgmac_mdio_probe(struct platform_device *pdev) bus->name = "Freescale XGMAC MDIO Bus"; bus->read = xgmac_mdio_read; bus->write = xgmac_mdio_write; - bus->reset = xgmac_mdio_reset; bus->irq = bus->priv; bus->parent = &pdev->dev; snprintf(bus->id, MII_BUS_ID_SIZE, "%llx", (unsigned long long)res.start); -- cgit v0.10.2 From aa842478044ff3e5a9e4b339f3bde5a0e78440e0 Mon Sep 17 00:00:00 2001 From: Shaohui Xie Date: Tue, 30 Dec 2014 16:28:00 +0800 Subject: net/fsl: remove irq assignment from xgmac_mdio Which is wrong and not used, so no extra space needed by mdiobus_alloc_size(), use mdiobus_alloc() instead. Signed-off-by: Shaohui Xie Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/freescale/xgmac_mdio.c b/drivers/net/ethernet/freescale/xgmac_mdio.c index 90adba1..9de5268 100644 --- a/drivers/net/ethernet/freescale/xgmac_mdio.c +++ b/drivers/net/ethernet/freescale/xgmac_mdio.c @@ -187,14 +187,13 @@ static int xgmac_mdio_probe(struct platform_device *pdev) return ret; } - bus = mdiobus_alloc_size(PHY_MAX_ADDR * sizeof(int)); + bus = mdiobus_alloc(); if (!bus) return -ENOMEM; bus->name = "Freescale XGMAC MDIO Bus"; bus->read = xgmac_mdio_read; bus->write = xgmac_mdio_write; - bus->irq = bus->priv; bus->parent = &pdev->dev; snprintf(bus->id, MII_BUS_ID_SIZE, "%llx", (unsigned long long)res.start); -- cgit v0.10.2 From 05930b5ec1d3b2a5c76e1959b54cae15587009c7 Mon Sep 17 00:00:00 2001 From: Shaohui Xie Date: Tue, 30 Dec 2014 16:28:21 +0800 Subject: net/fsl: remove hardcoded clock setting from xgmac_mdio There is no need to set the clock speed in read/write which will be performed unnecessarily for each mdio access. Init it during probe is enough. Also, the hardcoded clock value is not a proper way for all SoCs. Signed-off-by: Shaohui Xie Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/freescale/xgmac_mdio.c b/drivers/net/ethernet/freescale/xgmac_mdio.c index 9de5268..a352445 100644 --- a/drivers/net/ethernet/freescale/xgmac_mdio.c +++ b/drivers/net/ethernet/freescale/xgmac_mdio.c @@ -94,13 +94,6 @@ static int xgmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 val uint16_t dev_addr = regnum >> 16; int ret; - /* Setup the MII Mgmt clock speed */ - out_be32(®s->mdio_stat, MDIO_STAT_CLKDIV(100)); - - ret = xgmac_wait_until_free(&bus->dev, regs); - if (ret) - return ret; - /* Set the port and dev addr */ out_be32(®s->mdio_ctl, MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr)); @@ -135,13 +128,6 @@ static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum) uint16_t value; int ret; - /* Setup the MII Mgmt clock speed */ - out_be32(®s->mdio_stat, MDIO_STAT_CLKDIV(100)); - - ret = xgmac_wait_until_free(&bus->dev, regs); - if (ret) - return ret; - /* Set the Port and Device Addrs */ mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); out_be32(®s->mdio_ctl, mdio_ctl); -- cgit v0.10.2 From 9b174d88c257150562b0101fcc6cb6c3cb74275c Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Tue, 30 Dec 2014 19:10:15 -0800 Subject: net: Add Transparent Ethernet Bridging GRO support. Currently the only tunnel protocol that supports GRO with encapsulated Ethernet is VXLAN. This pulls out the Ethernet code into a proper layer so that it can be used by other tunnel protocols such as GRE and Geneve. Signed-off-by: Jesse Gross Signed-off-by: David S. Miller diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 7fbd89f..2ab0922 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -549,10 +549,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff { struct sk_buff *p, **pp = NULL; struct vxlanhdr *vh, *vh2; - struct ethhdr *eh, *eh2; - unsigned int hlen, off_vx, off_eth; - const struct packet_offload *ptype; - __be16 type; + unsigned int hlen, off_vx; int flush = 1; off_vx = skb_gro_offset(skb); @@ -563,17 +560,6 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff if (unlikely(!vh)) goto out; } - skb_gro_pull(skb, sizeof(struct vxlanhdr)); /* pull vxlan header */ - skb_gro_postpull_rcsum(skb, vh, sizeof(struct vxlanhdr)); - - off_eth = skb_gro_offset(skb); - hlen = off_eth + sizeof(*eh); - eh = skb_gro_header_fast(skb, off_eth); - if (skb_gro_header_hard(skb, hlen)) { - eh = skb_gro_header_slow(skb, hlen, off_eth); - if (unlikely(!eh)) - goto out; - } flush = 0; @@ -582,28 +568,16 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff continue; vh2 = (struct vxlanhdr *)(p->data + off_vx); - eh2 = (struct ethhdr *)(p->data + off_eth); - if (vh->vx_vni != vh2->vx_vni || compare_ether_header(eh, eh2)) { + if (vh->vx_vni != vh2->vx_vni) { NAPI_GRO_CB(p)->same_flow = 0; continue; } } - type = eh->h_proto; - - rcu_read_lock(); - ptype = gro_find_receive_by_type(type); - if (ptype == NULL) { - flush = 1; - goto out_unlock; - } - - skb_gro_pull(skb, sizeof(*eh)); /* pull inner eth header */ - skb_gro_postpull_rcsum(skb, eh, sizeof(*eh)); - pp = ptype->callbacks.gro_receive(head, skb); + skb_gro_pull(skb, sizeof(struct vxlanhdr)); + skb_gro_postpull_rcsum(skb, vh, sizeof(struct vxlanhdr)); + pp = eth_gro_receive(head, skb); -out_unlock: - rcu_read_unlock(); out: NAPI_GRO_CB(skb)->flush |= flush; @@ -612,24 +586,9 @@ out: static int vxlan_gro_complete(struct sk_buff *skb, int nhoff) { - struct ethhdr *eh; - struct packet_offload *ptype; - __be16 type; - int vxlan_len = sizeof(struct vxlanhdr) + sizeof(struct ethhdr); - int err = -ENOSYS; - udp_tunnel_gro_complete(skb, nhoff); - eh = (struct ethhdr *)(skb->data + nhoff + sizeof(struct vxlanhdr)); - type = eh->h_proto; - - rcu_read_lock(); - ptype = gro_find_complete_by_type(type); - if (ptype != NULL) - err = ptype->callbacks.gro_complete(skb, nhoff + vxlan_len); - - rcu_read_unlock(); - return err; + return eth_gro_complete(skb, nhoff + sizeof(struct vxlanhdr)); } /* Notify netdevs that UDP port started listening */ diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index 41c891d..1d869d1 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -52,6 +52,10 @@ struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs, #define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1) #define alloc_etherdev_mq(sizeof_priv, count) alloc_etherdev_mqs(sizeof_priv, count, count) +struct sk_buff **eth_gro_receive(struct sk_buff **head, + struct sk_buff *skb); +int eth_gro_complete(struct sk_buff *skb, int nhoff); + /* Reserved Ethernet Addresses per IEEE 802.1Q */ static const u8 eth_reserved_addr_base[ETH_ALEN] __aligned(2) = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index 33a140e..238f38d 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -424,3 +424,95 @@ ssize_t sysfs_format_mac(char *buf, const unsigned char *addr, int len) return scnprintf(buf, PAGE_SIZE, "%*phC\n", len, addr); } EXPORT_SYMBOL(sysfs_format_mac); + +struct sk_buff **eth_gro_receive(struct sk_buff **head, + struct sk_buff *skb) +{ + struct sk_buff *p, **pp = NULL; + struct ethhdr *eh, *eh2; + unsigned int hlen, off_eth; + const struct packet_offload *ptype; + __be16 type; + int flush = 1; + + off_eth = skb_gro_offset(skb); + hlen = off_eth + sizeof(*eh); + eh = skb_gro_header_fast(skb, off_eth); + if (skb_gro_header_hard(skb, hlen)) { + eh = skb_gro_header_slow(skb, hlen, off_eth); + if (unlikely(!eh)) + goto out; + } + + flush = 0; + + for (p = *head; p; p = p->next) { + if (!NAPI_GRO_CB(p)->same_flow) + continue; + + eh2 = (struct ethhdr *)(p->data + off_eth); + if (compare_ether_header(eh, eh2)) { + NAPI_GRO_CB(p)->same_flow = 0; + continue; + } + } + + type = eh->h_proto; + + rcu_read_lock(); + ptype = gro_find_receive_by_type(type); + if (ptype == NULL) { + flush = 1; + goto out_unlock; + } + + skb_gro_pull(skb, sizeof(*eh)); + skb_gro_postpull_rcsum(skb, eh, sizeof(*eh)); + pp = ptype->callbacks.gro_receive(head, skb); + +out_unlock: + rcu_read_unlock(); +out: + NAPI_GRO_CB(skb)->flush |= flush; + + return pp; +} +EXPORT_SYMBOL(eth_gro_receive); + +int eth_gro_complete(struct sk_buff *skb, int nhoff) +{ + struct ethhdr *eh = (struct ethhdr *)(skb->data + nhoff); + __be16 type = eh->h_proto; + struct packet_offload *ptype; + int err = -ENOSYS; + + if (skb->encapsulation) + skb_set_inner_mac_header(skb, nhoff); + + rcu_read_lock(); + ptype = gro_find_complete_by_type(type); + if (ptype != NULL) + err = ptype->callbacks.gro_complete(skb, nhoff + + sizeof(struct ethhdr)); + + rcu_read_unlock(); + return err; +} +EXPORT_SYMBOL(eth_gro_complete); + +static struct packet_offload eth_packet_offload __read_mostly = { + .type = cpu_to_be16(ETH_P_TEB), + .callbacks = { + .gro_receive = eth_gro_receive, + .gro_complete = eth_gro_complete, + }, +}; + +static int __init eth_offload_init(void) +{ + dev_add_offload(ð_packet_offload); + + return 0; +} + +fs_initcall(eth_offload_init); -- cgit v0.10.2 From a4c9ea5e8fec680134d22aa99b54d1cd8c226ebd Mon Sep 17 00:00:00 2001 From: Joe Stringer Date: Tue, 30 Dec 2014 19:10:16 -0800 Subject: geneve: Add Geneve GRO support This results in an approximately 30% increase in throughput when handling encapsulated bulk traffic. Signed-off-by: Joe Stringer Signed-off-by: Jesse Gross Signed-off-by: David S. Miller diff --git a/net/ipv4/geneve.c b/net/ipv4/geneve.c index 394a200..19e256e 100644 --- a/net/ipv4/geneve.c +++ b/net/ipv4/geneve.c @@ -149,6 +149,99 @@ int geneve_xmit_skb(struct geneve_sock *gs, struct rtable *rt, } EXPORT_SYMBOL_GPL(geneve_xmit_skb); +static int geneve_hlen(struct genevehdr *gh) +{ + return sizeof(*gh) + gh->opt_len * 4; +} + +static struct sk_buff **geneve_gro_receive(struct sk_buff **head, + struct sk_buff *skb) +{ + struct sk_buff *p, **pp = NULL; + struct genevehdr *gh, *gh2; + unsigned int hlen, gh_len, off_gnv; + const struct packet_offload *ptype; + __be16 type; + int flush = 1; + + off_gnv = skb_gro_offset(skb); + hlen = off_gnv + sizeof(*gh); + gh = skb_gro_header_fast(skb, off_gnv); + if (skb_gro_header_hard(skb, hlen)) { + gh = skb_gro_header_slow(skb, hlen, off_gnv); + if (unlikely(!gh)) + goto out; + } + + if (gh->ver != GENEVE_VER || gh->oam) + goto out; + gh_len = geneve_hlen(gh); + + hlen = off_gnv + gh_len; + if (skb_gro_header_hard(skb, hlen)) { + gh = skb_gro_header_slow(skb, hlen, off_gnv); + if (unlikely(!gh)) + goto out; + } + + flush = 0; + + for (p = *head; p; p = p->next) { + if (!NAPI_GRO_CB(p)->same_flow) + continue; + + gh2 = (struct genevehdr *)(p->data + off_gnv); + if (gh->opt_len != gh2->opt_len || + memcmp(gh, gh2, gh_len)) { + NAPI_GRO_CB(p)->same_flow = 0; + continue; + } + } + + type = gh->proto_type; + + rcu_read_lock(); + ptype = gro_find_receive_by_type(type); + if (ptype == NULL) { + flush = 1; + goto out_unlock; + } + + skb_gro_pull(skb, gh_len); + skb_gro_postpull_rcsum(skb, gh, gh_len); + pp = ptype->callbacks.gro_receive(head, skb); + +out_unlock: + rcu_read_unlock(); +out: + NAPI_GRO_CB(skb)->flush |= flush; + + return pp; +} + +static int geneve_gro_complete(struct sk_buff *skb, int nhoff) +{ + struct genevehdr *gh; + struct packet_offload *ptype; + __be16 type; + int gh_len; + int err = -ENOSYS; + + udp_tunnel_gro_complete(skb, nhoff); + + gh = (struct genevehdr *)(skb->data + nhoff); + gh_len = geneve_hlen(gh); + type = gh->proto_type; + + rcu_read_lock(); + ptype = gro_find_complete_by_type(type); + if (ptype != NULL) + err = ptype->callbacks.gro_complete(skb, nhoff + gh_len); + + rcu_read_unlock(); + return err; +} + static void geneve_notify_add_rx_port(struct geneve_sock *gs) { struct sock *sk = gs->sock->sk; @@ -278,8 +371,8 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port, /* Initialize the geneve udp offloads structure */ gs->udp_offloads.port = port; - gs->udp_offloads.callbacks.gro_receive = NULL; - gs->udp_offloads.callbacks.gro_complete = NULL; + gs->udp_offloads.callbacks.gro_receive = geneve_gro_receive; + gs->udp_offloads.callbacks.gro_complete = geneve_gro_complete; spin_lock(&gn->sock_lock); hlist_add_head_rcu(&gs->hlist, gs_head(net, port)); -- cgit v0.10.2 From e8768f971558019ed83eee8210375cd2143deef2 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 31 Dec 2014 13:33:41 +0100 Subject: net: skbuff: don't zero tc members when freeing skb Not needed, only four cases: - kfree_skb (or one of its aliases). Don't need to zero, memory will be freed. - kfree_skb_partial and head was stolen: memory will be freed. - skb_morph: The skb header fields (including tc ones) will be copied over from the 'to-be-morphed' skb right after skb_release_head_state returns. - skb_segment: Same as before, all the skb header fields are copied over from the original skb right away. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 395c15b..5a2a2e8 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -677,13 +677,6 @@ static void skb_release_head_state(struct sk_buff *skb) #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) nf_bridge_put(skb->nf_bridge); #endif -/* XXX: IS this still necessary? - JHS */ -#ifdef CONFIG_NET_SCHED - skb->tc_index = 0; -#ifdef CONFIG_NET_CLS_ACT - skb->tc_verd = 0; -#endif -#endif } /* Free everything but the sk_buff shell. */ -- cgit v0.10.2 From 300acfdec916be696373864226267b11302d3b84 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 31 Dec 2014 14:43:16 -0800 Subject: Bluetooth: Introduce force_bredr_smp debugfs option for testing Testing cross-transport pairing that starts on BR/EDR is only valid when using a controller with BR/EDR Secure Connections. Devices will indicate this by providing BR/EDR SMP fixed channel over L2CAP. To allow testing of this feature on Bluetooth 4.0 controller or controllers without the BR/EDR Secure Connections features, introduce a force_bredr_smp debugfs option that allows faking the required AES connection. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 1849a43..fddb93f 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -174,6 +174,7 @@ enum { HCI_DUT_MODE, HCI_FORCE_SC, HCI_FORCE_LESC, + HCI_FORCE_BREDR_SMP, HCI_FORCE_STATIC_ADDR, }; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d04dc00..11029b2 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -6968,7 +6968,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) if (test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags) && (bredr_sc_enabled(hcon->hdev) || - test_bit(HCI_FORCE_LESC, &hcon->hdev->dbg_flags))) + test_bit(HCI_FORCE_BREDR_SMP, &hcon->hdev->dbg_flags))) conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR; mutex_init(&conn->ident_lock); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 358264c..73643a6 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -20,6 +20,7 @@ SOFTWARE IS DISCLAIMED. */ +#include #include #include #include @@ -1675,7 +1676,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) if (conn->hcon->type == ACL_LINK) { /* We must have a BR/EDR SC link */ if (!test_bit(HCI_CONN_AES_CCM, &conn->hcon->flags) && - !test_bit(HCI_FORCE_LESC, &hdev->dbg_flags)) + !test_bit(HCI_FORCE_BREDR_SMP, &hdev->dbg_flags)) return SMP_CROSS_TRANSP_NOT_ALLOWED; set_bit(SMP_FLAG_SC, &smp->flags); @@ -2738,7 +2739,7 @@ static void bredr_pairing(struct l2cap_chan *chan) /* BR/EDR must use Secure Connections for SMP */ if (!test_bit(HCI_CONN_AES_CCM, &hcon->flags) && - !test_bit(HCI_FORCE_LESC, &hdev->dbg_flags)) + !test_bit(HCI_FORCE_BREDR_SMP, &hdev->dbg_flags)) return; /* If our LE support is not enabled don't do anything */ @@ -2976,6 +2977,66 @@ static void smp_del_chan(struct l2cap_chan *chan) l2cap_chan_put(chan); } +static ssize_t force_bredr_smp_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[3]; + + buf[0] = test_bit(HCI_FORCE_BREDR_SMP, &hdev->dbg_flags) ? 'Y': 'N'; + buf[1] = '\n'; + buf[2] = '\0'; + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static ssize_t force_bredr_smp_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[32]; + size_t buf_size = min(count, (sizeof(buf)-1)); + bool enable; + + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + buf[buf_size] = '\0'; + if (strtobool(buf, &enable)) + return -EINVAL; + + if (enable == test_bit(HCI_FORCE_BREDR_SMP, &hdev->dbg_flags)) + return -EALREADY; + + if (enable) { + struct l2cap_chan *chan; + + chan = smp_add_cid(hdev, L2CAP_CID_SMP_BREDR); + if (IS_ERR(chan)) + return PTR_ERR(chan); + + hdev->smp_bredr_data = chan; + } else { + struct l2cap_chan *chan; + + chan = hdev->smp_bredr_data; + hdev->smp_bredr_data = NULL; + smp_del_chan(chan); + } + + change_bit(HCI_FORCE_BREDR_SMP, &hdev->dbg_flags); + + return count; +} + +static const struct file_operations force_bredr_smp_fops = { + .open = simple_open, + .read = force_bredr_smp_read, + .write = force_bredr_smp_write, + .llseek = default_llseek, +}; + int smp_register(struct hci_dev *hdev) { struct l2cap_chan *chan; @@ -2988,9 +3049,18 @@ int smp_register(struct hci_dev *hdev) hdev->smp_data = chan; - if (!lmp_sc_capable(hdev) && - !test_bit(HCI_FORCE_LESC, &hdev->dbg_flags)) + /* If the controller does not support BR/EDR Secure Connections + * feature, then the BR/EDR SMP channel shall not be present. + * + * To test this with Bluetooth 4.0 controllers, create a debugfs + * switch that allows forcing BR/EDR SMP support and accepting + * cross-transport pairing on non-AES encrypted connections. + */ + if (!lmp_sc_capable(hdev)) { + debugfs_create_file("force_bredr_smp", 0644, hdev->debugfs, + hdev, &force_bredr_smp_fops); return 0; + } chan = smp_add_cid(hdev, L2CAP_CID_SMP_BREDR); if (IS_ERR(chan)) { -- cgit v0.10.2 From 91389af67c3a8d8f9eca5f51deda58fae4d9326e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 31 Dec 2014 14:43:17 -0800 Subject: Bluetooth: Remove broken force_lesc_support debugfs option The force_lesc_support debugfs option never really worked. It has a race condition between creating the debugfs entry and registering the L2CAP fixed channel for BR/EDR SMP support. Also this has been replaced with a working force_bredr_smp debugfs switch that developers can use now. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index fddb93f..884ba00 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -173,7 +173,6 @@ enum { enum { HCI_DUT_MODE, HCI_FORCE_SC, - HCI_FORCE_LESC, HCI_FORCE_BREDR_SMP, HCI_FORCE_STATIC_ADDR, }; diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index ee33ce8..dc8f994 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -421,50 +421,6 @@ static const struct file_operations force_sc_support_fops = { .llseek = default_llseek, }; -static ssize_t force_lesc_support_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct hci_dev *hdev = file->private_data; - char buf[3]; - - buf[0] = test_bit(HCI_FORCE_LESC, &hdev->dbg_flags) ? 'Y': 'N'; - buf[1] = '\n'; - buf[2] = '\0'; - return simple_read_from_buffer(user_buf, count, ppos, buf, 2); -} - -static ssize_t force_lesc_support_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct hci_dev *hdev = file->private_data; - char buf[32]; - size_t buf_size = min(count, (sizeof(buf)-1)); - bool enable; - - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; - if (strtobool(buf, &enable)) - return -EINVAL; - - if (enable == test_bit(HCI_FORCE_LESC, &hdev->dbg_flags)) - return -EALREADY; - - change_bit(HCI_FORCE_LESC, &hdev->dbg_flags); - - return count; -} - -static const struct file_operations force_lesc_support_fops = { - .open = simple_open, - .read = force_lesc_support_read, - .write = force_lesc_support_write, - .llseek = default_llseek, -}; - static int idle_timeout_set(void *data, u64 val) { struct hci_dev *hdev = data; @@ -568,11 +524,6 @@ void hci_debugfs_create_bredr(struct hci_dev *hdev) debugfs_create_file("force_sc_support", 0644, hdev->debugfs, hdev, &force_sc_support_fops); - - if (lmp_le_capable(hdev)) - debugfs_create_file("force_lesc_support", 0644, - hdev->debugfs, hdev, - &force_lesc_support_fops); } if (lmp_sniff_capable(hdev)) { -- cgit v0.10.2 From 05b3c3e7905d00a1fe2e9184fdd9b5eac427c736 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 31 Dec 2014 14:43:18 -0800 Subject: Bluetooth: Remove no longer needed force_sc_support debugfs option The force_sc_support debugfs option was introduced to easily work with pre-production Bluetooth 4.1 silicon. This option is no longer needed since controllers supporting BR/EDR Secure Connections feature are now available. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 884ba00..aee16bf 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -172,7 +172,6 @@ enum { */ enum { HCI_DUT_MODE, - HCI_FORCE_SC, HCI_FORCE_BREDR_SMP, HCI_FORCE_STATIC_ADDR, }; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3e7e511..89f4e3c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1017,8 +1017,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \ !test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) -#define bredr_sc_enabled(dev) ((lmp_sc_capable(dev) || \ - test_bit(HCI_FORCE_SC, &(dev)->dbg_flags)) && \ +#define bredr_sc_enabled(dev) (lmp_sc_capable(dev) && \ test_bit(HCI_SC_ENABLED, &(dev)->dev_flags)) /* ----- HCI protocols ----- */ diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index dc8f994..d72ebc2 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -375,52 +375,6 @@ static const struct file_operations sc_only_mode_fops = { .llseek = default_llseek, }; -static ssize_t force_sc_support_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct hci_dev *hdev = file->private_data; - char buf[3]; - - buf[0] = test_bit(HCI_FORCE_SC, &hdev->dbg_flags) ? 'Y': 'N'; - buf[1] = '\n'; - buf[2] = '\0'; - return simple_read_from_buffer(user_buf, count, ppos, buf, 2); -} - -static ssize_t force_sc_support_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct hci_dev *hdev = file->private_data; - char buf[32]; - size_t buf_size = min(count, (sizeof(buf)-1)); - bool enable; - - if (test_bit(HCI_UP, &hdev->flags)) - return -EBUSY; - - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; - if (strtobool(buf, &enable)) - return -EINVAL; - - if (enable == test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) - return -EALREADY; - - change_bit(HCI_FORCE_SC, &hdev->dbg_flags); - - return count; -} - -static const struct file_operations force_sc_support_fops = { - .open = simple_open, - .read = force_sc_support_read, - .write = force_sc_support_write, - .llseek = default_llseek, -}; - static int idle_timeout_set(void *data, u64 val) { struct hci_dev *hdev = data; @@ -521,9 +475,6 @@ void hci_debugfs_create_bredr(struct hci_dev *hdev) hdev, &auto_accept_delay_fops); debugfs_create_file("sc_only_mode", 0444, hdev->debugfs, hdev, &sc_only_mode_fops); - - debugfs_create_file("force_sc_support", 0644, hdev->debugfs, - hdev, &force_sc_support_fops); } if (lmp_sniff_capable(hdev)) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 3d2f7ad..6b3f553 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -570,8 +570,7 @@ static u32 get_supported_settings(struct hci_dev *hdev) settings |= MGMT_SETTING_HS; } - if (lmp_sc_capable(hdev) || - test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) + if (lmp_sc_capable(hdev)) settings |= MGMT_SETTING_SECURE_CONN; } @@ -4727,8 +4726,8 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev, BT_DBG("request for %s", hdev->name); - if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) && - !lmp_sc_capable(hdev) && !test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) + if (!lmp_sc_capable(hdev) && + !test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, MGMT_STATUS_NOT_SUPPORTED); @@ -4738,9 +4737,7 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); - if (!hdev_is_powered(hdev) || - (!lmp_sc_capable(hdev) && - !test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) || + if (!hdev_is_powered(hdev) || !lmp_sc_capable(hdev) || !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { bool changed; -- cgit v0.10.2 From cb0d2faeb1356f7d453bab3f1028f169d8ffe99c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 31 Dec 2014 14:43:19 -0800 Subject: Bluetooth: Fix scope of sc_only_mode debugfs entry The sc_only_mode debugfs entry is used to read the current state of the Secure Connections Only mode. Before Bluetooth 4.2 this mode was only for BR/EDR controllers and with that tight to the support Secure Simple Pairing. Since Secure Connections is now available for BR/EDR and LE this debugfs entry is no longer correctly place. Move it to the common section and enable it when either BR/EDR Secure Connections feature is supported or when the controller has LE support. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index d72ebc2..ead89a5 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -212,6 +212,24 @@ static int conn_info_max_age_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(conn_info_max_age_fops, conn_info_max_age_get, conn_info_max_age_set, "%llu\n"); +static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[3]; + + buf[0] = test_bit(HCI_SC_ONLY, &hdev->dev_flags) ? 'Y': 'N'; + buf[1] = '\n'; + buf[2] = '\0'; + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static const struct file_operations sc_only_mode_fops = { + .open = simple_open, + .read = sc_only_mode_read, + .llseek = default_llseek, +}; + void hci_debugfs_create_common(struct hci_dev *hdev) { debugfs_create_file("features", 0444, hdev->debugfs, hdev, @@ -230,6 +248,10 @@ void hci_debugfs_create_common(struct hci_dev *hdev) &conn_info_min_age_fops); debugfs_create_file("conn_info_max_age", 0644, hdev->debugfs, hdev, &conn_info_max_age_fops); + + if (lmp_sc_capable(hdev) || lmp_le_capable(hdev)) + debugfs_create_file("sc_only_mode", 0444, hdev->debugfs, + hdev, &sc_only_mode_fops); } static int inquiry_cache_show(struct seq_file *f, void *p) @@ -357,24 +379,6 @@ static int auto_accept_delay_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get, auto_accept_delay_set, "%llu\n"); -static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct hci_dev *hdev = file->private_data; - char buf[3]; - - buf[0] = test_bit(HCI_SC_ONLY, &hdev->dev_flags) ? 'Y': 'N'; - buf[1] = '\n'; - buf[2] = '\0'; - return simple_read_from_buffer(user_buf, count, ppos, buf, 2); -} - -static const struct file_operations sc_only_mode_fops = { - .open = simple_open, - .read = sc_only_mode_read, - .llseek = default_llseek, -}; - static int idle_timeout_set(void *data, u64 val) { struct hci_dev *hdev = data; @@ -470,12 +474,9 @@ void hci_debugfs_create_bredr(struct hci_dev *hdev) debugfs_create_file("voice_setting", 0444, hdev->debugfs, hdev, &voice_setting_fops); - if (lmp_ssp_capable(hdev)) { + if (lmp_ssp_capable(hdev)) debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs, hdev, &auto_accept_delay_fops); - debugfs_create_file("sc_only_mode", 0444, hdev->debugfs, - hdev, &sc_only_mode_fops); - } if (lmp_sniff_capable(hdev)) { debugfs_create_file("idle_timeout", 0644, hdev->debugfs, -- cgit v0.10.2 From 203de21bf6b19f81267c373ca5323e6386fe5809 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 31 Dec 2014 20:01:22 -0800 Subject: Bluetooth: Fix for a leftover debug of pairing credentials One of the LE Secure Connections security credentials was still using the BT_DBG instead of SMP_DBG. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 73643a6..96c7d51 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -300,7 +300,7 @@ static int smp_f6(struct crypto_hash *tfm_cmac, const u8 w[16], if (err) return err; - BT_DBG("res %16phN", res); + SMP_DBG("res %16phN", res); return err; } -- cgit v0.10.2 From ec6cef9cd98d9d2dac8ec8f1d11b8a3a9524e390 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 1 Jan 2015 02:05:16 -0800 Subject: Bluetooth: Fix SMP channel registration for unconfigured controllers When the Bluetooth controllers requires an unconfigured state (for example when the BD_ADDR is missing), then it is important to try to register the SMP channels when the controller transitions to the configured state. This also fixes an issue with the debugfs entires that are not present for controllers that start out as unconfigured. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 5ef5221..6c12110 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -931,10 +931,20 @@ static int __hci_init(struct hci_dev *hdev) if (err < 0) return err; - /* Only create debugfs entries during the initial setup - * phase and not every time the controller gets powered on. + /* This function is only called when the controller is actually in + * configured state. When the controller is marked as unconfigured, + * this initialization procedure is not run. + * + * It means that it is possible that a controller runs through its + * setup phase and then discovers missing settings. If that is the + * case, then this function will not be called. It then will only + * be called during the config phase. + * + * So only when in setup phase or config phase, create the debugfs + * entries and register the SMP channels. */ - if (!test_bit(HCI_SETUP, &hdev->dev_flags)) + if (!test_bit(HCI_SETUP, &hdev->dev_flags) && + !test_bit(HCI_CONFIG, &hdev->dev_flags)) return 0; hci_debugfs_create_common(hdev); -- cgit v0.10.2 From 4fcef8ed75afb32e4ef892fd838e89754ee72c12 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 1 Jan 2015 17:34:37 -0800 Subject: Bluetooth: Fix issue with Roper Class 1 Bluetooth Dongle The Roper Class 1 Bluetooth Dongle is another device that claims to support Bluetooth 1.2 specification, but does not support the HCI command for reading the local supported commands. < HCI Command: Read Local Version Information (0x04|0x0001) plen 0 > HCI Event: Command Complete (0x0e) plen 12 Read Local Version Information (0x04|0x0001) ncmd 1 status 0x00 HCI Version: 1.2 (0x2) HCI Revision: 0x0 LMP Version: 1.2 (0x2) LMP Subversion: 0x757 Manufacturer: Silicon Wave (11) It clearly claims Bluetooth 1.2 support and in that regard has the same issue as the AVM BlueFritz! USB devices (Silicon Wave based), but the HCI Read Local Supported Commands command fails. < HCI Command: Read Local Supported Commands (0x04|0x0002) plen 0 > HCI Event: Command Status (0x0f) plen 4 Read Local Supported Commands (0x04|0x0002) status 0x01 ncmd 1 Error: Unknown HCI Command Use the HCI_QUIRK_BROKEN_LOCAL_COMMANDS quirk for these devices and the failing command will be skipped. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index f051a93..8338813 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -49,7 +49,7 @@ static struct usb_driver btusb_driver; #define BTUSB_INTEL_BOOT 0x200 #define BTUSB_BCM_PATCHRAM 0x400 #define BTUSB_MARVELL 0x800 -#define BTUSB_AVM 0x1000 +#define BTUSB_SWAVE 0x1000 static const struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ @@ -86,7 +86,7 @@ static const struct usb_device_id btusb_table[] = { { USB_DEVICE(0x05ac, 0x8281) }, /* AVM BlueFRITZ! USB v2.0 */ - { USB_DEVICE(0x057c, 0x3800), .driver_info = BTUSB_AVM }, + { USB_DEVICE(0x057c, 0x3800), .driver_info = BTUSB_SWAVE }, /* Bluetooth Ultraport Module from IBM */ { USB_DEVICE(0x04bf, 0x030a) }, @@ -238,6 +238,9 @@ static const struct usb_device_id blacklist_table[] = { /* CONWISE Technology based adapters with buggy SCO support */ { USB_DEVICE(0x0e5e, 0x6622), .driver_info = BTUSB_BROKEN_ISOC }, + /* Roper Class 1 Bluetooth Dongle (Silicon Wave based) */ + { USB_DEVICE(0x1300, 0x0001), .driver_info = BTUSB_SWAVE }, + /* Digianswer devices */ { USB_DEVICE(0x08fd, 0x0001), .driver_info = BTUSB_DIGIANSWER }, { USB_DEVICE(0x08fd, 0x0002), .driver_info = BTUSB_IGNORE }, @@ -2081,7 +2084,7 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_MARVELL) hdev->set_bdaddr = btusb_set_bdaddr_marvell; - if (id->driver_info & BTUSB_AVM) + if (id->driver_info & BTUSB_SWAVE) set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks); if (id->driver_info & BTUSB_INTEL_BOOT) -- cgit v0.10.2 From 7841d5d622f8fb537e401f5b172c42bb111daf4a Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Thu, 1 Jan 2015 17:49:55 +0100 Subject: net: ethernet: chelsio: cxgb3: mc5.c: Remove some unused functions Removes some functions that are not used anywhere: dbgi_rd_rsp3() dbgi_wr_addr3() This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb3/mc5.c b/drivers/net/ethernet/chelsio/cxgb3/mc5.c index e13b7fe..338301b 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/mc5.c +++ b/drivers/net/ethernet/chelsio/cxgb3/mc5.c @@ -97,14 +97,6 @@ static int mc5_cmd_write(struct adapter *adapter, u32 cmd) F_DBGIRSPVALID, 1, MAX_WRITE_ATTEMPTS, 1); } -static inline void dbgi_wr_addr3(struct adapter *adapter, u32 v1, u32 v2, - u32 v3) -{ - t3_write_reg(adapter, A_MC5_DB_DBGI_REQ_ADDR0, v1); - t3_write_reg(adapter, A_MC5_DB_DBGI_REQ_ADDR1, v2); - t3_write_reg(adapter, A_MC5_DB_DBGI_REQ_ADDR2, v3); -} - static inline void dbgi_wr_data3(struct adapter *adapter, u32 v1, u32 v2, u32 v3) { @@ -113,14 +105,6 @@ static inline void dbgi_wr_data3(struct adapter *adapter, u32 v1, u32 v2, t3_write_reg(adapter, A_MC5_DB_DBGI_REQ_DATA2, v3); } -static inline void dbgi_rd_rsp3(struct adapter *adapter, u32 *v1, u32 *v2, - u32 *v3) -{ - *v1 = t3_read_reg(adapter, A_MC5_DB_DBGI_RSP_DATA0); - *v2 = t3_read_reg(adapter, A_MC5_DB_DBGI_RSP_DATA1); - *v3 = t3_read_reg(adapter, A_MC5_DB_DBGI_RSP_DATA2); -} - /* * Write data to the TCAM register at address (0, 0, addr_lo) using the TCAM * command cmd. The data to be written must have been set up by the caller. -- cgit v0.10.2 From 6af01a70f4f457dd6fdc556973e5429405bdf3aa Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Thu, 1 Jan 2015 18:01:26 +0100 Subject: net: fddi: skfp: smt.c: Remove unused function Remove the function smt_ifconfig() that is not used anywhere. This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: David S. Miller diff --git a/drivers/net/fddi/skfp/smt.c b/drivers/net/fddi/skfp/smt.c index 9edada8..cd78b7c 100644 --- a/drivers/net/fddi/skfp/smt.c +++ b/drivers/net/fddi/skfp/smt.c @@ -1736,18 +1736,6 @@ char *addr_to_string(struct fddi_addr *addr) } #endif -#ifdef AM29K -int smt_ifconfig(int argc, char *argv[]) -{ - if (argc >= 2 && !strcmp(argv[0],"opt_bypass") && - !strcmp(argv[1],"yes")) { - smc->mib.fddiSMTBypassPresent = 1 ; - return 0; - } - return amdfddi_config(0, argc, argv); -} -#endif - /* * return static mac index */ -- cgit v0.10.2 From 50ab97d71b488a8c9e1fc97815566e3dd4d291d9 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Thu, 1 Jan 2015 20:17:22 +0100 Subject: isdn: hisax: hfc4s8s_l1: Remove some unused functions Removes some functions that are not used anywhere: Read_hfc32() Write_hfc32() Write_hfc16() This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: David S. Miller diff --git a/drivers/isdn/hisax/hfc4s8s_l1.c b/drivers/isdn/hisax/hfc4s8s_l1.c index fc9f9d0..0e5d673 100644 --- a/drivers/isdn/hisax/hfc4s8s_l1.c +++ b/drivers/isdn/hisax/hfc4s8s_l1.c @@ -225,20 +225,6 @@ fWrite_hfc8(hfc4s8s_hw *a, u_char c) } static inline void -Write_hfc16(hfc4s8s_hw *a, u_char b, u_short c) -{ - SetRegAddr(a, b); - outw(c, a->iobase); -} - -static inline void -Write_hfc32(hfc4s8s_hw *a, u_char b, u_long c) -{ - SetRegAddr(a, b); - outl(c, a->iobase); -} - -static inline void fWrite_hfc32(hfc4s8s_hw *a, u_long c) { outl(c, a->iobase); @@ -266,13 +252,6 @@ Read_hfc16(hfc4s8s_hw *a, u_char b) } static inline u_long -Read_hfc32(hfc4s8s_hw *a, u_char b) -{ - SetRegAddr(a, b); - return (inl((volatile u_int) a->iobase)); -} - -static inline u_long fRead_hfc32(hfc4s8s_hw *a) { return (inl((volatile u_int) a->iobase)); -- cgit v0.10.2 From 3adc0becfec27cd582506b778df65d7c237e3215 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Fri, 2 Jan 2015 21:29:24 +0100 Subject: net: ethernet: cisco: enic: enic_dev: Remove some unused functions Removes some functions that are not used anywhere: enic_dev_enable2_done() enic_dev_enable2() enic_dev_deinit_done() enic_dev_init_prov2() enic_vnic_dev_deinit() This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/cisco/enic/enic_dev.c b/drivers/net/ethernet/cisco/enic/enic_dev.c index 87ddc44..f8d2a6a 100644 --- a/drivers/net/ethernet/cisco/enic/enic_dev.c +++ b/drivers/net/ethernet/cisco/enic/enic_dev.c @@ -177,40 +177,6 @@ int enic_dev_intr_coal_timer_info(struct enic *enic) return err; } -int enic_vnic_dev_deinit(struct enic *enic) -{ - int err; - - spin_lock_bh(&enic->devcmd_lock); - err = vnic_dev_deinit(enic->vdev); - spin_unlock_bh(&enic->devcmd_lock); - - return err; -} - -int enic_dev_init_prov2(struct enic *enic, struct vic_provinfo *vp) -{ - int err; - - spin_lock_bh(&enic->devcmd_lock); - err = vnic_dev_init_prov2(enic->vdev, - (u8 *)vp, vic_provinfo_size(vp)); - spin_unlock_bh(&enic->devcmd_lock); - - return err; -} - -int enic_dev_deinit_done(struct enic *enic, int *status) -{ - int err; - - spin_lock_bh(&enic->devcmd_lock); - err = vnic_dev_deinit_done(enic->vdev, status); - spin_unlock_bh(&enic->devcmd_lock); - - return err; -} - /* rtnl lock is held */ int enic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid) { @@ -237,28 +203,6 @@ int enic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid) return err; } -int enic_dev_enable2(struct enic *enic, int active) -{ - int err; - - spin_lock_bh(&enic->devcmd_lock); - err = vnic_dev_enable2(enic->vdev, active); - spin_unlock_bh(&enic->devcmd_lock); - - return err; -} - -int enic_dev_enable2_done(struct enic *enic, int *status) -{ - int err; - - spin_lock_bh(&enic->devcmd_lock); - err = vnic_dev_enable2_done(enic->vdev, status); - spin_unlock_bh(&enic->devcmd_lock); - - return err; -} - int enic_dev_status_to_errno(int devcmd_status) { switch (devcmd_status) { diff --git a/drivers/net/ethernet/cisco/enic/enic_dev.h b/drivers/net/ethernet/cisco/enic/enic_dev.h index 10bb970..f5bb058 100644 --- a/drivers/net/ethernet/cisco/enic/enic_dev.h +++ b/drivers/net/ethernet/cisco/enic/enic_dev.h @@ -55,11 +55,6 @@ int enic_dev_set_ig_vlan_rewrite_mode(struct enic *enic); int enic_dev_enable(struct enic *enic); int enic_dev_disable(struct enic *enic); int enic_dev_intr_coal_timer_info(struct enic *enic); -int enic_vnic_dev_deinit(struct enic *enic); -int enic_dev_init_prov2(struct enic *enic, struct vic_provinfo *vp); -int enic_dev_deinit_done(struct enic *enic, int *status); -int enic_dev_enable2(struct enic *enic, int arg); -int enic_dev_enable2_done(struct enic *enic, int *status); int enic_dev_status_to_errno(int devcmd_status); #endif /* _ENIC_DEV_H_ */ -- cgit v0.10.2 From b422da7c366fbf0899db1fdc808fc5fd6700dec5 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Fri, 2 Jan 2015 11:18:21 -0800 Subject: MAINTAINERS: Update Open vSwitch entry. OVS development is moved to netdev mailing list. Update tree and list in MAINTAINERS file. Signed-off-by: Pravin B Shelar Signed-off-by: David S. Miller diff --git a/MAINTAINERS b/MAINTAINERS index ddb9ac8..3309895 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7008,11 +7008,12 @@ F: arch/openrisc/ OPENVSWITCH M: Pravin Shelar +L: netdev@vger.kernel.org L: dev@openvswitch.org W: http://openvswitch.org -T: git git://git.kernel.org/pub/scm/linux/kernel/git/pshelar/openvswitch.git S: Maintained F: net/openvswitch/ +F: include/uapi/linux/openvswitch.h OPL4 DRIVER M: Clemens Ladisch -- cgit v0.10.2 From 1891172aa5c32f08ad9931b794edd71e91a4a527 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Fri, 2 Jan 2015 20:22:03 +0100 Subject: timecounter: provide a macro to initialize the cyclecounter mask field. There is no need for users of the timecounter/cyclecounter code to include clocksource.h just for a single macro. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/include/linux/timecounter.h b/include/linux/timecounter.h index 74f4549..4382035 100644 --- a/include/linux/timecounter.h +++ b/include/linux/timecounter.h @@ -19,6 +19,9 @@ #include +/* simplify initialization of mask field */ +#define CYCLECOUNTER_MASK(bits) (cycle_t)((bits) < 64 ? ((1ULL<<(bits))-1) : -1) + /** * struct cyclecounter - hardware abstraction for a free running counter * Provides completely state-free accessors to the underlying hardware. @@ -29,7 +32,7 @@ * @read: returns the current cycle value * @mask: bitmask for two's complement * subtraction of non 64 bit counters, - * see CLOCKSOURCE_MASK() helper macro + * see CYCLECOUNTER_MASK() helper macro * @mult: cycle to nanosecond multiplier * @shift: cycle to nanosecond divisor (power of two) */ -- cgit v0.10.2 From f28ba401dbd9e9fe63d9a7f9101638ca709185b2 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Fri, 2 Jan 2015 20:22:04 +0100 Subject: bnx2x: convert to CYCLECOUNTER_MASK macro. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 2c95132..0758c8b 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -14610,7 +14610,7 @@ static void bnx2x_init_cyclecounter(struct bnx2x *bp) { memset(&bp->cyclecounter, 0, sizeof(bp->cyclecounter)); bp->cyclecounter.read = bnx2x_cyclecounter_read; - bp->cyclecounter.mask = CLOCKSOURCE_MASK(64); + bp->cyclecounter.mask = CYCLECOUNTER_MASK(64); bp->cyclecounter.shift = 1; bp->cyclecounter.mult = 1; } -- cgit v0.10.2 From 4d045b4c06b1f7d65d8e69d39821dcfaa783feea Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Fri, 2 Jan 2015 20:22:05 +0100 Subject: e1000e: convert to CYCLECOUNTER_MASK macro. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 2537d36a..332a298 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -43,7 +43,6 @@ #include #include #include -#include #include "e1000.h" @@ -4190,7 +4189,7 @@ static int e1000_sw_init(struct e1000_adapter *adapter) /* Setup hardware time stamping cyclecounter */ if (adapter->flags & FLAG_HAS_HW_TIMESTAMP) { adapter->cc.read = e1000e_cyclecounter_read; - adapter->cc.mask = CLOCKSOURCE_MASK(64); + adapter->cc.mask = CYCLECOUNTER_MASK(64); adapter->cc.mult = 1; /* cc.shift set in e1000e_get_base_tininca() */ -- cgit v0.10.2 From b57c894040893da41085334d31f159df94c814b4 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Fri, 2 Jan 2015 20:22:06 +0100 Subject: igb: convert to CYCLECOUNTER_MASK macro. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index 8baf3fd..5e7a4e3 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -19,7 +19,6 @@ #include #include #include -#include #include "igb.h" @@ -766,7 +765,7 @@ void igb_ptp_init(struct igb_adapter *adapter) adapter->ptp_caps.settime = igb_ptp_settime_82576; adapter->ptp_caps.enable = igb_ptp_feature_enable; adapter->cc.read = igb_ptp_read_82576; - adapter->cc.mask = CLOCKSOURCE_MASK(64); + adapter->cc.mask = CYCLECOUNTER_MASK(64); adapter->cc.mult = 1; adapter->cc.shift = IGB_82576_TSYNC_SHIFT; /* Dial the nominal frequency. */ @@ -786,7 +785,7 @@ void igb_ptp_init(struct igb_adapter *adapter) adapter->ptp_caps.settime = igb_ptp_settime_82576; adapter->ptp_caps.enable = igb_ptp_feature_enable; adapter->cc.read = igb_ptp_read_82580; - adapter->cc.mask = CLOCKSOURCE_MASK(IGB_NBITS_82580); + adapter->cc.mask = CYCLECOUNTER_MASK(IGB_NBITS_82580); adapter->cc.mult = 1; adapter->cc.shift = 0; /* Enable the timer functions by clearing bit 31. */ -- cgit v0.10.2 From d312da293f787e1b19c57acb58e8c1b171c4a04a Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Fri, 2 Jan 2015 20:22:07 +0100 Subject: ixgbe: convert to CYCLECOUNTER_MASK macro. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c index 47c29ea..79c00f5 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c @@ -793,7 +793,7 @@ void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter) memset(&adapter->cc, 0, sizeof(adapter->cc)); adapter->cc.read = ixgbe_ptp_read; - adapter->cc.mask = CLOCKSOURCE_MASK(64); + adapter->cc.mask = CYCLECOUNTER_MASK(64); adapter->cc.shift = shift; adapter->cc.mult = 1; -- cgit v0.10.2 From d9f393734af52b7b09f02439164cc7182e17063c Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Fri, 2 Jan 2015 20:22:08 +0100 Subject: mlx4: include clocksource.h again This driver uses the function, clocksource_khz2mult, and so it really must include clocksource.h. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/en_clock.c b/drivers/net/ethernet/mellanox/mlx4/en_clock.c index e9cce4f..90b5309 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_clock.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_clock.c @@ -32,6 +32,7 @@ */ #include +#include #include "mlx4_en.h" -- cgit v0.10.2 From 5ce07a5cef5094168d25296773681bc287e21e3b Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Fri, 2 Jan 2015 20:22:09 +0100 Subject: microblaze: include the new timecounter header. The timecounter/cyclecounter code has moved, so users need the new include. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/arch/microblaze/kernel/timer.c b/arch/microblaze/kernel/timer.c index dd96f0e..c897745 100644 --- a/arch/microblaze/kernel/timer.c +++ b/arch/microblaze/kernel/timer.c @@ -17,6 +17,7 @@ #include #include #include +#include #include static void __iomem *timer_baseaddr; -- cgit v0.10.2 From 955d7fc93ed095c74fdc439c2893a87ca1652148 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 31 Dec 2014 19:39:08 +0100 Subject: ieee802154: handle socket functionality as module This patch makes the ieee802154 socket handling as module. Currently this is part of ieee802154 module. It pointed out that ieee802154 module has also two module_init/module_exit functions. One inside of core.c and the other in af_ieee802154.c. This patch will also solve this issue by handle the af_802154 as separate module. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/Kconfig b/net/ieee802154/Kconfig index c0d4154..98a190d 100644 --- a/net/ieee802154/Kconfig +++ b/net/ieee802154/Kconfig @@ -10,6 +10,15 @@ config IEEE802154 Say Y here to compile LR-WPAN support into the kernel or say M to compile it as modules. +config IEEE802154_SOCKET + tristate "IEEE 802.15.4 socket interface" + depends on IEEE802154 + default y + ---help--- + Socket interface for IEEE 802.15.4. Contains DGRAM sockets interface + for 802.15.4 dataframes. Also RAW socket interface to build MAC + header from userspace. + config IEEE802154_6LOWPAN tristate "6lowpan support over IEEE 802.15.4" depends on IEEE802154 && 6LOWPAN diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile index 9f6970f..53612d1 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile @@ -1,4 +1,5 @@ -obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o +obj-$(CONFIG_IEEE802154) += ieee802154.o +obj-$(CONFIG_IEEE802154_SOCKET) += af_802154.o obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o -- cgit v0.10.2 From 79300e3a438f93c7431b4bf40fa18580ccb34be1 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 31 Dec 2014 19:39:09 +0100 Subject: ieee802154: socket: change module name This patch changes the module name of af_802154 to ieee802154_socket. Just for keeping the name convention according to the 6LoWPAN module ieee802154_6lowpan. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile index 53612d1..19b2bec 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile @@ -1,10 +1,10 @@ obj-$(CONFIG_IEEE802154) += ieee802154.o -obj-$(CONFIG_IEEE802154_SOCKET) += af_802154.o +obj-$(CONFIG_IEEE802154_SOCKET) += ieee802154_socket.o obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \ header_ops.o sysfs.o nl802154.o -af_802154-y := af_ieee802154.o raw.o dgram.o +ieee802154_socket-y := af_ieee802154.o raw.o dgram.o ccflags-y += -D__CHECK_ENDIAN__ -- cgit v0.10.2 From 78f821b648267d3ee1a63093cd0c41cc99b97727 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 31 Dec 2014 19:39:10 +0100 Subject: ieee802154: socket: put handling into one file This patch puts all related socket handling into one file. This is just a cleanup to do all socket handling stuff inside of one implementation file. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile index 19b2bec..e6ddfcd 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile @@ -5,6 +5,6 @@ obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \ header_ops.o sysfs.o nl802154.o -ieee802154_socket-y := af_ieee802154.o raw.o dgram.o +ieee802154_socket-y := af_ieee802154.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/ieee802154/af802154.h b/net/ieee802154/af802154.h deleted file mode 100644 index 343b63e..0000000 --- a/net/ieee802154/af802154.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Internal interfaces for ieee 802.15.4 address family. - * - * Copyright 2007, 2008, 2009 Siemens AG - * - * 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. - * - * Written by: - * Sergey Lapin - * Dmitry Eremin-Solenikov - */ - -#ifndef AF802154_H -#define AF802154_H - -struct sk_buff; -struct net_device; -struct ieee802154_addr; -extern struct proto ieee802154_raw_prot; -extern struct proto ieee802154_dgram_prot; -void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb); -int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb); -struct net_device *ieee802154_get_dev(struct net *net, - const struct ieee802154_addr *addr); - -#endif diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c index d0a1282..64beb0b 100644 --- a/net/ieee802154/af_ieee802154.c +++ b/net/ieee802154/af_ieee802154.c @@ -34,10 +34,8 @@ #include #include -#include "af802154.h" - /* Utility function for families */ -struct net_device* +static struct net_device* ieee802154_get_dev(struct net *net, const struct ieee802154_addr *addr) { struct net_device *dev = NULL; @@ -182,6 +180,248 @@ static int ieee802154_sock_ioctl(struct socket *sock, unsigned int cmd, } } +/* RAW Sockets (802.15.4 created in userspace) */ +static HLIST_HEAD(raw_head); +static DEFINE_RWLOCK(raw_lock); + +static void raw_hash(struct sock *sk) +{ + write_lock_bh(&raw_lock); + sk_add_node(sk, &raw_head); + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); + write_unlock_bh(&raw_lock); +} + +static void raw_unhash(struct sock *sk) +{ + write_lock_bh(&raw_lock); + if (sk_del_node_init(sk)) + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); + write_unlock_bh(&raw_lock); +} + +static void raw_close(struct sock *sk, long timeout) +{ + sk_common_release(sk); +} + +static int raw_bind(struct sock *sk, struct sockaddr *_uaddr, int len) +{ + struct ieee802154_addr addr; + struct sockaddr_ieee802154 *uaddr = (struct sockaddr_ieee802154 *)_uaddr; + int err = 0; + struct net_device *dev = NULL; + + if (len < sizeof(*uaddr)) + return -EINVAL; + + uaddr = (struct sockaddr_ieee802154 *)_uaddr; + if (uaddr->family != AF_IEEE802154) + return -EINVAL; + + lock_sock(sk); + + ieee802154_addr_from_sa(&addr, &uaddr->addr); + dev = ieee802154_get_dev(sock_net(sk), &addr); + if (!dev) { + err = -ENODEV; + goto out; + } + + if (dev->type != ARPHRD_IEEE802154) { + err = -ENODEV; + goto out_put; + } + + sk->sk_bound_dev_if = dev->ifindex; + sk_dst_reset(sk); + +out_put: + dev_put(dev); +out: + release_sock(sk); + + return err; +} + +static int raw_connect(struct sock *sk, struct sockaddr *uaddr, + int addr_len) +{ + return -ENOTSUPP; +} + +static int raw_disconnect(struct sock *sk, int flags) +{ + return 0; +} + +static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, + struct msghdr *msg, size_t size) +{ + struct net_device *dev; + unsigned int mtu; + struct sk_buff *skb; + int hlen, tlen; + int err; + + if (msg->msg_flags & MSG_OOB) { + pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags); + return -EOPNOTSUPP; + } + + lock_sock(sk); + if (!sk->sk_bound_dev_if) + dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154); + else + dev = dev_get_by_index(sock_net(sk), sk->sk_bound_dev_if); + release_sock(sk); + + if (!dev) { + pr_debug("no dev\n"); + err = -ENXIO; + goto out; + } + + mtu = dev->mtu; + pr_debug("name = %s, mtu = %u\n", dev->name, mtu); + + if (size > mtu) { + pr_debug("size = %Zu, mtu = %u\n", size, mtu); + err = -EINVAL; + goto out_dev; + } + + hlen = LL_RESERVED_SPACE(dev); + tlen = dev->needed_tailroom; + skb = sock_alloc_send_skb(sk, hlen + tlen + size, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) + goto out_dev; + + skb_reserve(skb, hlen); + + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + + err = memcpy_from_msg(skb_put(skb, size), msg, size); + if (err < 0) + goto out_skb; + + skb->dev = dev; + skb->sk = sk; + skb->protocol = htons(ETH_P_IEEE802154); + + dev_put(dev); + + err = dev_queue_xmit(skb); + if (err > 0) + err = net_xmit_errno(err); + + return err ?: size; + +out_skb: + kfree_skb(skb); +out_dev: + dev_put(dev); +out: + return err; +} + +static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len, int noblock, int flags, int *addr_len) +{ + size_t copied = 0; + int err = -EOPNOTSUPP; + struct sk_buff *skb; + + skb = skb_recv_datagram(sk, flags, noblock, &err); + if (!skb) + goto out; + + copied = skb->len; + if (len < copied) { + msg->msg_flags |= MSG_TRUNC; + copied = len; + } + + err = skb_copy_datagram_msg(skb, 0, msg, copied); + if (err) + goto done; + + sock_recv_ts_and_drops(msg, sk, skb); + + if (flags & MSG_TRUNC) + copied = skb->len; +done: + skb_free_datagram(sk, skb); +out: + if (err) + return err; + return copied; +} + +static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + + if (sock_queue_rcv_skb(sk, skb) < 0) { + kfree_skb(skb); + return NET_RX_DROP; + } + + return NET_RX_SUCCESS; +} + +static void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb) +{ + struct sock *sk; + + read_lock(&raw_lock); + sk_for_each(sk, &raw_head) { + bh_lock_sock(sk); + if (!sk->sk_bound_dev_if || + sk->sk_bound_dev_if == dev->ifindex) { + struct sk_buff *clone; + + clone = skb_clone(skb, GFP_ATOMIC); + if (clone) + raw_rcv_skb(sk, clone); + } + bh_unlock_sock(sk); + } + read_unlock(&raw_lock); +} + +static int raw_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen) +{ + return -EOPNOTSUPP; +} + +static int raw_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen) +{ + return -EOPNOTSUPP; +} + +static struct proto ieee802154_raw_prot = { + .name = "IEEE-802.15.4-RAW", + .owner = THIS_MODULE, + .obj_size = sizeof(struct sock), + .close = raw_close, + .bind = raw_bind, + .sendmsg = raw_sendmsg, + .recvmsg = raw_recvmsg, + .hash = raw_hash, + .unhash = raw_unhash, + .connect = raw_connect, + .disconnect = raw_disconnect, + .getsockopt = raw_getsockopt, + .setsockopt = raw_setsockopt, +}; + static const struct proto_ops ieee802154_raw_ops = { .family = PF_IEEE802154, .owner = THIS_MODULE, @@ -207,6 +447,522 @@ static const struct proto_ops ieee802154_raw_ops = { #endif }; +/* DGRAM Sockets (802.15.4 dataframes) */ +static HLIST_HEAD(dgram_head); +static DEFINE_RWLOCK(dgram_lock); + +struct dgram_sock { + struct sock sk; + + struct ieee802154_addr src_addr; + struct ieee802154_addr dst_addr; + + unsigned int bound:1; + unsigned int connected:1; + unsigned int want_ack:1; + unsigned int secen:1; + unsigned int secen_override:1; + unsigned int seclevel:3; + unsigned int seclevel_override:1; +}; + +static inline struct dgram_sock *dgram_sk(const struct sock *sk) +{ + return container_of(sk, struct dgram_sock, sk); +} + +static void dgram_hash(struct sock *sk) +{ + write_lock_bh(&dgram_lock); + sk_add_node(sk, &dgram_head); + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); + write_unlock_bh(&dgram_lock); +} + +static void dgram_unhash(struct sock *sk) +{ + write_lock_bh(&dgram_lock); + if (sk_del_node_init(sk)) + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); + write_unlock_bh(&dgram_lock); +} + +static int dgram_init(struct sock *sk) +{ + struct dgram_sock *ro = dgram_sk(sk); + + ro->want_ack = 1; + return 0; +} + +static void dgram_close(struct sock *sk, long timeout) +{ + sk_common_release(sk); +} + +static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len) +{ + struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr; + struct ieee802154_addr haddr; + struct dgram_sock *ro = dgram_sk(sk); + int err = -EINVAL; + struct net_device *dev; + + lock_sock(sk); + + ro->bound = 0; + + if (len < sizeof(*addr)) + goto out; + + if (addr->family != AF_IEEE802154) + goto out; + + ieee802154_addr_from_sa(&haddr, &addr->addr); + dev = ieee802154_get_dev(sock_net(sk), &haddr); + if (!dev) { + err = -ENODEV; + goto out; + } + + if (dev->type != ARPHRD_IEEE802154) { + err = -ENODEV; + goto out_put; + } + + ro->src_addr = haddr; + + ro->bound = 1; + err = 0; +out_put: + dev_put(dev); +out: + release_sock(sk); + + return err; +} + +static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg) +{ + switch (cmd) { + case SIOCOUTQ: + { + int amount = sk_wmem_alloc_get(sk); + + return put_user(amount, (int __user *)arg); + } + + case SIOCINQ: + { + struct sk_buff *skb; + unsigned long amount; + + amount = 0; + spin_lock_bh(&sk->sk_receive_queue.lock); + skb = skb_peek(&sk->sk_receive_queue); + if (skb != NULL) { + /* We will only return the amount + * of this packet since that is all + * that will be read. + */ + amount = skb->len - ieee802154_hdr_length(skb); + } + spin_unlock_bh(&sk->sk_receive_queue.lock); + return put_user(amount, (int __user *)arg); + } + } + + return -ENOIOCTLCMD; +} + +/* FIXME: autobind */ +static int dgram_connect(struct sock *sk, struct sockaddr *uaddr, + int len) +{ + struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr; + struct dgram_sock *ro = dgram_sk(sk); + int err = 0; + + if (len < sizeof(*addr)) + return -EINVAL; + + if (addr->family != AF_IEEE802154) + return -EINVAL; + + lock_sock(sk); + + if (!ro->bound) { + err = -ENETUNREACH; + goto out; + } + + ieee802154_addr_from_sa(&ro->dst_addr, &addr->addr); + ro->connected = 1; + +out: + release_sock(sk); + return err; +} + +static int dgram_disconnect(struct sock *sk, int flags) +{ + struct dgram_sock *ro = dgram_sk(sk); + + lock_sock(sk); + ro->connected = 0; + release_sock(sk); + + return 0; +} + +static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk, + struct msghdr *msg, size_t size) +{ + struct net_device *dev; + unsigned int mtu; + struct sk_buff *skb; + struct ieee802154_mac_cb *cb; + struct dgram_sock *ro = dgram_sk(sk); + struct ieee802154_addr dst_addr; + int hlen, tlen; + int err; + + if (msg->msg_flags & MSG_OOB) { + pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags); + return -EOPNOTSUPP; + } + + if (!ro->connected && !msg->msg_name) + return -EDESTADDRREQ; + else if (ro->connected && msg->msg_name) + return -EISCONN; + + if (!ro->bound) + dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154); + else + dev = ieee802154_get_dev(sock_net(sk), &ro->src_addr); + + if (!dev) { + pr_debug("no dev\n"); + err = -ENXIO; + goto out; + } + mtu = dev->mtu; + pr_debug("name = %s, mtu = %u\n", dev->name, mtu); + + if (size > mtu) { + pr_debug("size = %Zu, mtu = %u\n", size, mtu); + err = -EMSGSIZE; + goto out_dev; + } + + hlen = LL_RESERVED_SPACE(dev); + tlen = dev->needed_tailroom; + skb = sock_alloc_send_skb(sk, hlen + tlen + size, + msg->msg_flags & MSG_DONTWAIT, + &err); + if (!skb) + goto out_dev; + + skb_reserve(skb, hlen); + + skb_reset_network_header(skb); + + cb = mac_cb_init(skb); + cb->type = IEEE802154_FC_TYPE_DATA; + cb->ackreq = ro->want_ack; + + if (msg->msg_name) { + DECLARE_SOCKADDR(struct sockaddr_ieee802154*, + daddr, msg->msg_name); + + ieee802154_addr_from_sa(&dst_addr, &daddr->addr); + } else { + dst_addr = ro->dst_addr; + } + + cb->secen = ro->secen; + cb->secen_override = ro->secen_override; + cb->seclevel = ro->seclevel; + cb->seclevel_override = ro->seclevel_override; + + err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &dst_addr, + ro->bound ? &ro->src_addr : NULL, size); + if (err < 0) + goto out_skb; + + err = memcpy_from_msg(skb_put(skb, size), msg, size); + if (err < 0) + goto out_skb; + + skb->dev = dev; + skb->sk = sk; + skb->protocol = htons(ETH_P_IEEE802154); + + dev_put(dev); + + err = dev_queue_xmit(skb); + if (err > 0) + err = net_xmit_errno(err); + + return err ?: size; + +out_skb: + kfree_skb(skb); +out_dev: + dev_put(dev); +out: + return err; +} + +static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk, + struct msghdr *msg, size_t len, int noblock, + int flags, int *addr_len) +{ + size_t copied = 0; + int err = -EOPNOTSUPP; + struct sk_buff *skb; + DECLARE_SOCKADDR(struct sockaddr_ieee802154 *, saddr, msg->msg_name); + + skb = skb_recv_datagram(sk, flags, noblock, &err); + if (!skb) + goto out; + + copied = skb->len; + if (len < copied) { + msg->msg_flags |= MSG_TRUNC; + copied = len; + } + + /* FIXME: skip headers if necessary ?! */ + err = skb_copy_datagram_msg(skb, 0, msg, copied); + if (err) + goto done; + + sock_recv_ts_and_drops(msg, sk, skb); + + if (saddr) { + saddr->family = AF_IEEE802154; + ieee802154_addr_to_sa(&saddr->addr, &mac_cb(skb)->source); + *addr_len = sizeof(*saddr); + } + + if (flags & MSG_TRUNC) + copied = skb->len; +done: + skb_free_datagram(sk, skb); +out: + if (err) + return err; + return copied; +} + +static int dgram_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + + if (sock_queue_rcv_skb(sk, skb) < 0) { + kfree_skb(skb); + return NET_RX_DROP; + } + + return NET_RX_SUCCESS; +} + +static inline bool +ieee802154_match_sock(__le64 hw_addr, __le16 pan_id, __le16 short_addr, + struct dgram_sock *ro) +{ + if (!ro->bound) + return true; + + if (ro->src_addr.mode == IEEE802154_ADDR_LONG && + hw_addr == ro->src_addr.extended_addr) + return true; + + if (ro->src_addr.mode == IEEE802154_ADDR_SHORT && + pan_id == ro->src_addr.pan_id && + short_addr == ro->src_addr.short_addr) + return true; + + return false; +} + +static int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb) +{ + struct sock *sk, *prev = NULL; + int ret = NET_RX_SUCCESS; + __le16 pan_id, short_addr; + __le64 hw_addr; + + /* Data frame processing */ + BUG_ON(dev->type != ARPHRD_IEEE802154); + + pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); + short_addr = ieee802154_mlme_ops(dev)->get_short_addr(dev); + hw_addr = ieee802154_devaddr_from_raw(dev->dev_addr); + + read_lock(&dgram_lock); + sk_for_each(sk, &dgram_head) { + if (ieee802154_match_sock(hw_addr, pan_id, short_addr, + dgram_sk(sk))) { + if (prev) { + struct sk_buff *clone; + + clone = skb_clone(skb, GFP_ATOMIC); + if (clone) + dgram_rcv_skb(prev, clone); + } + + prev = sk; + } + } + + if (prev) { + dgram_rcv_skb(prev, skb); + } else { + kfree_skb(skb); + ret = NET_RX_DROP; + } + read_unlock(&dgram_lock); + + return ret; +} + +static int dgram_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen) +{ + struct dgram_sock *ro = dgram_sk(sk); + + int val, len; + + if (level != SOL_IEEE802154) + return -EOPNOTSUPP; + + if (get_user(len, optlen)) + return -EFAULT; + + len = min_t(unsigned int, len, sizeof(int)); + + switch (optname) { + case WPAN_WANTACK: + val = ro->want_ack; + break; + case WPAN_SECURITY: + if (!ro->secen_override) + val = WPAN_SECURITY_DEFAULT; + else if (ro->secen) + val = WPAN_SECURITY_ON; + else + val = WPAN_SECURITY_OFF; + break; + case WPAN_SECURITY_LEVEL: + if (!ro->seclevel_override) + val = WPAN_SECURITY_LEVEL_DEFAULT; + else + val = ro->seclevel; + break; + default: + return -ENOPROTOOPT; + } + + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &val, len)) + return -EFAULT; + return 0; +} + +static int dgram_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen) +{ + struct dgram_sock *ro = dgram_sk(sk); + struct net *net = sock_net(sk); + int val; + int err = 0; + + if (optlen < sizeof(int)) + return -EINVAL; + + if (get_user(val, (int __user *)optval)) + return -EFAULT; + + lock_sock(sk); + + switch (optname) { + case WPAN_WANTACK: + ro->want_ack = !!val; + break; + case WPAN_SECURITY: + if (!ns_capable(net->user_ns, CAP_NET_ADMIN) && + !ns_capable(net->user_ns, CAP_NET_RAW)) { + err = -EPERM; + break; + } + + switch (val) { + case WPAN_SECURITY_DEFAULT: + ro->secen_override = 0; + break; + case WPAN_SECURITY_ON: + ro->secen_override = 1; + ro->secen = 1; + break; + case WPAN_SECURITY_OFF: + ro->secen_override = 1; + ro->secen = 0; + break; + default: + err = -EINVAL; + break; + } + break; + case WPAN_SECURITY_LEVEL: + if (!ns_capable(net->user_ns, CAP_NET_ADMIN) && + !ns_capable(net->user_ns, CAP_NET_RAW)) { + err = -EPERM; + break; + } + + if (val < WPAN_SECURITY_LEVEL_DEFAULT || + val > IEEE802154_SCF_SECLEVEL_ENC_MIC128) { + err = -EINVAL; + } else if (val == WPAN_SECURITY_LEVEL_DEFAULT) { + ro->seclevel_override = 0; + } else { + ro->seclevel_override = 1; + ro->seclevel = val; + } + break; + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + +static struct proto ieee802154_dgram_prot = { + .name = "IEEE-802.15.4-MAC", + .owner = THIS_MODULE, + .obj_size = sizeof(struct dgram_sock), + .init = dgram_init, + .close = dgram_close, + .bind = dgram_bind, + .sendmsg = dgram_sendmsg, + .recvmsg = dgram_recvmsg, + .hash = dgram_hash, + .unhash = dgram_unhash, + .connect = dgram_connect, + .disconnect = dgram_disconnect, + .ioctl = dgram_ioctl, + .getsockopt = dgram_getsockopt, + .setsockopt = dgram_setsockopt, +}; + static const struct proto_ops ieee802154_dgram_ops = { .family = PF_IEEE802154, .owner = THIS_MODULE, diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c deleted file mode 100644 index d1930b7..0000000 --- a/net/ieee802154/dgram.c +++ /dev/null @@ -1,549 +0,0 @@ -/* - * IEEE 802.15.4 dgram socket interface - * - * Copyright 2007, 2008 Siemens AG - * - * 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. - * - * Written by: - * Sergey Lapin - * Dmitry Eremin-Solenikov - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "af802154.h" - -static HLIST_HEAD(dgram_head); -static DEFINE_RWLOCK(dgram_lock); - -struct dgram_sock { - struct sock sk; - - struct ieee802154_addr src_addr; - struct ieee802154_addr dst_addr; - - unsigned int bound:1; - unsigned int connected:1; - unsigned int want_ack:1; - unsigned int secen:1; - unsigned int secen_override:1; - unsigned int seclevel:3; - unsigned int seclevel_override:1; -}; - -static inline struct dgram_sock *dgram_sk(const struct sock *sk) -{ - return container_of(sk, struct dgram_sock, sk); -} - -static void dgram_hash(struct sock *sk) -{ - write_lock_bh(&dgram_lock); - sk_add_node(sk, &dgram_head); - sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); - write_unlock_bh(&dgram_lock); -} - -static void dgram_unhash(struct sock *sk) -{ - write_lock_bh(&dgram_lock); - if (sk_del_node_init(sk)) - sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); - write_unlock_bh(&dgram_lock); -} - -static int dgram_init(struct sock *sk) -{ - struct dgram_sock *ro = dgram_sk(sk); - - ro->want_ack = 1; - return 0; -} - -static void dgram_close(struct sock *sk, long timeout) -{ - sk_common_release(sk); -} - -static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len) -{ - struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr; - struct ieee802154_addr haddr; - struct dgram_sock *ro = dgram_sk(sk); - int err = -EINVAL; - struct net_device *dev; - - lock_sock(sk); - - ro->bound = 0; - - if (len < sizeof(*addr)) - goto out; - - if (addr->family != AF_IEEE802154) - goto out; - - ieee802154_addr_from_sa(&haddr, &addr->addr); - dev = ieee802154_get_dev(sock_net(sk), &haddr); - if (!dev) { - err = -ENODEV; - goto out; - } - - if (dev->type != ARPHRD_IEEE802154) { - err = -ENODEV; - goto out_put; - } - - ro->src_addr = haddr; - - ro->bound = 1; - err = 0; -out_put: - dev_put(dev); -out: - release_sock(sk); - - return err; -} - -static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg) -{ - switch (cmd) { - case SIOCOUTQ: - { - int amount = sk_wmem_alloc_get(sk); - - return put_user(amount, (int __user *)arg); - } - - case SIOCINQ: - { - struct sk_buff *skb; - unsigned long amount; - - amount = 0; - spin_lock_bh(&sk->sk_receive_queue.lock); - skb = skb_peek(&sk->sk_receive_queue); - if (skb != NULL) { - /* We will only return the amount - * of this packet since that is all - * that will be read. - */ - amount = skb->len - ieee802154_hdr_length(skb); - } - spin_unlock_bh(&sk->sk_receive_queue.lock); - return put_user(amount, (int __user *)arg); - } - } - - return -ENOIOCTLCMD; -} - -/* FIXME: autobind */ -static int dgram_connect(struct sock *sk, struct sockaddr *uaddr, - int len) -{ - struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr; - struct dgram_sock *ro = dgram_sk(sk); - int err = 0; - - if (len < sizeof(*addr)) - return -EINVAL; - - if (addr->family != AF_IEEE802154) - return -EINVAL; - - lock_sock(sk); - - if (!ro->bound) { - err = -ENETUNREACH; - goto out; - } - - ieee802154_addr_from_sa(&ro->dst_addr, &addr->addr); - ro->connected = 1; - -out: - release_sock(sk); - return err; -} - -static int dgram_disconnect(struct sock *sk, int flags) -{ - struct dgram_sock *ro = dgram_sk(sk); - - lock_sock(sk); - ro->connected = 0; - release_sock(sk); - - return 0; -} - -static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t size) -{ - struct net_device *dev; - unsigned int mtu; - struct sk_buff *skb; - struct ieee802154_mac_cb *cb; - struct dgram_sock *ro = dgram_sk(sk); - struct ieee802154_addr dst_addr; - int hlen, tlen; - int err; - - if (msg->msg_flags & MSG_OOB) { - pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags); - return -EOPNOTSUPP; - } - - if (!ro->connected && !msg->msg_name) - return -EDESTADDRREQ; - else if (ro->connected && msg->msg_name) - return -EISCONN; - - if (!ro->bound) - dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154); - else - dev = ieee802154_get_dev(sock_net(sk), &ro->src_addr); - - if (!dev) { - pr_debug("no dev\n"); - err = -ENXIO; - goto out; - } - mtu = dev->mtu; - pr_debug("name = %s, mtu = %u\n", dev->name, mtu); - - if (size > mtu) { - pr_debug("size = %Zu, mtu = %u\n", size, mtu); - err = -EMSGSIZE; - goto out_dev; - } - - hlen = LL_RESERVED_SPACE(dev); - tlen = dev->needed_tailroom; - skb = sock_alloc_send_skb(sk, hlen + tlen + size, - msg->msg_flags & MSG_DONTWAIT, - &err); - if (!skb) - goto out_dev; - - skb_reserve(skb, hlen); - - skb_reset_network_header(skb); - - cb = mac_cb_init(skb); - cb->type = IEEE802154_FC_TYPE_DATA; - cb->ackreq = ro->want_ack; - - if (msg->msg_name) { - DECLARE_SOCKADDR(struct sockaddr_ieee802154*, - daddr, msg->msg_name); - - ieee802154_addr_from_sa(&dst_addr, &daddr->addr); - } else { - dst_addr = ro->dst_addr; - } - - cb->secen = ro->secen; - cb->secen_override = ro->secen_override; - cb->seclevel = ro->seclevel; - cb->seclevel_override = ro->seclevel_override; - - err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &dst_addr, - ro->bound ? &ro->src_addr : NULL, size); - if (err < 0) - goto out_skb; - - err = memcpy_from_msg(skb_put(skb, size), msg, size); - if (err < 0) - goto out_skb; - - skb->dev = dev; - skb->sk = sk; - skb->protocol = htons(ETH_P_IEEE802154); - - dev_put(dev); - - err = dev_queue_xmit(skb); - if (err > 0) - err = net_xmit_errno(err); - - return err ?: size; - -out_skb: - kfree_skb(skb); -out_dev: - dev_put(dev); -out: - return err; -} - -static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len, int noblock, - int flags, int *addr_len) -{ - size_t copied = 0; - int err = -EOPNOTSUPP; - struct sk_buff *skb; - DECLARE_SOCKADDR(struct sockaddr_ieee802154 *, saddr, msg->msg_name); - - skb = skb_recv_datagram(sk, flags, noblock, &err); - if (!skb) - goto out; - - copied = skb->len; - if (len < copied) { - msg->msg_flags |= MSG_TRUNC; - copied = len; - } - - /* FIXME: skip headers if necessary ?! */ - err = skb_copy_datagram_msg(skb, 0, msg, copied); - if (err) - goto done; - - sock_recv_ts_and_drops(msg, sk, skb); - - if (saddr) { - saddr->family = AF_IEEE802154; - ieee802154_addr_to_sa(&saddr->addr, &mac_cb(skb)->source); - *addr_len = sizeof(*saddr); - } - - if (flags & MSG_TRUNC) - copied = skb->len; -done: - skb_free_datagram(sk, skb); -out: - if (err) - return err; - return copied; -} - -static int dgram_rcv_skb(struct sock *sk, struct sk_buff *skb) -{ - skb = skb_share_check(skb, GFP_ATOMIC); - if (!skb) - return NET_RX_DROP; - - if (sock_queue_rcv_skb(sk, skb) < 0) { - kfree_skb(skb); - return NET_RX_DROP; - } - - return NET_RX_SUCCESS; -} - -static inline bool -ieee802154_match_sock(__le64 hw_addr, __le16 pan_id, __le16 short_addr, - struct dgram_sock *ro) -{ - if (!ro->bound) - return true; - - if (ro->src_addr.mode == IEEE802154_ADDR_LONG && - hw_addr == ro->src_addr.extended_addr) - return true; - - if (ro->src_addr.mode == IEEE802154_ADDR_SHORT && - pan_id == ro->src_addr.pan_id && - short_addr == ro->src_addr.short_addr) - return true; - - return false; -} - -int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb) -{ - struct sock *sk, *prev = NULL; - int ret = NET_RX_SUCCESS; - __le16 pan_id, short_addr; - __le64 hw_addr; - - /* Data frame processing */ - BUG_ON(dev->type != ARPHRD_IEEE802154); - - pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); - short_addr = ieee802154_mlme_ops(dev)->get_short_addr(dev); - hw_addr = ieee802154_devaddr_from_raw(dev->dev_addr); - - read_lock(&dgram_lock); - sk_for_each(sk, &dgram_head) { - if (ieee802154_match_sock(hw_addr, pan_id, short_addr, - dgram_sk(sk))) { - if (prev) { - struct sk_buff *clone; - - clone = skb_clone(skb, GFP_ATOMIC); - if (clone) - dgram_rcv_skb(prev, clone); - } - - prev = sk; - } - } - - if (prev) { - dgram_rcv_skb(prev, skb); - } else { - kfree_skb(skb); - ret = NET_RX_DROP; - } - read_unlock(&dgram_lock); - - return ret; -} - -static int dgram_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen) -{ - struct dgram_sock *ro = dgram_sk(sk); - - int val, len; - - if (level != SOL_IEEE802154) - return -EOPNOTSUPP; - - if (get_user(len, optlen)) - return -EFAULT; - - len = min_t(unsigned int, len, sizeof(int)); - - switch (optname) { - case WPAN_WANTACK: - val = ro->want_ack; - break; - case WPAN_SECURITY: - if (!ro->secen_override) - val = WPAN_SECURITY_DEFAULT; - else if (ro->secen) - val = WPAN_SECURITY_ON; - else - val = WPAN_SECURITY_OFF; - break; - case WPAN_SECURITY_LEVEL: - if (!ro->seclevel_override) - val = WPAN_SECURITY_LEVEL_DEFAULT; - else - val = ro->seclevel; - break; - default: - return -ENOPROTOOPT; - } - - if (put_user(len, optlen)) - return -EFAULT; - if (copy_to_user(optval, &val, len)) - return -EFAULT; - return 0; -} - -static int dgram_setsockopt(struct sock *sk, int level, int optname, - char __user *optval, unsigned int optlen) -{ - struct dgram_sock *ro = dgram_sk(sk); - struct net *net = sock_net(sk); - int val; - int err = 0; - - if (optlen < sizeof(int)) - return -EINVAL; - - if (get_user(val, (int __user *)optval)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - case WPAN_WANTACK: - ro->want_ack = !!val; - break; - case WPAN_SECURITY: - if (!ns_capable(net->user_ns, CAP_NET_ADMIN) && - !ns_capable(net->user_ns, CAP_NET_RAW)) { - err = -EPERM; - break; - } - - switch (val) { - case WPAN_SECURITY_DEFAULT: - ro->secen_override = 0; - break; - case WPAN_SECURITY_ON: - ro->secen_override = 1; - ro->secen = 1; - break; - case WPAN_SECURITY_OFF: - ro->secen_override = 1; - ro->secen = 0; - break; - default: - err = -EINVAL; - break; - } - break; - case WPAN_SECURITY_LEVEL: - if (!ns_capable(net->user_ns, CAP_NET_ADMIN) && - !ns_capable(net->user_ns, CAP_NET_RAW)) { - err = -EPERM; - break; - } - - if (val < WPAN_SECURITY_LEVEL_DEFAULT || - val > IEEE802154_SCF_SECLEVEL_ENC_MIC128) { - err = -EINVAL; - } else if (val == WPAN_SECURITY_LEVEL_DEFAULT) { - ro->seclevel_override = 0; - } else { - ro->seclevel_override = 1; - ro->seclevel = val; - } - break; - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -struct proto ieee802154_dgram_prot = { - .name = "IEEE-802.15.4-MAC", - .owner = THIS_MODULE, - .obj_size = sizeof(struct dgram_sock), - .init = dgram_init, - .close = dgram_close, - .bind = dgram_bind, - .sendmsg = dgram_sendmsg, - .recvmsg = dgram_recvmsg, - .hash = dgram_hash, - .unhash = dgram_unhash, - .connect = dgram_connect, - .disconnect = dgram_disconnect, - .ioctl = dgram_ioctl, - .getsockopt = dgram_getsockopt, - .setsockopt = dgram_setsockopt, -}; - diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c deleted file mode 100644 index 1674b115..0000000 --- a/net/ieee802154/raw.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Raw IEEE 802.15.4 sockets - * - * Copyright 2007, 2008 Siemens AG - * - * 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. - * - * Written by: - * Sergey Lapin - * Dmitry Eremin-Solenikov - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "af802154.h" - -static HLIST_HEAD(raw_head); -static DEFINE_RWLOCK(raw_lock); - -static void raw_hash(struct sock *sk) -{ - write_lock_bh(&raw_lock); - sk_add_node(sk, &raw_head); - sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); - write_unlock_bh(&raw_lock); -} - -static void raw_unhash(struct sock *sk) -{ - write_lock_bh(&raw_lock); - if (sk_del_node_init(sk)) - sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); - write_unlock_bh(&raw_lock); -} - -static void raw_close(struct sock *sk, long timeout) -{ - sk_common_release(sk); -} - -static int raw_bind(struct sock *sk, struct sockaddr *_uaddr, int len) -{ - struct ieee802154_addr addr; - struct sockaddr_ieee802154 *uaddr = (struct sockaddr_ieee802154 *)_uaddr; - int err = 0; - struct net_device *dev = NULL; - - if (len < sizeof(*uaddr)) - return -EINVAL; - - uaddr = (struct sockaddr_ieee802154 *)_uaddr; - if (uaddr->family != AF_IEEE802154) - return -EINVAL; - - lock_sock(sk); - - ieee802154_addr_from_sa(&addr, &uaddr->addr); - dev = ieee802154_get_dev(sock_net(sk), &addr); - if (!dev) { - err = -ENODEV; - goto out; - } - - if (dev->type != ARPHRD_IEEE802154) { - err = -ENODEV; - goto out_put; - } - - sk->sk_bound_dev_if = dev->ifindex; - sk_dst_reset(sk); - -out_put: - dev_put(dev); -out: - release_sock(sk); - - return err; -} - -static int raw_connect(struct sock *sk, struct sockaddr *uaddr, - int addr_len) -{ - return -ENOTSUPP; -} - -static int raw_disconnect(struct sock *sk, int flags) -{ - return 0; -} - -static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t size) -{ - struct net_device *dev; - unsigned int mtu; - struct sk_buff *skb; - int hlen, tlen; - int err; - - if (msg->msg_flags & MSG_OOB) { - pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags); - return -EOPNOTSUPP; - } - - lock_sock(sk); - if (!sk->sk_bound_dev_if) - dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154); - else - dev = dev_get_by_index(sock_net(sk), sk->sk_bound_dev_if); - release_sock(sk); - - if (!dev) { - pr_debug("no dev\n"); - err = -ENXIO; - goto out; - } - - mtu = dev->mtu; - pr_debug("name = %s, mtu = %u\n", dev->name, mtu); - - if (size > mtu) { - pr_debug("size = %Zu, mtu = %u\n", size, mtu); - err = -EINVAL; - goto out_dev; - } - - hlen = LL_RESERVED_SPACE(dev); - tlen = dev->needed_tailroom; - skb = sock_alloc_send_skb(sk, hlen + tlen + size, - msg->msg_flags & MSG_DONTWAIT, &err); - if (!skb) - goto out_dev; - - skb_reserve(skb, hlen); - - skb_reset_mac_header(skb); - skb_reset_network_header(skb); - - err = memcpy_from_msg(skb_put(skb, size), msg, size); - if (err < 0) - goto out_skb; - - skb->dev = dev; - skb->sk = sk; - skb->protocol = htons(ETH_P_IEEE802154); - - dev_put(dev); - - err = dev_queue_xmit(skb); - if (err > 0) - err = net_xmit_errno(err); - - return err ?: size; - -out_skb: - kfree_skb(skb); -out_dev: - dev_put(dev); -out: - return err; -} - -static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len, int noblock, int flags, int *addr_len) -{ - size_t copied = 0; - int err = -EOPNOTSUPP; - struct sk_buff *skb; - - skb = skb_recv_datagram(sk, flags, noblock, &err); - if (!skb) - goto out; - - copied = skb->len; - if (len < copied) { - msg->msg_flags |= MSG_TRUNC; - copied = len; - } - - err = skb_copy_datagram_msg(skb, 0, msg, copied); - if (err) - goto done; - - sock_recv_ts_and_drops(msg, sk, skb); - - if (flags & MSG_TRUNC) - copied = skb->len; -done: - skb_free_datagram(sk, skb); -out: - if (err) - return err; - return copied; -} - -static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb) -{ - skb = skb_share_check(skb, GFP_ATOMIC); - if (!skb) - return NET_RX_DROP; - - if (sock_queue_rcv_skb(sk, skb) < 0) { - kfree_skb(skb); - return NET_RX_DROP; - } - - return NET_RX_SUCCESS; -} - -void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb) -{ - struct sock *sk; - - read_lock(&raw_lock); - sk_for_each(sk, &raw_head) { - bh_lock_sock(sk); - if (!sk->sk_bound_dev_if || - sk->sk_bound_dev_if == dev->ifindex) { - struct sk_buff *clone; - - clone = skb_clone(skb, GFP_ATOMIC); - if (clone) - raw_rcv_skb(sk, clone); - } - bh_unlock_sock(sk); - } - read_unlock(&raw_lock); -} - -static int raw_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen) -{ - return -EOPNOTSUPP; -} - -static int raw_setsockopt(struct sock *sk, int level, int optname, - char __user *optval, unsigned int optlen) -{ - return -EOPNOTSUPP; -} - -struct proto ieee802154_raw_prot = { - .name = "IEEE-802.15.4-RAW", - .owner = THIS_MODULE, - .obj_size = sizeof(struct sock), - .close = raw_close, - .bind = raw_bind, - .sendmsg = raw_sendmsg, - .recvmsg = raw_recvmsg, - .hash = raw_hash, - .unhash = raw_unhash, - .connect = raw_connect, - .disconnect = raw_disconnect, - .getsockopt = raw_getsockopt, - .setsockopt = raw_setsockopt, -}; -- cgit v0.10.2 From df2f65de4b2d535c5b8e905768dd5fff1bfca445 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 31 Dec 2014 19:39:11 +0100 Subject: ieee802154: socket: fix checkpatch issue This patch solves the following checkpatch issue: CHECK: Comparison to NULL could be written "skb" + if (skb != NULL) { Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c index 64beb0b..2878d8c 100644 --- a/net/ieee802154/af_ieee802154.c +++ b/net/ieee802154/af_ieee802154.c @@ -560,7 +560,7 @@ static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg) amount = 0; spin_lock_bh(&sk->sk_receive_queue.lock); skb = skb_peek(&sk->sk_receive_queue); - if (skb != NULL) { + if (skb) { /* We will only return the amount * of this packet since that is all * that will be read. -- cgit v0.10.2 From 71e36b1b01b9573f09141acb76e1b4c0809a0b4a Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 31 Dec 2014 19:39:12 +0100 Subject: ieee802154: rename af_ieee802154.c to socket.c This patch renames the "af_ieee802154.c" to "socket.c". This is just a cleanup to have a short name for it which describes the implementationm stuff more human understandable. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile index e6ddfcd..88bde78 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile @@ -5,6 +5,6 @@ obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \ header_ops.o sysfs.o nl802154.o -ieee802154_socket-y := af_ieee802154.o +ieee802154_socket-y := socket.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c deleted file mode 100644 index 2878d8c..0000000 --- a/net/ieee802154/af_ieee802154.c +++ /dev/null @@ -1,1125 +0,0 @@ -/* - * IEEE802154.4 socket interface - * - * Copyright 2007, 2008 Siemens AG - * - * 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. - * - * Written by: - * Sergey Lapin - * Maxim Gorbachyov - */ - -#include -#include -#include -#include -#include -#include /* For TIOCOUTQ/INQ */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/* Utility function for families */ -static struct net_device* -ieee802154_get_dev(struct net *net, const struct ieee802154_addr *addr) -{ - struct net_device *dev = NULL; - struct net_device *tmp; - __le16 pan_id, short_addr; - u8 hwaddr[IEEE802154_ADDR_LEN]; - - switch (addr->mode) { - case IEEE802154_ADDR_LONG: - ieee802154_devaddr_to_raw(hwaddr, addr->extended_addr); - rcu_read_lock(); - dev = dev_getbyhwaddr_rcu(net, ARPHRD_IEEE802154, hwaddr); - if (dev) - dev_hold(dev); - rcu_read_unlock(); - break; - case IEEE802154_ADDR_SHORT: - if (addr->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST) || - addr->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) || - addr->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST)) - break; - - rtnl_lock(); - - for_each_netdev(net, tmp) { - if (tmp->type != ARPHRD_IEEE802154) - continue; - - pan_id = ieee802154_mlme_ops(tmp)->get_pan_id(tmp); - short_addr = - ieee802154_mlme_ops(tmp)->get_short_addr(tmp); - - if (pan_id == addr->pan_id && - short_addr == addr->short_addr) { - dev = tmp; - dev_hold(dev); - break; - } - } - - rtnl_unlock(); - break; - default: - pr_warn("Unsupported ieee802154 address type: %d\n", - addr->mode); - break; - } - - return dev; -} - -static int ieee802154_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - if (sk) { - sock->sk = NULL; - sk->sk_prot->close(sk, 0); - } - return 0; -} - -static int ieee802154_sock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - - return sk->sk_prot->sendmsg(iocb, sk, msg, len); -} - -static int ieee802154_sock_bind(struct socket *sock, struct sockaddr *uaddr, - int addr_len) -{ - struct sock *sk = sock->sk; - - if (sk->sk_prot->bind) - return sk->sk_prot->bind(sk, uaddr, addr_len); - - return sock_no_bind(sock, uaddr, addr_len); -} - -static int ieee802154_sock_connect(struct socket *sock, struct sockaddr *uaddr, - int addr_len, int flags) -{ - struct sock *sk = sock->sk; - - if (addr_len < sizeof(uaddr->sa_family)) - return -EINVAL; - - if (uaddr->sa_family == AF_UNSPEC) - return sk->sk_prot->disconnect(sk, flags); - - return sk->sk_prot->connect(sk, uaddr, addr_len); -} - -static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg, - unsigned int cmd) -{ - struct ifreq ifr; - int ret = -ENOIOCTLCMD; - struct net_device *dev; - - if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) - return -EFAULT; - - ifr.ifr_name[IFNAMSIZ-1] = 0; - - dev_load(sock_net(sk), ifr.ifr_name); - dev = dev_get_by_name(sock_net(sk), ifr.ifr_name); - - if (!dev) - return -ENODEV; - - if (dev->type == ARPHRD_IEEE802154 && dev->netdev_ops->ndo_do_ioctl) - ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, cmd); - - if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq))) - ret = -EFAULT; - dev_put(dev); - - return ret; -} - -static int ieee802154_sock_ioctl(struct socket *sock, unsigned int cmd, - unsigned long arg) -{ - struct sock *sk = sock->sk; - - switch (cmd) { - case SIOCGSTAMP: - return sock_get_timestamp(sk, (struct timeval __user *)arg); - case SIOCGSTAMPNS: - return sock_get_timestampns(sk, (struct timespec __user *)arg); - case SIOCGIFADDR: - case SIOCSIFADDR: - return ieee802154_dev_ioctl(sk, (struct ifreq __user *)arg, - cmd); - default: - if (!sk->sk_prot->ioctl) - return -ENOIOCTLCMD; - return sk->sk_prot->ioctl(sk, cmd, arg); - } -} - -/* RAW Sockets (802.15.4 created in userspace) */ -static HLIST_HEAD(raw_head); -static DEFINE_RWLOCK(raw_lock); - -static void raw_hash(struct sock *sk) -{ - write_lock_bh(&raw_lock); - sk_add_node(sk, &raw_head); - sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); - write_unlock_bh(&raw_lock); -} - -static void raw_unhash(struct sock *sk) -{ - write_lock_bh(&raw_lock); - if (sk_del_node_init(sk)) - sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); - write_unlock_bh(&raw_lock); -} - -static void raw_close(struct sock *sk, long timeout) -{ - sk_common_release(sk); -} - -static int raw_bind(struct sock *sk, struct sockaddr *_uaddr, int len) -{ - struct ieee802154_addr addr; - struct sockaddr_ieee802154 *uaddr = (struct sockaddr_ieee802154 *)_uaddr; - int err = 0; - struct net_device *dev = NULL; - - if (len < sizeof(*uaddr)) - return -EINVAL; - - uaddr = (struct sockaddr_ieee802154 *)_uaddr; - if (uaddr->family != AF_IEEE802154) - return -EINVAL; - - lock_sock(sk); - - ieee802154_addr_from_sa(&addr, &uaddr->addr); - dev = ieee802154_get_dev(sock_net(sk), &addr); - if (!dev) { - err = -ENODEV; - goto out; - } - - if (dev->type != ARPHRD_IEEE802154) { - err = -ENODEV; - goto out_put; - } - - sk->sk_bound_dev_if = dev->ifindex; - sk_dst_reset(sk); - -out_put: - dev_put(dev); -out: - release_sock(sk); - - return err; -} - -static int raw_connect(struct sock *sk, struct sockaddr *uaddr, - int addr_len) -{ - return -ENOTSUPP; -} - -static int raw_disconnect(struct sock *sk, int flags) -{ - return 0; -} - -static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t size) -{ - struct net_device *dev; - unsigned int mtu; - struct sk_buff *skb; - int hlen, tlen; - int err; - - if (msg->msg_flags & MSG_OOB) { - pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags); - return -EOPNOTSUPP; - } - - lock_sock(sk); - if (!sk->sk_bound_dev_if) - dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154); - else - dev = dev_get_by_index(sock_net(sk), sk->sk_bound_dev_if); - release_sock(sk); - - if (!dev) { - pr_debug("no dev\n"); - err = -ENXIO; - goto out; - } - - mtu = dev->mtu; - pr_debug("name = %s, mtu = %u\n", dev->name, mtu); - - if (size > mtu) { - pr_debug("size = %Zu, mtu = %u\n", size, mtu); - err = -EINVAL; - goto out_dev; - } - - hlen = LL_RESERVED_SPACE(dev); - tlen = dev->needed_tailroom; - skb = sock_alloc_send_skb(sk, hlen + tlen + size, - msg->msg_flags & MSG_DONTWAIT, &err); - if (!skb) - goto out_dev; - - skb_reserve(skb, hlen); - - skb_reset_mac_header(skb); - skb_reset_network_header(skb); - - err = memcpy_from_msg(skb_put(skb, size), msg, size); - if (err < 0) - goto out_skb; - - skb->dev = dev; - skb->sk = sk; - skb->protocol = htons(ETH_P_IEEE802154); - - dev_put(dev); - - err = dev_queue_xmit(skb); - if (err > 0) - err = net_xmit_errno(err); - - return err ?: size; - -out_skb: - kfree_skb(skb); -out_dev: - dev_put(dev); -out: - return err; -} - -static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len, int noblock, int flags, int *addr_len) -{ - size_t copied = 0; - int err = -EOPNOTSUPP; - struct sk_buff *skb; - - skb = skb_recv_datagram(sk, flags, noblock, &err); - if (!skb) - goto out; - - copied = skb->len; - if (len < copied) { - msg->msg_flags |= MSG_TRUNC; - copied = len; - } - - err = skb_copy_datagram_msg(skb, 0, msg, copied); - if (err) - goto done; - - sock_recv_ts_and_drops(msg, sk, skb); - - if (flags & MSG_TRUNC) - copied = skb->len; -done: - skb_free_datagram(sk, skb); -out: - if (err) - return err; - return copied; -} - -static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb) -{ - skb = skb_share_check(skb, GFP_ATOMIC); - if (!skb) - return NET_RX_DROP; - - if (sock_queue_rcv_skb(sk, skb) < 0) { - kfree_skb(skb); - return NET_RX_DROP; - } - - return NET_RX_SUCCESS; -} - -static void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb) -{ - struct sock *sk; - - read_lock(&raw_lock); - sk_for_each(sk, &raw_head) { - bh_lock_sock(sk); - if (!sk->sk_bound_dev_if || - sk->sk_bound_dev_if == dev->ifindex) { - struct sk_buff *clone; - - clone = skb_clone(skb, GFP_ATOMIC); - if (clone) - raw_rcv_skb(sk, clone); - } - bh_unlock_sock(sk); - } - read_unlock(&raw_lock); -} - -static int raw_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen) -{ - return -EOPNOTSUPP; -} - -static int raw_setsockopt(struct sock *sk, int level, int optname, - char __user *optval, unsigned int optlen) -{ - return -EOPNOTSUPP; -} - -static struct proto ieee802154_raw_prot = { - .name = "IEEE-802.15.4-RAW", - .owner = THIS_MODULE, - .obj_size = sizeof(struct sock), - .close = raw_close, - .bind = raw_bind, - .sendmsg = raw_sendmsg, - .recvmsg = raw_recvmsg, - .hash = raw_hash, - .unhash = raw_unhash, - .connect = raw_connect, - .disconnect = raw_disconnect, - .getsockopt = raw_getsockopt, - .setsockopt = raw_setsockopt, -}; - -static const struct proto_ops ieee802154_raw_ops = { - .family = PF_IEEE802154, - .owner = THIS_MODULE, - .release = ieee802154_sock_release, - .bind = ieee802154_sock_bind, - .connect = ieee802154_sock_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .getname = sock_no_getname, - .poll = datagram_poll, - .ioctl = ieee802154_sock_ioctl, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = sock_common_setsockopt, - .getsockopt = sock_common_getsockopt, - .sendmsg = ieee802154_sock_sendmsg, - .recvmsg = sock_common_recvmsg, - .mmap = sock_no_mmap, - .sendpage = sock_no_sendpage, -#ifdef CONFIG_COMPAT - .compat_setsockopt = compat_sock_common_setsockopt, - .compat_getsockopt = compat_sock_common_getsockopt, -#endif -}; - -/* DGRAM Sockets (802.15.4 dataframes) */ -static HLIST_HEAD(dgram_head); -static DEFINE_RWLOCK(dgram_lock); - -struct dgram_sock { - struct sock sk; - - struct ieee802154_addr src_addr; - struct ieee802154_addr dst_addr; - - unsigned int bound:1; - unsigned int connected:1; - unsigned int want_ack:1; - unsigned int secen:1; - unsigned int secen_override:1; - unsigned int seclevel:3; - unsigned int seclevel_override:1; -}; - -static inline struct dgram_sock *dgram_sk(const struct sock *sk) -{ - return container_of(sk, struct dgram_sock, sk); -} - -static void dgram_hash(struct sock *sk) -{ - write_lock_bh(&dgram_lock); - sk_add_node(sk, &dgram_head); - sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); - write_unlock_bh(&dgram_lock); -} - -static void dgram_unhash(struct sock *sk) -{ - write_lock_bh(&dgram_lock); - if (sk_del_node_init(sk)) - sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); - write_unlock_bh(&dgram_lock); -} - -static int dgram_init(struct sock *sk) -{ - struct dgram_sock *ro = dgram_sk(sk); - - ro->want_ack = 1; - return 0; -} - -static void dgram_close(struct sock *sk, long timeout) -{ - sk_common_release(sk); -} - -static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len) -{ - struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr; - struct ieee802154_addr haddr; - struct dgram_sock *ro = dgram_sk(sk); - int err = -EINVAL; - struct net_device *dev; - - lock_sock(sk); - - ro->bound = 0; - - if (len < sizeof(*addr)) - goto out; - - if (addr->family != AF_IEEE802154) - goto out; - - ieee802154_addr_from_sa(&haddr, &addr->addr); - dev = ieee802154_get_dev(sock_net(sk), &haddr); - if (!dev) { - err = -ENODEV; - goto out; - } - - if (dev->type != ARPHRD_IEEE802154) { - err = -ENODEV; - goto out_put; - } - - ro->src_addr = haddr; - - ro->bound = 1; - err = 0; -out_put: - dev_put(dev); -out: - release_sock(sk); - - return err; -} - -static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg) -{ - switch (cmd) { - case SIOCOUTQ: - { - int amount = sk_wmem_alloc_get(sk); - - return put_user(amount, (int __user *)arg); - } - - case SIOCINQ: - { - struct sk_buff *skb; - unsigned long amount; - - amount = 0; - spin_lock_bh(&sk->sk_receive_queue.lock); - skb = skb_peek(&sk->sk_receive_queue); - if (skb) { - /* We will only return the amount - * of this packet since that is all - * that will be read. - */ - amount = skb->len - ieee802154_hdr_length(skb); - } - spin_unlock_bh(&sk->sk_receive_queue.lock); - return put_user(amount, (int __user *)arg); - } - } - - return -ENOIOCTLCMD; -} - -/* FIXME: autobind */ -static int dgram_connect(struct sock *sk, struct sockaddr *uaddr, - int len) -{ - struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr; - struct dgram_sock *ro = dgram_sk(sk); - int err = 0; - - if (len < sizeof(*addr)) - return -EINVAL; - - if (addr->family != AF_IEEE802154) - return -EINVAL; - - lock_sock(sk); - - if (!ro->bound) { - err = -ENETUNREACH; - goto out; - } - - ieee802154_addr_from_sa(&ro->dst_addr, &addr->addr); - ro->connected = 1; - -out: - release_sock(sk); - return err; -} - -static int dgram_disconnect(struct sock *sk, int flags) -{ - struct dgram_sock *ro = dgram_sk(sk); - - lock_sock(sk); - ro->connected = 0; - release_sock(sk); - - return 0; -} - -static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t size) -{ - struct net_device *dev; - unsigned int mtu; - struct sk_buff *skb; - struct ieee802154_mac_cb *cb; - struct dgram_sock *ro = dgram_sk(sk); - struct ieee802154_addr dst_addr; - int hlen, tlen; - int err; - - if (msg->msg_flags & MSG_OOB) { - pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags); - return -EOPNOTSUPP; - } - - if (!ro->connected && !msg->msg_name) - return -EDESTADDRREQ; - else if (ro->connected && msg->msg_name) - return -EISCONN; - - if (!ro->bound) - dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154); - else - dev = ieee802154_get_dev(sock_net(sk), &ro->src_addr); - - if (!dev) { - pr_debug("no dev\n"); - err = -ENXIO; - goto out; - } - mtu = dev->mtu; - pr_debug("name = %s, mtu = %u\n", dev->name, mtu); - - if (size > mtu) { - pr_debug("size = %Zu, mtu = %u\n", size, mtu); - err = -EMSGSIZE; - goto out_dev; - } - - hlen = LL_RESERVED_SPACE(dev); - tlen = dev->needed_tailroom; - skb = sock_alloc_send_skb(sk, hlen + tlen + size, - msg->msg_flags & MSG_DONTWAIT, - &err); - if (!skb) - goto out_dev; - - skb_reserve(skb, hlen); - - skb_reset_network_header(skb); - - cb = mac_cb_init(skb); - cb->type = IEEE802154_FC_TYPE_DATA; - cb->ackreq = ro->want_ack; - - if (msg->msg_name) { - DECLARE_SOCKADDR(struct sockaddr_ieee802154*, - daddr, msg->msg_name); - - ieee802154_addr_from_sa(&dst_addr, &daddr->addr); - } else { - dst_addr = ro->dst_addr; - } - - cb->secen = ro->secen; - cb->secen_override = ro->secen_override; - cb->seclevel = ro->seclevel; - cb->seclevel_override = ro->seclevel_override; - - err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &dst_addr, - ro->bound ? &ro->src_addr : NULL, size); - if (err < 0) - goto out_skb; - - err = memcpy_from_msg(skb_put(skb, size), msg, size); - if (err < 0) - goto out_skb; - - skb->dev = dev; - skb->sk = sk; - skb->protocol = htons(ETH_P_IEEE802154); - - dev_put(dev); - - err = dev_queue_xmit(skb); - if (err > 0) - err = net_xmit_errno(err); - - return err ?: size; - -out_skb: - kfree_skb(skb); -out_dev: - dev_put(dev); -out: - return err; -} - -static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len, int noblock, - int flags, int *addr_len) -{ - size_t copied = 0; - int err = -EOPNOTSUPP; - struct sk_buff *skb; - DECLARE_SOCKADDR(struct sockaddr_ieee802154 *, saddr, msg->msg_name); - - skb = skb_recv_datagram(sk, flags, noblock, &err); - if (!skb) - goto out; - - copied = skb->len; - if (len < copied) { - msg->msg_flags |= MSG_TRUNC; - copied = len; - } - - /* FIXME: skip headers if necessary ?! */ - err = skb_copy_datagram_msg(skb, 0, msg, copied); - if (err) - goto done; - - sock_recv_ts_and_drops(msg, sk, skb); - - if (saddr) { - saddr->family = AF_IEEE802154; - ieee802154_addr_to_sa(&saddr->addr, &mac_cb(skb)->source); - *addr_len = sizeof(*saddr); - } - - if (flags & MSG_TRUNC) - copied = skb->len; -done: - skb_free_datagram(sk, skb); -out: - if (err) - return err; - return copied; -} - -static int dgram_rcv_skb(struct sock *sk, struct sk_buff *skb) -{ - skb = skb_share_check(skb, GFP_ATOMIC); - if (!skb) - return NET_RX_DROP; - - if (sock_queue_rcv_skb(sk, skb) < 0) { - kfree_skb(skb); - return NET_RX_DROP; - } - - return NET_RX_SUCCESS; -} - -static inline bool -ieee802154_match_sock(__le64 hw_addr, __le16 pan_id, __le16 short_addr, - struct dgram_sock *ro) -{ - if (!ro->bound) - return true; - - if (ro->src_addr.mode == IEEE802154_ADDR_LONG && - hw_addr == ro->src_addr.extended_addr) - return true; - - if (ro->src_addr.mode == IEEE802154_ADDR_SHORT && - pan_id == ro->src_addr.pan_id && - short_addr == ro->src_addr.short_addr) - return true; - - return false; -} - -static int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb) -{ - struct sock *sk, *prev = NULL; - int ret = NET_RX_SUCCESS; - __le16 pan_id, short_addr; - __le64 hw_addr; - - /* Data frame processing */ - BUG_ON(dev->type != ARPHRD_IEEE802154); - - pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); - short_addr = ieee802154_mlme_ops(dev)->get_short_addr(dev); - hw_addr = ieee802154_devaddr_from_raw(dev->dev_addr); - - read_lock(&dgram_lock); - sk_for_each(sk, &dgram_head) { - if (ieee802154_match_sock(hw_addr, pan_id, short_addr, - dgram_sk(sk))) { - if (prev) { - struct sk_buff *clone; - - clone = skb_clone(skb, GFP_ATOMIC); - if (clone) - dgram_rcv_skb(prev, clone); - } - - prev = sk; - } - } - - if (prev) { - dgram_rcv_skb(prev, skb); - } else { - kfree_skb(skb); - ret = NET_RX_DROP; - } - read_unlock(&dgram_lock); - - return ret; -} - -static int dgram_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen) -{ - struct dgram_sock *ro = dgram_sk(sk); - - int val, len; - - if (level != SOL_IEEE802154) - return -EOPNOTSUPP; - - if (get_user(len, optlen)) - return -EFAULT; - - len = min_t(unsigned int, len, sizeof(int)); - - switch (optname) { - case WPAN_WANTACK: - val = ro->want_ack; - break; - case WPAN_SECURITY: - if (!ro->secen_override) - val = WPAN_SECURITY_DEFAULT; - else if (ro->secen) - val = WPAN_SECURITY_ON; - else - val = WPAN_SECURITY_OFF; - break; - case WPAN_SECURITY_LEVEL: - if (!ro->seclevel_override) - val = WPAN_SECURITY_LEVEL_DEFAULT; - else - val = ro->seclevel; - break; - default: - return -ENOPROTOOPT; - } - - if (put_user(len, optlen)) - return -EFAULT; - if (copy_to_user(optval, &val, len)) - return -EFAULT; - return 0; -} - -static int dgram_setsockopt(struct sock *sk, int level, int optname, - char __user *optval, unsigned int optlen) -{ - struct dgram_sock *ro = dgram_sk(sk); - struct net *net = sock_net(sk); - int val; - int err = 0; - - if (optlen < sizeof(int)) - return -EINVAL; - - if (get_user(val, (int __user *)optval)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - case WPAN_WANTACK: - ro->want_ack = !!val; - break; - case WPAN_SECURITY: - if (!ns_capable(net->user_ns, CAP_NET_ADMIN) && - !ns_capable(net->user_ns, CAP_NET_RAW)) { - err = -EPERM; - break; - } - - switch (val) { - case WPAN_SECURITY_DEFAULT: - ro->secen_override = 0; - break; - case WPAN_SECURITY_ON: - ro->secen_override = 1; - ro->secen = 1; - break; - case WPAN_SECURITY_OFF: - ro->secen_override = 1; - ro->secen = 0; - break; - default: - err = -EINVAL; - break; - } - break; - case WPAN_SECURITY_LEVEL: - if (!ns_capable(net->user_ns, CAP_NET_ADMIN) && - !ns_capable(net->user_ns, CAP_NET_RAW)) { - err = -EPERM; - break; - } - - if (val < WPAN_SECURITY_LEVEL_DEFAULT || - val > IEEE802154_SCF_SECLEVEL_ENC_MIC128) { - err = -EINVAL; - } else if (val == WPAN_SECURITY_LEVEL_DEFAULT) { - ro->seclevel_override = 0; - } else { - ro->seclevel_override = 1; - ro->seclevel = val; - } - break; - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static struct proto ieee802154_dgram_prot = { - .name = "IEEE-802.15.4-MAC", - .owner = THIS_MODULE, - .obj_size = sizeof(struct dgram_sock), - .init = dgram_init, - .close = dgram_close, - .bind = dgram_bind, - .sendmsg = dgram_sendmsg, - .recvmsg = dgram_recvmsg, - .hash = dgram_hash, - .unhash = dgram_unhash, - .connect = dgram_connect, - .disconnect = dgram_disconnect, - .ioctl = dgram_ioctl, - .getsockopt = dgram_getsockopt, - .setsockopt = dgram_setsockopt, -}; - -static const struct proto_ops ieee802154_dgram_ops = { - .family = PF_IEEE802154, - .owner = THIS_MODULE, - .release = ieee802154_sock_release, - .bind = ieee802154_sock_bind, - .connect = ieee802154_sock_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .getname = sock_no_getname, - .poll = datagram_poll, - .ioctl = ieee802154_sock_ioctl, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = sock_common_setsockopt, - .getsockopt = sock_common_getsockopt, - .sendmsg = ieee802154_sock_sendmsg, - .recvmsg = sock_common_recvmsg, - .mmap = sock_no_mmap, - .sendpage = sock_no_sendpage, -#ifdef CONFIG_COMPAT - .compat_setsockopt = compat_sock_common_setsockopt, - .compat_getsockopt = compat_sock_common_getsockopt, -#endif -}; - -/* Create a socket. Initialise the socket, blank the addresses - * set the state. - */ -static int ieee802154_create(struct net *net, struct socket *sock, - int protocol, int kern) -{ - struct sock *sk; - int rc; - struct proto *proto; - const struct proto_ops *ops; - - if (!net_eq(net, &init_net)) - return -EAFNOSUPPORT; - - switch (sock->type) { - case SOCK_RAW: - proto = &ieee802154_raw_prot; - ops = &ieee802154_raw_ops; - break; - case SOCK_DGRAM: - proto = &ieee802154_dgram_prot; - ops = &ieee802154_dgram_ops; - break; - default: - rc = -ESOCKTNOSUPPORT; - goto out; - } - - rc = -ENOMEM; - sk = sk_alloc(net, PF_IEEE802154, GFP_KERNEL, proto); - if (!sk) - goto out; - rc = 0; - - sock->ops = ops; - - sock_init_data(sock, sk); - /* FIXME: sk->sk_destruct */ - sk->sk_family = PF_IEEE802154; - - /* Checksums on by default */ - sock_set_flag(sk, SOCK_ZAPPED); - - if (sk->sk_prot->hash) - sk->sk_prot->hash(sk); - - if (sk->sk_prot->init) { - rc = sk->sk_prot->init(sk); - if (rc) - sk_common_release(sk); - } -out: - return rc; -} - -static const struct net_proto_family ieee802154_family_ops = { - .family = PF_IEEE802154, - .create = ieee802154_create, - .owner = THIS_MODULE, -}; - -static int ieee802154_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) -{ - if (!netif_running(dev)) - goto drop; - pr_debug("got frame, type %d, dev %p\n", dev->type, dev); -#ifdef DEBUG - print_hex_dump_bytes("ieee802154_rcv ", - DUMP_PREFIX_NONE, skb->data, skb->len); -#endif - - if (!net_eq(dev_net(dev), &init_net)) - goto drop; - - ieee802154_raw_deliver(dev, skb); - - if (dev->type != ARPHRD_IEEE802154) - goto drop; - - if (skb->pkt_type != PACKET_OTHERHOST) - return ieee802154_dgram_deliver(dev, skb); - -drop: - kfree_skb(skb); - return NET_RX_DROP; -} - -static struct packet_type ieee802154_packet_type = { - .type = htons(ETH_P_IEEE802154), - .func = ieee802154_rcv, -}; - -static int __init af_ieee802154_init(void) -{ - int rc = -EINVAL; - - rc = proto_register(&ieee802154_raw_prot, 1); - if (rc) - goto out; - - rc = proto_register(&ieee802154_dgram_prot, 1); - if (rc) - goto err_dgram; - - /* Tell SOCKET that we are alive */ - rc = sock_register(&ieee802154_family_ops); - if (rc) - goto err_sock; - dev_add_pack(&ieee802154_packet_type); - - rc = 0; - goto out; - -err_sock: - proto_unregister(&ieee802154_dgram_prot); -err_dgram: - proto_unregister(&ieee802154_raw_prot); -out: - return rc; -} - -static void __exit af_ieee802154_remove(void) -{ - dev_remove_pack(&ieee802154_packet_type); - sock_unregister(PF_IEEE802154); - proto_unregister(&ieee802154_dgram_prot); - proto_unregister(&ieee802154_raw_prot); -} - -module_init(af_ieee802154_init); -module_exit(af_ieee802154_remove); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_NETPROTO(PF_IEEE802154); diff --git a/net/ieee802154/socket.c b/net/ieee802154/socket.c new file mode 100644 index 0000000..2878d8c --- /dev/null +++ b/net/ieee802154/socket.c @@ -0,0 +1,1125 @@ +/* + * IEEE802154.4 socket interface + * + * Copyright 2007, 2008 Siemens AG + * + * 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. + * + * Written by: + * Sergey Lapin + * Maxim Gorbachyov + */ + +#include +#include +#include +#include +#include +#include /* For TIOCOUTQ/INQ */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Utility function for families */ +static struct net_device* +ieee802154_get_dev(struct net *net, const struct ieee802154_addr *addr) +{ + struct net_device *dev = NULL; + struct net_device *tmp; + __le16 pan_id, short_addr; + u8 hwaddr[IEEE802154_ADDR_LEN]; + + switch (addr->mode) { + case IEEE802154_ADDR_LONG: + ieee802154_devaddr_to_raw(hwaddr, addr->extended_addr); + rcu_read_lock(); + dev = dev_getbyhwaddr_rcu(net, ARPHRD_IEEE802154, hwaddr); + if (dev) + dev_hold(dev); + rcu_read_unlock(); + break; + case IEEE802154_ADDR_SHORT: + if (addr->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST) || + addr->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) || + addr->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST)) + break; + + rtnl_lock(); + + for_each_netdev(net, tmp) { + if (tmp->type != ARPHRD_IEEE802154) + continue; + + pan_id = ieee802154_mlme_ops(tmp)->get_pan_id(tmp); + short_addr = + ieee802154_mlme_ops(tmp)->get_short_addr(tmp); + + if (pan_id == addr->pan_id && + short_addr == addr->short_addr) { + dev = tmp; + dev_hold(dev); + break; + } + } + + rtnl_unlock(); + break; + default: + pr_warn("Unsupported ieee802154 address type: %d\n", + addr->mode); + break; + } + + return dev; +} + +static int ieee802154_sock_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (sk) { + sock->sk = NULL; + sk->sk_prot->close(sk, 0); + } + return 0; +} + +static int ieee802154_sock_sendmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len) +{ + struct sock *sk = sock->sk; + + return sk->sk_prot->sendmsg(iocb, sk, msg, len); +} + +static int ieee802154_sock_bind(struct socket *sock, struct sockaddr *uaddr, + int addr_len) +{ + struct sock *sk = sock->sk; + + if (sk->sk_prot->bind) + return sk->sk_prot->bind(sk, uaddr, addr_len); + + return sock_no_bind(sock, uaddr, addr_len); +} + +static int ieee802154_sock_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int flags) +{ + struct sock *sk = sock->sk; + + if (addr_len < sizeof(uaddr->sa_family)) + return -EINVAL; + + if (uaddr->sa_family == AF_UNSPEC) + return sk->sk_prot->disconnect(sk, flags); + + return sk->sk_prot->connect(sk, uaddr, addr_len); +} + +static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg, + unsigned int cmd) +{ + struct ifreq ifr; + int ret = -ENOIOCTLCMD; + struct net_device *dev; + + if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) + return -EFAULT; + + ifr.ifr_name[IFNAMSIZ-1] = 0; + + dev_load(sock_net(sk), ifr.ifr_name); + dev = dev_get_by_name(sock_net(sk), ifr.ifr_name); + + if (!dev) + return -ENODEV; + + if (dev->type == ARPHRD_IEEE802154 && dev->netdev_ops->ndo_do_ioctl) + ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, cmd); + + if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq))) + ret = -EFAULT; + dev_put(dev); + + return ret; +} + +static int ieee802154_sock_ioctl(struct socket *sock, unsigned int cmd, + unsigned long arg) +{ + struct sock *sk = sock->sk; + + switch (cmd) { + case SIOCGSTAMP: + return sock_get_timestamp(sk, (struct timeval __user *)arg); + case SIOCGSTAMPNS: + return sock_get_timestampns(sk, (struct timespec __user *)arg); + case SIOCGIFADDR: + case SIOCSIFADDR: + return ieee802154_dev_ioctl(sk, (struct ifreq __user *)arg, + cmd); + default: + if (!sk->sk_prot->ioctl) + return -ENOIOCTLCMD; + return sk->sk_prot->ioctl(sk, cmd, arg); + } +} + +/* RAW Sockets (802.15.4 created in userspace) */ +static HLIST_HEAD(raw_head); +static DEFINE_RWLOCK(raw_lock); + +static void raw_hash(struct sock *sk) +{ + write_lock_bh(&raw_lock); + sk_add_node(sk, &raw_head); + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); + write_unlock_bh(&raw_lock); +} + +static void raw_unhash(struct sock *sk) +{ + write_lock_bh(&raw_lock); + if (sk_del_node_init(sk)) + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); + write_unlock_bh(&raw_lock); +} + +static void raw_close(struct sock *sk, long timeout) +{ + sk_common_release(sk); +} + +static int raw_bind(struct sock *sk, struct sockaddr *_uaddr, int len) +{ + struct ieee802154_addr addr; + struct sockaddr_ieee802154 *uaddr = (struct sockaddr_ieee802154 *)_uaddr; + int err = 0; + struct net_device *dev = NULL; + + if (len < sizeof(*uaddr)) + return -EINVAL; + + uaddr = (struct sockaddr_ieee802154 *)_uaddr; + if (uaddr->family != AF_IEEE802154) + return -EINVAL; + + lock_sock(sk); + + ieee802154_addr_from_sa(&addr, &uaddr->addr); + dev = ieee802154_get_dev(sock_net(sk), &addr); + if (!dev) { + err = -ENODEV; + goto out; + } + + if (dev->type != ARPHRD_IEEE802154) { + err = -ENODEV; + goto out_put; + } + + sk->sk_bound_dev_if = dev->ifindex; + sk_dst_reset(sk); + +out_put: + dev_put(dev); +out: + release_sock(sk); + + return err; +} + +static int raw_connect(struct sock *sk, struct sockaddr *uaddr, + int addr_len) +{ + return -ENOTSUPP; +} + +static int raw_disconnect(struct sock *sk, int flags) +{ + return 0; +} + +static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, + struct msghdr *msg, size_t size) +{ + struct net_device *dev; + unsigned int mtu; + struct sk_buff *skb; + int hlen, tlen; + int err; + + if (msg->msg_flags & MSG_OOB) { + pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags); + return -EOPNOTSUPP; + } + + lock_sock(sk); + if (!sk->sk_bound_dev_if) + dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154); + else + dev = dev_get_by_index(sock_net(sk), sk->sk_bound_dev_if); + release_sock(sk); + + if (!dev) { + pr_debug("no dev\n"); + err = -ENXIO; + goto out; + } + + mtu = dev->mtu; + pr_debug("name = %s, mtu = %u\n", dev->name, mtu); + + if (size > mtu) { + pr_debug("size = %Zu, mtu = %u\n", size, mtu); + err = -EINVAL; + goto out_dev; + } + + hlen = LL_RESERVED_SPACE(dev); + tlen = dev->needed_tailroom; + skb = sock_alloc_send_skb(sk, hlen + tlen + size, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) + goto out_dev; + + skb_reserve(skb, hlen); + + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + + err = memcpy_from_msg(skb_put(skb, size), msg, size); + if (err < 0) + goto out_skb; + + skb->dev = dev; + skb->sk = sk; + skb->protocol = htons(ETH_P_IEEE802154); + + dev_put(dev); + + err = dev_queue_xmit(skb); + if (err > 0) + err = net_xmit_errno(err); + + return err ?: size; + +out_skb: + kfree_skb(skb); +out_dev: + dev_put(dev); +out: + return err; +} + +static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len, int noblock, int flags, int *addr_len) +{ + size_t copied = 0; + int err = -EOPNOTSUPP; + struct sk_buff *skb; + + skb = skb_recv_datagram(sk, flags, noblock, &err); + if (!skb) + goto out; + + copied = skb->len; + if (len < copied) { + msg->msg_flags |= MSG_TRUNC; + copied = len; + } + + err = skb_copy_datagram_msg(skb, 0, msg, copied); + if (err) + goto done; + + sock_recv_ts_and_drops(msg, sk, skb); + + if (flags & MSG_TRUNC) + copied = skb->len; +done: + skb_free_datagram(sk, skb); +out: + if (err) + return err; + return copied; +} + +static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + + if (sock_queue_rcv_skb(sk, skb) < 0) { + kfree_skb(skb); + return NET_RX_DROP; + } + + return NET_RX_SUCCESS; +} + +static void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb) +{ + struct sock *sk; + + read_lock(&raw_lock); + sk_for_each(sk, &raw_head) { + bh_lock_sock(sk); + if (!sk->sk_bound_dev_if || + sk->sk_bound_dev_if == dev->ifindex) { + struct sk_buff *clone; + + clone = skb_clone(skb, GFP_ATOMIC); + if (clone) + raw_rcv_skb(sk, clone); + } + bh_unlock_sock(sk); + } + read_unlock(&raw_lock); +} + +static int raw_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen) +{ + return -EOPNOTSUPP; +} + +static int raw_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen) +{ + return -EOPNOTSUPP; +} + +static struct proto ieee802154_raw_prot = { + .name = "IEEE-802.15.4-RAW", + .owner = THIS_MODULE, + .obj_size = sizeof(struct sock), + .close = raw_close, + .bind = raw_bind, + .sendmsg = raw_sendmsg, + .recvmsg = raw_recvmsg, + .hash = raw_hash, + .unhash = raw_unhash, + .connect = raw_connect, + .disconnect = raw_disconnect, + .getsockopt = raw_getsockopt, + .setsockopt = raw_setsockopt, +}; + +static const struct proto_ops ieee802154_raw_ops = { + .family = PF_IEEE802154, + .owner = THIS_MODULE, + .release = ieee802154_sock_release, + .bind = ieee802154_sock_bind, + .connect = ieee802154_sock_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = datagram_poll, + .ioctl = ieee802154_sock_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_common_setsockopt, + .getsockopt = sock_common_getsockopt, + .sendmsg = ieee802154_sock_sendmsg, + .recvmsg = sock_common_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_sock_common_setsockopt, + .compat_getsockopt = compat_sock_common_getsockopt, +#endif +}; + +/* DGRAM Sockets (802.15.4 dataframes) */ +static HLIST_HEAD(dgram_head); +static DEFINE_RWLOCK(dgram_lock); + +struct dgram_sock { + struct sock sk; + + struct ieee802154_addr src_addr; + struct ieee802154_addr dst_addr; + + unsigned int bound:1; + unsigned int connected:1; + unsigned int want_ack:1; + unsigned int secen:1; + unsigned int secen_override:1; + unsigned int seclevel:3; + unsigned int seclevel_override:1; +}; + +static inline struct dgram_sock *dgram_sk(const struct sock *sk) +{ + return container_of(sk, struct dgram_sock, sk); +} + +static void dgram_hash(struct sock *sk) +{ + write_lock_bh(&dgram_lock); + sk_add_node(sk, &dgram_head); + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); + write_unlock_bh(&dgram_lock); +} + +static void dgram_unhash(struct sock *sk) +{ + write_lock_bh(&dgram_lock); + if (sk_del_node_init(sk)) + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); + write_unlock_bh(&dgram_lock); +} + +static int dgram_init(struct sock *sk) +{ + struct dgram_sock *ro = dgram_sk(sk); + + ro->want_ack = 1; + return 0; +} + +static void dgram_close(struct sock *sk, long timeout) +{ + sk_common_release(sk); +} + +static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len) +{ + struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr; + struct ieee802154_addr haddr; + struct dgram_sock *ro = dgram_sk(sk); + int err = -EINVAL; + struct net_device *dev; + + lock_sock(sk); + + ro->bound = 0; + + if (len < sizeof(*addr)) + goto out; + + if (addr->family != AF_IEEE802154) + goto out; + + ieee802154_addr_from_sa(&haddr, &addr->addr); + dev = ieee802154_get_dev(sock_net(sk), &haddr); + if (!dev) { + err = -ENODEV; + goto out; + } + + if (dev->type != ARPHRD_IEEE802154) { + err = -ENODEV; + goto out_put; + } + + ro->src_addr = haddr; + + ro->bound = 1; + err = 0; +out_put: + dev_put(dev); +out: + release_sock(sk); + + return err; +} + +static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg) +{ + switch (cmd) { + case SIOCOUTQ: + { + int amount = sk_wmem_alloc_get(sk); + + return put_user(amount, (int __user *)arg); + } + + case SIOCINQ: + { + struct sk_buff *skb; + unsigned long amount; + + amount = 0; + spin_lock_bh(&sk->sk_receive_queue.lock); + skb = skb_peek(&sk->sk_receive_queue); + if (skb) { + /* We will only return the amount + * of this packet since that is all + * that will be read. + */ + amount = skb->len - ieee802154_hdr_length(skb); + } + spin_unlock_bh(&sk->sk_receive_queue.lock); + return put_user(amount, (int __user *)arg); + } + } + + return -ENOIOCTLCMD; +} + +/* FIXME: autobind */ +static int dgram_connect(struct sock *sk, struct sockaddr *uaddr, + int len) +{ + struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr; + struct dgram_sock *ro = dgram_sk(sk); + int err = 0; + + if (len < sizeof(*addr)) + return -EINVAL; + + if (addr->family != AF_IEEE802154) + return -EINVAL; + + lock_sock(sk); + + if (!ro->bound) { + err = -ENETUNREACH; + goto out; + } + + ieee802154_addr_from_sa(&ro->dst_addr, &addr->addr); + ro->connected = 1; + +out: + release_sock(sk); + return err; +} + +static int dgram_disconnect(struct sock *sk, int flags) +{ + struct dgram_sock *ro = dgram_sk(sk); + + lock_sock(sk); + ro->connected = 0; + release_sock(sk); + + return 0; +} + +static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk, + struct msghdr *msg, size_t size) +{ + struct net_device *dev; + unsigned int mtu; + struct sk_buff *skb; + struct ieee802154_mac_cb *cb; + struct dgram_sock *ro = dgram_sk(sk); + struct ieee802154_addr dst_addr; + int hlen, tlen; + int err; + + if (msg->msg_flags & MSG_OOB) { + pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags); + return -EOPNOTSUPP; + } + + if (!ro->connected && !msg->msg_name) + return -EDESTADDRREQ; + else if (ro->connected && msg->msg_name) + return -EISCONN; + + if (!ro->bound) + dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154); + else + dev = ieee802154_get_dev(sock_net(sk), &ro->src_addr); + + if (!dev) { + pr_debug("no dev\n"); + err = -ENXIO; + goto out; + } + mtu = dev->mtu; + pr_debug("name = %s, mtu = %u\n", dev->name, mtu); + + if (size > mtu) { + pr_debug("size = %Zu, mtu = %u\n", size, mtu); + err = -EMSGSIZE; + goto out_dev; + } + + hlen = LL_RESERVED_SPACE(dev); + tlen = dev->needed_tailroom; + skb = sock_alloc_send_skb(sk, hlen + tlen + size, + msg->msg_flags & MSG_DONTWAIT, + &err); + if (!skb) + goto out_dev; + + skb_reserve(skb, hlen); + + skb_reset_network_header(skb); + + cb = mac_cb_init(skb); + cb->type = IEEE802154_FC_TYPE_DATA; + cb->ackreq = ro->want_ack; + + if (msg->msg_name) { + DECLARE_SOCKADDR(struct sockaddr_ieee802154*, + daddr, msg->msg_name); + + ieee802154_addr_from_sa(&dst_addr, &daddr->addr); + } else { + dst_addr = ro->dst_addr; + } + + cb->secen = ro->secen; + cb->secen_override = ro->secen_override; + cb->seclevel = ro->seclevel; + cb->seclevel_override = ro->seclevel_override; + + err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &dst_addr, + ro->bound ? &ro->src_addr : NULL, size); + if (err < 0) + goto out_skb; + + err = memcpy_from_msg(skb_put(skb, size), msg, size); + if (err < 0) + goto out_skb; + + skb->dev = dev; + skb->sk = sk; + skb->protocol = htons(ETH_P_IEEE802154); + + dev_put(dev); + + err = dev_queue_xmit(skb); + if (err > 0) + err = net_xmit_errno(err); + + return err ?: size; + +out_skb: + kfree_skb(skb); +out_dev: + dev_put(dev); +out: + return err; +} + +static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk, + struct msghdr *msg, size_t len, int noblock, + int flags, int *addr_len) +{ + size_t copied = 0; + int err = -EOPNOTSUPP; + struct sk_buff *skb; + DECLARE_SOCKADDR(struct sockaddr_ieee802154 *, saddr, msg->msg_name); + + skb = skb_recv_datagram(sk, flags, noblock, &err); + if (!skb) + goto out; + + copied = skb->len; + if (len < copied) { + msg->msg_flags |= MSG_TRUNC; + copied = len; + } + + /* FIXME: skip headers if necessary ?! */ + err = skb_copy_datagram_msg(skb, 0, msg, copied); + if (err) + goto done; + + sock_recv_ts_and_drops(msg, sk, skb); + + if (saddr) { + saddr->family = AF_IEEE802154; + ieee802154_addr_to_sa(&saddr->addr, &mac_cb(skb)->source); + *addr_len = sizeof(*saddr); + } + + if (flags & MSG_TRUNC) + copied = skb->len; +done: + skb_free_datagram(sk, skb); +out: + if (err) + return err; + return copied; +} + +static int dgram_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + + if (sock_queue_rcv_skb(sk, skb) < 0) { + kfree_skb(skb); + return NET_RX_DROP; + } + + return NET_RX_SUCCESS; +} + +static inline bool +ieee802154_match_sock(__le64 hw_addr, __le16 pan_id, __le16 short_addr, + struct dgram_sock *ro) +{ + if (!ro->bound) + return true; + + if (ro->src_addr.mode == IEEE802154_ADDR_LONG && + hw_addr == ro->src_addr.extended_addr) + return true; + + if (ro->src_addr.mode == IEEE802154_ADDR_SHORT && + pan_id == ro->src_addr.pan_id && + short_addr == ro->src_addr.short_addr) + return true; + + return false; +} + +static int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb) +{ + struct sock *sk, *prev = NULL; + int ret = NET_RX_SUCCESS; + __le16 pan_id, short_addr; + __le64 hw_addr; + + /* Data frame processing */ + BUG_ON(dev->type != ARPHRD_IEEE802154); + + pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); + short_addr = ieee802154_mlme_ops(dev)->get_short_addr(dev); + hw_addr = ieee802154_devaddr_from_raw(dev->dev_addr); + + read_lock(&dgram_lock); + sk_for_each(sk, &dgram_head) { + if (ieee802154_match_sock(hw_addr, pan_id, short_addr, + dgram_sk(sk))) { + if (prev) { + struct sk_buff *clone; + + clone = skb_clone(skb, GFP_ATOMIC); + if (clone) + dgram_rcv_skb(prev, clone); + } + + prev = sk; + } + } + + if (prev) { + dgram_rcv_skb(prev, skb); + } else { + kfree_skb(skb); + ret = NET_RX_DROP; + } + read_unlock(&dgram_lock); + + return ret; +} + +static int dgram_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen) +{ + struct dgram_sock *ro = dgram_sk(sk); + + int val, len; + + if (level != SOL_IEEE802154) + return -EOPNOTSUPP; + + if (get_user(len, optlen)) + return -EFAULT; + + len = min_t(unsigned int, len, sizeof(int)); + + switch (optname) { + case WPAN_WANTACK: + val = ro->want_ack; + break; + case WPAN_SECURITY: + if (!ro->secen_override) + val = WPAN_SECURITY_DEFAULT; + else if (ro->secen) + val = WPAN_SECURITY_ON; + else + val = WPAN_SECURITY_OFF; + break; + case WPAN_SECURITY_LEVEL: + if (!ro->seclevel_override) + val = WPAN_SECURITY_LEVEL_DEFAULT; + else + val = ro->seclevel; + break; + default: + return -ENOPROTOOPT; + } + + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &val, len)) + return -EFAULT; + return 0; +} + +static int dgram_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen) +{ + struct dgram_sock *ro = dgram_sk(sk); + struct net *net = sock_net(sk); + int val; + int err = 0; + + if (optlen < sizeof(int)) + return -EINVAL; + + if (get_user(val, (int __user *)optval)) + return -EFAULT; + + lock_sock(sk); + + switch (optname) { + case WPAN_WANTACK: + ro->want_ack = !!val; + break; + case WPAN_SECURITY: + if (!ns_capable(net->user_ns, CAP_NET_ADMIN) && + !ns_capable(net->user_ns, CAP_NET_RAW)) { + err = -EPERM; + break; + } + + switch (val) { + case WPAN_SECURITY_DEFAULT: + ro->secen_override = 0; + break; + case WPAN_SECURITY_ON: + ro->secen_override = 1; + ro->secen = 1; + break; + case WPAN_SECURITY_OFF: + ro->secen_override = 1; + ro->secen = 0; + break; + default: + err = -EINVAL; + break; + } + break; + case WPAN_SECURITY_LEVEL: + if (!ns_capable(net->user_ns, CAP_NET_ADMIN) && + !ns_capable(net->user_ns, CAP_NET_RAW)) { + err = -EPERM; + break; + } + + if (val < WPAN_SECURITY_LEVEL_DEFAULT || + val > IEEE802154_SCF_SECLEVEL_ENC_MIC128) { + err = -EINVAL; + } else if (val == WPAN_SECURITY_LEVEL_DEFAULT) { + ro->seclevel_override = 0; + } else { + ro->seclevel_override = 1; + ro->seclevel = val; + } + break; + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + +static struct proto ieee802154_dgram_prot = { + .name = "IEEE-802.15.4-MAC", + .owner = THIS_MODULE, + .obj_size = sizeof(struct dgram_sock), + .init = dgram_init, + .close = dgram_close, + .bind = dgram_bind, + .sendmsg = dgram_sendmsg, + .recvmsg = dgram_recvmsg, + .hash = dgram_hash, + .unhash = dgram_unhash, + .connect = dgram_connect, + .disconnect = dgram_disconnect, + .ioctl = dgram_ioctl, + .getsockopt = dgram_getsockopt, + .setsockopt = dgram_setsockopt, +}; + +static const struct proto_ops ieee802154_dgram_ops = { + .family = PF_IEEE802154, + .owner = THIS_MODULE, + .release = ieee802154_sock_release, + .bind = ieee802154_sock_bind, + .connect = ieee802154_sock_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = datagram_poll, + .ioctl = ieee802154_sock_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_common_setsockopt, + .getsockopt = sock_common_getsockopt, + .sendmsg = ieee802154_sock_sendmsg, + .recvmsg = sock_common_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_sock_common_setsockopt, + .compat_getsockopt = compat_sock_common_getsockopt, +#endif +}; + +/* Create a socket. Initialise the socket, blank the addresses + * set the state. + */ +static int ieee802154_create(struct net *net, struct socket *sock, + int protocol, int kern) +{ + struct sock *sk; + int rc; + struct proto *proto; + const struct proto_ops *ops; + + if (!net_eq(net, &init_net)) + return -EAFNOSUPPORT; + + switch (sock->type) { + case SOCK_RAW: + proto = &ieee802154_raw_prot; + ops = &ieee802154_raw_ops; + break; + case SOCK_DGRAM: + proto = &ieee802154_dgram_prot; + ops = &ieee802154_dgram_ops; + break; + default: + rc = -ESOCKTNOSUPPORT; + goto out; + } + + rc = -ENOMEM; + sk = sk_alloc(net, PF_IEEE802154, GFP_KERNEL, proto); + if (!sk) + goto out; + rc = 0; + + sock->ops = ops; + + sock_init_data(sock, sk); + /* FIXME: sk->sk_destruct */ + sk->sk_family = PF_IEEE802154; + + /* Checksums on by default */ + sock_set_flag(sk, SOCK_ZAPPED); + + if (sk->sk_prot->hash) + sk->sk_prot->hash(sk); + + if (sk->sk_prot->init) { + rc = sk->sk_prot->init(sk); + if (rc) + sk_common_release(sk); + } +out: + return rc; +} + +static const struct net_proto_family ieee802154_family_ops = { + .family = PF_IEEE802154, + .create = ieee802154_create, + .owner = THIS_MODULE, +}; + +static int ieee802154_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + if (!netif_running(dev)) + goto drop; + pr_debug("got frame, type %d, dev %p\n", dev->type, dev); +#ifdef DEBUG + print_hex_dump_bytes("ieee802154_rcv ", + DUMP_PREFIX_NONE, skb->data, skb->len); +#endif + + if (!net_eq(dev_net(dev), &init_net)) + goto drop; + + ieee802154_raw_deliver(dev, skb); + + if (dev->type != ARPHRD_IEEE802154) + goto drop; + + if (skb->pkt_type != PACKET_OTHERHOST) + return ieee802154_dgram_deliver(dev, skb); + +drop: + kfree_skb(skb); + return NET_RX_DROP; +} + +static struct packet_type ieee802154_packet_type = { + .type = htons(ETH_P_IEEE802154), + .func = ieee802154_rcv, +}; + +static int __init af_ieee802154_init(void) +{ + int rc = -EINVAL; + + rc = proto_register(&ieee802154_raw_prot, 1); + if (rc) + goto out; + + rc = proto_register(&ieee802154_dgram_prot, 1); + if (rc) + goto err_dgram; + + /* Tell SOCKET that we are alive */ + rc = sock_register(&ieee802154_family_ops); + if (rc) + goto err_sock; + dev_add_pack(&ieee802154_packet_type); + + rc = 0; + goto out; + +err_sock: + proto_unregister(&ieee802154_dgram_prot); +err_dgram: + proto_unregister(&ieee802154_raw_prot); +out: + return rc; +} + +static void __exit af_ieee802154_remove(void) +{ + dev_remove_pack(&ieee802154_packet_type); + sock_unregister(PF_IEEE802154); + proto_unregister(&ieee802154_dgram_prot); + proto_unregister(&ieee802154_raw_prot); +} + +module_init(af_ieee802154_init); +module_exit(af_ieee802154_remove); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NETPROTO(PF_IEEE802154); -- cgit v0.10.2 From 9dc52d49e28c05411ac855cf9223c48ed819507b Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 31 Dec 2014 19:39:13 +0100 Subject: ieee802154: handle config as menuconfig This patch handles the IEEE802154 Kconfig entry as menuconfig. Furthermore we move this entry out of "Network Options" and put it into "Networking" where the other networking subsystems are. This requires a menuconfig entry like all other networking subsystems. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/Kconfig b/net/ieee802154/Kconfig index 98a190d..9ea0af4 100644 --- a/net/ieee802154/Kconfig +++ b/net/ieee802154/Kconfig @@ -1,4 +1,4 @@ -config IEEE802154 +menuconfig IEEE802154 tristate "IEEE Std 802.15.4 Low-Rate Wireless Personal Area Networks support" ---help--- IEEE Std 802.15.4 defines a low data rate, low power and low @@ -10,9 +10,10 @@ config IEEE802154 Say Y here to compile LR-WPAN support into the kernel or say M to compile it as modules. +if IEEE802154 + config IEEE802154_SOCKET tristate "IEEE 802.15.4 socket interface" - depends on IEEE802154 default y ---help--- Socket interface for IEEE 802.15.4. Contains DGRAM sockets interface @@ -21,6 +22,8 @@ config IEEE802154_SOCKET config IEEE802154_6LOWPAN tristate "6lowpan support over IEEE 802.15.4" - depends on IEEE802154 && 6LOWPAN + depends on 6LOWPAN ---help--- IPv6 compression over IEEE 802.15.4. + +endif -- cgit v0.10.2 From 36cf942adf78c215f381554dfbac0ef8d05a6c21 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 2 Jan 2015 15:49:41 +0100 Subject: mac802154: fix kbuild test robot warning This patch fixs the following kbuild test robot warning: coccinelle warnings: (new ones prefixed by >>) >> net/mac802154/cfg.c:53:1-3: WARNING: PTR_ERR_OR_ZERO can be used Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c index 7d31da5..5d9f68c 100644 --- a/net/mac802154/cfg.c +++ b/net/mac802154/cfg.c @@ -51,10 +51,7 @@ ieee802154_add_iface(struct wpan_phy *phy, const char *name, struct net_device *err; err = ieee802154_if_add(local, name, type, extended_addr); - if (IS_ERR(err)) - return PTR_ERR(err); - - return 0; + return PTR_ERR_OR_ZERO(err); } static int -- cgit v0.10.2 From 8d24c0b43125ec26cc80e04588477a9a2afc025c Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Fri, 2 Jan 2015 23:00:14 +0100 Subject: rhashtable: Do hashing inside of rhashtable_lookup_compare() Hash the key inside of rhashtable_lookup_compare() like rhashtable_lookup() does. This allows to simplify the hashing functions and keep them private. Signed-off-by: Thomas Graf Cc: netfilter-devel@vger.kernel.org Signed-off-by: David S. Miller diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index b93fd89..1b51221 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -96,9 +96,6 @@ static inline int lockdep_rht_mutex_is_held(const struct rhashtable *ht) int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params); -u32 rhashtable_hashfn(const struct rhashtable *ht, const void *key, u32 len); -u32 rhashtable_obj_hashfn(const struct rhashtable *ht, void *ptr); - void rhashtable_insert(struct rhashtable *ht, struct rhash_head *node); bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *node); void rhashtable_remove_pprev(struct rhashtable *ht, struct rhash_head *obj, @@ -111,7 +108,7 @@ int rhashtable_expand(struct rhashtable *ht); int rhashtable_shrink(struct rhashtable *ht); void *rhashtable_lookup(const struct rhashtable *ht, const void *key); -void *rhashtable_lookup_compare(const struct rhashtable *ht, u32 hash, +void *rhashtable_lookup_compare(const struct rhashtable *ht, const void *key, bool (*compare)(void *, void *), void *arg); void rhashtable_destroy(const struct rhashtable *ht); diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 6c3c723..1ee0eb6 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -42,69 +42,39 @@ static void *rht_obj(const struct rhashtable *ht, const struct rhash_head *he) return (void *) he - ht->p.head_offset; } -static u32 __hashfn(const struct rhashtable *ht, const void *key, - u32 len, u32 hsize) +static u32 rht_bucket_index(const struct bucket_table *tbl, u32 hash) { - u32 h; - - h = ht->p.hashfn(key, len, ht->p.hash_rnd); - - return h & (hsize - 1); -} - -/** - * rhashtable_hashfn - compute hash for key of given length - * @ht: hash table to compute for - * @key: pointer to key - * @len: length of key - * - * Computes the hash value using the hash function provided in the 'hashfn' - * of struct rhashtable_params. The returned value is guaranteed to be - * smaller than the number of buckets in the hash table. - */ -u32 rhashtable_hashfn(const struct rhashtable *ht, const void *key, u32 len) -{ - struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); - - return __hashfn(ht, key, len, tbl->size); + return hash & (tbl->size - 1); } -EXPORT_SYMBOL_GPL(rhashtable_hashfn); -static u32 obj_hashfn(const struct rhashtable *ht, const void *ptr, u32 hsize) +static u32 obj_raw_hashfn(const struct rhashtable *ht, const void *ptr) { - if (unlikely(!ht->p.key_len)) { - u32 h; - - h = ht->p.obj_hashfn(ptr, ht->p.hash_rnd); + u32 hash; - return h & (hsize - 1); - } + if (unlikely(!ht->p.key_len)) + hash = ht->p.obj_hashfn(ptr, ht->p.hash_rnd); + else + hash = ht->p.hashfn(ptr + ht->p.key_offset, ht->p.key_len, + ht->p.hash_rnd); - return __hashfn(ht, ptr + ht->p.key_offset, ht->p.key_len, hsize); + return hash; } -/** - * rhashtable_obj_hashfn - compute hash for hashed object - * @ht: hash table to compute for - * @ptr: pointer to hashed object - * - * Computes the hash value using the hash function `hashfn` respectively - * 'obj_hashfn' depending on whether the hash table is set up to work with - * a fixed length key. The returned value is guaranteed to be smaller than - * the number of buckets in the hash table. - */ -u32 rhashtable_obj_hashfn(const struct rhashtable *ht, void *ptr) +static u32 key_hashfn(const struct rhashtable *ht, const void *key, u32 len) { struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); + u32 hash; + + hash = ht->p.hashfn(key, len, ht->p.hash_rnd); - return obj_hashfn(ht, ptr, tbl->size); + return rht_bucket_index(tbl, hash); } -EXPORT_SYMBOL_GPL(rhashtable_obj_hashfn); static u32 head_hashfn(const struct rhashtable *ht, - const struct rhash_head *he, u32 hsize) + const struct bucket_table *tbl, + const struct rhash_head *he) { - return obj_hashfn(ht, rht_obj(ht, he), hsize); + return rht_bucket_index(tbl, obj_raw_hashfn(ht, rht_obj(ht, he))); } static struct bucket_table *bucket_table_alloc(size_t nbuckets) @@ -170,9 +140,9 @@ static void hashtable_chain_unzip(const struct rhashtable *ht, * reaches a node that doesn't hash to the same bucket as the * previous node p. Call the previous node p; */ - h = head_hashfn(ht, p, new_tbl->size); + h = head_hashfn(ht, new_tbl, p); rht_for_each(he, p->next, ht) { - if (head_hashfn(ht, he, new_tbl->size) != h) + if (head_hashfn(ht, new_tbl, he) != h) break; p = he; } @@ -184,7 +154,7 @@ static void hashtable_chain_unzip(const struct rhashtable *ht, next = NULL; if (he) { rht_for_each(he, he->next, ht) { - if (head_hashfn(ht, he, new_tbl->size) == h) { + if (head_hashfn(ht, new_tbl, he) == h) { next = he; break; } @@ -237,9 +207,9 @@ int rhashtable_expand(struct rhashtable *ht) * single imprecise chain. */ for (i = 0; i < new_tbl->size; i++) { - h = i & (old_tbl->size - 1); + h = rht_bucket_index(old_tbl, i); rht_for_each(he, old_tbl->buckets[h], ht) { - if (head_hashfn(ht, he, new_tbl->size) == i) { + if (head_hashfn(ht, new_tbl, he) == i) { RCU_INIT_POINTER(new_tbl->buckets[i], he); break; } @@ -353,7 +323,7 @@ void rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj) ASSERT_RHT_MUTEX(ht); - hash = head_hashfn(ht, obj, tbl->size); + hash = head_hashfn(ht, tbl, obj); RCU_INIT_POINTER(obj->next, tbl->buckets[hash]); rcu_assign_pointer(tbl->buckets[hash], obj); ht->nelems++; @@ -413,7 +383,7 @@ bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *obj) ASSERT_RHT_MUTEX(ht); - h = head_hashfn(ht, obj, tbl->size); + h = head_hashfn(ht, tbl, obj); pprev = &tbl->buckets[h]; rht_for_each(he, tbl->buckets[h], ht) { @@ -452,7 +422,7 @@ void *rhashtable_lookup(const struct rhashtable *ht, const void *key) BUG_ON(!ht->p.key_len); - h = __hashfn(ht, key, ht->p.key_len, tbl->size); + h = key_hashfn(ht, key, ht->p.key_len); rht_for_each_rcu(he, tbl->buckets[h], ht) { if (memcmp(rht_obj(ht, he) + ht->p.key_offset, key, ht->p.key_len)) @@ -467,7 +437,7 @@ EXPORT_SYMBOL_GPL(rhashtable_lookup); /** * rhashtable_lookup_compare - search hash table with compare function * @ht: hash table - * @hash: hash value of desired entry + * @key: the pointer to the key * @compare: compare function, must return true on match * @arg: argument passed on to compare function * @@ -479,15 +449,14 @@ EXPORT_SYMBOL_GPL(rhashtable_lookup); * * Returns the first entry on which the compare function returned true. */ -void *rhashtable_lookup_compare(const struct rhashtable *ht, u32 hash, +void *rhashtable_lookup_compare(const struct rhashtable *ht, const void *key, bool (*compare)(void *, void *), void *arg) { const struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); struct rhash_head *he; + u32 hash; - if (unlikely(hash >= tbl->size)) - return NULL; - + hash = key_hashfn(ht, key, ht->p.key_len); rht_for_each_rcu(he, tbl->buckets[hash], ht) { if (!compare(rht_obj(ht, he), arg)) continue; diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index 1e316ce..614ee09 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -94,28 +94,40 @@ static void nft_hash_remove(const struct nft_set *set, kfree(he); } +struct nft_compare_arg { + const struct nft_set *set; + struct nft_set_elem *elem; +}; + +static bool nft_hash_compare(void *ptr, void *arg) +{ + struct nft_hash_elem *he = ptr; + struct nft_compare_arg *x = arg; + + if (!nft_data_cmp(&he->key, &x->elem->key, x->set->klen)) { + x->elem->cookie = &he->node; + x->elem->flags = 0; + if (x->set->flags & NFT_SET_MAP) + nft_data_copy(&x->elem->data, he->data); + + return true; + } + + return false; +} + static int nft_hash_get(const struct nft_set *set, struct nft_set_elem *elem) { const struct rhashtable *priv = nft_set_priv(set); - const struct bucket_table *tbl = rht_dereference_rcu(priv->tbl, priv); - struct rhash_head __rcu * const *pprev; - struct nft_hash_elem *he; - u32 h; - - h = rhashtable_hashfn(priv, &elem->key, set->klen); - pprev = &tbl->buckets[h]; - rht_for_each_entry_rcu(he, tbl->buckets[h], node) { - if (nft_data_cmp(&he->key, &elem->key, set->klen)) { - pprev = &he->node.next; - continue; - } + struct nft_compare_arg arg = { + .set = set, + .elem = elem, + }; - elem->cookie = (void *)pprev; - elem->flags = 0; - if (set->flags & NFT_SET_MAP) - nft_data_copy(&elem->data, he->data); + if (rhashtable_lookup_compare(priv, &elem->key, + &nft_hash_compare, &arg)) return 0; - } + return -ENOENT; } diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 84ea76c..a5d7ed6 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1002,11 +1002,8 @@ static struct sock *__netlink_lookup(struct netlink_table *table, u32 portid, .net = net, .portid = portid, }; - u32 hash; - hash = rhashtable_hashfn(&table->hash, &portid, sizeof(portid)); - - return rhashtable_lookup_compare(&table->hash, hash, + return rhashtable_lookup_compare(&table->hash, &portid, &netlink_compare, &arg); } -- cgit v0.10.2 From a4b18cda4c2676a4b4b59622b2e0394dc153e00b Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Fri, 2 Jan 2015 23:00:15 +0100 Subject: rhashtable: Use rht_obj() instead of manual offset calculation Signed-off-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 1ee0eb6..b658245 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -427,7 +427,7 @@ void *rhashtable_lookup(const struct rhashtable *ht, const void *key) if (memcmp(rht_obj(ht, he) + ht->p.key_offset, key, ht->p.key_len)) continue; - return (void *) he - ht->p.head_offset; + return rht_obj(ht, he); } return NULL; @@ -460,7 +460,7 @@ void *rhashtable_lookup_compare(const struct rhashtable *ht, const void *key, rht_for_each_rcu(he, tbl->buckets[hash], ht) { if (!compare(rht_obj(ht, he), arg)) continue; - return (void *) he - ht->p.head_offset; + return rht_obj(ht, he); } return NULL; -- cgit v0.10.2 From 88d6ed15acff1cb44b1d1f3c0a393b7f7744957a Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Fri, 2 Jan 2015 23:00:16 +0100 Subject: rhashtable: Convert bucket iterators to take table and index This patch is in preparation to introduce per bucket spinlocks. It extends all iterator macros to take the bucket table and bucket index. It also introduces a new rht_dereference_bucket() to handle protected accesses to buckets. It introduces a barrier() to the RCU iterators to the prevent the compiler from caching the first element. The lockdep verifier is introduced as stub which always succeeds and properly implement in the next patch when the locks are introduced. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index 1b51221..b54e24a 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -87,11 +87,18 @@ struct rhashtable { #ifdef CONFIG_PROVE_LOCKING int lockdep_rht_mutex_is_held(const struct rhashtable *ht); +int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, u32 hash); #else static inline int lockdep_rht_mutex_is_held(const struct rhashtable *ht) { return 1; } + +static inline int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, + u32 hash) +{ + return 1; +} #endif /* CONFIG_PROVE_LOCKING */ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params); @@ -119,92 +126,144 @@ void rhashtable_destroy(const struct rhashtable *ht); #define rht_dereference_rcu(p, ht) \ rcu_dereference_check(p, lockdep_rht_mutex_is_held(ht)) -#define rht_entry(ptr, type, member) container_of(ptr, type, member) -#define rht_entry_safe(ptr, type, member) \ -({ \ - typeof(ptr) __ptr = (ptr); \ - __ptr ? rht_entry(__ptr, type, member) : NULL; \ -}) +#define rht_dereference_bucket(p, tbl, hash) \ + rcu_dereference_protected(p, lockdep_rht_bucket_is_held(tbl, hash)) -#define rht_next_entry_safe(pos, ht, member) \ -({ \ - pos ? rht_entry_safe(rht_dereference((pos)->member.next, ht), \ - typeof(*(pos)), member) : NULL; \ -}) +#define rht_dereference_bucket_rcu(p, tbl, hash) \ + rcu_dereference_check(p, lockdep_rht_bucket_is_held(tbl, hash)) + +#define rht_entry(tpos, pos, member) \ + ({ tpos = container_of(pos, typeof(*tpos), member); 1; }) /** - * rht_for_each - iterate over hash chain - * @pos: &struct rhash_head to use as a loop cursor. - * @head: head of the hash chain (struct rhash_head *) - * @ht: pointer to your struct rhashtable + * rht_for_each_continue - continue iterating over hash chain + * @pos: the &struct rhash_head to use as a loop cursor. + * @head: the previous &struct rhash_head to continue from + * @tbl: the &struct bucket_table + * @hash: the hash value / bucket index */ -#define rht_for_each(pos, head, ht) \ - for (pos = rht_dereference(head, ht); \ +#define rht_for_each_continue(pos, head, tbl, hash) \ + for (pos = rht_dereference_bucket(head, tbl, hash); \ pos; \ - pos = rht_dereference((pos)->next, ht)) + pos = rht_dereference_bucket((pos)->next, tbl, hash)) + +/** + * rht_for_each - iterate over hash chain + * @pos: the &struct rhash_head to use as a loop cursor. + * @tbl: the &struct bucket_table + * @hash: the hash value / bucket index + */ +#define rht_for_each(pos, tbl, hash) \ + rht_for_each_continue(pos, (tbl)->buckets[hash], tbl, hash) + +/** + * rht_for_each_entry_continue - continue iterating over hash chain + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct rhash_head to use as a loop cursor. + * @head: the previous &struct rhash_head to continue from + * @tbl: the &struct bucket_table + * @hash: the hash value / bucket index + * @member: name of the &struct rhash_head within the hashable struct. + */ +#define rht_for_each_entry_continue(tpos, pos, head, tbl, hash, member) \ + for (pos = rht_dereference_bucket(head, tbl, hash); \ + pos && rht_entry(tpos, pos, member); \ + pos = rht_dereference_bucket((pos)->next, tbl, hash)) /** * rht_for_each_entry - iterate over hash chain of given type - * @pos: type * to use as a loop cursor. - * @head: head of the hash chain (struct rhash_head *) - * @ht: pointer to your struct rhashtable - * @member: name of the rhash_head within the hashable struct. + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct rhash_head to use as a loop cursor. + * @tbl: the &struct bucket_table + * @hash: the hash value / bucket index + * @member: name of the &struct rhash_head within the hashable struct. */ -#define rht_for_each_entry(pos, head, ht, member) \ - for (pos = rht_entry_safe(rht_dereference(head, ht), \ - typeof(*(pos)), member); \ - pos; \ - pos = rht_next_entry_safe(pos, ht, member)) +#define rht_for_each_entry(tpos, pos, tbl, hash, member) \ + rht_for_each_entry_continue(tpos, pos, (tbl)->buckets[hash], \ + tbl, hash, member) /** * rht_for_each_entry_safe - safely iterate over hash chain of given type - * @pos: type * to use as a loop cursor. - * @n: type * to use for temporary next object storage - * @head: head of the hash chain (struct rhash_head *) - * @ht: pointer to your struct rhashtable - * @member: name of the rhash_head within the hashable struct. + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct rhash_head to use as a loop cursor. + * @next: the &struct rhash_head to use as next in loop cursor. + * @tbl: the &struct bucket_table + * @hash: the hash value / bucket index + * @member: name of the &struct rhash_head within the hashable struct. * * This hash chain list-traversal primitive allows for the looped code to * remove the loop cursor from the list. */ -#define rht_for_each_entry_safe(pos, n, head, ht, member) \ - for (pos = rht_entry_safe(rht_dereference(head, ht), \ - typeof(*(pos)), member), \ - n = rht_next_entry_safe(pos, ht, member); \ - pos; \ - pos = n, \ - n = rht_next_entry_safe(pos, ht, member)) +#define rht_for_each_entry_safe(tpos, pos, next, tbl, hash, member) \ + for (pos = rht_dereference_bucket((tbl)->buckets[hash], tbl, hash), \ + next = pos ? rht_dereference_bucket(pos->next, tbl, hash) \ + : NULL; \ + pos && rht_entry(tpos, pos, member); \ + pos = next) + +/** + * rht_for_each_rcu_continue - continue iterating over rcu hash chain + * @pos: the &struct rhash_head to use as a loop cursor. + * @head: the previous &struct rhash_head to continue from + * @tbl: the &struct bucket_table + * @hash: the hash value / bucket index + * + * This hash chain list-traversal primitive may safely run concurrently with + * the _rcu mutation primitives such as rhashtable_insert() as long as the + * traversal is guarded by rcu_read_lock(). + */ +#define rht_for_each_rcu_continue(pos, head, tbl, hash) \ + for (({barrier(); }), \ + pos = rht_dereference_bucket_rcu(head, tbl, hash); \ + pos; \ + pos = rcu_dereference_raw(pos->next)) /** * rht_for_each_rcu - iterate over rcu hash chain - * @pos: &struct rhash_head to use as a loop cursor. - * @head: head of the hash chain (struct rhash_head *) - * @ht: pointer to your struct rhashtable + * @pos: the &struct rhash_head to use as a loop cursor. + * @tbl: the &struct bucket_table + * @hash: the hash value / bucket index * * This hash chain list-traversal primitive may safely run concurrently with - * the _rcu fkht mutation primitives such as rht_insert() as long as the + * the _rcu mutation primitives such as rhashtable_insert() as long as the * traversal is guarded by rcu_read_lock(). */ -#define rht_for_each_rcu(pos, head, ht) \ - for (pos = rht_dereference_rcu(head, ht); \ - pos; \ - pos = rht_dereference_rcu((pos)->next, ht)) +#define rht_for_each_rcu(pos, tbl, hash) \ + rht_for_each_rcu_continue(pos, (tbl)->buckets[hash], tbl, hash) + +/** + * rht_for_each_entry_rcu_continue - continue iterating over rcu hash chain + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct rhash_head to use as a loop cursor. + * @head: the previous &struct rhash_head to continue from + * @tbl: the &struct bucket_table + * @hash: the hash value / bucket index + * @member: name of the &struct rhash_head within the hashable struct. + * + * This hash chain list-traversal primitive may safely run concurrently with + * the _rcu mutation primitives such as rhashtable_insert() as long as the + * traversal is guarded by rcu_read_lock(). + */ +#define rht_for_each_entry_rcu_continue(tpos, pos, head, tbl, hash, member) \ + for (({barrier(); }), \ + pos = rht_dereference_bucket_rcu(head, tbl, hash); \ + pos && rht_entry(tpos, pos, member); \ + pos = rht_dereference_bucket_rcu(pos->next, tbl, hash)) /** * rht_for_each_entry_rcu - iterate over rcu hash chain of given type - * @pos: type * to use as a loop cursor. - * @head: head of the hash chain (struct rhash_head *) - * @member: name of the rhash_head within the hashable struct. + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct rhash_head to use as a loop cursor. + * @tbl: the &struct bucket_table + * @hash: the hash value / bucket index + * @member: name of the &struct rhash_head within the hashable struct. * * This hash chain list-traversal primitive may safely run concurrently with - * the _rcu fkht mutation primitives such as rht_insert() as long as the + * the _rcu mutation primitives such as rhashtable_insert() as long as the * traversal is guarded by rcu_read_lock(). */ -#define rht_for_each_entry_rcu(pos, head, member) \ - for (pos = rht_entry_safe(rcu_dereference_raw(head), \ - typeof(*(pos)), member); \ - pos; \ - pos = rht_entry_safe(rcu_dereference_raw((pos)->member.next), \ - typeof(*(pos)), member)) +#define rht_for_each_entry_rcu(tpos, pos, tbl, hash, member) \ + rht_for_each_entry_rcu_continue(tpos, pos, (tbl)->buckets[hash],\ + tbl, hash, member) #endif /* _LINUX_RHASHTABLE_H */ diff --git a/lib/rhashtable.c b/lib/rhashtable.c index b658245..ce450d0 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -35,6 +35,12 @@ int lockdep_rht_mutex_is_held(const struct rhashtable *ht) return ht->p.mutex_is_held(ht->p.parent); } EXPORT_SYMBOL_GPL(lockdep_rht_mutex_is_held); + +int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, u32 hash) +{ + return 1; +} +EXPORT_SYMBOL_GPL(lockdep_rht_bucket_is_held); #endif static void *rht_obj(const struct rhashtable *ht, const struct rhash_head *he) @@ -141,7 +147,7 @@ static void hashtable_chain_unzip(const struct rhashtable *ht, * previous node p. Call the previous node p; */ h = head_hashfn(ht, new_tbl, p); - rht_for_each(he, p->next, ht) { + rht_for_each_continue(he, p->next, old_tbl, n) { if (head_hashfn(ht, new_tbl, he) != h) break; p = he; @@ -153,7 +159,7 @@ static void hashtable_chain_unzip(const struct rhashtable *ht, */ next = NULL; if (he) { - rht_for_each(he, he->next, ht) { + rht_for_each_continue(he, he->next, old_tbl, n) { if (head_hashfn(ht, new_tbl, he) == h) { next = he; break; @@ -208,7 +214,7 @@ int rhashtable_expand(struct rhashtable *ht) */ for (i = 0; i < new_tbl->size; i++) { h = rht_bucket_index(old_tbl, i); - rht_for_each(he, old_tbl->buckets[h], ht) { + rht_for_each(he, old_tbl, h) { if (head_hashfn(ht, new_tbl, he) == i) { RCU_INIT_POINTER(new_tbl->buckets[i], he); break; @@ -286,7 +292,7 @@ int rhashtable_shrink(struct rhashtable *ht) * to the new bucket. */ for (pprev = &ntbl->buckets[i]; *pprev != NULL; - pprev = &rht_dereference(*pprev, ht)->next) + pprev = &rht_dereference_bucket(*pprev, ntbl, i)->next) ; RCU_INIT_POINTER(*pprev, tbl->buckets[i + ntbl->size]); } @@ -386,7 +392,7 @@ bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *obj) h = head_hashfn(ht, tbl, obj); pprev = &tbl->buckets[h]; - rht_for_each(he, tbl->buckets[h], ht) { + rht_for_each(he, tbl, h) { if (he != obj) { pprev = &he->next; continue; @@ -423,7 +429,7 @@ void *rhashtable_lookup(const struct rhashtable *ht, const void *key) BUG_ON(!ht->p.key_len); h = key_hashfn(ht, key, ht->p.key_len); - rht_for_each_rcu(he, tbl->buckets[h], ht) { + rht_for_each_rcu(he, tbl, h) { if (memcmp(rht_obj(ht, he) + ht->p.key_offset, key, ht->p.key_len)) continue; @@ -457,7 +463,7 @@ void *rhashtable_lookup_compare(const struct rhashtable *ht, const void *key, u32 hash; hash = key_hashfn(ht, key, ht->p.key_len); - rht_for_each_rcu(he, tbl->buckets[hash], ht) { + rht_for_each_rcu(he, tbl, hash) { if (!compare(rht_obj(ht, he), arg)) continue; return rht_obj(ht, he); @@ -625,6 +631,7 @@ static int __init test_rht_lookup(struct rhashtable *ht) static void test_bucket_stats(struct rhashtable *ht, bool quiet) { unsigned int cnt, rcu_cnt, i, total = 0; + struct rhash_head *pos; struct test_obj *obj; struct bucket_table *tbl; @@ -635,14 +642,14 @@ static void test_bucket_stats(struct rhashtable *ht, bool quiet) if (!quiet) pr_info(" [%#4x/%zu]", i, tbl->size); - rht_for_each_entry_rcu(obj, tbl->buckets[i], node) { + rht_for_each_entry_rcu(obj, pos, tbl, i, node) { cnt++; total++; if (!quiet) pr_cont(" [%p],", obj); } - rht_for_each_entry_rcu(obj, tbl->buckets[i], node) + rht_for_each_entry_rcu(obj, pos, tbl, i, node) rcu_cnt++; if (rcu_cnt != cnt) @@ -664,7 +671,8 @@ static void test_bucket_stats(struct rhashtable *ht, bool quiet) static int __init test_rhashtable(struct rhashtable *ht) { struct bucket_table *tbl; - struct test_obj *obj, *next; + struct test_obj *obj; + struct rhash_head *pos, *next; int err; unsigned int i; @@ -733,7 +741,7 @@ static int __init test_rhashtable(struct rhashtable *ht) error: tbl = rht_dereference_rcu(ht->tbl, ht); for (i = 0; i < tbl->size; i++) - rht_for_each_entry_safe(obj, next, tbl->buckets[i], ht, node) + rht_for_each_entry_safe(obj, pos, next, tbl, i, node) kfree(obj); return err; diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index 614ee09..d93f1f4 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -142,7 +142,9 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set, tbl = rht_dereference_rcu(priv->tbl, priv); for (i = 0; i < tbl->size; i++) { - rht_for_each_entry_rcu(he, tbl->buckets[i], node) { + struct rhash_head *pos; + + rht_for_each_entry_rcu(he, pos, tbl, i, node) { if (iter->count < iter->skip) goto cont; @@ -197,15 +199,13 @@ static void nft_hash_destroy(const struct nft_set *set) { const struct rhashtable *priv = nft_set_priv(set); const struct bucket_table *tbl = priv->tbl; - struct nft_hash_elem *he, *next; + struct nft_hash_elem *he; + struct rhash_head *pos, *next; unsigned int i; for (i = 0; i < tbl->size; i++) { - for (he = rht_entry(tbl->buckets[i], struct nft_hash_elem, node); - he != NULL; he = next) { - next = rht_entry(he->node.next, struct nft_hash_elem, node); + rht_for_each_entry_safe(he, pos, next, tbl, i, node) nft_hash_elem_destroy(set, he); - } } rhashtable_destroy(priv); } diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index a5d7ed6..57449b6 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2898,7 +2898,9 @@ static struct sock *netlink_seq_socket_idx(struct seq_file *seq, loff_t pos) const struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); for (j = 0; j < tbl->size; j++) { - rht_for_each_entry_rcu(nlk, tbl->buckets[j], node) { + struct rhash_head *node; + + rht_for_each_entry_rcu(nlk, node, tbl, j, node) { s = (struct sock *)nlk; if (sock_net(s) != seq_file_net(seq)) @@ -2926,6 +2928,8 @@ static void *netlink_seq_start(struct seq_file *seq, loff_t *pos) static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct rhashtable *ht; + const struct bucket_table *tbl; + struct rhash_head *node; struct netlink_sock *nlk; struct nl_seq_iter *iter; struct net *net; @@ -2942,17 +2946,17 @@ static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos) i = iter->link; ht = &nl_table[i].hash; - rht_for_each_entry(nlk, nlk->node.next, ht, node) + tbl = rht_dereference_rcu(ht->tbl, ht); + rht_for_each_entry_rcu_continue(nlk, node, nlk->node.next, tbl, iter->hash_idx, node) if (net_eq(sock_net((struct sock *)nlk), net)) return nlk; j = iter->hash_idx + 1; do { - const struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); for (; j < tbl->size; j++) { - rht_for_each_entry(nlk, tbl->buckets[j], ht, node) { + rht_for_each_entry_rcu(nlk, node, tbl, j, node) { if (net_eq(sock_net((struct sock *)nlk), net)) { iter->link = i; iter->hash_idx = j; diff --git a/net/netlink/diag.c b/net/netlink/diag.c index de8c74a..fcca36d 100644 --- a/net/netlink/diag.c +++ b/net/netlink/diag.c @@ -113,7 +113,9 @@ static int __netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, req = nlmsg_data(cb->nlh); for (i = 0; i < htbl->size; i++) { - rht_for_each_entry(nlsk, htbl->buckets[i], ht, node) { + struct rhash_head *pos; + + rht_for_each_entry(nlsk, pos, htbl, i, node) { sk = (struct sock *)nlsk; if (!net_eq(sock_net(sk), net)) -- cgit v0.10.2 From b8e1943e9f754219bcfb40bac4a605b5348acb25 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Fri, 2 Jan 2015 23:00:17 +0100 Subject: rhashtable: Factor out bucket_tail() function Subsequent patches will require access to the bucket tail. Access to the tail is relatively cheap as the automatic resizing of the table should keep the number of entries per bucket to no more than 0.75 on average. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/lib/rhashtable.c b/lib/rhashtable.c index ce450d0..0bd29c1 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -83,6 +83,18 @@ static u32 head_hashfn(const struct rhashtable *ht, return rht_bucket_index(tbl, obj_raw_hashfn(ht, rht_obj(ht, he))); } +static struct rhash_head __rcu **bucket_tail(struct bucket_table *tbl, u32 n) +{ + struct rhash_head __rcu **pprev; + + for (pprev = &tbl->buckets[n]; + rht_dereference_bucket(*pprev, tbl, n); + pprev = &rht_dereference_bucket(*pprev, tbl, n)->next) + ; + + return pprev; +} + static struct bucket_table *bucket_table_alloc(size_t nbuckets) { struct bucket_table *tbl; @@ -266,7 +278,6 @@ EXPORT_SYMBOL_GPL(rhashtable_expand); int rhashtable_shrink(struct rhashtable *ht) { struct bucket_table *ntbl, *tbl = rht_dereference(ht->tbl, ht); - struct rhash_head __rcu **pprev; unsigned int i; ASSERT_RHT_MUTEX(ht); @@ -286,15 +297,9 @@ int rhashtable_shrink(struct rhashtable *ht) */ for (i = 0; i < ntbl->size; i++) { ntbl->buckets[i] = tbl->buckets[i]; + RCU_INIT_POINTER(*bucket_tail(ntbl, i), + tbl->buckets[i + ntbl->size]); - /* Link each bucket in the new table to the first bucket - * in the old table that contains entries which will hash - * to the new bucket. - */ - for (pprev = &ntbl->buckets[i]; *pprev != NULL; - pprev = &rht_dereference_bucket(*pprev, ntbl, i)->next) - ; - RCU_INIT_POINTER(*pprev, tbl->buckets[i + ntbl->size]); } /* Publish the new, valid hash table */ -- cgit v0.10.2 From 897362e446436d245972e72c6bc5b33bd7a5c659 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Fri, 2 Jan 2015 23:00:18 +0100 Subject: nft_hash: Remove rhashtable_remove_pprev() The removal function of nft_hash currently stores a reference to the previous element during lookup which is used to optimize removal later on. This was possible because a lock is held throughout calling rhashtable_lookup() and rhashtable_remove(). With the introdution of deferred table resizing in parallel to lookups and insertions, the nftables lock will no longer synchronize all table mutations and the stored pprev may become invalid. Removing this optimization makes removal slightly more expensive on average but allows taking the resize cost out of the insert and remove path. Signed-off-by: Thomas Graf Cc: netfilter-devel@vger.kernel.org Signed-off-by: David S. Miller diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index b54e24a..f624d4b 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -105,8 +105,6 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params); void rhashtable_insert(struct rhashtable *ht, struct rhash_head *node); bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *node); -void rhashtable_remove_pprev(struct rhashtable *ht, struct rhash_head *obj, - struct rhash_head __rcu **pprev); bool rht_grow_above_75(const struct rhashtable *ht, size_t new_size); bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size); diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 0bd29c1..e6b85c4 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -345,32 +345,6 @@ void rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj) EXPORT_SYMBOL_GPL(rhashtable_insert); /** - * rhashtable_remove_pprev - remove object from hash table given previous element - * @ht: hash table - * @obj: pointer to hash head inside object - * @pprev: pointer to previous element - * - * Identical to rhashtable_remove() but caller is alreayd aware of the element - * in front of the element to be deleted. This is in particular useful for - * deletion when combined with walking or lookup. - */ -void rhashtable_remove_pprev(struct rhashtable *ht, struct rhash_head *obj, - struct rhash_head __rcu **pprev) -{ - struct bucket_table *tbl = rht_dereference(ht->tbl, ht); - - ASSERT_RHT_MUTEX(ht); - - RCU_INIT_POINTER(*pprev, obj->next); - ht->nelems--; - - if (ht->p.shrink_decision && - ht->p.shrink_decision(ht, tbl->size)) - rhashtable_shrink(ht); -} -EXPORT_SYMBOL_GPL(rhashtable_remove_pprev); - -/** * rhashtable_remove - remove object from hash table * @ht: hash table * @obj: pointer to hash head inside object @@ -403,7 +377,13 @@ bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *obj) continue; } - rhashtable_remove_pprev(ht, he, pprev); + RCU_INIT_POINTER(*pprev, he->next); + ht->nelems--; + + if (ht->p.shrink_decision && + ht->p.shrink_decision(ht, tbl->size)) + rhashtable_shrink(ht); + return true; } diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index d93f1f4..7f903cf 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -83,15 +83,10 @@ static void nft_hash_remove(const struct nft_set *set, const struct nft_set_elem *elem) { struct rhashtable *priv = nft_set_priv(set); - struct rhash_head *he, __rcu **pprev; - - pprev = elem->cookie; - he = rht_dereference((*pprev), priv); - - rhashtable_remove_pprev(priv, he, pprev); + rhashtable_remove(priv, elem->cookie); synchronize_rcu(); - kfree(he); + kfree(elem->cookie); } struct nft_compare_arg { @@ -105,7 +100,7 @@ static bool nft_hash_compare(void *ptr, void *arg) struct nft_compare_arg *x = arg; if (!nft_data_cmp(&he->key, &x->elem->key, x->set->klen)) { - x->elem->cookie = &he->node; + x->elem->cookie = he; x->elem->flags = 0; if (x->set->flags & NFT_SET_MAP) nft_data_copy(&x->elem->data, he->data); -- cgit v0.10.2 From 113948d841e8d78039e5dbbb5248f5b73e99eafa Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Fri, 2 Jan 2015 23:00:19 +0100 Subject: spinlock: Add spin_lock_bh_nested() Signed-off-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h index 262ba4e..3e18379 100644 --- a/include/linux/spinlock.h +++ b/include/linux/spinlock.h @@ -190,6 +190,8 @@ static inline void do_raw_spin_unlock(raw_spinlock_t *lock) __releases(lock) #ifdef CONFIG_DEBUG_LOCK_ALLOC # define raw_spin_lock_nested(lock, subclass) \ _raw_spin_lock_nested(lock, subclass) +# define raw_spin_lock_bh_nested(lock, subclass) \ + _raw_spin_lock_bh_nested(lock, subclass) # define raw_spin_lock_nest_lock(lock, nest_lock) \ do { \ @@ -205,6 +207,7 @@ static inline void do_raw_spin_unlock(raw_spinlock_t *lock) __releases(lock) # define raw_spin_lock_nested(lock, subclass) \ _raw_spin_lock(((void)(subclass), (lock))) # define raw_spin_lock_nest_lock(lock, nest_lock) _raw_spin_lock(lock) +# define raw_spin_lock_bh_nested(lock, subclass) _raw_spin_lock_bh(lock) #endif #if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) @@ -324,6 +327,11 @@ do { \ raw_spin_lock_nested(spinlock_check(lock), subclass); \ } while (0) +#define spin_lock_bh_nested(lock, subclass) \ +do { \ + raw_spin_lock_bh_nested(spinlock_check(lock), subclass);\ +} while (0) + #define spin_lock_nest_lock(lock, nest_lock) \ do { \ raw_spin_lock_nest_lock(spinlock_check(lock), nest_lock); \ diff --git a/include/linux/spinlock_api_smp.h b/include/linux/spinlock_api_smp.h index 42dfab8..5344268 100644 --- a/include/linux/spinlock_api_smp.h +++ b/include/linux/spinlock_api_smp.h @@ -22,6 +22,8 @@ int in_lock_functions(unsigned long addr); void __lockfunc _raw_spin_lock(raw_spinlock_t *lock) __acquires(lock); void __lockfunc _raw_spin_lock_nested(raw_spinlock_t *lock, int subclass) __acquires(lock); +void __lockfunc _raw_spin_lock_bh_nested(raw_spinlock_t *lock, int subclass) + __acquires(lock); void __lockfunc _raw_spin_lock_nest_lock(raw_spinlock_t *lock, struct lockdep_map *map) __acquires(lock); diff --git a/include/linux/spinlock_api_up.h b/include/linux/spinlock_api_up.h index d0d1888..d3afef9 100644 --- a/include/linux/spinlock_api_up.h +++ b/include/linux/spinlock_api_up.h @@ -57,6 +57,7 @@ #define _raw_spin_lock(lock) __LOCK(lock) #define _raw_spin_lock_nested(lock, subclass) __LOCK(lock) +#define _raw_spin_lock_bh_nested(lock, subclass) __LOCK(lock) #define _raw_read_lock(lock) __LOCK(lock) #define _raw_write_lock(lock) __LOCK(lock) #define _raw_spin_lock_bh(lock) __LOCK_BH(lock) diff --git a/kernel/locking/spinlock.c b/kernel/locking/spinlock.c index 4b082b5..db3ccb1 100644 --- a/kernel/locking/spinlock.c +++ b/kernel/locking/spinlock.c @@ -363,6 +363,14 @@ void __lockfunc _raw_spin_lock_nested(raw_spinlock_t *lock, int subclass) } EXPORT_SYMBOL(_raw_spin_lock_nested); +void __lockfunc _raw_spin_lock_bh_nested(raw_spinlock_t *lock, int subclass) +{ + __local_bh_disable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET); + spin_acquire(&lock->dep_map, subclass, 0, _RET_IP_); + LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock); +} +EXPORT_SYMBOL(_raw_spin_lock_bh_nested); + unsigned long __lockfunc _raw_spin_lock_irqsave_nested(raw_spinlock_t *lock, int subclass) { -- cgit v0.10.2 From 97defe1ecf868b8127f8e62395499d6a06e4c4b1 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Fri, 2 Jan 2015 23:00:20 +0100 Subject: rhashtable: Per bucket locks & deferred expansion/shrinking Introduces an array of spinlocks to protect bucket mutations. The number of spinlocks per CPU is configurable and selected based on the hash of the bucket. This allows for parallel insertions and removals of entries which do not share a lock. The patch also defers expansion and shrinking to a worker queue which allows insertion and removal from atomic context. Insertions and deletions may occur in parallel to it and are only held up briefly while the particular bucket is linked or unzipped. Mutations of the bucket table pointer is protected by a new mutex, read access is RCU protected. In the event of an expansion or shrinking, the new bucket table allocated is exposed as a so called future table as soon as the resize process starts. Lookups, deletions, and insertions will briefly use both tables. The future table becomes the main table after an RCU grace period and initial linking of the old to the new table was performed. Optimization of the chains to make use of the new number of buckets follows only the new table is in use. The side effect of this is that during that RCU grace period, a bucket traversal using any rht_for_each() variant on the main table will not see any insertions performed during the RCU grace period which would at that point land in the future table. The lookup will see them as it searches both tables if needed. Having multiple insertions and removals occur in parallel requires nelems to become an atomic counter. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index f624d4b..a1688f0 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -19,6 +19,7 @@ #define _LINUX_RHASHTABLE_H #include +#include struct rhash_head { struct rhash_head __rcu *next; @@ -26,8 +27,17 @@ struct rhash_head { #define INIT_HASH_HEAD(ptr) ((ptr)->next = NULL) +/** + * struct bucket_table - Table of hash buckets + * @size: Number of hash buckets + * @locks_mask: Mask to apply before accessing locks[] + * @locks: Array of spinlocks protecting individual buckets + * @buckets: size * hash buckets + */ struct bucket_table { size_t size; + unsigned int locks_mask; + spinlock_t *locks; struct rhash_head __rcu *buckets[]; }; @@ -45,11 +55,11 @@ struct rhashtable; * @hash_rnd: Seed to use while hashing * @max_shift: Maximum number of shifts while expanding * @min_shift: Minimum number of shifts while shrinking + * @locks_mul: Number of bucket locks to allocate per cpu (default: 128) * @hashfn: Function to hash key * @obj_hashfn: Function to hash object * @grow_decision: If defined, may return true if table should expand * @shrink_decision: If defined, may return true if table should shrink - * @mutex_is_held: Must return true if protecting mutex is held */ struct rhashtable_params { size_t nelem_hint; @@ -59,37 +69,42 @@ struct rhashtable_params { u32 hash_rnd; size_t max_shift; size_t min_shift; + size_t locks_mul; rht_hashfn_t hashfn; rht_obj_hashfn_t obj_hashfn; bool (*grow_decision)(const struct rhashtable *ht, size_t new_size); bool (*shrink_decision)(const struct rhashtable *ht, size_t new_size); -#ifdef CONFIG_PROVE_LOCKING - int (*mutex_is_held)(void *parent); - void *parent; -#endif }; /** * struct rhashtable - Hash table handle * @tbl: Bucket table + * @future_tbl: Table under construction during expansion/shrinking * @nelems: Number of elements in table * @shift: Current size (1 << shift) * @p: Configuration parameters + * @run_work: Deferred worker to expand/shrink asynchronously + * @mutex: Mutex to protect current/future table swapping + * @being_destroyed: True if table is set up for destruction */ struct rhashtable { struct bucket_table __rcu *tbl; - size_t nelems; + struct bucket_table __rcu *future_tbl; + atomic_t nelems; size_t shift; struct rhashtable_params p; + struct delayed_work run_work; + struct mutex mutex; + bool being_destroyed; }; #ifdef CONFIG_PROVE_LOCKING -int lockdep_rht_mutex_is_held(const struct rhashtable *ht); +int lockdep_rht_mutex_is_held(struct rhashtable *ht); int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, u32 hash); #else -static inline int lockdep_rht_mutex_is_held(const struct rhashtable *ht) +static inline int lockdep_rht_mutex_is_held(struct rhashtable *ht) { return 1; } @@ -112,11 +127,11 @@ bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size); int rhashtable_expand(struct rhashtable *ht); int rhashtable_shrink(struct rhashtable *ht); -void *rhashtable_lookup(const struct rhashtable *ht, const void *key); -void *rhashtable_lookup_compare(const struct rhashtable *ht, const void *key, +void *rhashtable_lookup(struct rhashtable *ht, const void *key); +void *rhashtable_lookup_compare(struct rhashtable *ht, const void *key, bool (*compare)(void *, void *), void *arg); -void rhashtable_destroy(const struct rhashtable *ht); +void rhashtable_destroy(struct rhashtable *ht); #define rht_dereference(p, ht) \ rcu_dereference_protected(p, lockdep_rht_mutex_is_held(ht)) diff --git a/lib/rhashtable.c b/lib/rhashtable.c index e6b85c4..312e343 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -26,19 +26,42 @@ #define HASH_DEFAULT_SIZE 64UL #define HASH_MIN_SIZE 4UL +#define BUCKET_LOCKS_PER_CPU 128UL + +enum { + RHT_LOCK_NORMAL, + RHT_LOCK_NESTED, + RHT_LOCK_NESTED2, +}; + +/* The bucket lock is selected based on the hash and protects mutations + * on a group of hash buckets. + * + * IMPORTANT: When holding the bucket lock of both the old and new table + * during expansions and shrinking, the old bucket lock must always be + * acquired first. + */ +static spinlock_t *bucket_lock(const struct bucket_table *tbl, u32 hash) +{ + return &tbl->locks[hash & tbl->locks_mask]; +} #define ASSERT_RHT_MUTEX(HT) BUG_ON(!lockdep_rht_mutex_is_held(HT)) +#define ASSERT_BUCKET_LOCK(TBL, HASH) \ + BUG_ON(!lockdep_rht_bucket_is_held(TBL, HASH)) #ifdef CONFIG_PROVE_LOCKING -int lockdep_rht_mutex_is_held(const struct rhashtable *ht) +int lockdep_rht_mutex_is_held(struct rhashtable *ht) { - return ht->p.mutex_is_held(ht->p.parent); + return (debug_locks) ? lockdep_is_held(&ht->mutex) : 1; } EXPORT_SYMBOL_GPL(lockdep_rht_mutex_is_held); int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, u32 hash) { - return 1; + spinlock_t *lock = bucket_lock(tbl, hash); + + return (debug_locks) ? lockdep_is_held(lock) : 1; } EXPORT_SYMBOL_GPL(lockdep_rht_bucket_is_held); #endif @@ -66,7 +89,7 @@ static u32 obj_raw_hashfn(const struct rhashtable *ht, const void *ptr) return hash; } -static u32 key_hashfn(const struct rhashtable *ht, const void *key, u32 len) +static u32 key_hashfn(struct rhashtable *ht, const void *key, u32 len) { struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); u32 hash; @@ -95,7 +118,49 @@ static struct rhash_head __rcu **bucket_tail(struct bucket_table *tbl, u32 n) return pprev; } -static struct bucket_table *bucket_table_alloc(size_t nbuckets) +static int alloc_bucket_locks(struct rhashtable *ht, struct bucket_table *tbl) +{ + unsigned int i, size; +#if defined(CONFIG_PROVE_LOCKING) + unsigned int nr_pcpus = 2; +#else + unsigned int nr_pcpus = num_possible_cpus(); +#endif + + nr_pcpus = min_t(unsigned int, nr_pcpus, 32UL); + size = roundup_pow_of_two(nr_pcpus * ht->p.locks_mul); + + /* Never allocate more than one lock per bucket */ + size = min_t(unsigned int, size, tbl->size); + + if (sizeof(spinlock_t) != 0) { +#ifdef CONFIG_NUMA + if (size * sizeof(spinlock_t) > PAGE_SIZE) + tbl->locks = vmalloc(size * sizeof(spinlock_t)); + else +#endif + tbl->locks = kmalloc_array(size, sizeof(spinlock_t), + GFP_KERNEL); + if (!tbl->locks) + return -ENOMEM; + for (i = 0; i < size; i++) + spin_lock_init(&tbl->locks[i]); + } + tbl->locks_mask = size - 1; + + return 0; +} + +static void bucket_table_free(const struct bucket_table *tbl) +{ + if (tbl) + kvfree(tbl->locks); + + kvfree(tbl); +} + +static struct bucket_table *bucket_table_alloc(struct rhashtable *ht, + size_t nbuckets) { struct bucket_table *tbl; size_t size; @@ -110,12 +175,12 @@ static struct bucket_table *bucket_table_alloc(size_t nbuckets) tbl->size = nbuckets; - return tbl; -} + if (alloc_bucket_locks(ht, tbl) < 0) { + bucket_table_free(tbl); + return NULL; + } -static void bucket_table_free(const struct bucket_table *tbl) -{ - kvfree(tbl); + return tbl; } /** @@ -126,7 +191,7 @@ static void bucket_table_free(const struct bucket_table *tbl) bool rht_grow_above_75(const struct rhashtable *ht, size_t new_size) { /* Expand table when exceeding 75% load */ - return ht->nelems > (new_size / 4 * 3); + return atomic_read(&ht->nelems) > (new_size / 4 * 3); } EXPORT_SYMBOL_GPL(rht_grow_above_75); @@ -138,41 +203,59 @@ EXPORT_SYMBOL_GPL(rht_grow_above_75); bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size) { /* Shrink table beneath 30% load */ - return ht->nelems < (new_size * 3 / 10); + return atomic_read(&ht->nelems) < (new_size * 3 / 10); } EXPORT_SYMBOL_GPL(rht_shrink_below_30); static void hashtable_chain_unzip(const struct rhashtable *ht, const struct bucket_table *new_tbl, - struct bucket_table *old_tbl, size_t n) + struct bucket_table *old_tbl, + size_t old_hash) { struct rhash_head *he, *p, *next; - unsigned int h; + spinlock_t *new_bucket_lock, *new_bucket_lock2 = NULL; + unsigned int new_hash, new_hash2; + + ASSERT_BUCKET_LOCK(old_tbl, old_hash); /* Old bucket empty, no work needed. */ - p = rht_dereference(old_tbl->buckets[n], ht); + p = rht_dereference_bucket(old_tbl->buckets[old_hash], old_tbl, + old_hash); if (!p) return; + new_hash = new_hash2 = head_hashfn(ht, new_tbl, p); + new_bucket_lock = bucket_lock(new_tbl, new_hash); + /* Advance the old bucket pointer one or more times until it * reaches a node that doesn't hash to the same bucket as the * previous node p. Call the previous node p; */ - h = head_hashfn(ht, new_tbl, p); - rht_for_each_continue(he, p->next, old_tbl, n) { - if (head_hashfn(ht, new_tbl, he) != h) + rht_for_each_continue(he, p->next, old_tbl, old_hash) { + new_hash2 = head_hashfn(ht, new_tbl, he); + if (new_hash != new_hash2) break; p = he; } - RCU_INIT_POINTER(old_tbl->buckets[n], p->next); + rcu_assign_pointer(old_tbl->buckets[old_hash], p->next); + + spin_lock_bh_nested(new_bucket_lock, RHT_LOCK_NESTED); + + /* If we have encountered an entry that maps to a different bucket in + * the new table, lock down that bucket as well as we might cut off + * the end of the chain. + */ + new_bucket_lock2 = bucket_lock(new_tbl, new_hash); + if (new_bucket_lock != new_bucket_lock2) + spin_lock_bh_nested(new_bucket_lock2, RHT_LOCK_NESTED2); /* Find the subsequent node which does hash to the same * bucket as node P, or NULL if no such node exists. */ next = NULL; if (he) { - rht_for_each_continue(he, he->next, old_tbl, n) { - if (head_hashfn(ht, new_tbl, he) == h) { + rht_for_each_continue(he, he->next, old_tbl, old_hash) { + if (head_hashfn(ht, new_tbl, he) == new_hash) { next = he; break; } @@ -182,7 +265,23 @@ static void hashtable_chain_unzip(const struct rhashtable *ht, /* Set p's next pointer to that subsequent node pointer, * bypassing the nodes which do not hash to p's bucket */ - RCU_INIT_POINTER(p->next, next); + rcu_assign_pointer(p->next, next); + + if (new_bucket_lock != new_bucket_lock2) + spin_unlock_bh(new_bucket_lock2); + spin_unlock_bh(new_bucket_lock); +} + +static void link_old_to_new(struct bucket_table *new_tbl, + unsigned int new_hash, struct rhash_head *entry) +{ + spinlock_t *new_bucket_lock; + + new_bucket_lock = bucket_lock(new_tbl, new_hash); + + spin_lock_bh_nested(new_bucket_lock, RHT_LOCK_NESTED); + rcu_assign_pointer(*bucket_tail(new_tbl, new_hash), entry); + spin_unlock_bh(new_bucket_lock); } /** @@ -195,43 +294,59 @@ static void hashtable_chain_unzip(const struct rhashtable *ht, * This function may only be called in a context where it is safe to call * synchronize_rcu(), e.g. not within a rcu_read_lock() section. * - * The caller must ensure that no concurrent table mutations take place. - * It is however valid to have concurrent lookups if they are RCU protected. + * The caller must ensure that no concurrent resizing occurs by holding + * ht->mutex. + * + * It is valid to have concurrent insertions and deletions protected by per + * bucket locks or concurrent RCU protected lookups and traversals. */ int rhashtable_expand(struct rhashtable *ht) { struct bucket_table *new_tbl, *old_tbl = rht_dereference(ht->tbl, ht); struct rhash_head *he; - unsigned int i, h; - bool complete; + spinlock_t *old_bucket_lock; + unsigned int new_hash, old_hash; + bool complete = false; ASSERT_RHT_MUTEX(ht); if (ht->p.max_shift && ht->shift >= ht->p.max_shift) return 0; - new_tbl = bucket_table_alloc(old_tbl->size * 2); + new_tbl = bucket_table_alloc(ht, old_tbl->size * 2); if (new_tbl == NULL) return -ENOMEM; ht->shift++; - /* For each new bucket, search the corresponding old bucket - * for the first entry that hashes to the new bucket, and - * link the new bucket to that entry. Since all the entries - * which will end up in the new bucket appear in the same - * old bucket, this constructs an entirely valid new hash - * table, but with multiple buckets "zipped" together into a - * single imprecise chain. + /* Make insertions go into the new, empty table right away. Deletions + * and lookups will be attempted in both tables until we synchronize. + * The synchronize_rcu() guarantees for the new table to be picked up + * so no new additions go into the old table while we relink. + */ + rcu_assign_pointer(ht->future_tbl, new_tbl); + synchronize_rcu(); + + /* For each new bucket, search the corresponding old bucket for the + * first entry that hashes to the new bucket, and link the end of + * newly formed bucket chain (containing entries added to future + * table) to that entry. Since all the entries which will end up in + * the new bucket appear in the same old bucket, this constructs an + * entirely valid new hash table, but with multiple buckets + * "zipped" together into a single imprecise chain. */ - for (i = 0; i < new_tbl->size; i++) { - h = rht_bucket_index(old_tbl, i); - rht_for_each(he, old_tbl, h) { - if (head_hashfn(ht, new_tbl, he) == i) { - RCU_INIT_POINTER(new_tbl->buckets[i], he); + for (new_hash = 0; new_hash < new_tbl->size; new_hash++) { + old_hash = rht_bucket_index(old_tbl, new_hash); + old_bucket_lock = bucket_lock(old_tbl, old_hash); + + spin_lock_bh(old_bucket_lock); + rht_for_each(he, old_tbl, old_hash) { + if (head_hashfn(ht, new_tbl, he) == new_hash) { + link_old_to_new(new_tbl, new_hash, he); break; } } + spin_unlock_bh(old_bucket_lock); } /* Publish the new table pointer. Lookups may now traverse @@ -241,7 +356,7 @@ int rhashtable_expand(struct rhashtable *ht) rcu_assign_pointer(ht->tbl, new_tbl); /* Unzip interleaved hash chains */ - do { + while (!complete && !ht->being_destroyed) { /* Wait for readers. All new readers will see the new * table, and thus no references to the old table will * remain. @@ -253,12 +368,17 @@ int rhashtable_expand(struct rhashtable *ht) * table): ... */ complete = true; - for (i = 0; i < old_tbl->size; i++) { - hashtable_chain_unzip(ht, new_tbl, old_tbl, i); - if (old_tbl->buckets[i] != NULL) + for (old_hash = 0; old_hash < old_tbl->size; old_hash++) { + old_bucket_lock = bucket_lock(old_tbl, old_hash); + spin_lock_bh(old_bucket_lock); + + hashtable_chain_unzip(ht, new_tbl, old_tbl, old_hash); + if (old_tbl->buckets[old_hash] != NULL) complete = false; + + spin_unlock_bh(old_bucket_lock); } - } while (!complete); + } bucket_table_free(old_tbl); return 0; @@ -272,38 +392,65 @@ EXPORT_SYMBOL_GPL(rhashtable_expand); * This function may only be called in a context where it is safe to call * synchronize_rcu(), e.g. not within a rcu_read_lock() section. * + * The caller must ensure that no concurrent resizing occurs by holding + * ht->mutex. + * * The caller must ensure that no concurrent table mutations take place. * It is however valid to have concurrent lookups if they are RCU protected. + * + * It is valid to have concurrent insertions and deletions protected by per + * bucket locks or concurrent RCU protected lookups and traversals. */ int rhashtable_shrink(struct rhashtable *ht) { - struct bucket_table *ntbl, *tbl = rht_dereference(ht->tbl, ht); - unsigned int i; + struct bucket_table *new_tbl, *tbl = rht_dereference(ht->tbl, ht); + spinlock_t *new_bucket_lock, *old_bucket_lock1, *old_bucket_lock2; + unsigned int new_hash; ASSERT_RHT_MUTEX(ht); if (ht->shift <= ht->p.min_shift) return 0; - ntbl = bucket_table_alloc(tbl->size / 2); - if (ntbl == NULL) + new_tbl = bucket_table_alloc(ht, tbl->size / 2); + if (new_tbl == NULL) return -ENOMEM; - ht->shift--; + rcu_assign_pointer(ht->future_tbl, new_tbl); + synchronize_rcu(); - /* Link each bucket in the new table to the first bucket - * in the old table that contains entries which will hash - * to the new bucket. + /* Link the first entry in the old bucket to the end of the + * bucket in the new table. As entries are concurrently being + * added to the new table, lock down the new bucket. As we + * always divide the size in half when shrinking, each bucket + * in the new table maps to exactly two buckets in the old + * table. + * + * As removals can occur concurrently on the old table, we need + * to lock down both matching buckets in the old table. */ - for (i = 0; i < ntbl->size; i++) { - ntbl->buckets[i] = tbl->buckets[i]; - RCU_INIT_POINTER(*bucket_tail(ntbl, i), - tbl->buckets[i + ntbl->size]); - + for (new_hash = 0; new_hash < new_tbl->size; new_hash++) { + old_bucket_lock1 = bucket_lock(tbl, new_hash); + old_bucket_lock2 = bucket_lock(tbl, new_hash + new_tbl->size); + new_bucket_lock = bucket_lock(new_tbl, new_hash); + + spin_lock_bh(old_bucket_lock1); + spin_lock_bh_nested(old_bucket_lock2, RHT_LOCK_NESTED); + spin_lock_bh_nested(new_bucket_lock, RHT_LOCK_NESTED2); + + rcu_assign_pointer(*bucket_tail(new_tbl, new_hash), + tbl->buckets[new_hash]); + rcu_assign_pointer(*bucket_tail(new_tbl, new_hash), + tbl->buckets[new_hash + new_tbl->size]); + + spin_unlock_bh(new_bucket_lock); + spin_unlock_bh(old_bucket_lock2); + spin_unlock_bh(old_bucket_lock1); } /* Publish the new, valid hash table */ - rcu_assign_pointer(ht->tbl, ntbl); + rcu_assign_pointer(ht->tbl, new_tbl); + ht->shift--; /* Wait for readers. No new readers will have references to the * old hash table. @@ -316,31 +463,63 @@ int rhashtable_shrink(struct rhashtable *ht) } EXPORT_SYMBOL_GPL(rhashtable_shrink); +static void rht_deferred_worker(struct work_struct *work) +{ + struct rhashtable *ht; + struct bucket_table *tbl; + + ht = container_of(work, struct rhashtable, run_work.work); + mutex_lock(&ht->mutex); + tbl = rht_dereference(ht->tbl, ht); + + if (ht->p.grow_decision && ht->p.grow_decision(ht, tbl->size)) + rhashtable_expand(ht); + else if (ht->p.shrink_decision && ht->p.shrink_decision(ht, tbl->size)) + rhashtable_shrink(ht); + + mutex_unlock(&ht->mutex); +} + /** * rhashtable_insert - insert object into hash hash table * @ht: hash table * @obj: pointer to hash head inside object * - * Will automatically grow the table via rhashtable_expand() if the the - * grow_decision function specified at rhashtable_init() returns true. + * Will take a per bucket spinlock to protect against mutual mutations + * on the same bucket. Multiple insertions may occur in parallel unless + * they map to the same bucket lock. * - * The caller must ensure that no concurrent table mutations occur. It is - * however valid to have concurrent lookups if they are RCU protected. + * It is safe to call this function from atomic context. + * + * Will trigger an automatic deferred table resizing if the size grows + * beyond the watermark indicated by grow_decision() which can be passed + * to rhashtable_init(). */ void rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj) { - struct bucket_table *tbl = rht_dereference(ht->tbl, ht); - u32 hash; + struct bucket_table *tbl; + spinlock_t *lock; + unsigned hash; - ASSERT_RHT_MUTEX(ht); + rcu_read_lock(); + tbl = rht_dereference_rcu(ht->future_tbl, ht); hash = head_hashfn(ht, tbl, obj); + lock = bucket_lock(tbl, hash); + + spin_lock_bh(lock); RCU_INIT_POINTER(obj->next, tbl->buckets[hash]); rcu_assign_pointer(tbl->buckets[hash], obj); - ht->nelems++; + spin_unlock_bh(lock); - if (ht->p.grow_decision && ht->p.grow_decision(ht, tbl->size)) - rhashtable_expand(ht); + atomic_inc(&ht->nelems); + + /* Only grow the table if no resizing is currently in progress. */ + if (ht->tbl != ht->future_tbl && + ht->p.grow_decision && ht->p.grow_decision(ht, tbl->size)) + schedule_delayed_work(&ht->run_work, 0); + + rcu_read_unlock(); } EXPORT_SYMBOL_GPL(rhashtable_insert); @@ -361,32 +540,56 @@ EXPORT_SYMBOL_GPL(rhashtable_insert); */ bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *obj) { - struct bucket_table *tbl = rht_dereference(ht->tbl, ht); + struct bucket_table *tbl; struct rhash_head __rcu **pprev; struct rhash_head *he; - u32 h; + spinlock_t *lock; + unsigned int hash; - ASSERT_RHT_MUTEX(ht); + rcu_read_lock(); + tbl = rht_dereference_rcu(ht->tbl, ht); + hash = head_hashfn(ht, tbl, obj); - h = head_hashfn(ht, tbl, obj); + lock = bucket_lock(tbl, hash); + spin_lock_bh(lock); - pprev = &tbl->buckets[h]; - rht_for_each(he, tbl, h) { +restart: + pprev = &tbl->buckets[hash]; + rht_for_each(he, tbl, hash) { if (he != obj) { pprev = &he->next; continue; } - RCU_INIT_POINTER(*pprev, he->next); - ht->nelems--; + rcu_assign_pointer(*pprev, obj->next); + atomic_dec(&ht->nelems); - if (ht->p.shrink_decision && + spin_unlock_bh(lock); + + if (ht->tbl != ht->future_tbl && + ht->p.shrink_decision && ht->p.shrink_decision(ht, tbl->size)) - rhashtable_shrink(ht); + schedule_delayed_work(&ht->run_work, 0); + + rcu_read_unlock(); return true; } + if (tbl != rht_dereference_rcu(ht->tbl, ht)) { + spin_unlock_bh(lock); + + tbl = rht_dereference_rcu(ht->tbl, ht); + hash = head_hashfn(ht, tbl, obj); + + lock = bucket_lock(tbl, hash); + spin_lock_bh(lock); + goto restart; + } + + spin_unlock_bh(lock); + rcu_read_unlock(); + return false; } EXPORT_SYMBOL_GPL(rhashtable_remove); @@ -402,25 +605,35 @@ EXPORT_SYMBOL_GPL(rhashtable_remove); * This lookup function may only be used for fixed key hash table (key_len * paramter set). It will BUG() if used inappropriately. * - * Lookups may occur in parallel with hash mutations as long as the lookup is - * guarded by rcu_read_lock(). The caller must take care of this. + * Lookups may occur in parallel with hashtable mutations and resizing. */ -void *rhashtable_lookup(const struct rhashtable *ht, const void *key) +void *rhashtable_lookup(struct rhashtable *ht, const void *key) { - const struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); + const struct bucket_table *tbl, *old_tbl; struct rhash_head *he; - u32 h; + u32 hash; BUG_ON(!ht->p.key_len); - h = key_hashfn(ht, key, ht->p.key_len); - rht_for_each_rcu(he, tbl, h) { + rcu_read_lock(); + old_tbl = rht_dereference_rcu(ht->tbl, ht); + tbl = rht_dereference_rcu(ht->future_tbl, ht); + hash = key_hashfn(ht, key, ht->p.key_len); +restart: + rht_for_each_rcu(he, tbl, rht_bucket_index(tbl, hash)) { if (memcmp(rht_obj(ht, he) + ht->p.key_offset, key, ht->p.key_len)) continue; + rcu_read_unlock(); return rht_obj(ht, he); } + if (unlikely(tbl != old_tbl)) { + tbl = old_tbl; + goto restart; + } + + rcu_read_unlock(); return NULL; } EXPORT_SYMBOL_GPL(rhashtable_lookup); @@ -435,25 +648,36 @@ EXPORT_SYMBOL_GPL(rhashtable_lookup); * Traverses the bucket chain behind the provided hash value and calls the * specified compare function for each entry. * - * Lookups may occur in parallel with hash mutations as long as the lookup is - * guarded by rcu_read_lock(). The caller must take care of this. + * Lookups may occur in parallel with hashtable mutations and resizing. * * Returns the first entry on which the compare function returned true. */ -void *rhashtable_lookup_compare(const struct rhashtable *ht, const void *key, +void *rhashtable_lookup_compare(struct rhashtable *ht, const void *key, bool (*compare)(void *, void *), void *arg) { - const struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); + const struct bucket_table *tbl, *old_tbl; struct rhash_head *he; u32 hash; + rcu_read_lock(); + + old_tbl = rht_dereference_rcu(ht->tbl, ht); + tbl = rht_dereference_rcu(ht->future_tbl, ht); hash = key_hashfn(ht, key, ht->p.key_len); - rht_for_each_rcu(he, tbl, hash) { +restart: + rht_for_each_rcu(he, tbl, rht_bucket_index(tbl, hash)) { if (!compare(rht_obj(ht, he), arg)) continue; + rcu_read_unlock(); return rht_obj(ht, he); } + if (unlikely(tbl != old_tbl)) { + tbl = old_tbl; + goto restart; + } + rcu_read_unlock(); + return NULL; } EXPORT_SYMBOL_GPL(rhashtable_lookup_compare); @@ -485,9 +709,6 @@ static size_t rounded_hashtable_size(struct rhashtable_params *params) * .key_offset = offsetof(struct test_obj, key), * .key_len = sizeof(int), * .hashfn = jhash, - * #ifdef CONFIG_PROVE_LOCKING - * .mutex_is_held = &my_mutex_is_held, - * #endif * }; * * Configuration Example 2: Variable length keys @@ -507,9 +728,6 @@ static size_t rounded_hashtable_size(struct rhashtable_params *params) * .head_offset = offsetof(struct test_obj, node), * .hashfn = jhash, * .obj_hashfn = my_hash_fn, - * #ifdef CONFIG_PROVE_LOCKING - * .mutex_is_held = &my_mutex_is_held, - * #endif * }; */ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params) @@ -529,18 +747,29 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params) if (params->nelem_hint) size = rounded_hashtable_size(params); - tbl = bucket_table_alloc(size); + memset(ht, 0, sizeof(*ht)); + mutex_init(&ht->mutex); + memcpy(&ht->p, params, sizeof(*params)); + + if (params->locks_mul) + ht->p.locks_mul = roundup_pow_of_two(params->locks_mul); + else + ht->p.locks_mul = BUCKET_LOCKS_PER_CPU; + + tbl = bucket_table_alloc(ht, size); if (tbl == NULL) return -ENOMEM; - memset(ht, 0, sizeof(*ht)); ht->shift = ilog2(tbl->size); - memcpy(&ht->p, params, sizeof(*params)); RCU_INIT_POINTER(ht->tbl, tbl); + RCU_INIT_POINTER(ht->future_tbl, tbl); if (!ht->p.hash_rnd) get_random_bytes(&ht->p.hash_rnd, sizeof(ht->p.hash_rnd)); + if (ht->p.grow_decision || ht->p.shrink_decision) + INIT_DEFERRABLE_WORK(&ht->run_work, rht_deferred_worker); + return 0; } EXPORT_SYMBOL_GPL(rhashtable_init); @@ -553,9 +782,16 @@ EXPORT_SYMBOL_GPL(rhashtable_init); * has to make sure that no resizing may happen by unpublishing the hashtable * and waiting for the quiescent cycle before releasing the bucket array. */ -void rhashtable_destroy(const struct rhashtable *ht) +void rhashtable_destroy(struct rhashtable *ht) { - bucket_table_free(ht->tbl); + ht->being_destroyed = true; + + mutex_lock(&ht->mutex); + + cancel_delayed_work(&ht->run_work); + bucket_table_free(rht_dereference(ht->tbl, ht)); + + mutex_unlock(&ht->mutex); } EXPORT_SYMBOL_GPL(rhashtable_destroy); @@ -570,13 +806,6 @@ EXPORT_SYMBOL_GPL(rhashtable_destroy); #define TEST_PTR ((void *) 0xdeadbeef) #define TEST_NEXPANDS 4 -#ifdef CONFIG_PROVE_LOCKING -static int test_mutex_is_held(void *parent) -{ - return 1; -} -#endif - struct test_obj { void *ptr; int value; @@ -646,10 +875,10 @@ static void test_bucket_stats(struct rhashtable *ht, bool quiet) i, tbl->buckets[i], cnt); } - pr_info(" Traversal complete: counted=%u, nelems=%zu, entries=%d\n", - total, ht->nelems, TEST_ENTRIES); + pr_info(" Traversal complete: counted=%u, nelems=%u, entries=%d\n", + total, atomic_read(&ht->nelems), TEST_ENTRIES); - if (total != ht->nelems || total != TEST_ENTRIES) + if (total != atomic_read(&ht->nelems) || total != TEST_ENTRIES) pr_warn("Test failed: Total count mismatch ^^^"); } @@ -688,7 +917,9 @@ static int __init test_rhashtable(struct rhashtable *ht) for (i = 0; i < TEST_NEXPANDS; i++) { pr_info(" Table expansion iteration %u...\n", i); + mutex_lock(&ht->mutex); rhashtable_expand(ht); + mutex_unlock(&ht->mutex); rcu_read_lock(); pr_info(" Verifying lookups...\n"); @@ -698,7 +929,9 @@ static int __init test_rhashtable(struct rhashtable *ht) for (i = 0; i < TEST_NEXPANDS; i++) { pr_info(" Table shrinkage iteration %u...\n", i); + mutex_lock(&ht->mutex); rhashtable_shrink(ht); + mutex_unlock(&ht->mutex); rcu_read_lock(); pr_info(" Verifying lookups...\n"); @@ -741,9 +974,6 @@ static int __init test_rht_init(void) .key_offset = offsetof(struct test_obj, value), .key_len = sizeof(int), .hashfn = jhash, -#ifdef CONFIG_PROVE_LOCKING - .mutex_is_held = &test_mutex_is_held, -#endif .grow_decision = rht_grow_above_75, .shrink_decision = rht_shrink_below_30, }; diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index 7f903cf..75887d7 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -33,7 +33,7 @@ static bool nft_hash_lookup(const struct nft_set *set, const struct nft_data *key, struct nft_data *data) { - const struct rhashtable *priv = nft_set_priv(set); + struct rhashtable *priv = nft_set_priv(set); const struct nft_hash_elem *he; he = rhashtable_lookup(priv, key); @@ -113,7 +113,7 @@ static bool nft_hash_compare(void *ptr, void *arg) static int nft_hash_get(const struct nft_set *set, struct nft_set_elem *elem) { - const struct rhashtable *priv = nft_set_priv(set); + struct rhashtable *priv = nft_set_priv(set); struct nft_compare_arg arg = { .set = set, .elem = elem, @@ -129,7 +129,7 @@ static int nft_hash_get(const struct nft_set *set, struct nft_set_elem *elem) static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set, struct nft_set_iter *iter) { - const struct rhashtable *priv = nft_set_priv(set); + struct rhashtable *priv = nft_set_priv(set); const struct bucket_table *tbl; const struct nft_hash_elem *he; struct nft_set_elem elem; @@ -162,13 +162,6 @@ static unsigned int nft_hash_privsize(const struct nlattr * const nla[]) return sizeof(struct rhashtable); } -#ifdef CONFIG_PROVE_LOCKING -static int lockdep_nfnl_lock_is_held(void *parent) -{ - return lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES); -} -#endif - static int nft_hash_init(const struct nft_set *set, const struct nft_set_desc *desc, const struct nlattr * const tb[]) @@ -182,9 +175,6 @@ static int nft_hash_init(const struct nft_set *set, .hashfn = jhash, .grow_decision = rht_grow_above_75, .shrink_decision = rht_shrink_below_30, -#ifdef CONFIG_PROVE_LOCKING - .mutex_is_held = lockdep_nfnl_lock_is_held, -#endif }; return rhashtable_init(priv, ¶ms); @@ -192,16 +182,23 @@ static int nft_hash_init(const struct nft_set *set, static void nft_hash_destroy(const struct nft_set *set) { - const struct rhashtable *priv = nft_set_priv(set); - const struct bucket_table *tbl = priv->tbl; + struct rhashtable *priv = nft_set_priv(set); + const struct bucket_table *tbl; struct nft_hash_elem *he; struct rhash_head *pos, *next; unsigned int i; + /* Stop an eventual async resizing */ + priv->being_destroyed = true; + mutex_lock(&priv->mutex); + + tbl = rht_dereference(priv->tbl, priv); for (i = 0; i < tbl->size; i++) { rht_for_each_entry_safe(he, pos, next, tbl, i, node) nft_hash_elem_destroy(set, he); } + mutex_unlock(&priv->mutex); + rhashtable_destroy(priv); } diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 57449b6..738c3bf 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -114,15 +114,6 @@ static atomic_t nl_table_users = ATOMIC_INIT(0); DEFINE_MUTEX(nl_sk_hash_lock); EXPORT_SYMBOL_GPL(nl_sk_hash_lock); -#ifdef CONFIG_PROVE_LOCKING -static int lockdep_nl_sk_hash_is_held(void *parent) -{ - if (debug_locks) - return lockdep_is_held(&nl_sk_hash_lock) || lockdep_is_held(&nl_table_lock); - return 1; -} -#endif - static ATOMIC_NOTIFIER_HEAD(netlink_chain); static DEFINE_SPINLOCK(netlink_tap_lock); @@ -1063,7 +1054,8 @@ static int netlink_insert(struct sock *sk, struct net *net, u32 portid) goto err; err = -ENOMEM; - if (BITS_PER_LONG > 32 && unlikely(table->hash.nelems >= UINT_MAX)) + if (BITS_PER_LONG > 32 && + unlikely(atomic_read(&table->hash.nelems) >= UINT_MAX)) goto err; nlk_sk(sk)->portid = portid; @@ -3122,9 +3114,6 @@ static int __init netlink_proto_init(void) .max_shift = 16, /* 64K */ .grow_decision = rht_grow_above_75, .shrink_decision = rht_shrink_below_30, -#ifdef CONFIG_PROVE_LOCKING - .mutex_is_held = lockdep_nl_sk_hash_is_held, -#endif }; if (err != 0) -- cgit v0.10.2 From f89bd6f87a53ce5a7d60662429591ebac2745c10 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Fri, 2 Jan 2015 23:00:21 +0100 Subject: rhashtable: Supports for nulls marker In order to allow for wider usage of rhashtable, use a special nulls marker to terminate each chain. The reason for not using the existing nulls_list is that the prev pointer usage would not be valid as entries can be linked in two different buckets at the same time. The 4 nulls base bits can be set through the rhashtable_params structure like this: struct rhashtable_params params = { [...] .nulls_base = (1U << RHT_BASE_SHIFT), }; This reduces the hash length from 32 bits to 27 bits. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/include/linux/list_nulls.h b/include/linux/list_nulls.h index 5d10ae3..e8c300e 100644 --- a/include/linux/list_nulls.h +++ b/include/linux/list_nulls.h @@ -21,8 +21,9 @@ struct hlist_nulls_head { struct hlist_nulls_node { struct hlist_nulls_node *next, **pprev; }; +#define NULLS_MARKER(value) (1UL | (((long)value) << 1)) #define INIT_HLIST_NULLS_HEAD(ptr, nulls) \ - ((ptr)->first = (struct hlist_nulls_node *) (1UL | (((long)nulls) << 1))) + ((ptr)->first = (struct hlist_nulls_node *) NULLS_MARKER(nulls)) #define hlist_nulls_entry(ptr, type, member) container_of(ptr,type,member) /** diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index a1688f0..de7cac7 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -18,15 +18,32 @@ #ifndef _LINUX_RHASHTABLE_H #define _LINUX_RHASHTABLE_H -#include +#include #include +/* + * The end of the chain is marked with a special nulls marks which has + * the following format: + * + * +-------+-----------------------------------------------------+-+ + * | Base | Hash |1| + * +-------+-----------------------------------------------------+-+ + * + * Base (4 bits) : Reserved to distinguish between multiple tables. + * Specified via &struct rhashtable_params.nulls_base. + * Hash (27 bits): Full hash (unmasked) of first element added to bucket + * 1 (1 bit) : Nulls marker (always set) + * + * The remaining bits of the next pointer remain unused for now. + */ +#define RHT_BASE_BITS 4 +#define RHT_HASH_BITS 27 +#define RHT_BASE_SHIFT RHT_HASH_BITS + struct rhash_head { struct rhash_head __rcu *next; }; -#define INIT_HASH_HEAD(ptr) ((ptr)->next = NULL) - /** * struct bucket_table - Table of hash buckets * @size: Number of hash buckets @@ -55,6 +72,7 @@ struct rhashtable; * @hash_rnd: Seed to use while hashing * @max_shift: Maximum number of shifts while expanding * @min_shift: Minimum number of shifts while shrinking + * @nulls_base: Base value to generate nulls marker * @locks_mul: Number of bucket locks to allocate per cpu (default: 128) * @hashfn: Function to hash key * @obj_hashfn: Function to hash object @@ -69,6 +87,7 @@ struct rhashtable_params { u32 hash_rnd; size_t max_shift; size_t min_shift; + u32 nulls_base; size_t locks_mul; rht_hashfn_t hashfn; rht_obj_hashfn_t obj_hashfn; @@ -100,6 +119,24 @@ struct rhashtable { bool being_destroyed; }; +static inline unsigned long rht_marker(const struct rhashtable *ht, u32 hash) +{ + return NULLS_MARKER(ht->p.nulls_base + hash); +} + +#define INIT_RHT_NULLS_HEAD(ptr, ht, hash) \ + ((ptr) = (typeof(ptr)) rht_marker(ht, hash)) + +static inline bool rht_is_a_nulls(const struct rhash_head *ptr) +{ + return ((unsigned long) ptr & 1); +} + +static inline unsigned long rht_get_nulls_value(const struct rhash_head *ptr) +{ + return ((unsigned long) ptr) >> 1; +} + #ifdef CONFIG_PROVE_LOCKING int lockdep_rht_mutex_is_held(struct rhashtable *ht); int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, u32 hash); @@ -157,7 +194,7 @@ void rhashtable_destroy(struct rhashtable *ht); */ #define rht_for_each_continue(pos, head, tbl, hash) \ for (pos = rht_dereference_bucket(head, tbl, hash); \ - pos; \ + !rht_is_a_nulls(pos); \ pos = rht_dereference_bucket((pos)->next, tbl, hash)) /** @@ -180,7 +217,7 @@ void rhashtable_destroy(struct rhashtable *ht); */ #define rht_for_each_entry_continue(tpos, pos, head, tbl, hash, member) \ for (pos = rht_dereference_bucket(head, tbl, hash); \ - pos && rht_entry(tpos, pos, member); \ + (!rht_is_a_nulls(pos)) && rht_entry(tpos, pos, member); \ pos = rht_dereference_bucket((pos)->next, tbl, hash)) /** @@ -209,9 +246,9 @@ void rhashtable_destroy(struct rhashtable *ht); */ #define rht_for_each_entry_safe(tpos, pos, next, tbl, hash, member) \ for (pos = rht_dereference_bucket((tbl)->buckets[hash], tbl, hash), \ - next = pos ? rht_dereference_bucket(pos->next, tbl, hash) \ - : NULL; \ - pos && rht_entry(tpos, pos, member); \ + next = !rht_is_a_nulls(pos) ? \ + rht_dereference_bucket(pos->next, tbl, hash) : NULL; \ + (!rht_is_a_nulls(pos)) && rht_entry(tpos, pos, member); \ pos = next) /** @@ -228,7 +265,7 @@ void rhashtable_destroy(struct rhashtable *ht); #define rht_for_each_rcu_continue(pos, head, tbl, hash) \ for (({barrier(); }), \ pos = rht_dereference_bucket_rcu(head, tbl, hash); \ - pos; \ + !rht_is_a_nulls(pos); \ pos = rcu_dereference_raw(pos->next)) /** @@ -260,7 +297,7 @@ void rhashtable_destroy(struct rhashtable *ht); #define rht_for_each_entry_rcu_continue(tpos, pos, head, tbl, hash, member) \ for (({barrier(); }), \ pos = rht_dereference_bucket_rcu(head, tbl, hash); \ - pos && rht_entry(tpos, pos, member); \ + (!rht_is_a_nulls(pos)) && rht_entry(tpos, pos, member); \ pos = rht_dereference_bucket_rcu(pos->next, tbl, hash)) /** diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 312e343..cbad192 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -28,6 +28,9 @@ #define HASH_MIN_SIZE 4UL #define BUCKET_LOCKS_PER_CPU 128UL +/* Base bits plus 1 bit for nulls marker */ +#define HASH_RESERVED_SPACE (RHT_BASE_BITS + 1) + enum { RHT_LOCK_NORMAL, RHT_LOCK_NESTED, @@ -86,7 +89,7 @@ static u32 obj_raw_hashfn(const struct rhashtable *ht, const void *ptr) hash = ht->p.hashfn(ptr + ht->p.key_offset, ht->p.key_len, ht->p.hash_rnd); - return hash; + return hash >> HASH_RESERVED_SPACE; } static u32 key_hashfn(struct rhashtable *ht, const void *key, u32 len) @@ -95,6 +98,7 @@ static u32 key_hashfn(struct rhashtable *ht, const void *key, u32 len) u32 hash; hash = ht->p.hashfn(key, len, ht->p.hash_rnd); + hash >>= HASH_RESERVED_SPACE; return rht_bucket_index(tbl, hash); } @@ -111,7 +115,7 @@ static struct rhash_head __rcu **bucket_tail(struct bucket_table *tbl, u32 n) struct rhash_head __rcu **pprev; for (pprev = &tbl->buckets[n]; - rht_dereference_bucket(*pprev, tbl, n); + !rht_is_a_nulls(rht_dereference_bucket(*pprev, tbl, n)); pprev = &rht_dereference_bucket(*pprev, tbl, n)->next) ; @@ -164,6 +168,7 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht, { struct bucket_table *tbl; size_t size; + int i; size = sizeof(*tbl) + nbuckets * sizeof(tbl->buckets[0]); tbl = kzalloc(size, GFP_KERNEL | __GFP_NOWARN); @@ -180,6 +185,9 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht, return NULL; } + for (i = 0; i < nbuckets; i++) + INIT_RHT_NULLS_HEAD(tbl->buckets[i], ht, i); + return tbl; } @@ -221,7 +229,7 @@ static void hashtable_chain_unzip(const struct rhashtable *ht, /* Old bucket empty, no work needed. */ p = rht_dereference_bucket(old_tbl->buckets[old_hash], old_tbl, old_hash); - if (!p) + if (rht_is_a_nulls(p)) return; new_hash = new_hash2 = head_hashfn(ht, new_tbl, p); @@ -252,8 +260,8 @@ static void hashtable_chain_unzip(const struct rhashtable *ht, /* Find the subsequent node which does hash to the same * bucket as node P, or NULL if no such node exists. */ - next = NULL; - if (he) { + INIT_RHT_NULLS_HEAD(next, ht, old_hash); + if (!rht_is_a_nulls(he)) { rht_for_each_continue(he, he->next, old_tbl, old_hash) { if (head_hashfn(ht, new_tbl, he) == new_hash) { next = he; @@ -369,11 +377,15 @@ int rhashtable_expand(struct rhashtable *ht) */ complete = true; for (old_hash = 0; old_hash < old_tbl->size; old_hash++) { + struct rhash_head *head; + old_bucket_lock = bucket_lock(old_tbl, old_hash); spin_lock_bh(old_bucket_lock); hashtable_chain_unzip(ht, new_tbl, old_tbl, old_hash); - if (old_tbl->buckets[old_hash] != NULL) + head = rht_dereference_bucket(old_tbl->buckets[old_hash], + old_tbl, old_hash); + if (!rht_is_a_nulls(head)) complete = false; spin_unlock_bh(old_bucket_lock); @@ -498,6 +510,7 @@ static void rht_deferred_worker(struct work_struct *work) void rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj) { struct bucket_table *tbl; + struct rhash_head *head; spinlock_t *lock; unsigned hash; @@ -508,7 +521,12 @@ void rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj) lock = bucket_lock(tbl, hash); spin_lock_bh(lock); - RCU_INIT_POINTER(obj->next, tbl->buckets[hash]); + head = rht_dereference_bucket(tbl->buckets[hash], tbl, hash); + if (rht_is_a_nulls(head)) + INIT_RHT_NULLS_HEAD(obj->next, ht, hash); + else + RCU_INIT_POINTER(obj->next, head); + rcu_assign_pointer(tbl->buckets[hash], obj); spin_unlock_bh(lock); @@ -709,6 +727,7 @@ static size_t rounded_hashtable_size(struct rhashtable_params *params) * .key_offset = offsetof(struct test_obj, key), * .key_len = sizeof(int), * .hashfn = jhash, + * .nulls_base = (1U << RHT_BASE_SHIFT), * }; * * Configuration Example 2: Variable length keys @@ -741,6 +760,9 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params) (!params->key_len && !params->obj_hashfn)) return -EINVAL; + if (params->nulls_base && params->nulls_base < (1U << RHT_BASE_SHIFT)) + return -EINVAL; + params->min_shift = max_t(size_t, params->min_shift, ilog2(HASH_MIN_SIZE)); @@ -974,6 +996,7 @@ static int __init test_rht_init(void) .key_offset = offsetof(struct test_obj, value), .key_len = sizeof(int), .hashfn = jhash, + .nulls_base = (3U << RHT_BASE_SHIFT), .grow_decision = rht_grow_above_75, .shrink_decision = rht_shrink_below_30, }; -- cgit v0.10.2 From 21e4902aea80ef35afc00ee8d2abdea4f519b7f7 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Fri, 2 Jan 2015 23:00:22 +0100 Subject: netlink: Lockless lookup with RCU grace period in socket release Defers the release of the socket reference using call_rcu() to allow using an RCU read-side protected call to rhashtable_lookup() This restores behaviour and performance gains as previously introduced by e341694 ("netlink: Convert netlink_lookup() to use RCU protected hash table") without the side effect of severely delayed socket destruction. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 738c3bf..298e1df 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -97,12 +97,12 @@ static int netlink_dump(struct sock *sk); static void netlink_skb_destructor(struct sk_buff *skb); /* nl_table locking explained: - * Lookup and traversal are protected with nl_sk_hash_lock or nl_table_lock - * combined with an RCU read-side lock. Insertion and removal are protected - * with nl_sk_hash_lock while using RCU list modification primitives and may - * run in parallel to nl_table_lock protected lookups. Destruction of the - * Netlink socket may only occur *after* nl_table_lock has been acquired - * either during or after the socket has been removed from the list. + * Lookup and traversal are protected with an RCU read-side lock. Insertion + * and removal are protected with nl_sk_hash_lock while using RCU list + * modification primitives and may run in parallel to RCU protected lookups. + * Destruction of the Netlink socket may only occur *after* nl_table_lock has + * been acquired * either during or after the socket has been removed from + * the list and after an RCU grace period. */ DEFINE_RWLOCK(nl_table_lock); EXPORT_SYMBOL_GPL(nl_table_lock); @@ -1003,13 +1003,11 @@ static struct sock *netlink_lookup(struct net *net, int protocol, u32 portid) struct netlink_table *table = &nl_table[protocol]; struct sock *sk; - read_lock(&nl_table_lock); rcu_read_lock(); sk = __netlink_lookup(table, portid, net); if (sk) sock_hold(sk); rcu_read_unlock(); - read_unlock(&nl_table_lock); return sk; } @@ -1183,6 +1181,13 @@ out_module: goto out; } +static void deferred_put_nlk_sk(struct rcu_head *head) +{ + struct netlink_sock *nlk = container_of(head, struct netlink_sock, rcu); + + sock_put(&nlk->sk); +} + static int netlink_release(struct socket *sock) { struct sock *sk = sock->sk; @@ -1248,7 +1253,7 @@ static int netlink_release(struct socket *sock) local_bh_disable(); sock_prot_inuse_add(sock_net(sk), &netlink_proto, -1); local_bh_enable(); - sock_put(sk); + call_rcu(&nlk->rcu, deferred_put_nlk_sk); return 0; } @@ -1263,7 +1268,6 @@ static int netlink_autobind(struct socket *sock) retry: cond_resched(); - netlink_table_grab(); rcu_read_lock(); if (__netlink_lookup(table, portid, net)) { /* Bind collision, search negative portid values. */ @@ -1271,11 +1275,9 @@ retry: if (rover > -4097) rover = -4097; rcu_read_unlock(); - netlink_table_ungrab(); goto retry; } rcu_read_unlock(); - netlink_table_ungrab(); err = netlink_insert(sk, net, portid); if (err == -EADDRINUSE) @@ -2910,9 +2912,8 @@ static struct sock *netlink_seq_socket_idx(struct seq_file *seq, loff_t pos) } static void *netlink_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(nl_table_lock) __acquires(RCU) + __acquires(RCU) { - read_lock(&nl_table_lock); rcu_read_lock(); return *pos ? netlink_seq_socket_idx(seq, *pos - 1) : SEQ_START_TOKEN; } @@ -2964,10 +2965,9 @@ static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos) } static void netlink_seq_stop(struct seq_file *seq, void *v) - __releases(RCU) __releases(nl_table_lock) + __releases(RCU) { rcu_read_unlock(); - read_unlock(&nl_table_lock); } diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h index f123a88..fd96fa7 100644 --- a/net/netlink/af_netlink.h +++ b/net/netlink/af_netlink.h @@ -50,6 +50,7 @@ struct netlink_sock { #endif /* CONFIG_NETLINK_MMAP */ struct rhash_head node; + struct rcu_head rcu; }; static inline struct netlink_sock *nlk_sk(struct sock *sk) -- cgit v0.10.2 From adc2ae0bb6daa2287d1a6567d18be01da546f7a7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 3 Jan 2015 17:56:14 +0100 Subject: ALSA: pcm: Remove unused functions declaration Remove function declarations for functions that don't have a matching implementation. For snd_pcm_build_linear_format the implementation was removed in 64d27f96cb719cf8 ("[ALSA] Support 3-bytes 24bit format in PCM OSS emulation"). All the others never had one (as far as git history goes). Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/include/sound/pcm.h b/include/sound/pcm.h index bf32cea..cd09c1b 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -518,7 +518,6 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream); int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, struct file *file, struct snd_pcm_substream **rsubstream); void snd_pcm_detach_substream(struct snd_pcm_substream *substream); -void snd_pcm_vma_notify_data(void *client, void *data); int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, struct vm_area_struct *area); @@ -984,21 +983,15 @@ int snd_pcm_format_physical_width(snd_pcm_format_t format); /* in bits */ ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples); const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format); int snd_pcm_format_set_silence(snd_pcm_format_t format, void *buf, unsigned int frames); -snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsigned, int big_endian); void snd_pcm_set_ops(struct snd_pcm * pcm, int direction, const struct snd_pcm_ops *ops); void snd_pcm_set_sync(struct snd_pcm_substream *substream); -int snd_pcm_lib_interleave_len(struct snd_pcm_substream *substream); int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg); int snd_pcm_update_state(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime); int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream); -int snd_pcm_playback_xrun_check(struct snd_pcm_substream *substream); -int snd_pcm_capture_xrun_check(struct snd_pcm_substream *substream); -int snd_pcm_playback_xrun_asap(struct snd_pcm_substream *substream); -int snd_pcm_capture_xrun_asap(struct snd_pcm_substream *substream); void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr); void snd_pcm_period_elapsed(struct snd_pcm_substream *substream); snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, -- cgit v0.10.2 From 04422da990b33bf814944a3dd5d49c920eae4f7b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 2 Jan 2015 23:35:18 -0800 Subject: Bluetooth: Remove dead code for manufacturer inquiry mode quirks There are some old Bluetooth modules from Silicon Wave and Broadcom which support Inquiry Result with RSSI, but do not advertise it. The core has quirks in the code to enable that inquiry mode. However as it stands right now, that code is not even executed since entering the function to determine which inquiry mode requires that the device has the feature bit for Inquiry Result with RSSI set in the first place. So this makes this dead code that hasn't work for a long time. In conclusion, just remove these extra quirks and simplify the setup of the inquiry mode to be inline and with that a lot easier to read and understand. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6c12110..ebac859 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -497,43 +497,6 @@ static void le_setup(struct hci_request *req) set_bit(HCI_LE_ENABLED, &hdev->dev_flags); } -static u8 hci_get_inquiry_mode(struct hci_dev *hdev) -{ - if (lmp_ext_inq_capable(hdev)) - return 0x02; - - if (lmp_inq_rssi_capable(hdev)) - return 0x01; - - if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 && - hdev->lmp_subver == 0x0757) - return 0x01; - - if (hdev->manufacturer == 15) { - if (hdev->hci_rev == 0x03 && hdev->lmp_subver == 0x6963) - return 0x01; - if (hdev->hci_rev == 0x09 && hdev->lmp_subver == 0x6963) - return 0x01; - if (hdev->hci_rev == 0x00 && hdev->lmp_subver == 0x6965) - return 0x01; - } - - if (hdev->manufacturer == 31 && hdev->hci_rev == 0x2005 && - hdev->lmp_subver == 0x1805) - return 0x01; - - return 0x00; -} - -static void hci_setup_inquiry_mode(struct hci_request *req) -{ - u8 mode; - - mode = hci_get_inquiry_mode(req->hdev); - - hci_req_add(req, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode); -} - static void hci_setup_event_mask(struct hci_request *req) { struct hci_dev *hdev = req->hdev; @@ -658,8 +621,17 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt) } } - if (lmp_inq_rssi_capable(hdev)) - hci_setup_inquiry_mode(req); + if (lmp_inq_rssi_capable(hdev)) { + u8 mode; + + /* If Extended Inquiry Result events are supported, then + * they are clearly preferred over Inquiry Result with RSSI + * events. + */ + mode = lmp_ext_inq_capable(hdev) ? 0x02 : 0x01; + + hci_req_add(req, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode); + } if (lmp_inq_tx_pwr_capable(hdev)) hci_req_add(req, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL); -- cgit v0.10.2 From 043ec9bf7b9d7cdce84d2e8d3df9b9eb520d929e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 2 Jan 2015 23:35:19 -0800 Subject: Bluetooth: Introduce HCI_QUIRK_FIXUP_INQUIRY_MODE option The HCI_QUIRK_FIXUP_INQUIRY_MODE option allows to force Inquiry Result with RSSI setting on controllers that do not indicate support for it, but where it is known to be fully functional. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index aee16bf..d0bca31 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -102,6 +102,18 @@ enum { */ HCI_QUIRK_FIXUP_BUFFER_SIZE, + /* When this quirk is set, then a controller that does not + * indicate support for Inquiry Result with RSSI is assumed to + * support it anyway. Some early Bluetooth 1.2 controllers had + * wrongly configured local features that will require forcing + * them to enable this mode. Getting RSSI information with the + * inquiry responses is preferred since it allows for a better + * user expierence. + * + * This quirk must be set before hci_register_dev is called. + */ + HCI_QUIRK_FIXUP_INQUIRY_MODE, + /* When this quirk is set, then the HCI Read Local Supported * Commands command is not supported. In general Bluetooth 1.2 * and later controllers should support this command. However diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ebac859..bc5486e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -621,7 +621,8 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt) } } - if (lmp_inq_rssi_capable(hdev)) { + if (lmp_inq_rssi_capable(hdev) || + test_bit(HCI_QUIRK_FIXUP_INQUIRY_MODE, &hdev->quirks)) { u8 mode; /* If Extended Inquiry Result events are supported, then -- cgit v0.10.2 From 661cf88acd84875c275820ed01e42b25c4e9a1e2 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 2 Jan 2015 23:35:20 -0800 Subject: Bluetooth: Use HCI_QUIRK_FIXUP_INQUIRY_MODE for Silicon Wave devices The Silicon Wave based devices do support Inquiry Result with RSSI and so let the core know to enable them. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 8338813..1ea6351 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2084,8 +2084,10 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_MARVELL) hdev->set_bdaddr = btusb_set_bdaddr_marvell; - if (id->driver_info & BTUSB_SWAVE) + if (id->driver_info & BTUSB_SWAVE) { + set_bit(HCI_QUIRK_FIXUP_INQUIRY_MODE, &hdev->quirks); set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks); + } if (id->driver_info & BTUSB_INTEL_BOOT) set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); -- cgit v0.10.2 From 57dca36ee25bcab20a71705175c00b6f114cdab4 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 3 Jan 2015 14:54:56 +0100 Subject: ALSA: seq: Deletion of unnecessary checks before the function call "snd_midi_event_free" The snd_midi_event_free() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c index 3a45696..e79cc44 100644 --- a/sound/core/seq/oss/seq_oss_midi.c +++ b/sound/core/seq/oss/seq_oss_midi.c @@ -237,8 +237,7 @@ snd_seq_oss_midi_check_exit_port(int client, int port) spin_unlock_irqrestore(®ister_lock, flags); snd_use_lock_free(&mdev->use_lock); snd_use_lock_sync(&mdev->use_lock); - if (mdev->coder) - snd_midi_event_free(mdev->coder); + snd_midi_event_free(mdev->coder); kfree(mdev); } spin_lock_irqsave(®ister_lock, flags); @@ -265,8 +264,7 @@ snd_seq_oss_midi_clear_all(void) spin_lock_irqsave(®ister_lock, flags); for (i = 0; i < max_midi_devs; i++) { if ((mdev = midi_devs[i]) != NULL) { - if (mdev->coder) - snd_midi_event_free(mdev->coder); + snd_midi_event_free(mdev->coder); kfree(mdev); midi_devs[i] = NULL; } diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index a1fd77a..68fec77 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -268,8 +268,7 @@ static void snd_seq_midisynth_delete(struct seq_midisynth *msynth) snd_seq_event_port_detach(msynth->seq_client, msynth->seq_port); } - if (msynth->parser) - snd_midi_event_free(msynth->parser); + snd_midi_event_free(msynth->parser); } /* register new midi synth port */ -- cgit v0.10.2 From d1f45e6037f1195d10cf363e94a08ee2f10f9b41 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 3 Jan 2015 15:10:52 +0100 Subject: sound: oss: Deletion of unnecessary checks before the function call "vfree" The vfree() function performs also input parameter validation. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/oss/pss.c b/sound/oss/pss.c index ca0d6e9..81314f9 100644 --- a/sound/oss/pss.c +++ b/sound/oss/pss.c @@ -1228,7 +1228,7 @@ static void __exit cleanup_pss(void) { if(!pss_no_sound) { - if(fw_load && pss_synth) + if (fw_load) vfree(pss_synth); if(pssmss) unload_pss_mss(&cfg2); diff --git a/sound/oss/trix.c b/sound/oss/trix.c index 944e0c0..3c494dc 100644 --- a/sound/oss/trix.c +++ b/sound/oss/trix.c @@ -487,7 +487,7 @@ static int __init init_trix(void) static void __exit cleanup_trix(void) { - if (fw_load && trix_boot) + if (fw_load) vfree(trix_boot); if (sb) unload_trix_sb(&cfg2); -- cgit v0.10.2 From a827977ac31cb5d1f4720efede9bbbf241789993 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 3 Jan 2015 17:06:04 +0100 Subject: ALSA: emu10k1: Delete an unnecessary check before the function call "kfree" The kfree() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index b672755..3c60b43 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -166,11 +166,8 @@ static struct snd_pcm_hardware snd_p16v_capture_hw = { static void snd_p16v_pcm_free_substream(struct snd_pcm_runtime *runtime) { struct snd_emu10k1_pcm *epcm = runtime->private_data; - - if (epcm) { - /* dev_dbg(emu->card->dev, "epcm free: %p\n", epcm); */ - kfree(epcm); - } + + kfree(epcm); } /* open_playback callback */ -- cgit v0.10.2 From 1fac1686377410169952db63d282490bc0554662 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 3 Jan 2015 17:37:28 +0100 Subject: ALSA: oxygen: Delete an unnecessary check before the function call "snd_pcm_suspend" The snd_pcm_suspend() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index b67e306..61a62c0 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -736,8 +736,7 @@ static int oxygen_pci_suspend(struct device *dev) snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); for (i = 0; i < PCM_COUNT; ++i) - if (chip->streams[i]) - snd_pcm_suspend(chip->streams[i]); + snd_pcm_suspend(chip->streams[i]); if (chip->model.suspend) chip->model.suspend(chip); -- cgit v0.10.2 From b172e0aae6d14baf646a36052d03b301535f4ef5 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 3 Jan 2015 18:28:21 +0100 Subject: ALSA: emux: Delete an unnecessary check before the function call "snd_sf_free" The snd_sf_free() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c index 9352207..f27a1c8 100644 --- a/sound/synth/emux/emux.c +++ b/sound/synth/emux/emux.c @@ -160,12 +160,8 @@ int snd_emux_free(struct snd_emux *emu) snd_emux_detach_seq_oss(emu); #endif snd_emux_detach_seq(emu); - snd_emux_delete_hwdep(emu); - - if (emu->sflist) - snd_sf_free(emu->sflist); - + snd_sf_free(emu->sflist); kfree(emu->voices); kfree(emu->name); kfree(emu); -- cgit v0.10.2 From aa57b93b27eae1156c9a66a4ea85076f9a38d135 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 3 Jan 2015 20:43:01 +0100 Subject: ALSA: aoa: Delete an unnecessary check before the function call "snd_pcm_suspend_all" The snd_pcm_suspend_all() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c index 4e2b4fb..837ba99 100644 --- a/sound/aoa/soundbus/i2sbus/core.c +++ b/sound/aoa/soundbus/i2sbus/core.c @@ -381,10 +381,8 @@ static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state) list_for_each_entry(i2sdev, &control->list, item) { /* Notify Alsa */ - if (i2sdev->sound.pcm) { - /* Suspend PCM streams */ - snd_pcm_suspend_all(i2sdev->sound.pcm); - } + /* Suspend PCM streams */ + snd_pcm_suspend_all(i2sdev->sound.pcm); /* Notify codecs */ list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { -- cgit v0.10.2 From b76c0e95ab0f535e210df588b8ac42dd75b31c75 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 3 Jan 2015 21:02:32 +0100 Subject: ALSA: sb: Delete an unnecessary check before the function call "snd_emux_free" The snd_emux_free() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/isa/sb/emu8000_synth.c b/sound/isa/sb/emu8000_synth.c index 95b39be..72332df 100644 --- a/sound/isa/sb/emu8000_synth.c +++ b/sound/isa/sb/emu8000_synth.c @@ -103,8 +103,7 @@ static int snd_emu8000_delete_device(struct snd_seq_device *dev) hw = dev->driver_data; if (hw->pcm) snd_device_free(dev->card, hw->pcm); - if (hw->emu) - snd_emux_free(hw->emu); + snd_emux_free(hw->emu); snd_util_memhdr_free(hw->memhdr); hw->emu = NULL; hw->memhdr = NULL; -- cgit v0.10.2 From ff6defa6a8fae12205d64f55db395b1fcf35af8e Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 3 Jan 2015 22:55:54 +0100 Subject: ALSA: Deletion of checks before the function call "iounmap" The iounmap() function performs also input parameter validation. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c index 837ba99..b9737fa 100644 --- a/sound/aoa/soundbus/i2sbus/core.c +++ b/sound/aoa/soundbus/i2sbus/core.c @@ -74,10 +74,9 @@ static void i2sbus_release_dev(struct device *dev) int i; i2sdev = container_of(dev, struct i2sbus_dev, sound.ofdev.dev); - - if (i2sdev->intfregs) iounmap(i2sdev->intfregs); - if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma); - if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma); + iounmap(i2sdev->intfregs); + iounmap(i2sdev->out.dbdma); + iounmap(i2sdev->in.dbdma); for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) release_and_free_resource(i2sdev->allocated_resource[i]); free_dbdma_descriptor_ring(i2sdev, &i2sdev->out.dbdma_ring); @@ -318,9 +317,9 @@ static int i2sbus_add_dev(struct macio_dev *macio, free_irq(dev->interrupts[i], dev); free_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring); free_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring); - if (dev->intfregs) iounmap(dev->intfregs); - if (dev->out.dbdma) iounmap(dev->out.dbdma); - if (dev->in.dbdma) iounmap(dev->in.dbdma); + iounmap(dev->intfregs); + iounmap(dev->out.dbdma); + iounmap(dev->in.dbdma); for (i=0;i<3;i++) release_and_free_resource(dev->allocated_resource[i]); mutex_destroy(&dev->lock); diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c index 0e83a73..4140b1b 100644 --- a/sound/arm/aaci.c +++ b/sound/arm/aaci.c @@ -889,8 +889,8 @@ static int aaci_probe_ac97(struct aaci *aaci) static void aaci_free_card(struct snd_card *card) { struct aaci *aaci = card->private_data; - if (aaci->base) - iounmap(aaci->base); + + iounmap(aaci->base); } static struct aaci *aaci_init_card(struct amba_device *dev) diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c index ec01de1..bdcb572 100644 --- a/sound/drivers/ml403-ac97cr.c +++ b/sound/drivers/ml403-ac97cr.c @@ -1094,8 +1094,7 @@ static int snd_ml403_ac97cr_free(struct snd_ml403_ac97cr *ml403_ac97cr) if (ml403_ac97cr->capture_irq >= 0) free_irq(ml403_ac97cr->capture_irq, ml403_ac97cr); /* give back "port" */ - if (ml403_ac97cr->port != NULL) - iounmap(ml403_ac97cr->port); + iounmap(ml403_ac97cr->port); kfree(ml403_ac97cr); PDEBUG(INIT_INFO, "free(): (done)\n"); return 0; diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c index 65b3682..4c07266 100644 --- a/sound/isa/msnd/msnd_pinnacle.c +++ b/sound/isa/msnd/msnd_pinnacle.c @@ -627,8 +627,7 @@ static int snd_msnd_attach(struct snd_card *card) return 0; err_release_region: - if (chip->mappedbase) - iounmap(chip->mappedbase); + iounmap(chip->mappedbase); release_mem_region(chip->base, BUFFSIZE); release_region(chip->io, DSP_NUMIO); free_irq(chip->irq, chip); diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c index 29604a2..f2350c1 100644 --- a/sound/parisc/harmony.c +++ b/sound/parisc/harmony.c @@ -893,9 +893,7 @@ snd_harmony_free(struct snd_harmony *h) if (h->irq >= 0) free_irq(h->irq, h); - if (h->iobase) - iounmap(h->iobase); - + iounmap(h->iobase); kfree(h); return 0; } diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index 547ee30..0de3129 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -853,12 +853,9 @@ snd_ad1889_free(struct snd_ad1889 *chip) free_irq(chip->irq, chip); skip_hw: - if (chip->iobase) - iounmap(chip->iobase); - + iounmap(chip->iobase); pci_release_regions(chip->pci); pci_disable_device(chip->pci); - kfree(chip); return 0; } diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index 72af66b..67d1133 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -541,10 +541,8 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev) hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); /* unmap PCI memory space, mapped during device init. */ - for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; idx++) { - if (pci.ap_mem_base[idx]) - iounmap(pci.ap_mem_base[idx]); - } + for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; ++idx) + iounmap(pci.ap_mem_base[idx]); if (pa->irq) free_irq(pa->irq, pa); diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 9c1c445..d24188f 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1585,8 +1585,7 @@ static int snd_atiixp_free(struct atiixp *chip) __hw_end: if (chip->irq >= 0) free_irq(chip->irq, chip); - if (chip->remap_addr) - iounmap(chip->remap_addr); + iounmap(chip->remap_addr); pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip); diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index b2f63e0..c321a97 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -1211,8 +1211,7 @@ static int snd_atiixp_free(struct atiixp_modem *chip) __hw_end: if (chip->irq >= 0) free_irq(chip->irq, chip); - if (chip->remap_addr) - iounmap(chip->remap_addr); + iounmap(chip->remap_addr); pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip); diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c index e1cf019..8d2fee7 100644 --- a/sound/pci/aw2/aw2-alsa.c +++ b/sound/pci/aw2/aw2-alsa.c @@ -229,9 +229,7 @@ static int snd_aw2_dev_free(struct snd_device *device) if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); /* release the i/o ports & memory */ - if (chip->iobase_virt) - iounmap(chip->iobase_virt); - + iounmap(chip->iobase_virt); pci_release_regions(chip->pci); /* disable the PCI entry */ pci_disable_device(chip->pci); diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 058b997..e82ceac 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -690,8 +690,7 @@ static int snd_bt87x_free(struct snd_bt87x *chip) snd_bt87x_stop(chip); if (chip->irq >= 0) free_irq(chip->irq, chip); - if (chip->mmio) - iounmap(chip->mmio); + iounmap(chip->mmio); pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip); diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 05a4337..ea33911 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -1316,10 +1316,8 @@ static int snd_cs4281_free(struct cs4281 *chip) if (chip->irq >= 0) free_irq(chip->irq, chip); - if (chip->ba0) - iounmap(chip->ba0); - if (chip->ba1) - iounmap(chip->ba1); + iounmap(chip->ba0); + iounmap(chip->ba1); pci_release_regions(chip->pci); pci_disable_device(chip->pci); diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index dfec84e..128bbfe 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2949,8 +2949,8 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip) for (idx = 0; idx < 5; idx++) { struct snd_cs46xx_region *region = &chip->region.idx[idx]; - if (region->remap_addr) - iounmap(region->remap_addr); + + iounmap(region->remap_addr); release_and_free_resource(region->resource); } diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index b425aa8..b8b0d8e 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1985,10 +1985,7 @@ static int hw_card_shutdown(struct hw *hw) free_irq(hw->irq, hw); hw->irq = -1; - - if (hw->mem_base) - iounmap(hw->mem_base); - + iounmap(hw->mem_base); hw->mem_base = NULL; if (hw->io_base) diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 253899d..4e16b4d 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -2110,10 +2110,7 @@ static int hw_card_shutdown(struct hw *hw) free_irq(hw->irq, hw); hw->irq = -1; - - if (hw->mem_base) - iounmap(hw->mem_base); - + iounmap(hw->mem_base); hw->mem_base = NULL; if (hw->io_base) diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 21228ad..98d4f35 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -1872,12 +1872,8 @@ static int snd_echo_free(struct echoaudio *chip) if (chip->comm_page) snd_dma_free_pages(&chip->commpage_dma_buf); - if (chip->dsp_registers) - iounmap(chip->dsp_registers); - + iounmap(chip->dsp_registers); release_and_free_resource(chip->iores); - - pci_disable_device(chip->pci); /* release chip data */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index d426a0b..a971425 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1138,8 +1138,7 @@ static int azx_free(struct azx *chip) free_irq(chip->irq, (void*)chip); if (chip->msi) pci_disable_msi(chip->pci); - if (chip->remap_addr) - iounmap(chip->remap_addr); + iounmap(chip->remap_addr); azx_free_stream_pages(chip); if (chip->region_requested) diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c index 4cf4be5..9ff60008 100644 --- a/sound/pci/lola/lola.c +++ b/sound/pci/lola/lola.c @@ -551,10 +551,8 @@ static void lola_free(struct lola *chip) lola_free_mixer(chip); if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); - if (chip->bar[0].remap_addr) - iounmap(chip->bar[0].remap_addr); - if (chip->bar[1].remap_addr) - iounmap(chip->bar[1].remap_addr); + iounmap(chip->bar[0].remap_addr); + iounmap(chip->bar[1].remap_addr); if (chip->rb.area) snd_dma_free_pages(&chip->rb); pci_release_regions(chip->pci); diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 1faf47e..c3a9f39 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -1114,10 +1114,9 @@ static int snd_mixart_free(struct mixart_mgr *mgr) } /* release the i/o ports */ - for (i = 0; i < 2; i++) { - if (mgr->mem[i].virt) - iounmap(mgr->mem[i].virt); - } + for (i = 0; i < 2; ++i) + iounmap(mgr->mem[i].virt); + pci_release_regions(mgr->pci); /* free flowarray */ diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 4e41a4e..3f52a44 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1460,10 +1460,8 @@ static int snd_nm256_free(struct nm256 *chip) if (chip->irq >= 0) free_irq(chip->irq, chip); - if (chip->cport) - iounmap(chip->cport); - if (chip->buffer) - iounmap(chip->buffer); + iounmap(chip->cport); + iounmap(chip->buffer); release_and_free_resource(chip->res_cport); release_and_free_resource(chip->res_buffer); diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index cf5a6c8..fe66bcb 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -5309,9 +5309,7 @@ static int snd_hdsp_free(struct hdsp *hdsp) release_firmware(hdsp->firmware); vfree(hdsp->fw_uploaded); - - if (hdsp->iobase) - iounmap(hdsp->iobase); + iounmap(hdsp->iobase); if (hdsp->port) pci_release_regions(hdsp->pci); diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 3342705..8109b8e 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -6965,9 +6965,7 @@ static int snd_hdspm_free(struct hdspm * hdspm) free_irq(hdspm->irq, (void *) hdspm); kfree(hdspm->mixer); - - if (hdspm->iobase) - iounmap(hdspm->iobase); + iounmap(hdspm->iobase); if (hdspm->port) pci_release_regions(hdspm->pci); diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 6521521..648911c 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -1756,8 +1756,7 @@ static int snd_rme9652_free(struct snd_rme9652 *rme9652) if (rme9652->irq >= 0) free_irq(rme9652->irq, (void *)rme9652); - if (rme9652->iobase) - iounmap(rme9652->iobase); + iounmap(rme9652->iobase); if (rme9652->port) pci_release_regions(rme9652->pci); diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index 7f6a0a0..5e9437b 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -1064,12 +1064,9 @@ static int sis_chip_free(struct sis7019 *sis) if (sis->irq >= 0) free_irq(sis->irq, sis); - if (sis->ioaddr) - iounmap(sis->ioaddr); - + iounmap(sis->ioaddr); pci_release_regions(sis->pci); pci_disable_device(sis->pci); - sis_free_suspend(sis); return 0; } diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index f5581a9..de7f06f 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -2246,8 +2246,7 @@ static int snd_ymfpci_free(struct snd_ymfpci *chip) release_and_free_resource(chip->mpu_res); release_and_free_resource(chip->fm_res); snd_ymfpci_free_gameport(chip); - if (chip->reg_area_virt) - iounmap(chip->reg_area_virt); + iounmap(chip->reg_area_virt); if (chip->work_ptr.area) snd_dma_free_pages(&chip->work_ptr); diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 5a13b22..d399df4 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -867,16 +867,11 @@ static int snd_pmac_free(struct snd_pmac *chip) snd_pmac_dbdma_free(chip, &chip->capture.cmd); snd_pmac_dbdma_free(chip, &chip->extra_dma); snd_pmac_dbdma_free(chip, &emergency_dbdma); - if (chip->macio_base) - iounmap(chip->macio_base); - if (chip->latch_base) - iounmap(chip->latch_base); - if (chip->awacs) - iounmap(chip->awacs); - if (chip->playback.dma) - iounmap(chip->playback.dma); - if (chip->capture.dma) - iounmap(chip->capture.dma); + iounmap(chip->macio_base); + iounmap(chip->latch_base); + iounmap(chip->awacs); + iounmap(chip->playback.dma); + iounmap(chip->capture.dma); if (chip->node) { int i; -- cgit v0.10.2 From 67e9a2ce6e070b1e5db2dfe9c61890fc30ae954d Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Sun, 4 Jan 2015 19:34:13 +0200 Subject: hsi: nokia-modem: fix uninitialized device pointer modem->device was never initialized. This resulted in logs such as: [ 241.386322] (NULL device *): CMT rst line change detected Signed-off-by: Aaro Koskinen Signed-off-by: Sebastian Reichel diff --git a/drivers/hsi/clients/nokia-modem.c b/drivers/hsi/clients/nokia-modem.c index f0c2145..eb4dc63 100644 --- a/drivers/hsi/clients/nokia-modem.c +++ b/drivers/hsi/clients/nokia-modem.c @@ -162,6 +162,7 @@ static int nokia_modem_probe(struct device *dev) return -ENOMEM; } dev_set_drvdata(dev, modem); + modem->device = dev; irq = irq_of_parse_and_map(np, 0); if (!irq) { -- cgit v0.10.2 From 61f3cade763dca46127146a52d829e30b8f48921 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Fri, 2 Jan 2015 18:26:02 -0800 Subject: geneve: Remove workqueue. The work queue is used only to free the UDP socket upon destruction. This is not necessary with Geneve and generally makes the code more difficult to reason about. It also introduces nondeterministic behavior such as when a socket is rapidly deleted and recreated, which could fail as the the deletion happens asynchronously. Signed-off-by: Jesse Gross Signed-off-by: David S. Miller diff --git a/include/net/geneve.h b/include/net/geneve.h index 112132c..56c7e1a 100644 --- a/include/net/geneve.h +++ b/include/net/geneve.h @@ -71,7 +71,6 @@ struct geneve_sock { struct hlist_node hlist; geneve_rcv_t *rcv; void *rcv_data; - struct work_struct del_work; struct socket *sock; struct rcu_head rcu; atomic_t refcnt; diff --git a/net/ipv4/geneve.c b/net/ipv4/geneve.c index 19e256e..136a829 100644 --- a/net/ipv4/geneve.c +++ b/net/ipv4/geneve.c @@ -61,8 +61,6 @@ struct geneve_net { static int geneve_net_id; -static struct workqueue_struct *geneve_wq; - static inline struct genevehdr *geneve_hdr(const struct sk_buff *skb) { return (struct genevehdr *)(udp_hdr(skb) + 1); @@ -307,15 +305,6 @@ error: return 1; } -static void geneve_del_work(struct work_struct *work) -{ - struct geneve_sock *gs = container_of(work, struct geneve_sock, - del_work); - - udp_tunnel_sock_release(gs->sock); - kfree_rcu(gs, rcu); -} - static struct socket *geneve_create_sock(struct net *net, bool ipv6, __be16 port) { @@ -356,8 +345,6 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port, if (!gs) return ERR_PTR(-ENOMEM); - INIT_WORK(&gs->del_work, geneve_del_work); - sock = geneve_create_sock(net, ipv6, port); if (IS_ERR(sock)) { kfree(gs); @@ -430,7 +417,8 @@ void geneve_sock_release(struct geneve_sock *gs) geneve_notify_del_rx_port(gs); spin_unlock(&gn->sock_lock); - queue_work(geneve_wq, &gs->del_work); + udp_tunnel_sock_release(gs->sock); + kfree_rcu(gs, rcu); } EXPORT_SYMBOL_GPL(geneve_sock_release); @@ -458,10 +446,6 @@ static int __init geneve_init_module(void) { int rc; - geneve_wq = alloc_workqueue("geneve", 0, 0); - if (!geneve_wq) - return -ENOMEM; - rc = register_pernet_subsys(&geneve_net_ops); if (rc) return rc; @@ -474,7 +458,6 @@ late_initcall(geneve_init_module); static void __exit geneve_cleanup_module(void) { - destroy_workqueue(geneve_wq); unregister_pernet_subsys(&geneve_net_ops); } module_exit(geneve_cleanup_module); -- cgit v0.10.2 From 829a3ada9cc7d4c30fa61f8033403fb6c8f8092a Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Fri, 2 Jan 2015 18:26:03 -0800 Subject: geneve: Simplify locking. The existing Geneve locking scheme was pulled over directly from VXLAN. However, VXLAN has a number of built in mechanisms which make the locking more complex and are unlikely to be necessary with Geneve. This simplifies the locking to use a basic scheme of a mutex when doing updates plus RCU on receive. In addition to making the code easier to read, this also avoids the possibility of a race when creating or destroying sockets since UDP sockets and the list of Geneve sockets are protected by different locks. After this change, the entire operation is atomic. Signed-off-by: Jesse Gross Signed-off-by: David S. Miller diff --git a/include/net/geneve.h b/include/net/geneve.h index 56c7e1a..b40f4af 100644 --- a/include/net/geneve.h +++ b/include/net/geneve.h @@ -73,7 +73,7 @@ struct geneve_sock { void *rcv_data; struct socket *sock; struct rcu_head rcu; - atomic_t refcnt; + int refcnt; struct udp_offload udp_offloads; }; diff --git a/net/ipv4/geneve.c b/net/ipv4/geneve.c index 136a829..ad8dbae 100644 --- a/net/ipv4/geneve.c +++ b/net/ipv4/geneve.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -50,13 +51,15 @@ #include #endif +/* Protects sock_list and refcounts. */ +static DEFINE_MUTEX(geneve_mutex); + #define PORT_HASH_BITS 8 #define PORT_HASH_SIZE (1<sock->sk)->inet_sport == port) return gs; } @@ -336,7 +339,6 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port, geneve_rcv_t *rcv, void *data, bool ipv6) { - struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_sock *gs; struct socket *sock; struct udp_tunnel_sock_cfg tunnel_cfg; @@ -352,7 +354,7 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port, } gs->sock = sock; - atomic_set(&gs->refcnt, 1); + gs->refcnt = 1; gs->rcv = rcv; gs->rcv_data = data; @@ -360,11 +362,7 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port, gs->udp_offloads.port = port; gs->udp_offloads.callbacks.gro_receive = geneve_gro_receive; gs->udp_offloads.callbacks.gro_complete = geneve_gro_complete; - - spin_lock(&gn->sock_lock); - hlist_add_head_rcu(&gs->hlist, gs_head(net, port)); geneve_notify_add_rx_port(gs); - spin_unlock(&gn->sock_lock); /* Mark socket as an encapsulation socket */ tunnel_cfg.sk_user_data = gs; @@ -373,6 +371,8 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port, tunnel_cfg.encap_destroy = NULL; setup_udp_tunnel_sock(net, sock, &tunnel_cfg); + hlist_add_head(&gs->hlist, gs_head(net, port)); + return gs; } @@ -380,25 +380,21 @@ struct geneve_sock *geneve_sock_add(struct net *net, __be16 port, geneve_rcv_t *rcv, void *data, bool no_share, bool ipv6) { - struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_sock *gs; - gs = geneve_socket_create(net, port, rcv, data, ipv6); - if (!IS_ERR(gs)) - return gs; - - if (no_share) /* Return error if sharing is not allowed. */ - return ERR_PTR(-EINVAL); + mutex_lock(&geneve_mutex); - spin_lock(&gn->sock_lock); gs = geneve_find_sock(net, port); - if (gs && ((gs->rcv != rcv) || - !atomic_add_unless(&gs->refcnt, 1, 0))) + if (gs) { + if (!no_share && gs->rcv == rcv) + gs->refcnt++; + else gs = ERR_PTR(-EBUSY); - spin_unlock(&gn->sock_lock); + } else { + gs = geneve_socket_create(net, port, rcv, data, ipv6); + } - if (!gs) - gs = ERR_PTR(-EINVAL); + mutex_unlock(&geneve_mutex); return gs; } @@ -406,19 +402,18 @@ EXPORT_SYMBOL_GPL(geneve_sock_add); void geneve_sock_release(struct geneve_sock *gs) { - struct net *net = sock_net(gs->sock->sk); - struct geneve_net *gn = net_generic(net, geneve_net_id); + mutex_lock(&geneve_mutex); - if (!atomic_dec_and_test(&gs->refcnt)) - return; + if (--gs->refcnt) + goto unlock; - spin_lock(&gn->sock_lock); - hlist_del_rcu(&gs->hlist); + hlist_del(&gs->hlist); geneve_notify_del_rx_port(gs); - spin_unlock(&gn->sock_lock); - udp_tunnel_sock_release(gs->sock); kfree_rcu(gs, rcu); + +unlock: + mutex_unlock(&geneve_mutex); } EXPORT_SYMBOL_GPL(geneve_sock_release); @@ -427,8 +422,6 @@ static __net_init int geneve_init_net(struct net *net) struct geneve_net *gn = net_generic(net, geneve_net_id); unsigned int h; - spin_lock_init(&gn->sock_lock); - for (h = 0; h < PORT_HASH_SIZE; ++h) INIT_HLIST_HEAD(&gn->sock_list[h]); @@ -454,7 +447,7 @@ static int __init geneve_init_module(void) return 0; } -late_initcall(geneve_init_module); +module_init(geneve_init_module); static void __exit geneve_cleanup_module(void) { -- cgit v0.10.2 From df5dba8e52be50e615e03ef73b34611d82587f42 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Fri, 2 Jan 2015 18:26:04 -0800 Subject: geneve: Remove socket hash table. The hash table for open Geneve ports is used only on creation and deletion time. It is not performance critical and is not likely to grow to a large number of items. Therefore, this can be changed to use a simple linked list. Signed-off-by: Jesse Gross Signed-off-by: David S. Miller diff --git a/include/net/geneve.h b/include/net/geneve.h index b40f4af..03aa2ad 100644 --- a/include/net/geneve.h +++ b/include/net/geneve.h @@ -68,7 +68,7 @@ struct geneve_sock; typedef void (geneve_rcv_t)(struct geneve_sock *gs, struct sk_buff *skb); struct geneve_sock { - struct hlist_node hlist; + struct list_head list; geneve_rcv_t *rcv; void *rcv_data; struct socket *sock; diff --git a/net/ipv4/geneve.c b/net/ipv4/geneve.c index ad8dbae..4fe5a59 100644 --- a/net/ipv4/geneve.c +++ b/net/ipv4/geneve.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -54,12 +53,9 @@ /* Protects sock_list and refcounts. */ static DEFINE_MUTEX(geneve_mutex); -#define PORT_HASH_BITS 8 -#define PORT_HASH_SIZE (1<sock_list[hash_32(ntohs(port), PORT_HASH_BITS)]; -} - /* Find geneve socket based on network namespace and UDP port */ static struct geneve_sock *geneve_find_sock(struct net *net, __be16 port) { + struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_sock *gs; - hlist_for_each_entry(gs, gs_head(net, port), hlist) { + list_for_each_entry(gs, &gn->sock_list, list) { if (inet_sk(gs->sock->sk)->inet_sport == port) return gs; } @@ -339,6 +329,7 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port, geneve_rcv_t *rcv, void *data, bool ipv6) { + struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_sock *gs; struct socket *sock; struct udp_tunnel_sock_cfg tunnel_cfg; @@ -371,7 +362,7 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port, tunnel_cfg.encap_destroy = NULL; setup_udp_tunnel_sock(net, sock, &tunnel_cfg); - hlist_add_head(&gs->hlist, gs_head(net, port)); + list_add(&gs->list, &gn->sock_list); return gs; } @@ -407,7 +398,7 @@ void geneve_sock_release(struct geneve_sock *gs) if (--gs->refcnt) goto unlock; - hlist_del(&gs->hlist); + list_del(&gs->list); geneve_notify_del_rx_port(gs); udp_tunnel_sock_release(gs->sock); kfree_rcu(gs, rcu); @@ -420,17 +411,14 @@ EXPORT_SYMBOL_GPL(geneve_sock_release); static __net_init int geneve_init_net(struct net *net) { struct geneve_net *gn = net_generic(net, geneve_net_id); - unsigned int h; - for (h = 0; h < PORT_HASH_SIZE; ++h) - INIT_HLIST_HEAD(&gn->sock_list[h]); + INIT_LIST_HEAD(&gn->sock_list); return 0; } static struct pernet_operations geneve_net_ops = { .init = geneve_init_net, - .exit = NULL, .id = &geneve_net_id, .size = sizeof(struct geneve_net), }; -- cgit v0.10.2 From 46b1e4f9115d48d17bb92f612e4fa45a521a0537 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Fri, 2 Jan 2015 18:26:05 -0800 Subject: geneve: Check family when reusing sockets. When searching for an existing socket to reuse, the address family is not taken into account - only port number. This means that an IPv4 socket could be used for IPv6 traffic and vice versa, which is sure to cause problems when passing packets. It is not possible to trigger this problem currently because the only user of Geneve creates just IPv4 sockets. However, that is likely to change in the near future. Signed-off-by: Jesse Gross Signed-off-by: David S. Miller diff --git a/net/ipv4/geneve.c b/net/ipv4/geneve.c index 4fe5a59..5b52046 100644 --- a/net/ipv4/geneve.c +++ b/net/ipv4/geneve.c @@ -65,14 +65,15 @@ static inline struct genevehdr *geneve_hdr(const struct sk_buff *skb) return (struct genevehdr *)(udp_hdr(skb) + 1); } -/* Find geneve socket based on network namespace and UDP port */ -static struct geneve_sock *geneve_find_sock(struct net *net, __be16 port) +static struct geneve_sock *geneve_find_sock(struct net *net, + sa_family_t family, __be16 port) { struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_sock *gs; list_for_each_entry(gs, &gn->sock_list, list) { - if (inet_sk(gs->sock->sk)->inet_sport == port) + if (inet_sk(gs->sock->sk)->inet_sport == port && + inet_sk(gs->sock->sk)->sk.sk_family == family) return gs; } @@ -375,7 +376,7 @@ struct geneve_sock *geneve_sock_add(struct net *net, __be16 port, mutex_lock(&geneve_mutex); - gs = geneve_find_sock(net, port); + gs = geneve_find_sock(net, ipv6 ? AF_INET6 : AF_INET, port); if (gs) { if (!no_share && gs->rcv == rcv) gs->refcnt++; -- cgit v0.10.2 From 3f255dcc970aede74463d863c9cf3cd9fb9146e3 Mon Sep 17 00:00:00 2001 From: Govindarajulu Varadarajan <_govind@gmx.com> Date: Sat, 3 Jan 2015 19:35:44 +0530 Subject: enic: reconfigure resources for kdump crash kernel When running in kdump kernel, reduce number of resources used by the driver. This will enable NIC to operate in low memory kdump kernel environment. Also change the driver version to .83 Signed-off-by: Govindarajulu Varadarajan <_govind@gmx.com> Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h index d2a1035..84b6a2b 100644 --- a/drivers/net/ethernet/cisco/enic/enic.h +++ b/drivers/net/ethernet/cisco/enic/enic.h @@ -33,7 +33,7 @@ #define DRV_NAME "enic" #define DRV_DESCRIPTION "Cisco VIC Ethernet NIC Driver" -#define DRV_VERSION "2.1.1.67" +#define DRV_VERSION "2.1.1.83" #define DRV_COPYRIGHT "Copyright 2008-2013 Cisco Systems, Inc" #define ENIC_BARS_MAX 6 diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 142c9b5..9027fc1 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -45,6 +45,7 @@ #ifdef CONFIG_NET_RX_BUSY_POLL #include #endif +#include #include "cq_enet_desc.h" #include "vnic_dev.h" @@ -2265,6 +2266,18 @@ static void enic_dev_deinit(struct enic *enic) enic_clear_intr_mode(enic); } +static void enic_kdump_kernel_config(struct enic *enic) +{ + if (is_kdump_kernel()) { + dev_info(enic_get_dev(enic), "Running from within kdump kernel. Using minimal resources\n"); + enic->rq_count = 1; + enic->wq_count = 1; + enic->config.rq_desc_count = ENIC_MIN_RQ_DESCS; + enic->config.wq_desc_count = ENIC_MIN_WQ_DESCS; + enic->config.mtu = min_t(u16, 1500, enic->config.mtu); + } +} + static int enic_dev_init(struct enic *enic) { struct device *dev = enic_get_dev(enic); @@ -2294,6 +2307,10 @@ static int enic_dev_init(struct enic *enic) enic_get_res_counts(enic); + /* modify resource count if we are in kdump_kernel + */ + enic_kdump_kernel_config(enic); + /* Set interrupt mode based on resource counts and system * capabilities */ -- cgit v0.10.2 From 3620af0efa89f167a4baf1acf9f6528d16ff45f4 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 3 Jan 2015 17:50:16 -0800 Subject: qlcnic: Fix dump_skb output Use normal facilities to avoid printing each byte on a separate line. Now emits at KERN_DEBUG instead of KERN_INFO. Signed-off-by: Joe Perches Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c index 18e5de7..d166e53 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "qlcnic.h" @@ -1465,14 +1466,14 @@ void qlcnic_post_rx_buffers(struct qlcnic_adapter *adapter, static void dump_skb(struct sk_buff *skb, struct qlcnic_adapter *adapter) { - int i; - unsigned char *data = skb->data; - - pr_info(KERN_INFO "\n"); - for (i = 0; i < skb->len; i++) { - QLCDB(adapter, DRV, "%02x ", data[i]); - if ((i & 0x0f) == 8) - pr_info(KERN_INFO "\n"); + if (adapter->ahw->msg_enable & NETIF_MSG_DRV) { + char prefix[30]; + + scnprintf(prefix, sizeof(prefix), "%s: %s: ", + dev_name(&adapter->pdev->dev), __func__); + + print_hex_dump_debug(prefix, DUMP_PREFIX_NONE, 16, 1, + skb->data, skb->len, true); } } -- cgit v0.10.2 From 86b35b64ed7b6b38305dee67a0f2ddff2ca5455d Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Sun, 4 Jan 2015 15:25:09 +0800 Subject: rhashtable: fix missing header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixup below build error: include/linux/rhashtable.h: At top level: include/linux/rhashtable.h:118:34: error: field ‘mutex’ has incomplete type Signed-off-by: Ying Xue Acked-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index de7cac7..de1459c7 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -20,6 +20,7 @@ #include #include +#include /* * The end of the chain is marked with a special nulls marks which has -- cgit v0.10.2 From a3449ded128d037e6b2bd7a59b0741de56506066 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Sun, 4 Jan 2015 15:24:35 +0800 Subject: list_nulls: fix missing header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixup below build error: include/linux/list_nulls.h: In function ‘hlist_nulls_del’: include/linux/list_nulls.h:84:13: error: ‘LIST_POISON2’ undeclared (first use in this function) Signed-off-by: Ying Xue Signed-off-by: David S. Miller diff --git a/include/linux/list_nulls.h b/include/linux/list_nulls.h index e8c300e..f266661 100644 --- a/include/linux/list_nulls.h +++ b/include/linux/list_nulls.h @@ -1,6 +1,9 @@ #ifndef _LINUX_LIST_NULLS_H #define _LINUX_LIST_NULLS_H +#include +#include + /* * Special version of lists, where end of list is not a NULL pointer, * but a 'nulls' marker, which can have many different values. -- cgit v0.10.2 From 2c561bc362da5131a26ef9bd34a58918e23a019c Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 5 Jan 2015 11:04:37 +0100 Subject: udf: Update Kconfig description Update description of UDF in Kconfig to mention that UDF is also suitable for removable USB disks. Signed-off-by: Jan Kara diff --git a/fs/udf/Kconfig b/fs/udf/Kconfig index 0e0e99b..c6e17a7 100644 --- a/fs/udf/Kconfig +++ b/fs/udf/Kconfig @@ -2,10 +2,12 @@ config UDF_FS tristate "UDF file system support" select CRC_ITU_T help - This is the new file system used on some CD-ROMs and DVDs. Say Y if - you intend to mount DVD discs or CDRW's written in packet mode, or - if written to by other UDF utilities, such as DirectCD. - Please read . + This is a file system used on some CD-ROMs and DVDs. Since the + file system is supported by multiple operating systems and is more + compatible with standard unix file systems, it is also suitable for + removable USB disks. Say Y if you intend to mount DVD discs or CDRW's + written in packet mode, or if you want to use UDF for removable USB + disks. Please read . To compile this file system support as a module, choose M here: the module will be called udf. -- cgit v0.10.2 From 6744e90b0fa1fc6a908fa26935a5ed9e63413063 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sat, 27 Dec 2014 16:01:22 +0100 Subject: ext3: destroy sbi mutexes in put_super Call mutex_destroy() on superblock mutexes in ext3_put_super(). Otherwise mutex debugging code isn't able to detect that mutex is used after being freed. Signed-off-by: Fabian Frederick Signed-off-by: Jan Kara diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 9b4e7d7..d4dbf3c 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -466,6 +466,8 @@ static void ext3_put_super (struct super_block * sb) } sb->s_fs_info = NULL; kfree(sbi->s_blockgroup_lock); + mutex_destroy(&sbi->s_orphan_lock); + mutex_destroy(&sbi->s_resize_lock); kfree(sbi); } -- cgit v0.10.2 From 1803f594cbf9bb2e662ac945038113d0d0cc5e89 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Jan 2015 11:16:42 +0100 Subject: nl80211: document NL80211_BSS_STATUS_AUTHENTICATED isn't used The flag is no longer used (and hasn't been for a long time) since trying to track authentication (and make decisions based on state) was just causing issues all over - see commit 95de817b9034d50860319f6033ec85d25024694c. Signed-off-by: Johannes Berg diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 18cb0aa..54f3911 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3265,6 +3265,9 @@ enum nl80211_bss { /** * enum nl80211_bss_status - BSS "status" * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS. + * Note that this is no longer used since cfg80211 no longer + * keeps track of whether or not authentication was done with + * a given BSS. * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS. * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS. * -- cgit v0.10.2 From 0f8162326fafb266ad21ca33b2ee0e9703dd653b Mon Sep 17 00:00:00 2001 From: Duan Jiong Date: Wed, 10 Dec 2014 17:21:59 +0800 Subject: netfilter: nfnetlink: remove redundant variable nskb Actually after netlink_skb_clone() is called, the nskb and skb will point to the same thing, but they are used just like they are different, sometimes this is confusing, so i think there is no necessary to keep nskb anymore. Signed-off-by: Duan Jiong Signed-off-by: Pablo Neira Ayuso diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 13c2e17..a7a7e82 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -272,7 +272,7 @@ static void nfnl_err_deliver(struct list_head *err_list, struct sk_buff *skb) static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, u_int16_t subsys_id) { - struct sk_buff *nskb, *oskb = skb; + struct sk_buff *oskb = skb; struct net *net = sock_net(skb->sk); const struct nfnetlink_subsystem *ss; const struct nfnl_callback *nc; @@ -283,12 +283,11 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, if (subsys_id >= NFNL_SUBSYS_COUNT) return netlink_ack(skb, nlh, -EINVAL); replay: - nskb = netlink_skb_clone(oskb, GFP_KERNEL); - if (!nskb) + skb = netlink_skb_clone(oskb, GFP_KERNEL); + if (!skb) return netlink_ack(oskb, nlh, -ENOMEM); - nskb->sk = oskb->sk; - skb = nskb; + skb->sk = oskb->sk; nfnl_lock(subsys_id); ss = rcu_dereference_protected(table[subsys_id].subsys, @@ -305,7 +304,7 @@ replay: { nfnl_unlock(subsys_id); netlink_ack(skb, nlh, -EOPNOTSUPP); - return kfree_skb(nskb); + return kfree_skb(skb); } } @@ -385,7 +384,7 @@ replay: nfnl_err_reset(&err_list); ss->abort(oskb); nfnl_unlock(subsys_id); - kfree_skb(nskb); + kfree_skb(skb); goto replay; } } @@ -426,7 +425,7 @@ done: nfnl_err_deliver(&err_list, oskb); nfnl_unlock(subsys_id); - kfree_skb(nskb); + kfree_skb(skb); } static void nfnetlink_rcv(struct sk_buff *skb) -- cgit v0.10.2 From e236fe93e306055c0f927eb09b4d5b10deadbb28 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 1 Sep 2014 15:56:23 +0200 Subject: microblaze: Use empty asm-generic/linkage.h The difference between microblaze and default version in linux/linkage.h is just value stored in the padding bytes which was 0 and in generic is 0x90. Different value shouldn't have any effect. Signed-off-by: Michal Simek diff --git a/arch/microblaze/include/asm/linkage.h b/arch/microblaze/include/asm/linkage.h index 3a8e36d..0540bba 100644 --- a/arch/microblaze/include/asm/linkage.h +++ b/arch/microblaze/include/asm/linkage.h @@ -1,15 +1 @@ -/* - * Copyright (C) 2006 Atmark Techno, Inc. - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ - -#ifndef _ASM_MICROBLAZE_LINKAGE_H -#define _ASM_MICROBLAZE_LINKAGE_H - -#define __ALIGN .align 4 -#define __ALIGN_STR ".align 4" - -#endif /* _ASM_MICROBLAZE_LINKAGE_H */ +#include -- cgit v0.10.2 From add4b1b02da7e7ec35c34dd04d351ac53f3f0dd8 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 18 Dec 2014 15:29:54 +0100 Subject: microblaze: Wire-up execveat syscall Add new execveat syscall. Signed-off-by: Michal Simek diff --git a/arch/microblaze/include/asm/unistd.h b/arch/microblaze/include/asm/unistd.h index 0a53362..76ed17b 100644 --- a/arch/microblaze/include/asm/unistd.h +++ b/arch/microblaze/include/asm/unistd.h @@ -38,6 +38,6 @@ #endif /* __ASSEMBLY__ */ -#define __NR_syscalls 388 +#define __NR_syscalls 389 #endif /* _ASM_MICROBLAZE_UNISTD_H */ diff --git a/arch/microblaze/include/uapi/asm/unistd.h b/arch/microblaze/include/uapi/asm/unistd.h index c712677..32850c7 100644 --- a/arch/microblaze/include/uapi/asm/unistd.h +++ b/arch/microblaze/include/uapi/asm/unistd.h @@ -403,5 +403,6 @@ #define __NR_getrandom 385 #define __NR_memfd_create 386 #define __NR_bpf 387 +#define __NR_execveat 388 #endif /* _UAPI_ASM_MICROBLAZE_UNISTD_H */ diff --git a/arch/microblaze/kernel/syscall_table.S b/arch/microblaze/kernel/syscall_table.S index 0166e89..29c8568 100644 --- a/arch/microblaze/kernel/syscall_table.S +++ b/arch/microblaze/kernel/syscall_table.S @@ -388,3 +388,4 @@ ENTRY(sys_call_table) .long sys_getrandom /* 385 */ .long sys_memfd_create .long sys_bpf + .long sys_execveat -- cgit v0.10.2 From 32db31da49ffc969d95c434cc47864b30b1bff9c Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 18 Dec 2014 15:30:49 +0100 Subject: microblaze: Remove unused prom_parse.c of_parse_dma_window is completely unused. Signed-off-by: Michal Simek diff --git a/arch/microblaze/kernel/Makefile b/arch/microblaze/kernel/Makefile index 08d50cc..f08baca 100644 --- a/arch/microblaze/kernel/Makefile +++ b/arch/microblaze/kernel/Makefile @@ -16,7 +16,7 @@ extra-y := head.o vmlinux.lds obj-y += dma.o exceptions.o \ hw_exception_handler.o intc.o irq.o \ - platform.o process.o prom.o prom_parse.o ptrace.o \ + platform.o process.o prom.o ptrace.o \ reset.o setup.o signal.o sys_microblaze.o timer.o traps.o unwind.o obj-y += cpu/ diff --git a/arch/microblaze/kernel/prom_parse.c b/arch/microblaze/kernel/prom_parse.c deleted file mode 100644 index 068762f..0000000 --- a/arch/microblaze/kernel/prom_parse.c +++ /dev/null @@ -1,35 +0,0 @@ -#undef DEBUG - -#include -#include -#include -#include -#include -#include -#include - -void of_parse_dma_window(struct device_node *dn, const void *dma_window_prop, - unsigned long *busno, unsigned long *phys, unsigned long *size) -{ - const u32 *dma_window; - u32 cells; - const unsigned char *prop; - - dma_window = dma_window_prop; - - /* busno is always one cell */ - *busno = *(dma_window++); - - prop = of_get_property(dn, "ibm,#dma-address-cells", NULL); - if (!prop) - prop = of_get_property(dn, "#address-cells", NULL); - - cells = prop ? *(u32 *)prop : of_n_addr_cells(dn); - *phys = of_read_number(dma_window, cells); - - dma_window += cells; - - prop = of_get_property(dn, "ibm,#dma-size-cells", NULL); - cells = prop ? *(u32 *)prop : of_n_size_cells(dn); - *size = of_read_number(dma_window, cells); -} -- cgit v0.10.2 From b366f11b9a81ad0cc7ed3cbdaca15bb98f96db25 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 18 Dec 2014 15:31:59 +0100 Subject: microblaze: Remove unused prom header from reset.c Completely unused header by this file. Signed-off-by: Michal Simek diff --git a/arch/microblaze/kernel/reset.c b/arch/microblaze/kernel/reset.c index fbe58c6..bab4c83 100644 --- a/arch/microblaze/kernel/reset.c +++ b/arch/microblaze/kernel/reset.c @@ -9,7 +9,6 @@ #include #include -#include /* Trigger specific functions */ #ifdef CONFIG_GPIOLIB -- cgit v0.10.2 From f396a4d2314bf1cb83148d9b8fc47995e6c27e1f Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 18 Dec 2014 15:32:57 +0100 Subject: microblaze: Declare microblaze_kgdb_break in header This patch removes the warning: arch/microblaze/kernel/kgdb.c:81:6: warning: no previous prototype for 'microblaze_kgdb_break' [-Wmissing-prototypes] Signed-off-by: Michal Simek diff --git a/arch/microblaze/include/asm/kgdb.h b/arch/microblaze/include/asm/kgdb.h index 78b17d4..ad27acb 100644 --- a/arch/microblaze/include/asm/kgdb.h +++ b/arch/microblaze/include/asm/kgdb.h @@ -23,6 +23,9 @@ static inline void arch_kgdb_breakpoint(void) __asm__ __volatile__("brki r16, 0x18;"); } +struct pt_regs; +asmlinkage void microblaze_kgdb_break(struct pt_regs *regs); + #endif /* __ASSEMBLY__ */ #endif /* __MICROBLAZE_KGDB_H__ */ #endif /* __KERNEL__ */ diff --git a/arch/microblaze/kernel/kgdb.c b/arch/microblaze/kernel/kgdb.c index 09a5e82..4bd44b6 100644 --- a/arch/microblaze/kernel/kgdb.c +++ b/arch/microblaze/kernel/kgdb.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #define GDB_REG 0 @@ -77,7 +78,7 @@ void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) pt_regb[i] = gdb_regs[i]; } -void microblaze_kgdb_break(struct pt_regs *regs) +asmlinkage void microblaze_kgdb_break(struct pt_regs *regs) { if (kgdb_handle_exception(1, SIGTRAP, 0, regs) != 0) return; -- cgit v0.10.2 From 8543e6c96762fcc930af5725088c2b9e4865c3aa Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 18 Dec 2014 15:41:13 +0100 Subject: microblaze: Use unsigned return type in do_syscall_trace_enter Registers are not signed types. The patch removes warnings: arch/microblaze/kernel/ptrace.c: In function 'do_syscall_trace_enter': arch/microblaze/kernel/ptrace.c:152:14: warning: signed and unsigned type in conditional expression [-Wsign-compare] return ret ?: regs->r12; Signed-off-by: Michal Simek diff --git a/arch/microblaze/include/asm/syscall.h b/arch/microblaze/include/asm/syscall.h index 53cfaf3..04a5bec 100644 --- a/arch/microblaze/include/asm/syscall.h +++ b/arch/microblaze/include/asm/syscall.h @@ -97,7 +97,7 @@ static inline void syscall_set_arguments(struct task_struct *task, microblaze_set_syscall_arg(regs, i++, *args++); } -asmlinkage long do_syscall_trace_enter(struct pt_regs *regs); +asmlinkage unsigned long do_syscall_trace_enter(struct pt_regs *regs); asmlinkage void do_syscall_trace_leave(struct pt_regs *regs); static inline int syscall_get_arch(void) diff --git a/arch/microblaze/kernel/ptrace.c b/arch/microblaze/kernel/ptrace.c index bb10637..8cfa98c 100644 --- a/arch/microblaze/kernel/ptrace.c +++ b/arch/microblaze/kernel/ptrace.c @@ -132,9 +132,9 @@ long arch_ptrace(struct task_struct *child, long request, return rval; } -asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) +asmlinkage unsigned long do_syscall_trace_enter(struct pt_regs *regs) { - long ret = 0; + unsigned long ret = 0; secure_computing_strict(regs->r12); -- cgit v0.10.2 From e14ebe417c7c4e58c50ef143d99d797757749762 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 18 Dec 2014 15:45:38 +0100 Subject: microblaze: Mark get_frame_size as static It is used only locally in unwind.c. The patch removes warning: arch/microblaze/kernel/unwind.c:62:13: warning: no previous prototype for 'get_frame_size' [-Wmissing-prototypes] inline long get_frame_size(unsigned long instr) Signed-off-by: Michal Simek diff --git a/arch/microblaze/kernel/unwind.c b/arch/microblaze/kernel/unwind.c index 1f7b8d4..61c04ee 100644 --- a/arch/microblaze/kernel/unwind.c +++ b/arch/microblaze/kernel/unwind.c @@ -59,7 +59,7 @@ struct stack_trace; * * Return - Number of stack bytes the instruction reserves or reclaims */ -inline long get_frame_size(unsigned long instr) +static inline long get_frame_size(unsigned long instr) { return abs((s16)(instr & 0xFFFF)); } -- cgit v0.10.2 From b6db0a56218c38455e52780338ad61e284a67e4c Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 18 Dec 2014 15:51:30 +0100 Subject: microblaze: Change extern inline to static inline With compilers which follow the C99 standard (like modern versions of gcc and clang), "extern inline" does the opposite thing from older versions of gcc (emits code for an externally linkable version of the inline function). "static inline" does the intended behavior in all cases instead. Description taken from: "staging, rtl8192e, LLVMLinux: Change extern inline to static inline" (sha1: 6d91857d4826b382b3fd4fad95f52713be646f96) The patch removes compilation warnings W=1: ./arch/microblaze/include/asm/delay.h:18:20: warning: no previous prototype for '__delay' [-Wmissing-prototypes] extern inline void __delay(unsigned long loops) ./arch/microblaze/include/asm/delay.h:46:20: warning: no previous prototype for '__udelay' [-Wmissing-prototypes] extern inline void __udelay(unsigned int x) ./arch/microblaze/include/asm/pgalloc.h:63:22: warning: no previous prototype for 'get_pgd_slow' [-Wmissing-prototypes] extern inline pgd_t *get_pgd_slow(void) ./arch/microblaze/include/asm/pgalloc.h:73:22: warning: no previous prototype for 'get_pgd_fast' [-Wmissing-prototypes] extern inline pgd_t *get_pgd_fast(void) ./arch/microblaze/include/asm/pgalloc.h:87:20: warning: no previous prototype for 'free_pgd_fast' [-Wmissing-prototypes] extern inline void free_pgd_fast(pgd_t *pgd) ./arch/microblaze/include/asm/pgalloc.h:94:20: warning: no previous prototype for 'free_pgd_slow' [-Wmissing-prototypes] extern inline void free_pgd_slow(pgd_t *pgd) ./arch/microblaze/include/asm/pgalloc.h:149:20: warning: no previous prototype for 'pte_free_fast' [-Wmissing-prototypes] extern inline void pte_free_fast(pte_t *pte) ./arch/microblaze/include/asm/pgalloc.h:156:20: warning: no previous prototype for 'pte_free_kernel' [-Wmissing-prototypes] extern inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) ./arch/microblaze/include/asm/pgalloc.h:161:20: warning: no previous prototype for 'pte_free_slow' [-Wmissing-prototypes] extern inline void pte_free_slow(struct page *ptepage) Signed-off-by: Michal Simek diff --git a/arch/microblaze/include/asm/delay.h b/arch/microblaze/include/asm/delay.h index 60cb39d..ea2a9cd 100644 --- a/arch/microblaze/include/asm/delay.h +++ b/arch/microblaze/include/asm/delay.h @@ -15,7 +15,7 @@ #include -extern inline void __delay(unsigned long loops) +static inline void __delay(unsigned long loops) { asm volatile ("# __delay \n\t" \ "1: addi %0, %0, -1\t\n" \ @@ -43,7 +43,7 @@ extern inline void __delay(unsigned long loops) extern unsigned long loops_per_jiffy; -extern inline void __udelay(unsigned int x) +static inline void __udelay(unsigned int x) { unsigned long long tmp = diff --git a/arch/microblaze/include/asm/pgalloc.h b/arch/microblaze/include/asm/pgalloc.h index 7fdf7fa..61436d6 100644 --- a/arch/microblaze/include/asm/pgalloc.h +++ b/arch/microblaze/include/asm/pgalloc.h @@ -60,7 +60,7 @@ extern unsigned long get_zero_page_fast(void); extern void __bad_pte(pmd_t *pmd); -extern inline pgd_t *get_pgd_slow(void) +static inline pgd_t *get_pgd_slow(void) { pgd_t *ret; @@ -70,7 +70,7 @@ extern inline pgd_t *get_pgd_slow(void) return ret; } -extern inline pgd_t *get_pgd_fast(void) +static inline pgd_t *get_pgd_fast(void) { unsigned long *ret; @@ -84,14 +84,14 @@ extern inline pgd_t *get_pgd_fast(void) return (pgd_t *)ret; } -extern inline void free_pgd_fast(pgd_t *pgd) +static inline void free_pgd_fast(pgd_t *pgd) { *(unsigned long **)pgd = pgd_quicklist; pgd_quicklist = (unsigned long *) pgd; pgtable_cache_size++; } -extern inline void free_pgd_slow(pgd_t *pgd) +static inline void free_pgd_slow(pgd_t *pgd) { free_page((unsigned long)pgd); } @@ -146,19 +146,19 @@ static inline pte_t *pte_alloc_one_fast(struct mm_struct *mm, return (pte_t *)ret; } -extern inline void pte_free_fast(pte_t *pte) +static inline void pte_free_fast(pte_t *pte) { *(unsigned long **)pte = pte_quicklist; pte_quicklist = (unsigned long *) pte; pgtable_cache_size++; } -extern inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) +static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) { free_page((unsigned long)pte); } -extern inline void pte_free_slow(struct page *ptepage) +static inline void pte_free_slow(struct page *ptepage) { __free_page(ptepage); } -- cgit v0.10.2 From e76fdb324844cc0367f8c6ea5d13278a81568ccb Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 18 Dec 2014 15:56:17 +0100 Subject: microblaze: Use unsigned type for "for" loop because of comparison-kgdb.c This patch removes warnings reported by W=1: arch/microblaze/kernel/kgdb.c: In function 'pt_regs_to_gdb_regs': arch/microblaze/kernel/kgdb.c:43:16: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] arch/microblaze/kernel/kgdb.c:51:16: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] arch/microblaze/kernel/kgdb.c: In function 'gdb_regs_to_pt_regs': arch/microblaze/kernel/kgdb.c:77:16: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] arch/microblaze/kernel/kgdb.c: In function 'sleeping_thread_to_gdb_regs': arch/microblaze/kernel/kgdb.c:99:16: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] arch/microblaze/kernel/kgdb.c:103:16: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] Signed-off-by: Michal Simek diff --git a/arch/microblaze/kernel/kgdb.c b/arch/microblaze/kernel/kgdb.c index 4bd44b6..8736af5 100644 --- a/arch/microblaze/kernel/kgdb.c +++ b/arch/microblaze/kernel/kgdb.c @@ -36,9 +36,10 @@ struct pvr_s pvr; void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) { - int i; + unsigned int i; unsigned long *pt_regb = (unsigned long *)regs; int temp; + /* registers r0 - r31, pc, msr, ear, esr, fsr + do not save pt_mode */ for (i = 0; i < (sizeof(struct pt_regs) / 4) - 1; i++) gdb_regs[i] = pt_regb[i]; @@ -68,7 +69,7 @@ void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) { - int i; + unsigned int i; unsigned long *pt_regb = (unsigned long *)regs; /* pt_regs and gdb_regs have the same 37 values. @@ -92,7 +93,7 @@ asmlinkage void microblaze_kgdb_break(struct pt_regs *regs) /* untested */ void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) { - int i; + unsigned int i; unsigned long *pt_regb = (unsigned long *)(p->thread.regs); /* registers r0 - r31, pc, msr, ear, esr, fsr + do not save pt_mode */ -- cgit v0.10.2 From bdb96e3cad21f5973c95f0e6687db4a57eff7c53 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 18 Dec 2014 16:02:00 +0100 Subject: microblaze: Use unsigned type for proper comparison in cpuinfo*.c Compare the same types together. Compilation warnings: arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c: In function 'set_cpuinfo_pvr_full': arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c:47:20: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c:52:19: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c:57:18: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c:94:20: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] arch/microblaze/kernel/cpu/cpuinfo-static.c: In function 'set_cpuinfo_static': arch/microblaze/kernel/cpu/cpuinfo-static.c:40:20: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] Signed-off-by: Michal Simek diff --git a/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c b/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c index 93c26cf..a32daec9 100644 --- a/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c +++ b/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c @@ -33,7 +33,7 @@ void set_cpuinfo_pvr_full(struct cpuinfo *ci, struct device_node *cpu) { struct pvr_s pvr; - int temp; /* for saving temp value */ + u32 temp; /* for saving temp value */ get_pvr(&pvr); CI(ver_code, VERSION); diff --git a/arch/microblaze/kernel/cpu/cpuinfo-static.c b/arch/microblaze/kernel/cpu/cpuinfo-static.c index 4854285..85dbda4 100644 --- a/arch/microblaze/kernel/cpu/cpuinfo-static.c +++ b/arch/microblaze/kernel/cpu/cpuinfo-static.c @@ -22,7 +22,7 @@ static const char cpu_ver_string[] = CONFIG_XILINX_MICROBLAZE0_HW_VER; void __init set_cpuinfo_static(struct cpuinfo *ci, struct device_node *cpu) { - int i = 0; + u32 i = 0; ci->use_instr = (fcpu(cpu, "xlnx,use-barrel") ? PVR0_USE_BARREL_MASK : 0) | -- cgit v0.10.2 From b18c5d15e8714336365d9d51782d5b53afa0443c Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Wed, 24 Dec 2014 23:04:54 +0800 Subject: netfilter: nfnetlink_cthelper: Remove 'const' and '&' to avoid warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The related code can be simplified, and also can avoid related warnings (with allmodconfig under parisc): CC [M] net/netfilter/nfnetlink_cthelper.o net/netfilter/nfnetlink_cthelper.c: In function ‘nfnl_cthelper_from_nlattr’: net/netfilter/nfnetlink_cthelper.c:97:9: warning: passing argument 1 o ‘memcpy’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-array-qualifiers] memcpy(&help->data, nla_data(attr), help->helper->data_len); ^ In file included from include/linux/string.h:17:0, from include/uapi/linux/uuid.h:25, from include/linux/uuid.h:23, from include/linux/mod_devicetable.h:12, from ./arch/parisc/include/asm/hardware.h:4, from ./arch/parisc/include/asm/processor.h:15, from ./arch/parisc/include/asm/spinlock.h:6, from ./arch/parisc/include/asm/atomic.h:21, from include/linux/atomic.h:4, from ./arch/parisc/include/asm/bitops.h:12, from include/linux/bitops.h:36, from include/linux/kernel.h:10, from include/linux/list.h:8, from include/linux/module.h:9, from net/netfilter/nfnetlink_cthelper.c:11: ./arch/parisc/include/asm/string.h:8:8: note: expected ‘void *’ but argument is of type ‘const char (*)[]’ void * memcpy(void * dest,const void *src,size_t count); ^ Signed-off-by: Chen Gang Signed-off-by: Pablo Neira Ayuso diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 9e287cb..a5599fc 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -86,7 +86,7 @@ nfnl_cthelper_parse_tuple(struct nf_conntrack_tuple *tuple, static int nfnl_cthelper_from_nlattr(struct nlattr *attr, struct nf_conn *ct) { - const struct nf_conn_help *help = nfct_help(ct); + struct nf_conn_help *help = nfct_help(ct); if (attr == NULL) return -EINVAL; @@ -94,7 +94,7 @@ nfnl_cthelper_from_nlattr(struct nlattr *attr, struct nf_conn *ct) if (help->helper->data_len == 0) return -EINVAL; - memcpy(&help->data, nla_data(attr), help->helper->data_len); + memcpy(help->data, nla_data(attr), help->helper->data_len); return 0; } -- cgit v0.10.2 From b44b565cf548d78c1fa80321b14bf6b5308f337a Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 29 Dec 2014 16:22:11 +0800 Subject: netfilter: nf_ct_seqadj: print ack seq in the right host byte order new_start_seq and new_end_seq are network byte order, print the host byte order in debug message and print seq number as the type of unsigned int. Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso diff --git a/net/netfilter/nf_conntrack_seqadj.c b/net/netfilter/nf_conntrack_seqadj.c index f6e2ae9..ce3e840c8 100644 --- a/net/netfilter/nf_conntrack_seqadj.c +++ b/net/netfilter/nf_conntrack_seqadj.c @@ -98,9 +98,9 @@ static void nf_ct_sack_block_adjust(struct sk_buff *skb, new_end_seq = htonl(ntohl(sack->end_seq) - seq->offset_before); - pr_debug("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n", - ntohl(sack->start_seq), new_start_seq, - ntohl(sack->end_seq), new_end_seq); + pr_debug("sack_adjust: start_seq: %u->%u, end_seq: %u->%u\n", + ntohl(sack->start_seq), ntohl(new_start_seq), + ntohl(sack->end_seq), ntohl(new_end_seq)); inet_proto_csum_replace4(&tcph->check, skb, sack->start_seq, new_start_seq, 0); -- cgit v0.10.2 From 8c38d28ba8da98f7102c31d35359b4dbe9d1f329 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 22 Oct 2014 03:37:08 +0200 Subject: clocksource: exynos_mct: Fix bitmask regression for exynos4_mct_write EXYNOS4_MCT_L_MASK is defined as 0xffffff00, so applying this bitmask produces a number outside the range 0x00 to 0xff, which always results in execution of the default switch statement. Obviously this is wrong and git history shows that the bitmask inversion was incorrectly set during a refactoring of the MCT code. Fix this by putting the inversion at the correct position again. Cc: stable@vger.kernel.org Acked-by: Kukjin Kim Reported-by: GP Orcullo Reviewed-by: Doug Anderson Signed-off-by: Tobias Jakobi Signed-off-by: Daniel Lezcano diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index 9403061..83564c9 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -97,8 +97,8 @@ static void exynos4_mct_write(unsigned int value, unsigned long offset) writel_relaxed(value, reg_base + offset); if (likely(offset >= EXYNOS4_MCT_L_BASE(0))) { - stat_addr = (offset & ~EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET; - switch (offset & EXYNOS4_MCT_L_MASK) { + stat_addr = (offset & EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET; + switch (offset & ~EXYNOS4_MCT_L_MASK) { case MCT_L_TCON_OFFSET: mask = 1 << 3; /* L_TCON write status */ break; -- cgit v0.10.2 From ff4bcc84a9e720ffa6cb7cf01e9e938568147cd6 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Mon, 8 Dec 2014 13:42:02 -0800 Subject: clocksource: kona: fix __iomem annotation It makes no sense to hide the __iomem annotation from the function that uses it, especially since it causes a sparse warning: drivers/clocksource/bcm_kona_timer.c:118:38: warning: incorrect type in argument 1 (different address spaces) drivers/clocksource/bcm_kona_timer.c:118:38: expected void *timer_base drivers/clocksource/bcm_kona_timer.c:118:38: got void [noderef] *static [toplevel] tmr_regs Signed-off-by: Olof Johansson Acked-by: Florian Fainelli Signed-off-by: Daniel Lezcano diff --git a/drivers/clocksource/bcm_kona_timer.c b/drivers/clocksource/bcm_kona_timer.c index 0595dc6..f1e33d0 100644 --- a/drivers/clocksource/bcm_kona_timer.c +++ b/drivers/clocksource/bcm_kona_timer.c @@ -68,9 +68,8 @@ static void kona_timer_disable_and_clear(void __iomem *base) } static void -kona_timer_get_counter(void *timer_base, uint32_t *msw, uint32_t *lsw) +kona_timer_get_counter(void __iomem *timer_base, uint32_t *msw, uint32_t *lsw) { - void __iomem *base = IOMEM(timer_base); int loop_limit = 4; /* @@ -86,9 +85,9 @@ kona_timer_get_counter(void *timer_base, uint32_t *msw, uint32_t *lsw) */ while (--loop_limit) { - *msw = readl(base + KONA_GPTIMER_STCHI_OFFSET); - *lsw = readl(base + KONA_GPTIMER_STCLO_OFFSET); - if (*msw == readl(base + KONA_GPTIMER_STCHI_OFFSET)) + *msw = readl(timer_base + KONA_GPTIMER_STCHI_OFFSET); + *lsw = readl(timer_base + KONA_GPTIMER_STCLO_OFFSET); + if (*msw == readl(timer_base + KONA_GPTIMER_STCHI_OFFSET)) break; } if (!loop_limit) { -- cgit v0.10.2 From f2a5473861cf69c03d0f0ee5d0ea1b853b9e582e Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 16 Dec 2014 18:48:54 +0900 Subject: clocksource: sh_tmu: Set cpu_possible_mask to fix SMP broadcast Update the TMU driver to use cpu_possible_mask as cpumask to make r8a7779 SMP work as expected with or without the ARM TWD timer. Signed-off-by: Magnus Damm Signed-off-by: Daniel Lezcano diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 0f665b8..f150ca82 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -428,7 +428,7 @@ static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch, ced->features = CLOCK_EVT_FEAT_PERIODIC; ced->features |= CLOCK_EVT_FEAT_ONESHOT; ced->rating = 200; - ced->cpumask = cpumask_of(0); + ced->cpumask = cpu_possible_mask; ced->set_next_event = sh_tmu_clock_event_next; ced->set_mode = sh_tmu_clock_event_mode; ced->suspend = sh_tmu_clock_event_suspend; -- cgit v0.10.2 From 5c0b8e0de76a86edb99e46612fd9d341b4c4fa0a Mon Sep 17 00:00:00 2001 From: Suman Tripathi Date: Mon, 29 Dec 2014 08:52:46 +0530 Subject: ahci_xgene: Fix the endianess issue in APM X-Gene SoC AHCI SATA controller driver. This patch fixes the big endian mode issue with function xgene_ahci_read_id. Signed-off-by: Suman Tripathi Signed-off-by: Tejun Heo diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c index feeb8f1..f190b92 100644 --- a/drivers/ata/ahci_xgene.c +++ b/drivers/ata/ahci_xgene.c @@ -188,7 +188,7 @@ static unsigned int xgene_ahci_read_id(struct ata_device *dev, * * Clear reserved bit 8 (DEVSLP bit) as we don't support DEVSLP */ - id[ATA_ID_FEATURE_SUPP] &= ~(1 << 8); + id[ATA_ID_FEATURE_SUPP] &= cpu_to_le16(~(1 << 8)); return 0; } -- cgit v0.10.2 From 1102407bb714dcebb43f385335bcb72f6b8843bc Mon Sep 17 00:00:00 2001 From: Suman Tripathi Date: Mon, 29 Dec 2014 08:52:47 +0530 Subject: ahci_xgene: Fix the DMA state machine lockup for the ATA_CMD_PACKET PIO mode command. This patch addresses the issue with ATA_CMD_PACKET pio mode command for enumeration and device detection with ATAPI devices. The X-Gene AHCI controller has an errata in which it cannot clear the BSY bit after the PIO setup FIS. The dma state machine enters CMFatalErrorUpdate state and locks up. Signed-off-by: Suman Tripathi Signed-off-by: Tejun Heo diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c index f190b92..cbcd208 100644 --- a/drivers/ata/ahci_xgene.c +++ b/drivers/ata/ahci_xgene.c @@ -125,10 +125,11 @@ static int xgene_ahci_restart_engine(struct ata_port *ap) * xgene_ahci_qc_issue - Issue commands to the device * @qc: Command to issue * - * Due to Hardware errata for IDENTIFY DEVICE command, the controller cannot - * clear the BSY bit after receiving the PIO setup FIS. This results in the dma - * state machine goes into the CMFatalErrorUpdate state and locks up. By - * restarting the dma engine, it removes the controller out of lock up state. + * Due to Hardware errata for IDENTIFY DEVICE command and PACKET + * command of ATAPI protocol set, the controller cannot clear the BSY bit + * after receiving the PIO setup FIS. This results in the DMA state machine + * going into the CMFatalErrorUpdate state and locks up. By restarting the + * DMA engine, it removes the controller out of lock up state. */ static unsigned int xgene_ahci_qc_issue(struct ata_queued_cmd *qc) { @@ -137,7 +138,8 @@ static unsigned int xgene_ahci_qc_issue(struct ata_queued_cmd *qc) struct xgene_ahci_context *ctx = hpriv->plat_data; int rc = 0; - if (unlikely(ctx->last_cmd[ap->port_no] == ATA_CMD_ID_ATA)) + if (unlikely((ctx->last_cmd[ap->port_no] == ATA_CMD_ID_ATA) || + (ctx->last_cmd[ap->port_no] == ATA_CMD_PACKET))) xgene_ahci_restart_engine(ap); rc = ahci_qc_issue(qc); -- cgit v0.10.2 From 62c204ddfe9649f0b1d273549f4bd68c23ed7c0d Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Thu, 18 Dec 2014 17:50:49 +0000 Subject: fs: Make efivarfs a pseudo filesystem, built by default with EFI efivars is currently enabled under MISC_FILESYSTEMS, which is decribed as "such as filesystems that came from other operating systems". In reality, it is a pseudo filesystem, providing access to the kernel UEFI variable interface. Since this is the preferred interface for accessing UEFI variables, over the legacy efivars interface, also build it by default as a module if CONFIG_EFI. Signed-off-by: Leif Lindholm Signed-off-by: Matt Fleming diff --git a/fs/Kconfig b/fs/Kconfig index 664991a..a6bb530 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -165,6 +165,7 @@ config HUGETLB_PAGE def_bool HUGETLBFS source "fs/configfs/Kconfig" +source "fs/efivarfs/Kconfig" endmenu @@ -209,7 +210,6 @@ source "fs/sysv/Kconfig" source "fs/ufs/Kconfig" source "fs/exofs/Kconfig" source "fs/f2fs/Kconfig" -source "fs/efivarfs/Kconfig" endif # MISC_FILESYSTEMS diff --git a/fs/efivarfs/Kconfig b/fs/efivarfs/Kconfig index 367bbb1..c2499ef 100644 --- a/fs/efivarfs/Kconfig +++ b/fs/efivarfs/Kconfig @@ -1,6 +1,7 @@ config EFIVAR_FS tristate "EFI Variable filesystem" depends on EFI + default m help efivarfs is a replacement filesystem for the old EFI variable support via sysfs, as it doesn't suffer from the -- cgit v0.10.2 From f595f76defe2b03c45231b9b7f8689be3d26d940 Mon Sep 17 00:00:00 2001 From: Henrik Austad Date: Fri, 26 Dec 2014 09:26:25 +0100 Subject: Update of Documentation/cgroups/00-INDEX unified-hierarchy.txt was added by 65731578 (cgroup: add documentation about unified hierarchy) Cc: Tejun Heo Cc: Li Zefan Cc: Jonathan Corbet Cc: cgroups@vger.kernel.org Cc: linux-doc@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Henrik Austad Signed-off-by: Tejun Heo diff --git a/Documentation/cgroups/00-INDEX b/Documentation/cgroups/00-INDEX index bc461b6..96ce071 100644 --- a/Documentation/cgroups/00-INDEX +++ b/Documentation/cgroups/00-INDEX @@ -24,3 +24,5 @@ net_prio.txt - Network priority cgroups details and usages. resource_counter.txt - Resource Counter API. +unified-hierarchy.txt + - Description the new/next cgroup interface. -- cgit v0.10.2 From 8e3e36e87c9e624a30d31c576b839eed3ac8abf4 Mon Sep 17 00:00:00 2001 From: Cheng-Yi Chiang Date: Mon, 5 Jan 2015 19:26:59 +0800 Subject: ASoC: ts3a227e: Check and report jack status at probe ts3a227e does not trigger interrupt to report jack status when system boots from warm reset because ts3a227e's power remains on during warm reset. Read jack status at probe to get current jack status. Note that if system boots from EC reset, then this issue will not happen. Signed-off-by: Cheng-Yi Chiang Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index 1d12057..9f2dced 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -254,6 +254,7 @@ static int ts3a227e_i2c_probe(struct i2c_client *i2c, struct ts3a227e *ts3a227e; struct device *dev = &i2c->dev; int ret; + unsigned int acc_reg; ts3a227e = devm_kzalloc(&i2c->dev, sizeof(*ts3a227e), GFP_KERNEL); if (ts3a227e == NULL) @@ -283,6 +284,11 @@ static int ts3a227e_i2c_probe(struct i2c_client *i2c, INTB_DISABLE | ADC_COMPLETE_INT_DISABLE, ADC_COMPLETE_INT_DISABLE); + /* Read jack status because chip might not trigger interrupt at boot. */ + regmap_read(ts3a227e->regmap, TS3A227E_REG_ACCESSORY_STATUS, &acc_reg); + ts3a227e_new_jack_state(ts3a227e, acc_reg); + ts3a227e_jack_report(ts3a227e); + return 0; } -- cgit v0.10.2 From 36aae28e3df4127e296f2680d65cb6310ce61021 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 12 Dec 2014 17:16:31 +0200 Subject: libata: export ata_get_cmd_descript() The driver sata_dwc_460ex is using this symbol. To build it as a module we have to have the symbol exported. This patch adds EXPORT_SYMBOL_GPL() macro for that. tj: Updated to use EXPORT_SYMBOL_GPL() instead of EXPORT_SYMBOL() as the only known user is an in-tree driver. Suggested by Sergei. Signed-off-by: Andy Shevchenko Signed-off-by: Tejun Heo Cc: Sergei Shtylyov diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 3dbec89..8d00c26 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -2389,6 +2389,7 @@ const char *ata_get_cmd_descript(u8 command) return NULL; } +EXPORT_SYMBOL_GPL(ata_get_cmd_descript); /** * ata_eh_link_report - report error handling to user -- cgit v0.10.2 From 650705cf73f810c8af4e65a4042a9b6c91667544 Mon Sep 17 00:00:00 2001 From: Nizam Haider Date: Mon, 5 Jan 2015 17:31:03 +0530 Subject: spi/gpio: fixed space coding style issue fixed a coding style issue Signed-off-by: Nizam Haider Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index aee4e75..702b0b8 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -92,7 +92,7 @@ struct spi_gpio { /*----------------------------------------------------------------------*/ -static inline struct spi_gpio * __pure +static inline struct spi_gpio *__pure spi_to_spi_gpio(const struct spi_device *spi) { const struct spi_bitbang *bang; @@ -103,7 +103,7 @@ spi_to_spi_gpio(const struct spi_device *spi) return spi_gpio; } -static inline struct spi_gpio_platform_data * __pure +static inline struct spi_gpio_platform_data *__pure spi_to_pdata(const struct spi_device *spi) { return &spi_to_spi_gpio(spi)->pdata; -- cgit v0.10.2 From 544c55c810a55dcfd2febbc33105642923be9192 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 3 Jan 2015 19:03:55 +0100 Subject: ASoC: Intel: Delete an unnecessary check before the function call "sst_dma_free" The sst_dma_free() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-dsp.c b/sound/soc/intel/sst-dsp.c index 86e4108..64e9421 100644 --- a/sound/soc/intel/sst-dsp.c +++ b/sound/soc/intel/sst-dsp.c @@ -410,8 +410,7 @@ void sst_dsp_free(struct sst_dsp *sst) if (sst->ops->free) sst->ops->free(sst); - if (sst->dma) - sst_dma_free(sst->dma); + sst_dma_free(sst->dma); } EXPORT_SYMBOL_GPL(sst_dsp_free); -- cgit v0.10.2 From 180cf794d4f5e0e82a4df30b8bacd9b472f80f44 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 3 Jan 2015 19:25:55 +0100 Subject: ASoC: fsi: Deletion of unnecessary checks before the function call "clk_enable" The clk_enable() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Mark Brown diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 8869971..d49f25f 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -820,12 +820,9 @@ static int fsi_clk_enable(struct device *dev, return ret; } - if (clock->xck) - clk_enable(clock->xck); - if (clock->ick) - clk_enable(clock->ick); - if (clock->div) - clk_enable(clock->div); + clk_enable(clock->xck); + clk_enable(clock->ick); + clk_enable(clock->div); clock->count++; } -- cgit v0.10.2 From 4e3461d34f4cd632b403342ea1df33135e5e3ad3 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 3 Jan 2015 19:49:37 +0100 Subject: ASoC: Intel: Delete an unnecessary check before the function call "release_firmware" The release_firmware() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_loader.c b/sound/soc/intel/sst/sst_loader.c index b580f96..7888cd7 100644 --- a/sound/soc/intel/sst/sst_loader.c +++ b/sound/soc/intel/sst/sst_loader.c @@ -324,8 +324,7 @@ void sst_firmware_load_cb(const struct firmware *fw, void *context) if (ctx->sst_state != SST_RESET || ctx->fw_in_mem != NULL) { - if (fw != NULL) - release_firmware(fw); + release_firmware(fw); mutex_unlock(&ctx->sst_lock); return; } -- cgit v0.10.2 From d297933cc7fcfbaaf2d37570baac73287bf0357d Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 5 Jan 2015 09:32:56 +0800 Subject: spi: dw: Fix detecting FIFO depth Current code tries to find the highest valid fifo depth by checking the value it wrote to DW_SPI_TXFLTR. There are a few problems in current code: 1) There is an off-by-one in dws->fifo_len setting because it assumes the latest register write fails so the latest valid value should be fifo - 1. 2) We know the depth could be from 2 to 256 from HW spec, so it is not necessary to test fifo == 257. In the case fifo is 257, it means the latest valid setting is fifo = 256. So after the for loop iteration, we should check fifo == 2 case instead of fifo == 257 if detecting the FIFO depth fails. This patch fixes above issues. Signed-off-by: Axel Lin Reviewed-and-tested-by: Andy Shevchenko Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index d0d5542..1a0f266 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -621,13 +621,13 @@ static void spi_hw_init(struct dw_spi *dws) if (!dws->fifo_len) { u32 fifo; - for (fifo = 2; fifo <= 257; fifo++) { + for (fifo = 2; fifo <= 256; fifo++) { dw_writew(dws, DW_SPI_TXFLTR, fifo); if (fifo != dw_readw(dws, DW_SPI_TXFLTR)) break; } - dws->fifo_len = (fifo == 257) ? 0 : fifo; + dws->fifo_len = (fifo == 2) ? 0 : fifo - 1; dw_writew(dws, DW_SPI_TXFLTR, 0); } } -- cgit v0.10.2 From d83901e82010cb3b25e69a9bbe991e9fbd940725 Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Sun, 4 Jan 2015 09:15:04 +0800 Subject: ASoC: Intel: Don't change offset of block allocator during fixed allocate The offset of block allocator, ba->offset, should not be changed during fixed address allocating, for the caller may treat it as the offset of allocated memory and use it. In the case that we allocate more than 1 blocks, we should make sure this offset is correct. Here introduces a temp allocator for the later continuous allocating. Signed-off-by: Jie Yang Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c index ef2e8b5..b3f9489 100644 --- a/sound/soc/intel/sst-firmware.c +++ b/sound/soc/intel/sst-firmware.c @@ -706,6 +706,7 @@ static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba struct list_head *block_list) { struct sst_mem_block *block, *tmp; + struct sst_block_allocator ba_tmp = *ba; u32 end = ba->offset + ba->size, block_end; int err; @@ -730,9 +731,9 @@ static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba if (ba->offset >= block->offset && ba->offset < block_end) { /* align ba to block boundary */ - ba->size -= block_end - ba->offset; - ba->offset = block_end; - err = block_alloc_contiguous(dsp, ba, block_list); + ba_tmp.size -= block_end - ba->offset; + ba_tmp.offset = block_end; + err = block_alloc_contiguous(dsp, &ba_tmp, block_list); if (err < 0) return -ENOMEM; @@ -767,10 +768,10 @@ static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba list_move(&block->list, &dsp->used_block_list); list_add(&block->module_list, block_list); /* align ba to block boundary */ - ba->size -= block_end - ba->offset; - ba->offset = block_end; + ba_tmp.size -= block_end - ba->offset; + ba_tmp.offset = block_end; - err = block_alloc_contiguous(dsp, ba, block_list); + err = block_alloc_contiguous(dsp, &ba_tmp, block_list); if (err < 0) return -ENOMEM; -- cgit v0.10.2 From 121b567d8f4dce55c8095a842766fc1e5523be94 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:04:19 +0100 Subject: regulator: fan53555: Constify struct regmap_config and slew_rates array The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Make const also slew_rates array. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c index 6c43ab2..3c25db8 100644 --- a/drivers/regulator/fan53555.c +++ b/drivers/regulator/fan53555.c @@ -147,7 +147,7 @@ static unsigned int fan53555_get_mode(struct regulator_dev *rdev) return REGULATOR_MODE_NORMAL; } -static int slew_rates[] = { +static const int slew_rates[] = { 64000, 32000, 16000, @@ -296,7 +296,7 @@ static int fan53555_regulator_register(struct fan53555_device_info *di, return PTR_ERR_OR_ZERO(di->rdev); } -static struct regmap_config fan53555_regmap_config = { +static const struct regmap_config fan53555_regmap_config = { .reg_bits = 8, .val_bits = 8, }; -- cgit v0.10.2 From 035f3324b35296171a9e6bc5744f66daa0ebcfef Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:05:47 +0100 Subject: regulator: max8649: Constify struct regmap_config and regulator_ops The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Make struct regulator_ops const as well. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c index c8bddcc..8122957 100644 --- a/drivers/regulator/max8649.c +++ b/drivers/regulator/max8649.c @@ -115,7 +115,7 @@ static unsigned int max8649_get_mode(struct regulator_dev *rdev) return REGULATOR_MODE_NORMAL; } -static struct regulator_ops max8649_dcdc_ops = { +static const struct regulator_ops max8649_dcdc_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, @@ -143,7 +143,7 @@ static struct regulator_desc dcdc_desc = { .enable_is_inverted = true, }; -static struct regmap_config max8649_regmap_config = { +static const struct regmap_config max8649_regmap_config = { .reg_bits = 8, .val_bits = 8, }; -- cgit v0.10.2 From 4604a061e6c2462330d56bb7edcd1ca3397a4b73 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:05:48 +0100 Subject: regulator: tps65023: Constify struct regmap_config and regulator_ops The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Make struct regulator_ops const as well. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index 7380af8..b941e56 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -173,7 +173,7 @@ static int tps65023_dcdc_set_voltage_sel(struct regulator_dev *dev, } /* Operations permitted on VDCDCx */ -static struct regulator_ops tps65023_dcdc_ops = { +static const struct regulator_ops tps65023_dcdc_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -184,7 +184,7 @@ static struct regulator_ops tps65023_dcdc_ops = { }; /* Operations permitted on LDOx */ -static struct regulator_ops tps65023_ldo_ops = { +static const struct regulator_ops tps65023_ldo_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -194,7 +194,7 @@ static struct regulator_ops tps65023_ldo_ops = { .map_voltage = regulator_map_voltage_ascend, }; -static struct regmap_config tps65023_regmap_config = { +static const struct regmap_config tps65023_regmap_config = { .reg_bits = 8, .val_bits = 8, }; -- cgit v0.10.2 From 9caf5067b9cb92c4383909ad1748c804ce223c7d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:07:01 +0100 Subject: spi: meson: Constify struct regmap_config The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-meson-spifc.c b/drivers/spi/spi-meson-spifc.c index 1bbac03..5468fc7 100644 --- a/drivers/spi/spi-meson-spifc.c +++ b/drivers/spi/spi-meson-spifc.c @@ -85,7 +85,7 @@ struct meson_spifc { struct device *dev; }; -static struct regmap_config spifc_regmap_config = { +static const struct regmap_config spifc_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, -- cgit v0.10.2 From ae6f636b8b2ea9d297a07fc7ef8ae54707d67b36 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Wed, 31 Dec 2014 16:20:37 +0000 Subject: ASoC: adi: Add missing return statement. The probe routine was disabling the clock even if the system was configured successfully. Add a return statement to leave clocks enabled. Signed-off-by: Andrew Jackson Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/adi/axi-i2s.c b/sound/soc/adi/axi-i2s.c index 7752860..4c23381 100644 --- a/sound/soc/adi/axi-i2s.c +++ b/sound/soc/adi/axi-i2s.c @@ -240,6 +240,8 @@ static int axi_i2s_probe(struct platform_device *pdev) if (ret) goto err_clk_disable; + return 0; + err_clk_disable: clk_disable_unprepare(i2s->clk); return ret; -- cgit v0.10.2 From 67bf9cda4b498b8cea4a40be67a470afe57d2e88 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 2 Jan 2015 17:48:51 +0200 Subject: spi: dw-mid: fix FIFO size The FIFO size is 40 accordingly to the specifications, but this means 0x40, i.e. 64 bytes. This patch fixes the typo and enables FIFO size autodetection for Intel MID devices. Fixes: 7063c0d942a1 (spi/dw_spi: add DMA support) Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 7281316..a67d37c 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -271,7 +271,6 @@ int dw_spi_mid_init(struct dw_spi *dws) iounmap(clk_reg); dws->num_cs = 16; - dws->fifo_len = 40; /* FIFO has 40 words buffer */ #ifdef CONFIG_SPI_DW_MID_DMA dws->dma_priv = kzalloc(sizeof(struct mid_dma), GFP_KERNEL); -- cgit v0.10.2 From 2b730cc38d3ca592b4f9f71989e7f0c79b37b3b3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:18:21 +0100 Subject: ASoC: alc5632: Constify struct regmap_config and snd_soc_codec_driver The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Make struct snd_soc_codec_driver const as well (snd_soc_register_codec() accepts pointer to const). Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index d1fdbc2..0763026 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -1066,7 +1066,7 @@ static int alc5632_probe(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_device_alc5632 = { +static const struct snd_soc_codec_driver soc_codec_device_alc5632 = { .probe = alc5632_probe, .resume = alc5632_resume, .set_bias_level = alc5632_set_bias_level, @@ -1080,7 +1080,7 @@ static struct snd_soc_codec_driver soc_codec_device_alc5632 = { .num_dapm_routes = ARRAY_SIZE(alc5632_dapm_routes), }; -static struct regmap_config alc5632_regmap = { +static const struct regmap_config alc5632_regmap = { .reg_bits = 8, .val_bits = 16, -- cgit v0.10.2 From d883641f87a42cffcdbbdc664fce1def20ed02b6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:18:22 +0100 Subject: ASoC: cs35l32: Constify struct regmap_config and snd_soc_codec_driver The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Make struct snd_soc_codec_driver const as well (snd_soc_register_codec() accepts pointer to const). Signed-off-by: Krzysztof Kozlowski Acked-by: Brian Austin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index ec55c590..f2b8aad 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -264,7 +264,7 @@ static int cs35l32_codec_set_sysclk(struct snd_soc_codec *codec, CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO_MASK, val); } -static struct snd_soc_codec_driver soc_codec_dev_cs35l32 = { +static const struct snd_soc_codec_driver soc_codec_dev_cs35l32 = { .set_sysclk = cs35l32_codec_set_sysclk, .dapm_widgets = cs35l32_dapm_widgets, @@ -288,7 +288,7 @@ static const struct reg_default cs35l32_monitor_patch[] = { { 0x00, 0x00 }, }; -static struct regmap_config cs35l32_regmap = { +static const struct regmap_config cs35l32_regmap = { .reg_bits = 8, .val_bits = 8, -- cgit v0.10.2 From b5fbcbab50b4f8065666d3633f47722bf8969b34 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:18:23 +0100 Subject: ASoC: cs42l52: Constify struct regmap_config and snd_soc_codec_driver The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Make struct snd_soc_codec_driver const as well (snd_soc_register_codec() accepts pointer to const). Signed-off-by: Krzysztof Kozlowski Acked-by: Brian Austin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 35fbef7..1589e7a 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -1103,7 +1103,7 @@ static int cs42l52_remove(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_cs42l52 = { +static const struct snd_soc_codec_driver soc_codec_dev_cs42l52 = { .probe = cs42l52_probe, .remove = cs42l52_remove, .set_bias_level = cs42l52_set_bias_level, @@ -1130,7 +1130,7 @@ static const struct reg_default cs42l52_threshold_patch[] = { }; -static struct regmap_config cs42l52_regmap = { +static const struct regmap_config cs42l52_regmap = { .reg_bits = 8, .val_bits = 8, -- cgit v0.10.2 From cf0efa1c71c86b3ba731b9f3bad37ea4ceee6fbe Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:18:24 +0100 Subject: ASoC: cs42l56: Constify struct regmap_config and snd_soc_codec_driver The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Make struct snd_soc_codec_driver const as well (snd_soc_register_codec() accepts pointer to const). Signed-off-by: Krzysztof Kozlowski Acked-by: Brian Austin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index 2ddc7ac..cbc654f 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -1164,7 +1164,7 @@ static int cs42l56_remove(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_cs42l56 = { +static const struct snd_soc_codec_driver soc_codec_dev_cs42l56 = { .probe = cs42l56_probe, .remove = cs42l56_remove, .set_bias_level = cs42l56_set_bias_level, @@ -1179,7 +1179,7 @@ static struct snd_soc_codec_driver soc_codec_dev_cs42l56 = { .num_controls = ARRAY_SIZE(cs42l56_snd_controls), }; -static struct regmap_config cs42l56_regmap = { +static const struct regmap_config cs42l56_regmap = { .reg_bits = 8, .val_bits = 8, -- cgit v0.10.2 From cd2ee8ad55a1728f291a760cece9ceac11462b9e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:18:25 +0100 Subject: ASoC: cs42l73: Constify struct regmap_config and snd_soc_codec_driver The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Make struct snd_soc_codec_driver const as well (snd_soc_register_codec() accepts pointer to const). Signed-off-by: Krzysztof Kozlowski Acked-by: Brian Austin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 7c55537..8ecedba 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -1347,7 +1347,7 @@ static int cs42l73_probe(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_cs42l73 = { +static const struct snd_soc_codec_driver soc_codec_dev_cs42l73 = { .probe = cs42l73_probe, .set_bias_level = cs42l73_set_bias_level, .suspend_bias_off = true, @@ -1361,7 +1361,7 @@ static struct snd_soc_codec_driver soc_codec_dev_cs42l73 = { .num_controls = ARRAY_SIZE(cs42l73_snd_controls), }; -static struct regmap_config cs42l73_regmap = { +static const struct regmap_config cs42l73_regmap = { .reg_bits = 8, .val_bits = 8, -- cgit v0.10.2 From c0b363891816ea60d95a7dfcfc72f9955a7f1d32 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:18:26 +0100 Subject: ASoC: wm8804: Constify struct regmap_config and snd_soc_codec_driver The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Make struct snd_soc_codec_driver const as well (snd_soc_register_codec() accepts pointer to const). Signed-off-by: Krzysztof Kozlowski Acked-by: Brian Austin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index 1315f76..b2b0e68 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -648,7 +648,7 @@ static struct snd_soc_dai_driver wm8804_dai = { .symmetric_rates = 1 }; -static struct snd_soc_codec_driver soc_codec_dev_wm8804 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = { .probe = wm8804_probe, .remove = wm8804_remove, .set_bias_level = wm8804_set_bias_level, @@ -664,7 +664,7 @@ static const struct of_device_id wm8804_of_match[] = { }; MODULE_DEVICE_TABLE(of, wm8804_of_match); -static struct regmap_config wm8804_regmap_config = { +static const struct regmap_config wm8804_regmap_config = { .reg_bits = 8, .val_bits = 8, -- cgit v0.10.2 From b682d7baf11ab0d29d511632eb0195b5fa004295 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:18:27 +0100 Subject: ASoC: wm8988: Constify struct regmap_config and snd_soc_codec_driver The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Make struct snd_soc_codec_driver const as well (snd_soc_register_codec() accepts pointer to const). Signed-off-by: Krzysztof Kozlowski Acked-by: Brian Austin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index e418199..d320c52 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -813,7 +813,7 @@ static int wm8988_probe(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm8988 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8988 = { .probe = wm8988_probe, .set_bias_level = wm8988_set_bias_level, .suspend_bias_off = true, @@ -826,7 +826,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8988 = { .num_dapm_routes = ARRAY_SIZE(wm8988_dapm_routes), }; -static struct regmap_config wm8988_regmap = { +static const struct regmap_config wm8988_regmap = { .reg_bits = 7, .val_bits = 9, -- cgit v0.10.2 From 27ad02f3b6452e0b650835438eb10ba76cb7dfaf Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:18:28 +0100 Subject: ASoC: wm8804: Constify struct regmap_config and snd_soc_codec_driver The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Make struct snd_soc_codec_driver (snd_soc_register_codec() accepts pointer to const) and array of default register values const as well. Signed-off-by: Krzysztof Kozlowski Acked-by: Brian Austin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index c280f0a..120228b 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -44,7 +44,7 @@ static const char *wm8995_supply_names[WM8995_NUM_SUPPLIES] = { "MICVDD" }; -static struct reg_default wm8995_reg_defaults[] = { +static const struct reg_default wm8995_reg_defaults[] = { { 0, 0x8995 }, { 5, 0x0100 }, { 16, 0x000b }, @@ -2190,7 +2190,7 @@ static struct snd_soc_dai_driver wm8995_dai[] = { } }; -static struct snd_soc_codec_driver soc_codec_dev_wm8995 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8995 = { .probe = wm8995_probe, .remove = wm8995_remove, .set_bias_level = wm8995_set_bias_level, @@ -2204,7 +2204,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8995 = { .num_dapm_routes = ARRAY_SIZE(wm8995_intercon), }; -static struct regmap_config wm8995_regmap = { +static const struct regmap_config wm8995_regmap = { .reg_bits = 16, .val_bits = 16, -- cgit v0.10.2 From de7621e870d527e949b5b2351e599398e5fac9ee Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 13:56:07 +0100 Subject: ASoC: dmaengine: Remove unnecessary snd_pcm_lib_preallocate_free_for_all() The ALSA core takes care that all preallocated memory is freed when the PCM itself is freed. There is no need to do this manually in the driver. Similarly there is also no need to do it on the pcm_new() error path as the PCM will be freed if a error is returned. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index b329b84..4864392 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -200,11 +200,6 @@ static int dmaengine_pcm_open(struct snd_pcm_substream *substream) return snd_dmaengine_pcm_open(substream, chan); } -static void dmaengine_pcm_free(struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - static struct dma_chan *dmaengine_pcm_compat_request_channel( struct snd_soc_pcm_runtime *rtd, struct snd_pcm_substream *substream) @@ -283,8 +278,7 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) if (!pcm->chan[i]) { dev_err(rtd->platform->dev, "Missing dma channel for stream: %d\n", i); - ret = -EINVAL; - goto err_free; + return -EINVAL; } ret = snd_pcm_lib_preallocate_pages(substream, @@ -293,7 +287,7 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) prealloc_buffer_size, max_buffer_size); if (ret) - goto err_free; + return ret; /* * This will only return false if we know for sure that at least @@ -307,10 +301,6 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) } return 0; - -err_free: - dmaengine_pcm_free(rtd->pcm); - return ret; } static snd_pcm_uframes_t dmaengine_pcm_pointer( @@ -341,7 +331,6 @@ static const struct snd_soc_platform_driver dmaengine_pcm_platform = { }, .ops = &dmaengine_pcm_ops, .pcm_new = dmaengine_pcm_new, - .pcm_free = dmaengine_pcm_free, }; static const char * const dmaengine_pcm_dma_channel_names[] = { -- cgit v0.10.2 From 12e29a075d4c0c9767261068cdcbfa4b346a451b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 13:56:08 +0100 Subject: ASoC: au1x: Remove unnecessary snd_pcm_lib_preallocate_free_for_all() The ALSA core takes care that all preallocated memory is freed when the PCM itself is freed. There is no need to do this manually in the driver. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index b06b8d8..dd94fea 100644 --- a/sound/soc/au1x/dbdma2.c +++ b/sound/soc/au1x/dbdma2.c @@ -315,11 +315,6 @@ static struct snd_pcm_ops au1xpsc_pcm_ops = { .pointer = au1xpsc_pcm_pointer, }; -static void au1xpsc_pcm_free_dma_buffers(struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - static int au1xpsc_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; @@ -335,7 +330,6 @@ static int au1xpsc_pcm_new(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_platform_driver au1xpsc_soc_platform = { .ops = &au1xpsc_pcm_ops, .pcm_new = au1xpsc_pcm_new, - .pcm_free = au1xpsc_pcm_free_dma_buffers, }; static int au1xpsc_pcm_drvprobe(struct platform_device *pdev) diff --git a/sound/soc/au1x/dma.c b/sound/soc/au1x/dma.c index 6ffaaff..24cc7f4 100644 --- a/sound/soc/au1x/dma.c +++ b/sound/soc/au1x/dma.c @@ -287,11 +287,6 @@ static struct snd_pcm_ops alchemy_pcm_ops = { .pointer = alchemy_pcm_pointer, }; -static void alchemy_pcm_free_dma_buffers(struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - static int alchemy_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct snd_pcm *pcm = rtd->pcm; @@ -305,7 +300,6 @@ static int alchemy_pcm_new(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_platform_driver alchemy_pcm_soc_platform = { .ops = &alchemy_pcm_ops, .pcm_new = alchemy_pcm_new, - .pcm_free = alchemy_pcm_free_dma_buffers, }; static int alchemy_pcm_drvprobe(struct platform_device *pdev) -- cgit v0.10.2 From 3a96878e6d01b70836cbf8a0e968619ef2e2aceb Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 13:56:10 +0100 Subject: ASoC: nuc900: Remove unnecessary snd_pcm_lib_preallocate_free_for_all() The ALSA core takes care that all preallocated memory is freed when the PCM itself is freed. There is no need to do this manually in the driver. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/nuc900/nuc900-pcm.c b/sound/soc/nuc900/nuc900-pcm.c index b779a3d..b809fa9 100644 --- a/sound/soc/nuc900/nuc900-pcm.c +++ b/sound/soc/nuc900/nuc900-pcm.c @@ -306,11 +306,6 @@ static struct snd_pcm_ops nuc900_dma_ops = { .mmap = nuc900_dma_mmap, }; -static void nuc900_dma_free_dma_buffers(struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - static int nuc900_dma_new(struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; @@ -330,7 +325,6 @@ static int nuc900_dma_new(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_platform_driver nuc900_soc_platform = { .ops = &nuc900_dma_ops, .pcm_new = nuc900_dma_new, - .pcm_free = nuc900_dma_free_dma_buffers, }; static int nuc900_soc_platform_probe(struct platform_device *pdev) -- cgit v0.10.2 From ddf3335b3e716ab2161e4db5b70984aef35075a3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 13:56:11 +0100 Subject: ASoC: rcar: Remove unnecessary snd_pcm_lib_preallocate_free_for_all() The ALSA core takes care that all preallocated memory is freed when the PCM itself is freed. There is no need to do this manually in the driver. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 75308bb..d9c81cd 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1140,15 +1140,9 @@ static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd) PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); } -static void rsnd_pcm_free(struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - static struct snd_soc_platform_driver rsnd_soc_platform = { .ops = &rsnd_pcm_ops, .pcm_new = rsnd_pcm_new, - .pcm_free = rsnd_pcm_free, }; static const struct snd_soc_component_driver rsnd_soc_component = { -- cgit v0.10.2 From 0db54e74a9e44afb92bc547277827258dbf5dc8f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 13:56:12 +0100 Subject: ASoC: sh: Remove unnecessary snd_pcm_lib_preallocate_free_for_all() The ALSA core takes care that all preallocated memory is freed when the PCM itself is freed. There is no need to do this manually in the driver. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c index a5b2c4e..fd11404 100644 --- a/sound/soc/sh/dma-sh7760.c +++ b/sound/soc/sh/dma-sh7760.c @@ -305,11 +305,6 @@ static struct snd_pcm_ops camelot_pcm_ops = { .pointer = camelot_pos, }; -static void camelot_pcm_free(struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - static int camelot_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct snd_pcm *pcm = rtd->pcm; @@ -328,7 +323,6 @@ static int camelot_pcm_new(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_platform_driver sh7760_soc_platform = { .ops = &camelot_pcm_ops, .pcm_new = camelot_pcm_new, - .pcm_free = camelot_pcm_free, }; static int sh7760_soc_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 8869971..422faa9 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1765,11 +1765,6 @@ static struct snd_pcm_ops fsi_pcm_ops = { #define PREALLOC_BUFFER (32 * 1024) #define PREALLOC_BUFFER_MAX (32 * 1024) -static void fsi_pcm_free(struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - static int fsi_pcm_new(struct snd_soc_pcm_runtime *rtd) { return snd_pcm_lib_preallocate_pages_for_all( @@ -1821,7 +1816,6 @@ static struct snd_soc_dai_driver fsi_soc_dai[] = { static struct snd_soc_platform_driver fsi_soc_platform = { .ops = &fsi_pcm_ops, .pcm_new = fsi_pcm_new, - .pcm_free = fsi_pcm_free, }; static const struct snd_soc_component_driver fsi_soc_component = { diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c index 32eb6da..82902f5 100644 --- a/sound/soc/sh/siu_pcm.c +++ b/sound/soc/sh/siu_pcm.c @@ -589,7 +589,6 @@ static void siu_pcm_free(struct snd_pcm *pcm) tasklet_kill(&port_info->playback.tasklet); siu_free_port(port_info); - snd_pcm_lib_preallocate_free_for_all(pcm); dev_dbg(pcm->card->dev, "%s\n", __func__); } -- cgit v0.10.2 From 691384503426f97f647e98eed85410eae71c2754 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 13:56:13 +0100 Subject: ASoC: txx9: Remove unnecessary snd_pcm_lib_preallocate_free_for_all() The ALSA core takes care that all preallocated memory is freed when the PCM itself is freed. There is no need to do this manually in the driver. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index 070e44e..88eacfd 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -282,11 +282,6 @@ static struct snd_pcm_ops txx9aclc_pcm_ops = { .pointer = txx9aclc_pcm_pointer, }; -static void txx9aclc_pcm_free_dma_buffers(struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; @@ -412,7 +407,6 @@ static struct snd_soc_platform_driver txx9aclc_soc_platform = { .remove = txx9aclc_pcm_remove, .ops = &txx9aclc_pcm_ops, .pcm_new = txx9aclc_pcm_new, - .pcm_free = txx9aclc_pcm_free_dma_buffers, }; static int txx9aclc_soc_platform_probe(struct platform_device *pdev) -- cgit v0.10.2 From 889ee2c7d70e1ae9fc9341d0e82676c519ffede2 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:02:31 +0100 Subject: at86rf230: Constify struct regmap_config The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: David S. Miller diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 80632fc..7b051ea 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -427,7 +427,7 @@ at86rf230_reg_precious(struct device *dev, unsigned int reg) } } -static struct regmap_config at86rf230_regmap_spi_config = { +static const struct regmap_config at86rf230_regmap_spi_config = { .reg_bits = 8, .val_bits = 8, .write_flag_mask = CMD_REG | CMD_WRITE, -- cgit v0.10.2 From 5f07b3c51abe330c3dd702622c419efffb5757f0 Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Mon, 5 Jan 2015 05:48:34 -0500 Subject: be2net: support TX batching using skb->xmit_more flag This patch uses skb->xmit_more flag to batch TX requests. TX is flushed either when xmit_more is false or there is no more space in the TXQ. Skyhawk-R and BEx chips require an even number of wrbs to be posted. So, when a batch of TX requests is accumulated, the last header wrb may need to be fixed with an extra dummy wrb. This patch refactors be_xmit() routine as a sequence of be_xmit_enqueue() and be_xmit_flush() calls. The Tx completion code is also updated to be able to unmap/free a batch of skbs rather than a single skb. Signed-off-by: Sathya Perla Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 712e7f8..9fa2569 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -243,7 +243,6 @@ struct be_tx_stats { u64 tx_bytes; u64 tx_pkts; u64 tx_reqs; - u64 tx_wrbs; u64 tx_compl; ulong tx_jiffies; u32 tx_stops; @@ -266,6 +265,9 @@ struct be_tx_obj { /* Remember the skbs that were transmitted */ struct sk_buff *sent_skb_list[TX_Q_LEN]; struct be_tx_stats stats; + u16 pend_wrb_cnt; /* Number of WRBs yet to be given to HW */ + u16 last_req_wrb_cnt; /* wrb cnt of the last req in the Q */ + u16 last_req_hdr; /* index of the last req's hdr-wrb */ } ____cacheline_aligned_in_smp; /* Struct to remember the pages posted for rx frags */ diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index 73a500c..32c53bc 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -193,8 +193,6 @@ static const struct be_ethtool_stat et_tx_stats[] = { {DRVSTAT_TX_INFO(tx_pkts)}, /* Number of skbs queued for trasmission by the driver */ {DRVSTAT_TX_INFO(tx_reqs)}, - /* Number of TX work request blocks DMAed to HW */ - {DRVSTAT_TX_INFO(tx_wrbs)}, /* Number of times the TX queue was stopped due to lack * of spaces in the TXQ. */ diff --git a/drivers/net/ethernet/emulex/benet/be_hw.h b/drivers/net/ethernet/emulex/benet/be_hw.h index 295ee08..6d7b3a4 100644 --- a/drivers/net/ethernet/emulex/benet/be_hw.h +++ b/drivers/net/ethernet/emulex/benet/be_hw.h @@ -311,6 +311,11 @@ struct amap_eth_hdr_wrb { u8 vlan_tag[16]; } __packed; +#define TX_HDR_WRB_COMPL 1 /* word 2 */ +#define TX_HDR_WRB_EVT (1 << 1) /* word 2 */ +#define TX_HDR_WRB_NUM_SHIFT 13 /* word 2: bits 13:17 */ +#define TX_HDR_WRB_NUM_MASK 0x1F /* word 2: bits 13:17 */ + struct be_eth_hdr_wrb { u32 dw[4]; }; diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 41a0a54..37a26b0 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -662,41 +662,22 @@ void be_link_status_update(struct be_adapter *adapter, u8 link_status) netif_carrier_off(netdev); } -static void be_tx_stats_update(struct be_tx_obj *txo, - u32 wrb_cnt, u32 copied, u32 gso_segs, - bool stopped) +static void be_tx_stats_update(struct be_tx_obj *txo, struct sk_buff *skb) { struct be_tx_stats *stats = tx_stats(txo); u64_stats_update_begin(&stats->sync); stats->tx_reqs++; - stats->tx_wrbs += wrb_cnt; - stats->tx_bytes += copied; - stats->tx_pkts += (gso_segs ? gso_segs : 1); - if (stopped) - stats->tx_stops++; + stats->tx_bytes += skb->len; + stats->tx_pkts += (skb_shinfo(skb)->gso_segs ? : 1); u64_stats_update_end(&stats->sync); } -/* Determine number of WRB entries needed to xmit data in an skb */ -static u32 wrb_cnt_for_skb(struct be_adapter *adapter, struct sk_buff *skb, - bool *dummy) +/* Returns number of WRBs needed for the skb */ +static u32 skb_wrb_cnt(struct sk_buff *skb) { - int cnt = (skb->len > skb->data_len); - - cnt += skb_shinfo(skb)->nr_frags; - - /* to account for hdr wrb */ - cnt++; - if (lancer_chip(adapter) || !(cnt & 1)) { - *dummy = false; - } else { - /* add a dummy to make it an even num */ - cnt++; - *dummy = true; - } - BUG_ON(cnt > BE_MAX_TX_FRAG_COUNT); - return cnt; + /* +1 for the header wrb */ + return 1 + (skb_headlen(skb) ? 1 : 0) + skb_shinfo(skb)->nr_frags; } static inline void wrb_fill(struct be_eth_wrb *wrb, u64 addr, int len) @@ -770,11 +751,14 @@ static void wrb_fill_hdr(struct be_adapter *adapter, struct be_eth_hdr_wrb *hdr, SET_TX_WRB_HDR_BITS(vlan_tag, hdr, vlan_tag); } - /* To skip HW VLAN tagging: evt = 1, compl = 0 */ - SET_TX_WRB_HDR_BITS(complete, hdr, !skip_hw_vlan); - SET_TX_WRB_HDR_BITS(event, hdr, 1); SET_TX_WRB_HDR_BITS(num_wrb, hdr, wrb_cnt); SET_TX_WRB_HDR_BITS(len, hdr, len); + + /* Hack to skip HW VLAN tagging needs evt = 1, compl = 0 + * When this hack is not needed, the evt bit is set while ringing DB + */ + if (skip_hw_vlan) + SET_TX_WRB_HDR_BITS(event, hdr, 1); } static void unmap_tx_frag(struct device *dev, struct be_eth_wrb *wrb, @@ -794,22 +778,24 @@ static void unmap_tx_frag(struct device *dev, struct be_eth_wrb *wrb, } } -static int make_tx_wrbs(struct be_adapter *adapter, struct be_queue_info *txq, - struct sk_buff *skb, u32 wrb_cnt, bool dummy_wrb, - bool skip_hw_vlan) +/* Returns the number of WRBs used up by the skb */ +static u32 be_xmit_enqueue(struct be_adapter *adapter, struct be_tx_obj *txo, + struct sk_buff *skb, bool skip_hw_vlan) { - dma_addr_t busaddr; - int i, copied = 0; + u32 i, copied = 0, wrb_cnt = skb_wrb_cnt(skb); struct device *dev = &adapter->pdev->dev; - struct sk_buff *first_skb = skb; - struct be_eth_wrb *wrb; + struct be_queue_info *txq = &txo->q; struct be_eth_hdr_wrb *hdr; bool map_single = false; - u16 map_head; + struct be_eth_wrb *wrb; + dma_addr_t busaddr; + u16 head = txq->head; hdr = queue_head_node(txq); + wrb_fill_hdr(adapter, hdr, skb, wrb_cnt, skb->len, skip_hw_vlan); + be_dws_cpu_to_le(hdr, sizeof(*hdr)); + queue_head_inc(txq); - map_head = txq->head; if (skb->len > skb->data_len) { int len = skb_headlen(skb); @@ -839,19 +825,23 @@ static int make_tx_wrbs(struct be_adapter *adapter, struct be_queue_info *txq, copied += skb_frag_size(frag); } - if (dummy_wrb) { - wrb = queue_head_node(txq); - wrb_fill(wrb, 0, 0); - be_dws_cpu_to_le(wrb, sizeof(*wrb)); - queue_head_inc(txq); - } + BUG_ON(txo->sent_skb_list[head]); + txo->sent_skb_list[head] = skb; + txo->last_req_hdr = head; + atomic_add(wrb_cnt, &txq->used); + txo->last_req_wrb_cnt = wrb_cnt; + txo->pend_wrb_cnt += wrb_cnt; - wrb_fill_hdr(adapter, hdr, first_skb, wrb_cnt, copied, skip_hw_vlan); - be_dws_cpu_to_le(hdr, sizeof(*hdr)); + be_tx_stats_update(txo, skb); + return wrb_cnt; - return copied; dma_err: - txq->head = map_head; + /* Bring the queue back to the state it was in before this + * routine was invoked. + */ + txq->head = head; + /* skip the first wrb (hdr); it's not mapped */ + queue_head_inc(txq); while (copied) { wrb = queue_head_node(txq); unmap_tx_frag(dev, wrb, map_single); @@ -860,6 +850,7 @@ dma_err: adapter->drv_stats.dma_map_errors++; queue_head_inc(txq); } + txq->head = head; return 0; } @@ -1030,52 +1021,64 @@ static struct sk_buff *be_xmit_workarounds(struct be_adapter *adapter, return skb; } +static void be_xmit_flush(struct be_adapter *adapter, struct be_tx_obj *txo) +{ + struct be_queue_info *txq = &txo->q; + struct be_eth_hdr_wrb *hdr = queue_index_node(txq, txo->last_req_hdr); + + /* Mark the last request eventable if it hasn't been marked already */ + if (!(hdr->dw[2] & cpu_to_le32(TX_HDR_WRB_EVT))) + hdr->dw[2] |= cpu_to_le32(TX_HDR_WRB_EVT | TX_HDR_WRB_COMPL); + + /* compose a dummy wrb if there are odd set of wrbs to notify */ + if (!lancer_chip(adapter) && (txo->pend_wrb_cnt & 1)) { + wrb_fill(queue_head_node(txq), 0, 0); + queue_head_inc(txq); + atomic_inc(&txq->used); + txo->pend_wrb_cnt++; + hdr->dw[2] &= ~cpu_to_le32(TX_HDR_WRB_NUM_MASK << + TX_HDR_WRB_NUM_SHIFT); + hdr->dw[2] |= cpu_to_le32((txo->last_req_wrb_cnt + 1) << + TX_HDR_WRB_NUM_SHIFT); + } + be_txq_notify(adapter, txo, txo->pend_wrb_cnt); + txo->pend_wrb_cnt = 0; +} + static netdev_tx_t be_xmit(struct sk_buff *skb, struct net_device *netdev) { + bool skip_hw_vlan = false, flush = !skb->xmit_more; struct be_adapter *adapter = netdev_priv(netdev); - struct be_tx_obj *txo = &adapter->tx_obj[skb_get_queue_mapping(skb)]; + u16 q_idx = skb_get_queue_mapping(skb); + struct be_tx_obj *txo = &adapter->tx_obj[q_idx]; struct be_queue_info *txq = &txo->q; - bool dummy_wrb, stopped = false; - u32 wrb_cnt = 0, copied = 0; - bool skip_hw_vlan = false; - u32 start = txq->head; + u16 wrb_cnt; skb = be_xmit_workarounds(adapter, skb, &skip_hw_vlan); - if (!skb) { - tx_stats(txo)->tx_drv_drops++; - return NETDEV_TX_OK; - } - - wrb_cnt = wrb_cnt_for_skb(adapter, skb, &dummy_wrb); + if (unlikely(!skb)) + goto drop; - copied = make_tx_wrbs(adapter, txq, skb, wrb_cnt, dummy_wrb, - skip_hw_vlan); - if (copied) { - int gso_segs = skb_shinfo(skb)->gso_segs; + wrb_cnt = be_xmit_enqueue(adapter, txo, skb, skip_hw_vlan); + if (unlikely(!wrb_cnt)) { + dev_kfree_skb_any(skb); + goto drop; + } - /* record the sent skb in the sent_skb table */ - BUG_ON(txo->sent_skb_list[start]); - txo->sent_skb_list[start] = skb; + if ((atomic_read(&txq->used) + BE_MAX_TX_FRAG_COUNT) >= txq->len) { + netif_stop_subqueue(netdev, q_idx); + tx_stats(txo)->tx_stops++; + } - /* Ensure txq has space for the next skb; Else stop the queue - * *BEFORE* ringing the tx doorbell, so that we serialze the - * tx compls of the current transmit which'll wake up the queue - */ - atomic_add(wrb_cnt, &txq->used); - if ((BE_MAX_TX_FRAG_COUNT + atomic_read(&txq->used)) >= - txq->len) { - netif_stop_subqueue(netdev, skb_get_queue_mapping(skb)); - stopped = true; - } + if (flush || __netif_subqueue_stopped(netdev, q_idx)) + be_xmit_flush(adapter, txo); - be_txq_notify(adapter, txo, wrb_cnt); + return NETDEV_TX_OK; +drop: + tx_stats(txo)->tx_drv_drops++; + /* Flush the already enqueued tx requests */ + if (flush && txo->pend_wrb_cnt) + be_xmit_flush(adapter, txo); - be_tx_stats_update(txo, wrb_cnt, copied, gso_segs, stopped); - } else { - txq->head = start; - tx_stats(txo)->tx_drv_drops++; - dev_kfree_skb_any(skb); - } return NETDEV_TX_OK; } @@ -1959,32 +1962,34 @@ static struct be_eth_tx_compl *be_tx_compl_get(struct be_queue_info *tx_cq) static u16 be_tx_compl_process(struct be_adapter *adapter, struct be_tx_obj *txo, u16 last_index) { + struct sk_buff **sent_skbs = txo->sent_skb_list; struct be_queue_info *txq = &txo->q; + u16 frag_index, num_wrbs = 0; + struct sk_buff *skb = NULL; + bool unmap_skb_hdr = false; struct be_eth_wrb *wrb; - struct sk_buff **sent_skbs = txo->sent_skb_list; - struct sk_buff *sent_skb; - u16 cur_index, num_wrbs = 1; /* account for hdr wrb */ - bool unmap_skb_hdr = true; - - sent_skb = sent_skbs[txq->tail]; - BUG_ON(!sent_skb); - sent_skbs[txq->tail] = NULL; - - /* skip header wrb */ - queue_tail_inc(txq); do { - cur_index = txq->tail; + if (sent_skbs[txq->tail]) { + /* Free skb from prev req */ + if (skb) + dev_consume_skb_any(skb); + skb = sent_skbs[txq->tail]; + sent_skbs[txq->tail] = NULL; + queue_tail_inc(txq); /* skip hdr wrb */ + num_wrbs++; + unmap_skb_hdr = true; + } wrb = queue_tail_node(txq); + frag_index = txq->tail; unmap_tx_frag(&adapter->pdev->dev, wrb, - (unmap_skb_hdr && skb_headlen(sent_skb))); + (unmap_skb_hdr && skb_headlen(skb))); unmap_skb_hdr = false; - - num_wrbs++; queue_tail_inc(txq); - } while (cur_index != last_index); + num_wrbs++; + } while (frag_index != last_index); + dev_consume_skb_any(skb); - dev_consume_skb_any(sent_skb); return num_wrbs; } @@ -2068,12 +2073,11 @@ static void be_rx_cq_clean(struct be_rx_obj *rxo) static void be_tx_compl_clean(struct be_adapter *adapter) { + u16 end_idx, notified_idx, cmpl = 0, timeo = 0, num_wrbs = 0; + struct device *dev = &adapter->pdev->dev; struct be_tx_obj *txo; struct be_queue_info *txq; struct be_eth_tx_compl *txcp; - u16 end_idx, cmpl = 0, timeo = 0, num_wrbs = 0; - struct sk_buff *sent_skb; - bool dummy_wrb; int i, pending_txqs; /* Stop polling for compls when HW has been silent for 10ms */ @@ -2095,7 +2099,7 @@ static void be_tx_compl_clean(struct be_adapter *adapter) atomic_sub(num_wrbs, &txq->used); timeo = 0; } - if (atomic_read(&txq->used) == 0) + if (atomic_read(&txq->used) == txo->pend_wrb_cnt) pending_txqs--; } @@ -2105,21 +2109,29 @@ static void be_tx_compl_clean(struct be_adapter *adapter) mdelay(1); } while (true); + /* Free enqueued TX that was never notified to HW */ for_all_tx_queues(adapter, txo, i) { txq = &txo->q; - if (atomic_read(&txq->used)) - dev_err(&adapter->pdev->dev, "%d pending tx-compls\n", - atomic_read(&txq->used)); - /* free posted tx for which compls will never arrive */ - while (atomic_read(&txq->used)) { - sent_skb = txo->sent_skb_list[txq->tail]; + if (atomic_read(&txq->used)) { + dev_info(dev, "txq%d: cleaning %d pending tx-wrbs\n", + i, atomic_read(&txq->used)); + notified_idx = txq->tail; end_idx = txq->tail; - num_wrbs = wrb_cnt_for_skb(adapter, sent_skb, - &dummy_wrb); - index_adv(&end_idx, num_wrbs - 1, txq->len); + index_adv(&end_idx, atomic_read(&txq->used) - 1, + txq->len); + /* Use the tx-compl process logic to handle requests + * that were not sent to the HW. + */ num_wrbs = be_tx_compl_process(adapter, txo, end_idx); atomic_sub(num_wrbs, &txq->used); + BUG_ON(atomic_read(&txq->used)); + txo->pend_wrb_cnt = 0; + /* Since hw was never notified of these requests, + * reset TXQ indices + */ + txq->head = notified_idx; + txq->tail = notified_idx; } } } -- cgit v0.10.2 From f612b815d75b054ec06c8be260409a7ab271c253 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Mon, 5 Jan 2015 16:30:43 +0530 Subject: RDMA/cxgb4/cxgb4vf/csiostor: Cleanup SGE register defines This patch cleanups all SGE related macros/register defines that are defined in t4_regs.h and the affected files. Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/infiniband/hw/cxgb4/t4.h b/drivers/infiniband/hw/cxgb4/t4.h index c04e513..29e764e 100644 --- a/drivers/infiniband/hw/cxgb4/t4.h +++ b/drivers/infiniband/hw/cxgb4/t4.h @@ -465,14 +465,14 @@ static inline void t4_ring_sq_db(struct t4_wq *wq, u16 inc, u8 t5, } else { PDBG("%s: DB wq->sq.pidx = %d\n", __func__, wq->sq.pidx); - writel(PIDX_T5(inc), wq->sq.udb); + writel(PIDX_T5_V(inc), wq->sq.udb); } /* Flush user doorbell area writes. */ wmb(); return; } - writel(QID(wq->sq.qid) | PIDX(inc), wq->db); + writel(QID_V(wq->sq.qid) | PIDX_V(inc), wq->db); } static inline void t4_ring_rq_db(struct t4_wq *wq, u16 inc, u8 t5, @@ -489,14 +489,14 @@ static inline void t4_ring_rq_db(struct t4_wq *wq, u16 inc, u8 t5, } else { PDBG("%s: DB wq->rq.pidx = %d\n", __func__, wq->rq.pidx); - writel(PIDX_T5(inc), wq->rq.udb); + writel(PIDX_T5_V(inc), wq->rq.udb); } /* Flush user doorbell area writes. */ wmb(); return; } - writel(QID(wq->rq.qid) | PIDX(inc), wq->db); + writel(QID_V(wq->rq.qid) | PIDX_V(inc), wq->db); } static inline int t4_wq_in_error(struct t4_wq *wq) @@ -561,14 +561,14 @@ static inline int t4_arm_cq(struct t4_cq *cq, int se) u32 val; set_bit(CQ_ARMED, &cq->flags); - while (cq->cidx_inc > CIDXINC_MASK) { - val = SEINTARM(0) | CIDXINC(CIDXINC_MASK) | TIMERREG(7) | - INGRESSQID(cq->cqid); + while (cq->cidx_inc > CIDXINC_M) { + val = SEINTARM_V(0) | CIDXINC_V(CIDXINC_M) | TIMERREG_V(7) | + INGRESSQID_V(cq->cqid); writel(val, cq->gts); - cq->cidx_inc -= CIDXINC_MASK; + cq->cidx_inc -= CIDXINC_M; } - val = SEINTARM(se) | CIDXINC(cq->cidx_inc) | TIMERREG(6) | - INGRESSQID(cq->cqid); + val = SEINTARM_V(se) | CIDXINC_V(cq->cidx_inc) | TIMERREG_V(6) | + INGRESSQID_V(cq->cqid); writel(val, cq->gts); cq->cidx_inc = 0; return 0; @@ -597,11 +597,11 @@ static inline void t4_swcq_consume(struct t4_cq *cq) static inline void t4_hwcq_consume(struct t4_cq *cq) { cq->bits_type_ts = cq->queue[cq->cidx].bits_type_ts; - if (++cq->cidx_inc == (cq->size >> 4) || cq->cidx_inc == CIDXINC_MASK) { + if (++cq->cidx_inc == (cq->size >> 4) || cq->cidx_inc == CIDXINC_M) { u32 val; - val = SEINTARM(0) | CIDXINC(cq->cidx_inc) | TIMERREG(7) | - INGRESSQID(cq->cqid); + val = SEINTARM_V(0) | CIDXINC_V(cq->cidx_inc) | TIMERREG_V(7) | + INGRESSQID_V(cq->cqid); writel(val, cq->gts); cq->cidx_inc = 0; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index ccf3436..5e0d57a 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -66,6 +66,7 @@ #include "cxgb4.h" #include "t4_regs.h" +#include "t4_values.h" #include "t4_msg.h" #include "t4fw_api.h" #include "cxgb4_dcb.h" @@ -1050,9 +1051,9 @@ static void enable_rx(struct adapter *adap) if (q->handler) napi_enable(&q->napi); /* 0-increment GTS to start the timer and enable interrupts */ - t4_write_reg(adap, MYPF_REG(SGE_PF_GTS), - SEINTARM(q->intr_params) | - INGRESSQID(q->cntxt_id)); + t4_write_reg(adap, MYPF_REG(SGE_PF_GTS_A), + SEINTARM_V(q->intr_params) | + INGRESSQID_V(q->cntxt_id)); } } @@ -3702,14 +3703,20 @@ int cxgb4_sync_txq_pidx(struct net_device *dev, u16 qid, u16 pidx, if (pidx != hw_pidx) { u16 delta; + u32 val; if (pidx >= hw_pidx) delta = pidx - hw_pidx; else delta = size - hw_pidx + pidx; + + if (is_t4(adap->params.chip)) + val = PIDX_V(delta); + else + val = PIDX_T5_V(delta); wmb(); - t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL), - QID(qid) | PIDX(delta)); + t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL_A), + QID_V(qid) | val); } out: return ret; @@ -3721,8 +3728,8 @@ void cxgb4_disable_db_coalescing(struct net_device *dev) struct adapter *adap; adap = netdev2adap(dev); - t4_set_reg_field(adap, A_SGE_DOORBELL_CONTROL, F_NOCOALESCE, - F_NOCOALESCE); + t4_set_reg_field(adap, A_SGE_DOORBELL_CONTROL, NOCOALESCE_F, + NOCOALESCE_F); } EXPORT_SYMBOL(cxgb4_disable_db_coalescing); @@ -3731,7 +3738,7 @@ void cxgb4_enable_db_coalescing(struct net_device *dev) struct adapter *adap; adap = netdev2adap(dev); - t4_set_reg_field(adap, A_SGE_DOORBELL_CONTROL, F_NOCOALESCE, 0); + t4_set_reg_field(adap, A_SGE_DOORBELL_CONTROL, NOCOALESCE_F, 0); } EXPORT_SYMBOL(cxgb4_enable_db_coalescing); @@ -3809,8 +3816,8 @@ u64 cxgb4_read_sge_timestamp(struct net_device *dev) struct adapter *adap; adap = netdev2adap(dev); - lo = t4_read_reg(adap, SGE_TIMESTAMP_LO); - hi = GET_TSVAL(t4_read_reg(adap, SGE_TIMESTAMP_HI)); + lo = t4_read_reg(adap, SGE_TIMESTAMP_LO_A); + hi = TSVAL_G(t4_read_reg(adap, SGE_TIMESTAMP_HI_A)); return ((u64)hi << 32) | (u64)lo; } @@ -3904,8 +3911,8 @@ static void enable_txq_db(struct adapter *adap, struct sge_txq *q) * are committed before we tell HW about them. */ wmb(); - t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL), - QID(q->cntxt_id) | PIDX(q->db_pidx_inc)); + t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL_A), + QID_V(q->cntxt_id) | PIDX_V(q->db_pidx_inc)); q->db_pidx_inc = 0; } q->db_disabled = 0; @@ -3952,9 +3959,9 @@ static void process_db_full(struct work_struct *work) drain_db_fifo(adap, dbfifo_drain_delay); enable_dbs(adap); notify_rdma_uld(adap, CXGB4_CONTROL_DB_EMPTY); - t4_set_reg_field(adap, SGE_INT_ENABLE3, - DBFIFO_HP_INT | DBFIFO_LP_INT, - DBFIFO_HP_INT | DBFIFO_LP_INT); + t4_set_reg_field(adap, SGE_INT_ENABLE3_A, + DBFIFO_HP_INT_F | DBFIFO_LP_INT_F, + DBFIFO_HP_INT_F | DBFIFO_LP_INT_F); } static void sync_txq_pidx(struct adapter *adap, struct sge_txq *q) @@ -3968,14 +3975,20 @@ static void sync_txq_pidx(struct adapter *adap, struct sge_txq *q) goto out; if (q->db_pidx != hw_pidx) { u16 delta; + u32 val; if (q->db_pidx >= hw_pidx) delta = q->db_pidx - hw_pidx; else delta = q->size - hw_pidx + q->db_pidx; + + if (is_t4(adap->params.chip)) + val = PIDX_V(delta); + else + val = PIDX_T5_V(delta); wmb(); - t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL), - QID(q->cntxt_id) | PIDX(delta)); + t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL_A), + QID_V(q->cntxt_id) | val); } out: q->db_disabled = 0; @@ -4024,7 +4037,7 @@ static void process_db_drop(struct work_struct *work) dev_err(adap->pdev_dev, "doorbell drop recovery: " "qid=%d, pidx_inc=%d\n", qid, pidx_inc); else - writel(PIDX_T5(pidx_inc) | QID(bar2_qid), + writel(PIDX_T5_V(pidx_inc) | QID_V(bar2_qid), adap->bar2 + bar2_qoffset + SGE_UDB_KDOORBELL); /* Re-enable BAR2 WC */ @@ -4039,8 +4052,8 @@ void t4_db_full(struct adapter *adap) if (is_t4(adap->params.chip)) { disable_dbs(adap); notify_rdma_uld(adap, CXGB4_CONTROL_DB_FULL); - t4_set_reg_field(adap, SGE_INT_ENABLE3, - DBFIFO_HP_INT | DBFIFO_LP_INT, 0); + t4_set_reg_field(adap, SGE_INT_ENABLE3_A, + DBFIFO_HP_INT_F | DBFIFO_LP_INT_F, 0); queue_work(adap->workq, &adap->db_full_task); } } @@ -4089,8 +4102,8 @@ static void uld_attach(struct adapter *adap, unsigned int uld) /* MODQ_REQ_MAP sets queues 0-3 to chan 0-3 */ for (i = 0; i < NCHAN; i++) lli.tx_modq[i] = i; - lli.gts_reg = adap->regs + MYPF_REG(SGE_PF_GTS); - lli.db_reg = adap->regs + MYPF_REG(SGE_PF_KDOORBELL); + lli.gts_reg = adap->regs + MYPF_REG(SGE_PF_GTS_A); + lli.db_reg = adap->regs + MYPF_REG(SGE_PF_KDOORBELL_A); lli.fw_vers = adap->params.fw_vers; lli.dbfifo_int_thresh = dbfifo_int_thresh; lli.sge_ingpadboundary = adap->sge.fl_align; @@ -4783,7 +4796,7 @@ static const struct net_device_ops cxgb4_netdev_ops = { void t4_fatal_err(struct adapter *adap) { - t4_set_reg_field(adap, SGE_CONTROL, GLOBALENABLE, 0); + t4_set_reg_field(adap, SGE_CONTROL_A, GLOBALENABLE_F, 0); t4_intr_disable(adap); dev_alert(adap->pdev_dev, "encountered fatal error, adapter stopped\n"); } @@ -5013,9 +5026,9 @@ static int adap_init0_tweaks(struct adapter *adapter) rx_dma_offset); rx_dma_offset = 2; } - t4_set_reg_field(adapter, SGE_CONTROL, - PKTSHIFT_MASK, - PKTSHIFT(rx_dma_offset)); + t4_set_reg_field(adapter, SGE_CONTROL_A, + PKTSHIFT_V(PKTSHIFT_M), + PKTSHIFT_V(rx_dma_offset)); /* * Don't include the "IP Pseudo Header" in CPL_RX_PKT checksums: Linux @@ -5332,8 +5345,7 @@ static int adap_init0_no_config(struct adapter *adapter, int reset) s->timer_val[SGE_NTIMERS - 1] = MAX_SGE_TIMERVAL; s->counter_val[0] = 1; for (i = 1; i < SGE_NCOUNTERS; i++) - s->counter_val[i] = min(intr_cnt[i - 1], - THRESHOLD_0_GET(THRESHOLD_0_MASK)); + s->counter_val[i] = min(intr_cnt[i - 1], THRESHOLD_0_M); t4_sge_init(adapter); #ifdef CONFIG_PCI_IOV @@ -6467,9 +6479,11 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (!is_t4(adapter->params.chip)) { - s_qpp = QUEUESPERPAGEPF1 * adapter->fn; - qpp = 1 << QUEUESPERPAGEPF0_GET(t4_read_reg(adapter, - SGE_EGRESS_QUEUES_PER_PAGE_PF) >> s_qpp); + s_qpp = (QUEUESPERPAGEPF0_S + + (QUEUESPERPAGEPF1_S - QUEUESPERPAGEPF0_S) * + adapter->fn); + qpp = 1 << QUEUESPERPAGEPF0_G(t4_read_reg(adapter, + SGE_EGRESS_QUEUES_PER_PAGE_PF_A) >> s_qpp); num_seg = PAGE_SIZE / SEGMENT_SIZE; /* Each segment size is 128B. Write coalescing is enabled only diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index ebf935a..4449fc7 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -45,6 +45,7 @@ #include #include "cxgb4.h" #include "t4_regs.h" +#include "t4_values.h" #include "t4_msg.h" #include "t4fw_api.h" @@ -521,10 +522,12 @@ static inline void ring_fl_db(struct adapter *adap, struct sge_fl *q) { u32 val; if (q->pend_cred >= 8) { - val = PIDX(q->pend_cred / 8); - if (!is_t4(adap->params.chip)) - val |= DBTYPE(1); - val |= DBPRIO(1); + if (is_t4(adap->params.chip)) + val = PIDX_V(q->pend_cred / 8); + else + val = PIDX_T5_V(q->pend_cred / 8) | + DBTYPE_F; + val |= DBPRIO_F; wmb(); /* If we don't have access to the new User Doorbell (T5+), use @@ -532,10 +535,10 @@ static inline void ring_fl_db(struct adapter *adap, struct sge_fl *q) * mechanism. */ if (unlikely(q->bar2_addr == NULL)) { - t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL), - val | QID(q->cntxt_id)); + t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL_A), + val | QID_V(q->cntxt_id)); } else { - writel(val | QID(q->bar2_qid), + writel(val | QID_V(q->bar2_qid), q->bar2_addr + SGE_UDB_KDOORBELL); /* This Write memory Barrier will force the write to @@ -884,7 +887,7 @@ static inline void ring_tx_db(struct adapter *adap, struct sge_txq *q, int n) * doorbell mechanism; otherwise use the new BAR2 mechanism. */ if (unlikely(q->bar2_addr == NULL)) { - u32 val = PIDX(n); + u32 val = PIDX_V(n); unsigned long flags; /* For T4 we need to participate in the Doorbell Recovery @@ -892,14 +895,14 @@ static inline void ring_tx_db(struct adapter *adap, struct sge_txq *q, int n) */ spin_lock_irqsave(&q->db_lock, flags); if (!q->db_disabled) - t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL), - QID(q->cntxt_id) | val); + t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL_A), + QID_V(q->cntxt_id) | val); else q->db_pidx_inc += n; q->db_pidx = q->pidx; spin_unlock_irqrestore(&q->db_lock, flags); } else { - u32 val = PIDX_T5(n); + u32 val = PIDX_T5_V(n); /* T4 and later chips share the same PIDX field offset within * the doorbell, but T5 and later shrank the field in order to @@ -907,7 +910,7 @@ static inline void ring_tx_db(struct adapter *adap, struct sge_txq *q, int n) * large in the first place (14 bits) so we just use the T5 * and later limits and warn if a Queue ID is too large. */ - WARN_ON(val & DBPRIO(1)); + WARN_ON(val & DBPRIO_F); /* If we're only writing a single TX Descriptor and we can use * Inferred QID registers, we can use the Write Combining @@ -923,7 +926,7 @@ static inline void ring_tx_db(struct adapter *adap, struct sge_txq *q, int n) (q->bar2_addr + SGE_UDB_WCDOORBELL), wr); } else { - writel(val | QID(q->bar2_qid), + writel(val | QID_V(q->bar2_qid), q->bar2_addr + SGE_UDB_KDOORBELL); } @@ -2001,16 +2004,16 @@ static int napi_rx_handler(struct napi_struct *napi, int budget) } else params = QINTR_TIMER_IDX(7); - val = CIDXINC(work_done) | SEINTARM(params); + val = CIDXINC_V(work_done) | SEINTARM_V(params); /* If we don't have access to the new User GTS (T5+), use the old * doorbell mechanism; otherwise use the new BAR2 mechanism. */ if (unlikely(q->bar2_addr == NULL)) { - t4_write_reg(q->adap, MYPF_REG(SGE_PF_GTS), - val | INGRESSQID((u32)q->cntxt_id)); + t4_write_reg(q->adap, MYPF_REG(SGE_PF_GTS_A), + val | INGRESSQID_V((u32)q->cntxt_id)); } else { - writel(val | INGRESSQID(q->bar2_qid), + writel(val | INGRESSQID_V(q->bar2_qid), q->bar2_addr + SGE_UDB_GTS); wmb(); } @@ -2056,16 +2059,16 @@ static unsigned int process_intrq(struct adapter *adap) rspq_next(q); } - val = CIDXINC(credits) | SEINTARM(q->intr_params); + val = CIDXINC_V(credits) | SEINTARM_V(q->intr_params); /* If we don't have access to the new User GTS (T5+), use the old * doorbell mechanism; otherwise use the new BAR2 mechanism. */ if (unlikely(q->bar2_addr == NULL)) { - t4_write_reg(adap, MYPF_REG(SGE_PF_GTS), - val | INGRESSQID(q->cntxt_id)); + t4_write_reg(adap, MYPF_REG(SGE_PF_GTS_A), + val | INGRESSQID_V(q->cntxt_id)); } else { - writel(val | INGRESSQID(q->bar2_qid), + writel(val | INGRESSQID_V(q->bar2_qid), q->bar2_addr + SGE_UDB_GTS); wmb(); } @@ -2770,8 +2773,8 @@ static int t4_sge_init_soft(struct adapter *adap) * process_responses() and that only packet data is going to the * Free Lists. */ - if ((t4_read_reg(adap, SGE_CONTROL) & RXPKTCPLMODE_MASK) != - RXPKTCPLMODE(X_RXPKTCPLMODE_SPLIT)) { + if ((t4_read_reg(adap, SGE_CONTROL_A) & RXPKTCPLMODE_F) != + RXPKTCPLMODE_V(RXPKTCPLMODE_SPLIT_X)) { dev_err(adap->pdev_dev, "bad SGE CPL MODE\n"); return -EINVAL; } @@ -2785,7 +2788,7 @@ static int t4_sge_init_soft(struct adapter *adap) * XXX meet our needs! */ #define READ_FL_BUF(x) \ - t4_read_reg(adap, SGE_FL_BUFFER_SIZE0+(x)*sizeof(u32)) + t4_read_reg(adap, SGE_FL_BUFFER_SIZE0_A+(x)*sizeof(u32)) fl_small_pg = READ_FL_BUF(RX_SMALL_PG_BUF); fl_large_pg = READ_FL_BUF(RX_LARGE_PG_BUF); @@ -2839,11 +2842,11 @@ static int t4_sge_init_soft(struct adapter *adap) s->timer_val[5] = core_ticks_to_us(adap, TIMERVALUE5_GET(timer_value_4_and_5)); - ingress_rx_threshold = t4_read_reg(adap, SGE_INGRESS_RX_THRESHOLD); - s->counter_val[0] = THRESHOLD_0_GET(ingress_rx_threshold); - s->counter_val[1] = THRESHOLD_1_GET(ingress_rx_threshold); - s->counter_val[2] = THRESHOLD_2_GET(ingress_rx_threshold); - s->counter_val[3] = THRESHOLD_3_GET(ingress_rx_threshold); + ingress_rx_threshold = t4_read_reg(adap, SGE_INGRESS_RX_THRESHOLD_A); + s->counter_val[0] = THRESHOLD_0_G(ingress_rx_threshold); + s->counter_val[1] = THRESHOLD_1_G(ingress_rx_threshold); + s->counter_val[2] = THRESHOLD_2_G(ingress_rx_threshold); + s->counter_val[3] = THRESHOLD_3_G(ingress_rx_threshold); return 0; } @@ -2856,8 +2859,7 @@ static int t4_sge_init_hard(struct adapter *adap) * Set up our basic SGE mode to deliver CPL messages to our Ingress * Queue and Packet Date to the Free List. */ - t4_set_reg_field(adap, SGE_CONTROL, RXPKTCPLMODE_MASK, - RXPKTCPLMODE_MASK); + t4_set_reg_field(adap, SGE_CONTROL_A, RXPKTCPLMODE_F, RXPKTCPLMODE_F); /* * Set up to drop DOORBELL writes when the DOORBELL FIFO overflows @@ -2887,22 +2889,22 @@ static int t4_sge_init_hard(struct adapter *adap) s->fl_pg_order = FL_PG_ORDER; if (s->fl_pg_order) t4_write_reg(adap, - SGE_FL_BUFFER_SIZE0+RX_LARGE_PG_BUF*sizeof(u32), + SGE_FL_BUFFER_SIZE0_A+RX_LARGE_PG_BUF*sizeof(u32), PAGE_SIZE << FL_PG_ORDER); - t4_write_reg(adap, SGE_FL_BUFFER_SIZE0+RX_SMALL_MTU_BUF*sizeof(u32), + t4_write_reg(adap, SGE_FL_BUFFER_SIZE0_A+RX_SMALL_MTU_BUF*sizeof(u32), FL_MTU_SMALL_BUFSIZE(adap)); - t4_write_reg(adap, SGE_FL_BUFFER_SIZE0+RX_LARGE_MTU_BUF*sizeof(u32), + t4_write_reg(adap, SGE_FL_BUFFER_SIZE0_A+RX_LARGE_MTU_BUF*sizeof(u32), FL_MTU_LARGE_BUFSIZE(adap)); /* * Note that the SGE Ingress Packet Count Interrupt Threshold and * Timer Holdoff values must be supplied by our caller. */ - t4_write_reg(adap, SGE_INGRESS_RX_THRESHOLD, - THRESHOLD_0(s->counter_val[0]) | - THRESHOLD_1(s->counter_val[1]) | - THRESHOLD_2(s->counter_val[2]) | - THRESHOLD_3(s->counter_val[3])); + t4_write_reg(adap, SGE_INGRESS_RX_THRESHOLD_A, + THRESHOLD_0_V(s->counter_val[0]) | + THRESHOLD_1_V(s->counter_val[1]) | + THRESHOLD_2_V(s->counter_val[2]) | + THRESHOLD_3_V(s->counter_val[3])); t4_write_reg(adap, SGE_TIMER_VALUE_0_AND_1, TIMERVALUE0(us_to_core_ticks(adap, s->timer_val[0])) | TIMERVALUE1(us_to_core_ticks(adap, s->timer_val[1]))); @@ -2927,9 +2929,9 @@ int t4_sge_init(struct adapter *adap) * Ingress Padding Boundary and Egress Status Page Size are set up by * t4_fixup_host_params(). */ - sge_control = t4_read_reg(adap, SGE_CONTROL); - s->pktshift = PKTSHIFT_GET(sge_control); - s->stat_len = (sge_control & EGRSTATUSPAGESIZE_MASK) ? 128 : 64; + sge_control = t4_read_reg(adap, SGE_CONTROL_A); + s->pktshift = PKTSHIFT_G(sge_control); + s->stat_len = (sge_control & EGRSTATUSPAGESIZE_F) ? 128 : 64; /* T4 uses a single control field to specify both the PCIe Padding and * Packing Boundary. T5 introduced the ability to specify these @@ -2937,8 +2939,8 @@ int t4_sge_init(struct adapter *adap) * within Packed Buffer Mode is the maximum of these two * specifications. */ - ingpadboundary = 1 << (INGPADBOUNDARY_GET(sge_control) + - X_INGPADBOUNDARY_SHIFT); + ingpadboundary = 1 << (INGPADBOUNDARY_G(sge_control) + + INGPADBOUNDARY_SHIFT_X); if (is_t4(adap->params.chip)) { s->fl_align = ingpadboundary; } else { @@ -2975,11 +2977,11 @@ int t4_sge_init(struct adapter *adap) * buffers and a new field which only applies to Packed Mode Free List * buffers. */ - sge_conm_ctrl = t4_read_reg(adap, SGE_CONM_CTRL); + sge_conm_ctrl = t4_read_reg(adap, SGE_CONM_CTRL_A); if (is_t4(adap->params.chip)) - egress_threshold = EGRTHRESHOLD_GET(sge_conm_ctrl); + egress_threshold = EGRTHRESHOLD_G(sge_conm_ctrl); else - egress_threshold = EGRTHRESHOLDPACKING_GET(sge_conm_ctrl); + egress_threshold = EGRTHRESHOLDPACKING_G(sge_conm_ctrl); s->fl_starve_thres = 2*egress_threshold + 1; setup_timer(&s->rx_timer, sge_rx_timer_cb, (unsigned long)adap); diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index c132d90..ac00cab 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -35,6 +35,7 @@ #include #include "cxgb4.h" #include "t4_regs.h" +#include "t4_values.h" #include "t4fw_api.h" /** @@ -1499,43 +1500,43 @@ static void sge_intr_handler(struct adapter *adapter) u64 v; static const struct intr_info sge_intr_info[] = { - { ERR_CPL_EXCEED_IQE_SIZE, + { ERR_CPL_EXCEED_IQE_SIZE_F, "SGE received CPL exceeding IQE size", -1, 1 }, - { ERR_INVALID_CIDX_INC, + { ERR_INVALID_CIDX_INC_F, "SGE GTS CIDX increment too large", -1, 0 }, - { ERR_CPL_OPCODE_0, "SGE received 0-length CPL", -1, 0 }, - { DBFIFO_LP_INT, NULL, -1, 0, t4_db_full }, - { DBFIFO_HP_INT, NULL, -1, 0, t4_db_full }, - { ERR_DROPPED_DB, NULL, -1, 0, t4_db_dropped }, - { ERR_DATA_CPL_ON_HIGH_QID1 | ERR_DATA_CPL_ON_HIGH_QID0, + { ERR_CPL_OPCODE_0_F, "SGE received 0-length CPL", -1, 0 }, + { DBFIFO_LP_INT_F, NULL, -1, 0, t4_db_full }, + { DBFIFO_HP_INT_F, NULL, -1, 0, t4_db_full }, + { ERR_DROPPED_DB_F, NULL, -1, 0, t4_db_dropped }, + { ERR_DATA_CPL_ON_HIGH_QID1_F | ERR_DATA_CPL_ON_HIGH_QID0_F, "SGE IQID > 1023 received CPL for FL", -1, 0 }, - { ERR_BAD_DB_PIDX3, "SGE DBP 3 pidx increment too large", -1, + { ERR_BAD_DB_PIDX3_F, "SGE DBP 3 pidx increment too large", -1, 0 }, - { ERR_BAD_DB_PIDX2, "SGE DBP 2 pidx increment too large", -1, + { ERR_BAD_DB_PIDX2_F, "SGE DBP 2 pidx increment too large", -1, 0 }, - { ERR_BAD_DB_PIDX1, "SGE DBP 1 pidx increment too large", -1, + { ERR_BAD_DB_PIDX1_F, "SGE DBP 1 pidx increment too large", -1, 0 }, - { ERR_BAD_DB_PIDX0, "SGE DBP 0 pidx increment too large", -1, + { ERR_BAD_DB_PIDX0_F, "SGE DBP 0 pidx increment too large", -1, 0 }, - { ERR_ING_CTXT_PRIO, + { ERR_ING_CTXT_PRIO_F, "SGE too many priority ingress contexts", -1, 0 }, - { ERR_EGR_CTXT_PRIO, + { ERR_EGR_CTXT_PRIO_F, "SGE too many priority egress contexts", -1, 0 }, - { INGRESS_SIZE_ERR, "SGE illegal ingress QID", -1, 0 }, - { EGRESS_SIZE_ERR, "SGE illegal egress QID", -1, 0 }, + { INGRESS_SIZE_ERR_F, "SGE illegal ingress QID", -1, 0 }, + { EGRESS_SIZE_ERR_F, "SGE illegal egress QID", -1, 0 }, { 0 } }; - v = (u64)t4_read_reg(adapter, SGE_INT_CAUSE1) | - ((u64)t4_read_reg(adapter, SGE_INT_CAUSE2) << 32); + v = (u64)t4_read_reg(adapter, SGE_INT_CAUSE1_A) | + ((u64)t4_read_reg(adapter, SGE_INT_CAUSE2_A) << 32); if (v) { dev_alert(adapter->pdev_dev, "SGE parity error (%#llx)\n", (unsigned long long)v); - t4_write_reg(adapter, SGE_INT_CAUSE1, v); - t4_write_reg(adapter, SGE_INT_CAUSE2, v >> 32); + t4_write_reg(adapter, SGE_INT_CAUSE1_A, v); + t4_write_reg(adapter, SGE_INT_CAUSE2_A, v >> 32); } - if (t4_handle_intr_status(adapter, SGE_INT_CAUSE3, sge_intr_info) || + if (t4_handle_intr_status(adapter, SGE_INT_CAUSE3_A, sge_intr_info) || v != 0) t4_fatal_err(adapter); } @@ -2025,15 +2026,15 @@ void t4_intr_enable(struct adapter *adapter) { u32 pf = SOURCEPF_GET(t4_read_reg(adapter, PL_WHOAMI)); - t4_write_reg(adapter, SGE_INT_ENABLE3, ERR_CPL_EXCEED_IQE_SIZE | - ERR_INVALID_CIDX_INC | ERR_CPL_OPCODE_0 | - ERR_DROPPED_DB | ERR_DATA_CPL_ON_HIGH_QID1 | - ERR_DATA_CPL_ON_HIGH_QID0 | ERR_BAD_DB_PIDX3 | - ERR_BAD_DB_PIDX2 | ERR_BAD_DB_PIDX1 | - ERR_BAD_DB_PIDX0 | ERR_ING_CTXT_PRIO | - ERR_EGR_CTXT_PRIO | INGRESS_SIZE_ERR | - DBFIFO_HP_INT | DBFIFO_LP_INT | - EGRESS_SIZE_ERR); + t4_write_reg(adapter, SGE_INT_ENABLE3_A, ERR_CPL_EXCEED_IQE_SIZE_F | + ERR_INVALID_CIDX_INC_F | ERR_CPL_OPCODE_0_F | + ERR_DROPPED_DB_F | ERR_DATA_CPL_ON_HIGH_QID1_F | + ERR_DATA_CPL_ON_HIGH_QID0_F | ERR_BAD_DB_PIDX3_F | + ERR_BAD_DB_PIDX2_F | ERR_BAD_DB_PIDX1_F | + ERR_BAD_DB_PIDX0_F | ERR_ING_CTXT_PRIO_F | + ERR_EGR_CTXT_PRIO_F | INGRESS_SIZE_ERR_F | + DBFIFO_HP_INT_F | DBFIFO_LP_INT_F | + EGRESS_SIZE_ERR_F); t4_write_reg(adapter, MYPF_REG(PL_PF_INT_ENABLE), PF_INTR_MASK); t4_set_reg_field(adapter, PL_INT_MAP0, 0, 1 << pf); } @@ -3148,22 +3149,23 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size, unsigned int fl_align = cache_line_size < 32 ? 32 : cache_line_size; unsigned int fl_align_log = fls(fl_align) - 1; - t4_write_reg(adap, SGE_HOST_PAGE_SIZE, - HOSTPAGESIZEPF0(sge_hps) | - HOSTPAGESIZEPF1(sge_hps) | - HOSTPAGESIZEPF2(sge_hps) | - HOSTPAGESIZEPF3(sge_hps) | - HOSTPAGESIZEPF4(sge_hps) | - HOSTPAGESIZEPF5(sge_hps) | - HOSTPAGESIZEPF6(sge_hps) | - HOSTPAGESIZEPF7(sge_hps)); + t4_write_reg(adap, SGE_HOST_PAGE_SIZE_A, + HOSTPAGESIZEPF0_V(sge_hps) | + HOSTPAGESIZEPF1_V(sge_hps) | + HOSTPAGESIZEPF2_V(sge_hps) | + HOSTPAGESIZEPF3_V(sge_hps) | + HOSTPAGESIZEPF4_V(sge_hps) | + HOSTPAGESIZEPF5_V(sge_hps) | + HOSTPAGESIZEPF6_V(sge_hps) | + HOSTPAGESIZEPF7_V(sge_hps)); if (is_t4(adap->params.chip)) { - t4_set_reg_field(adap, SGE_CONTROL, - INGPADBOUNDARY_MASK | - EGRSTATUSPAGESIZE_MASK, - INGPADBOUNDARY(fl_align_log - 5) | - EGRSTATUSPAGESIZE(stat_len != 64)); + t4_set_reg_field(adap, SGE_CONTROL_A, + INGPADBOUNDARY_V(INGPADBOUNDARY_M) | + EGRSTATUSPAGESIZE_F, + INGPADBOUNDARY_V(fl_align_log - + INGPADBOUNDARY_SHIFT_X) | + EGRSTATUSPAGESIZE_V(stat_len != 64)); } else { /* T5 introduced the separation of the Free List Padding and * Packing Boundaries. Thus, we can select a smaller Padding @@ -3193,15 +3195,15 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size, fl_align = 64; fl_align_log = 6; } - t4_set_reg_field(adap, SGE_CONTROL, - INGPADBOUNDARY_MASK | - EGRSTATUSPAGESIZE_MASK, - INGPADBOUNDARY(INGPCIEBOUNDARY_32B_X) | - EGRSTATUSPAGESIZE(stat_len != 64)); + t4_set_reg_field(adap, SGE_CONTROL_A, + INGPADBOUNDARY_V(INGPADBOUNDARY_M) | + EGRSTATUSPAGESIZE_F, + INGPADBOUNDARY_V(INGPCIEBOUNDARY_32B_X) | + EGRSTATUSPAGESIZE_V(stat_len != 64)); t4_set_reg_field(adap, SGE_CONTROL2_A, INGPACKBOUNDARY_V(INGPACKBOUNDARY_M), INGPACKBOUNDARY_V(fl_align_log - - INGPACKBOUNDARY_SHIFT_X)); + INGPACKBOUNDARY_SHIFT_X)); } /* * Adjust various SGE Free List Host Buffer Sizes. @@ -3224,12 +3226,12 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size, * Default Firmware Configuration File but we need to adjust it for * this host's cache line size. */ - t4_write_reg(adap, SGE_FL_BUFFER_SIZE0, page_size); - t4_write_reg(adap, SGE_FL_BUFFER_SIZE2, - (t4_read_reg(adap, SGE_FL_BUFFER_SIZE2) + fl_align-1) + t4_write_reg(adap, SGE_FL_BUFFER_SIZE0_A, page_size); + t4_write_reg(adap, SGE_FL_BUFFER_SIZE2_A, + (t4_read_reg(adap, SGE_FL_BUFFER_SIZE2_A) + fl_align-1) & ~(fl_align-1)); - t4_write_reg(adap, SGE_FL_BUFFER_SIZE3, - (t4_read_reg(adap, SGE_FL_BUFFER_SIZE3) + fl_align-1) + t4_write_reg(adap, SGE_FL_BUFFER_SIZE3_A, + (t4_read_reg(adap, SGE_FL_BUFFER_SIZE3_A) + fl_align-1) & ~(fl_align-1)); t4_write_reg(adap, ULP_RX_TDDP_PSZ, HPZ0(page_shift - 12)); @@ -4133,7 +4135,7 @@ int t4_init_sge_params(struct adapter *adapter) /* Extract the SGE Page Size for our PF. */ - hps = t4_read_reg(adapter, SGE_HOST_PAGE_SIZE); + hps = t4_read_reg(adapter, SGE_HOST_PAGE_SIZE_A); s_hps = (HOSTPAGESIZEPF0_S + (HOSTPAGESIZEPF1_S - HOSTPAGESIZEPF0_S) * adapter->fn); sge_params->hps = ((hps >> s_hps) & HOSTPAGESIZEPF0_M); @@ -4142,10 +4144,10 @@ int t4_init_sge_params(struct adapter *adapter) */ s_qpp = (QUEUESPERPAGEPF0_S + (QUEUESPERPAGEPF1_S - QUEUESPERPAGEPF0_S) * adapter->fn); - qpp = t4_read_reg(adapter, SGE_EGRESS_QUEUES_PER_PAGE_PF); - sge_params->eq_qpp = ((qpp >> s_qpp) & QUEUESPERPAGEPF0_MASK); + qpp = t4_read_reg(adapter, SGE_EGRESS_QUEUES_PER_PAGE_PF_A); + sge_params->eq_qpp = ((qpp >> s_qpp) & QUEUESPERPAGEPF0_M); qpp = t4_read_reg(adapter, SGE_INGRESS_QUEUES_PER_PAGE_PF); - sge_params->iq_qpp = ((qpp >> s_qpp) & QUEUESPERPAGEPF0_MASK); + sge_params->iq_qpp = ((qpp >> s_qpp) & QUEUESPERPAGEPF0_M); return 0; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index d7bd34e..29c0911 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -63,233 +63,311 @@ #define MC_BIST_STATUS_REG(reg_addr, idx) ((reg_addr) + (idx) * 4) #define EDC_BIST_STATUS_REG(reg_addr, idx) ((reg_addr) + (idx) * 4) -#define SGE_PF_KDOORBELL 0x0 -#define QID_MASK 0xffff8000U -#define QID_SHIFT 15 -#define QID(x) ((x) << QID_SHIFT) -#define DBPRIO(x) ((x) << 14) -#define DBTYPE(x) ((x) << 13) -#define PIDX_MASK 0x00003fffU -#define PIDX_SHIFT 0 -#define PIDX(x) ((x) << PIDX_SHIFT) -#define PIDX_SHIFT_T5 0 -#define PIDX_T5(x) ((x) << PIDX_SHIFT_T5) - - -#define SGE_TIMERREGS 6 -#define SGE_PF_GTS 0x4 -#define INGRESSQID_MASK 0xffff0000U -#define INGRESSQID_SHIFT 16 -#define INGRESSQID(x) ((x) << INGRESSQID_SHIFT) -#define TIMERREG_MASK 0x0000e000U -#define TIMERREG_SHIFT 13 -#define TIMERREG(x) ((x) << TIMERREG_SHIFT) -#define SEINTARM_MASK 0x00001000U -#define SEINTARM_SHIFT 12 -#define SEINTARM(x) ((x) << SEINTARM_SHIFT) -#define CIDXINC_MASK 0x00000fffU -#define CIDXINC_SHIFT 0 -#define CIDXINC(x) ((x) << CIDXINC_SHIFT) - -#define X_RXPKTCPLMODE_SPLIT 1 -#define X_INGPADBOUNDARY_SHIFT 5 - -#define SGE_CONTROL 0x1008 -#define SGE_CONTROL2_A 0x1124 -#define DCASYSTYPE 0x00080000U -#define RXPKTCPLMODE_MASK 0x00040000U -#define RXPKTCPLMODE_SHIFT 18 -#define RXPKTCPLMODE(x) ((x) << RXPKTCPLMODE_SHIFT) -#define EGRSTATUSPAGESIZE_MASK 0x00020000U -#define EGRSTATUSPAGESIZE_SHIFT 17 -#define EGRSTATUSPAGESIZE(x) ((x) << EGRSTATUSPAGESIZE_SHIFT) -#define PKTSHIFT_MASK 0x00001c00U -#define PKTSHIFT_SHIFT 10 -#define PKTSHIFT(x) ((x) << PKTSHIFT_SHIFT) -#define PKTSHIFT_GET(x) (((x) & PKTSHIFT_MASK) >> PKTSHIFT_SHIFT) -#define INGPCIEBOUNDARY_32B_X 0 -#define INGPCIEBOUNDARY_MASK 0x00000380U -#define INGPCIEBOUNDARY_SHIFT 7 -#define INGPCIEBOUNDARY(x) ((x) << INGPCIEBOUNDARY_SHIFT) -#define INGPADBOUNDARY_MASK 0x00000070U -#define INGPADBOUNDARY_SHIFT 4 -#define INGPADBOUNDARY(x) ((x) << INGPADBOUNDARY_SHIFT) -#define INGPADBOUNDARY_GET(x) (((x) & INGPADBOUNDARY_MASK) \ - >> INGPADBOUNDARY_SHIFT) -#define INGPACKBOUNDARY_16B_X 0 -#define INGPACKBOUNDARY_SHIFT_X 5 +#define SGE_PF_KDOORBELL_A 0x0 -#define INGPACKBOUNDARY_S 16 -#define INGPACKBOUNDARY_M 0x7U -#define INGPACKBOUNDARY_V(x) ((x) << INGPACKBOUNDARY_S) -#define INGPACKBOUNDARY_G(x) (((x) >> INGPACKBOUNDARY_S) \ - & INGPACKBOUNDARY_M) -#define EGRPCIEBOUNDARY_MASK 0x0000000eU -#define EGRPCIEBOUNDARY_SHIFT 1 -#define EGRPCIEBOUNDARY(x) ((x) << EGRPCIEBOUNDARY_SHIFT) -#define GLOBALENABLE 0x00000001U +#define QID_S 15 +#define QID_V(x) ((x) << QID_S) + +#define DBPRIO_S 14 +#define DBPRIO_V(x) ((x) << DBPRIO_S) +#define DBPRIO_F DBPRIO_V(1U) + +#define PIDX_S 0 +#define PIDX_V(x) ((x) << PIDX_S) + +#define SGE_VF_KDOORBELL_A 0x0 + +#define DBTYPE_S 13 +#define DBTYPE_V(x) ((x) << DBTYPE_S) +#define DBTYPE_F DBTYPE_V(1U) + +#define PIDX_T5_S 0 +#define PIDX_T5_M 0x1fffU +#define PIDX_T5_V(x) ((x) << PIDX_T5_S) +#define PIDX_T5_G(x) (((x) >> PIDX_T5_S) & PIDX_T5_M) + +#define SGE_PF_GTS_A 0x4 -#define SGE_HOST_PAGE_SIZE 0x100c +#define INGRESSQID_S 16 +#define INGRESSQID_V(x) ((x) << INGRESSQID_S) -#define HOSTPAGESIZEPF7_MASK 0x0000000fU -#define HOSTPAGESIZEPF7_SHIFT 28 -#define HOSTPAGESIZEPF7(x) ((x) << HOSTPAGESIZEPF7_SHIFT) +#define TIMERREG_S 13 +#define TIMERREG_V(x) ((x) << TIMERREG_S) -#define HOSTPAGESIZEPF6_MASK 0x0000000fU -#define HOSTPAGESIZEPF6_SHIFT 24 -#define HOSTPAGESIZEPF6(x) ((x) << HOSTPAGESIZEPF6_SHIFT) +#define SEINTARM_S 12 +#define SEINTARM_V(x) ((x) << SEINTARM_S) -#define HOSTPAGESIZEPF5_MASK 0x0000000fU -#define HOSTPAGESIZEPF5_SHIFT 20 -#define HOSTPAGESIZEPF5(x) ((x) << HOSTPAGESIZEPF5_SHIFT) +#define CIDXINC_S 0 +#define CIDXINC_M 0xfffU +#define CIDXINC_V(x) ((x) << CIDXINC_S) -#define HOSTPAGESIZEPF4_MASK 0x0000000fU -#define HOSTPAGESIZEPF4_SHIFT 16 -#define HOSTPAGESIZEPF4(x) ((x) << HOSTPAGESIZEPF4_SHIFT) +#define SGE_CONTROL_A 0x1008 +#define SGE_CONTROL2_A 0x1124 -#define HOSTPAGESIZEPF3_MASK 0x0000000fU -#define HOSTPAGESIZEPF3_SHIFT 12 -#define HOSTPAGESIZEPF3(x) ((x) << HOSTPAGESIZEPF3_SHIFT) +#define RXPKTCPLMODE_S 18 +#define RXPKTCPLMODE_V(x) ((x) << RXPKTCPLMODE_S) +#define RXPKTCPLMODE_F RXPKTCPLMODE_V(1U) -#define HOSTPAGESIZEPF2_MASK 0x0000000fU -#define HOSTPAGESIZEPF2_SHIFT 8 -#define HOSTPAGESIZEPF2(x) ((x) << HOSTPAGESIZEPF2_SHIFT) +#define EGRSTATUSPAGESIZE_S 17 +#define EGRSTATUSPAGESIZE_V(x) ((x) << EGRSTATUSPAGESIZE_S) +#define EGRSTATUSPAGESIZE_F EGRSTATUSPAGESIZE_V(1U) -#define HOSTPAGESIZEPF1_M 0x0000000fU -#define HOSTPAGESIZEPF1_S 4 -#define HOSTPAGESIZEPF1(x) ((x) << HOSTPAGESIZEPF1_S) +#define PKTSHIFT_S 10 +#define PKTSHIFT_M 0x7U +#define PKTSHIFT_V(x) ((x) << PKTSHIFT_S) +#define PKTSHIFT_G(x) (((x) >> PKTSHIFT_S) & PKTSHIFT_M) -#define HOSTPAGESIZEPF0_M 0x0000000fU -#define HOSTPAGESIZEPF0_S 0 -#define HOSTPAGESIZEPF0(x) ((x) << HOSTPAGESIZEPF0_S) +#define INGPCIEBOUNDARY_S 7 +#define INGPCIEBOUNDARY_V(x) ((x) << INGPCIEBOUNDARY_S) -#define SGE_EGRESS_QUEUES_PER_PAGE_PF 0x1010 +#define INGPADBOUNDARY_S 4 +#define INGPADBOUNDARY_M 0x7U +#define INGPADBOUNDARY_V(x) ((x) << INGPADBOUNDARY_S) +#define INGPADBOUNDARY_G(x) (((x) >> INGPADBOUNDARY_S) & INGPADBOUNDARY_M) + +#define EGRPCIEBOUNDARY_S 1 +#define EGRPCIEBOUNDARY_V(x) ((x) << EGRPCIEBOUNDARY_S) + +#define INGPACKBOUNDARY_S 16 +#define INGPACKBOUNDARY_M 0x7U +#define INGPACKBOUNDARY_V(x) ((x) << INGPACKBOUNDARY_S) +#define INGPACKBOUNDARY_G(x) (((x) >> INGPACKBOUNDARY_S) \ + & INGPACKBOUNDARY_M) + +#define GLOBALENABLE_S 0 +#define GLOBALENABLE_V(x) ((x) << GLOBALENABLE_S) +#define GLOBALENABLE_F GLOBALENABLE_V(1U) + +#define SGE_HOST_PAGE_SIZE_A 0x100c + +#define HOSTPAGESIZEPF7_S 28 +#define HOSTPAGESIZEPF7_M 0xfU +#define HOSTPAGESIZEPF7_V(x) ((x) << HOSTPAGESIZEPF7_S) +#define HOSTPAGESIZEPF7_G(x) (((x) >> HOSTPAGESIZEPF7_S) & HOSTPAGESIZEPF7_M) + +#define HOSTPAGESIZEPF6_S 24 +#define HOSTPAGESIZEPF6_M 0xfU +#define HOSTPAGESIZEPF6_V(x) ((x) << HOSTPAGESIZEPF6_S) +#define HOSTPAGESIZEPF6_G(x) (((x) >> HOSTPAGESIZEPF6_S) & HOSTPAGESIZEPF6_M) + +#define HOSTPAGESIZEPF5_S 20 +#define HOSTPAGESIZEPF5_M 0xfU +#define HOSTPAGESIZEPF5_V(x) ((x) << HOSTPAGESIZEPF5_S) +#define HOSTPAGESIZEPF5_G(x) (((x) >> HOSTPAGESIZEPF5_S) & HOSTPAGESIZEPF5_M) + +#define HOSTPAGESIZEPF4_S 16 +#define HOSTPAGESIZEPF4_M 0xfU +#define HOSTPAGESIZEPF4_V(x) ((x) << HOSTPAGESIZEPF4_S) +#define HOSTPAGESIZEPF4_G(x) (((x) >> HOSTPAGESIZEPF4_S) & HOSTPAGESIZEPF4_M) + +#define HOSTPAGESIZEPF3_S 12 +#define HOSTPAGESIZEPF3_M 0xfU +#define HOSTPAGESIZEPF3_V(x) ((x) << HOSTPAGESIZEPF3_S) +#define HOSTPAGESIZEPF3_G(x) (((x) >> HOSTPAGESIZEPF3_S) & HOSTPAGESIZEPF3_M) + +#define HOSTPAGESIZEPF2_S 8 +#define HOSTPAGESIZEPF2_M 0xfU +#define HOSTPAGESIZEPF2_V(x) ((x) << HOSTPAGESIZEPF2_S) +#define HOSTPAGESIZEPF2_G(x) (((x) >> HOSTPAGESIZEPF2_S) & HOSTPAGESIZEPF2_M) + +#define HOSTPAGESIZEPF1_S 4 +#define HOSTPAGESIZEPF1_M 0xfU +#define HOSTPAGESIZEPF1_V(x) ((x) << HOSTPAGESIZEPF1_S) +#define HOSTPAGESIZEPF1_G(x) (((x) >> HOSTPAGESIZEPF1_S) & HOSTPAGESIZEPF1_M) + +#define HOSTPAGESIZEPF0_S 0 +#define HOSTPAGESIZEPF0_M 0xfU +#define HOSTPAGESIZEPF0_V(x) ((x) << HOSTPAGESIZEPF0_S) +#define HOSTPAGESIZEPF0_G(x) (((x) >> HOSTPAGESIZEPF0_S) & HOSTPAGESIZEPF0_M) + +#define SGE_EGRESS_QUEUES_PER_PAGE_PF_A 0x1010 #define SGE_EGRESS_QUEUES_PER_PAGE_VF_A 0x1014 #define QUEUESPERPAGEPF1_S 4 #define QUEUESPERPAGEPF0_S 0 -#define QUEUESPERPAGEPF0_MASK 0x0000000fU -#define QUEUESPERPAGEPF0_GET(x) ((x) & QUEUESPERPAGEPF0_MASK) - -#define QUEUESPERPAGEPF0 0 -#define QUEUESPERPAGEPF1 4 +#define QUEUESPERPAGEPF0_M 0xfU +#define QUEUESPERPAGEPF0_V(x) ((x) << QUEUESPERPAGEPF0_S) +#define QUEUESPERPAGEPF0_G(x) (((x) >> QUEUESPERPAGEPF0_S) & QUEUESPERPAGEPF0_M) -/* T5 and later support a new BAR2-based doorbell mechanism for Egress Queues. - * The User Doorbells are each 128 bytes in length with a Simple Doorbell at - * offsets 8x and a Write Combining single 64-byte Egress Queue Unit - * (X_IDXSIZE_UNIT) Gather Buffer interface at offset 64. For Ingress Queues, - * we have a Going To Sleep register at offsets 8x+4. - * - * As noted above, we have many instances of the Simple Doorbell and Going To - * Sleep registers at offsets 8x and 8x+4, respectively. We want to use a - * non-64-byte aligned offset for the Simple Doorbell in order to attempt to - * avoid buffering of the writes to the Simple Doorbell and we want to use a - * non-contiguous offset for the Going To Sleep writes in order to avoid - * possible combining between them. - */ -#define SGE_UDB_SIZE 128 -#define SGE_UDB_KDOORBELL 8 -#define SGE_UDB_GTS 20 -#define SGE_UDB_WCDOORBELL 64 - -#define SGE_INT_CAUSE1 0x1024 -#define SGE_INT_CAUSE2 0x1030 -#define SGE_INT_CAUSE3 0x103c -#define ERR_FLM_DBP 0x80000000U -#define ERR_FLM_IDMA1 0x40000000U -#define ERR_FLM_IDMA0 0x20000000U -#define ERR_FLM_HINT 0x10000000U -#define ERR_PCIE_ERROR3 0x08000000U -#define ERR_PCIE_ERROR2 0x04000000U -#define ERR_PCIE_ERROR1 0x02000000U -#define ERR_PCIE_ERROR0 0x01000000U -#define ERR_TIMER_ABOVE_MAX_QID 0x00800000U -#define ERR_CPL_EXCEED_IQE_SIZE 0x00400000U -#define ERR_INVALID_CIDX_INC 0x00200000U -#define ERR_ITP_TIME_PAUSED 0x00100000U -#define ERR_CPL_OPCODE_0 0x00080000U -#define ERR_DROPPED_DB 0x00040000U -#define ERR_DATA_CPL_ON_HIGH_QID1 0x00020000U -#define ERR_DATA_CPL_ON_HIGH_QID0 0x00010000U -#define ERR_BAD_DB_PIDX3 0x00008000U -#define ERR_BAD_DB_PIDX2 0x00004000U -#define ERR_BAD_DB_PIDX1 0x00002000U -#define ERR_BAD_DB_PIDX0 0x00001000U -#define ERR_ING_PCIE_CHAN 0x00000800U -#define ERR_ING_CTXT_PRIO 0x00000400U -#define ERR_EGR_CTXT_PRIO 0x00000200U -#define DBFIFO_HP_INT 0x00000100U -#define DBFIFO_LP_INT 0x00000080U -#define REG_ADDRESS_ERR 0x00000040U -#define INGRESS_SIZE_ERR 0x00000020U -#define EGRESS_SIZE_ERR 0x00000010U -#define ERR_INV_CTXT3 0x00000008U -#define ERR_INV_CTXT2 0x00000004U -#define ERR_INV_CTXT1 0x00000002U -#define ERR_INV_CTXT0 0x00000001U - -#define SGE_INT_ENABLE3 0x1040 -#define SGE_FL_BUFFER_SIZE0 0x1044 -#define SGE_FL_BUFFER_SIZE1 0x1048 -#define SGE_FL_BUFFER_SIZE2 0x104c -#define SGE_FL_BUFFER_SIZE3 0x1050 -#define SGE_FL_BUFFER_SIZE4 0x1054 -#define SGE_FL_BUFFER_SIZE5 0x1058 -#define SGE_FL_BUFFER_SIZE6 0x105c -#define SGE_FL_BUFFER_SIZE7 0x1060 -#define SGE_FL_BUFFER_SIZE8 0x1064 - -#define SGE_INGRESS_RX_THRESHOLD 0x10a0 -#define THRESHOLD_0_MASK 0x3f000000U -#define THRESHOLD_0_SHIFT 24 -#define THRESHOLD_0(x) ((x) << THRESHOLD_0_SHIFT) -#define THRESHOLD_0_GET(x) (((x) & THRESHOLD_0_MASK) >> THRESHOLD_0_SHIFT) -#define THRESHOLD_1_MASK 0x003f0000U -#define THRESHOLD_1_SHIFT 16 -#define THRESHOLD_1(x) ((x) << THRESHOLD_1_SHIFT) -#define THRESHOLD_1_GET(x) (((x) & THRESHOLD_1_MASK) >> THRESHOLD_1_SHIFT) -#define THRESHOLD_2_MASK 0x00003f00U -#define THRESHOLD_2_SHIFT 8 -#define THRESHOLD_2(x) ((x) << THRESHOLD_2_SHIFT) -#define THRESHOLD_2_GET(x) (((x) & THRESHOLD_2_MASK) >> THRESHOLD_2_SHIFT) -#define THRESHOLD_3_MASK 0x0000003fU -#define THRESHOLD_3_SHIFT 0 -#define THRESHOLD_3(x) ((x) << THRESHOLD_3_SHIFT) -#define THRESHOLD_3_GET(x) (((x) & THRESHOLD_3_MASK) >> THRESHOLD_3_SHIFT) - -#define SGE_CONM_CTRL 0x1094 -#define EGRTHRESHOLD_MASK 0x00003f00U -#define EGRTHRESHOLDshift 8 -#define EGRTHRESHOLD(x) ((x) << EGRTHRESHOLDshift) -#define EGRTHRESHOLD_GET(x) (((x) & EGRTHRESHOLD_MASK) >> EGRTHRESHOLDshift) - -#define EGRTHRESHOLDPACKING_MASK 0x3fU -#define EGRTHRESHOLDPACKING_SHIFT 14 -#define EGRTHRESHOLDPACKING(x) ((x) << EGRTHRESHOLDPACKING_SHIFT) -#define EGRTHRESHOLDPACKING_GET(x) (((x) >> EGRTHRESHOLDPACKING_SHIFT) & \ - EGRTHRESHOLDPACKING_MASK) - -#define SGE_DBFIFO_STATUS 0x10a4 -#define HP_INT_THRESH_SHIFT 28 -#define HP_INT_THRESH_MASK 0xfU -#define HP_INT_THRESH(x) ((x) << HP_INT_THRESH_SHIFT) -#define LP_INT_THRESH_SHIFT 12 -#define LP_INT_THRESH_MASK 0xfU -#define LP_INT_THRESH(x) ((x) << LP_INT_THRESH_SHIFT) - -#define SGE_DOORBELL_CONTROL 0x10a8 -#define ENABLE_DROP (1 << 13) - -#define S_NOCOALESCE 26 -#define V_NOCOALESCE(x) ((x) << S_NOCOALESCE) -#define F_NOCOALESCE V_NOCOALESCE(1U) - -#define SGE_TIMESTAMP_LO 0x1098 -#define SGE_TIMESTAMP_HI 0x109c -#define S_TSVAL 0 -#define M_TSVAL 0xfffffffU -#define GET_TSVAL(x) (((x) >> S_TSVAL) & M_TSVAL) +#define SGE_INT_CAUSE1_A 0x1024 +#define SGE_INT_CAUSE2_A 0x1030 +#define SGE_INT_CAUSE3_A 0x103c + +#define ERR_FLM_DBP_S 31 +#define ERR_FLM_DBP_V(x) ((x) << ERR_FLM_DBP_S) +#define ERR_FLM_DBP_F ERR_FLM_DBP_V(1U) + +#define ERR_FLM_IDMA1_S 30 +#define ERR_FLM_IDMA1_V(x) ((x) << ERR_FLM_IDMA1_S) +#define ERR_FLM_IDMA1_F ERR_FLM_IDMA1_V(1U) + +#define ERR_FLM_IDMA0_S 29 +#define ERR_FLM_IDMA0_V(x) ((x) << ERR_FLM_IDMA0_S) +#define ERR_FLM_IDMA0_F ERR_FLM_IDMA0_V(1U) + +#define ERR_FLM_HINT_S 28 +#define ERR_FLM_HINT_V(x) ((x) << ERR_FLM_HINT_S) +#define ERR_FLM_HINT_F ERR_FLM_HINT_V(1U) + +#define ERR_PCIE_ERROR3_S 27 +#define ERR_PCIE_ERROR3_V(x) ((x) << ERR_PCIE_ERROR3_S) +#define ERR_PCIE_ERROR3_F ERR_PCIE_ERROR3_V(1U) + +#define ERR_PCIE_ERROR2_S 26 +#define ERR_PCIE_ERROR2_V(x) ((x) << ERR_PCIE_ERROR2_S) +#define ERR_PCIE_ERROR2_F ERR_PCIE_ERROR2_V(1U) + +#define ERR_PCIE_ERROR1_S 25 +#define ERR_PCIE_ERROR1_V(x) ((x) << ERR_PCIE_ERROR1_S) +#define ERR_PCIE_ERROR1_F ERR_PCIE_ERROR1_V(1U) + +#define ERR_PCIE_ERROR0_S 24 +#define ERR_PCIE_ERROR0_V(x) ((x) << ERR_PCIE_ERROR0_S) +#define ERR_PCIE_ERROR0_F ERR_PCIE_ERROR0_V(1U) + +#define ERR_CPL_EXCEED_IQE_SIZE_S 22 +#define ERR_CPL_EXCEED_IQE_SIZE_V(x) ((x) << ERR_CPL_EXCEED_IQE_SIZE_S) +#define ERR_CPL_EXCEED_IQE_SIZE_F ERR_CPL_EXCEED_IQE_SIZE_V(1U) + +#define ERR_INVALID_CIDX_INC_S 21 +#define ERR_INVALID_CIDX_INC_V(x) ((x) << ERR_INVALID_CIDX_INC_S) +#define ERR_INVALID_CIDX_INC_F ERR_INVALID_CIDX_INC_V(1U) + +#define ERR_CPL_OPCODE_0_S 19 +#define ERR_CPL_OPCODE_0_V(x) ((x) << ERR_CPL_OPCODE_0_S) +#define ERR_CPL_OPCODE_0_F ERR_CPL_OPCODE_0_V(1U) + +#define ERR_DROPPED_DB_S 18 +#define ERR_DROPPED_DB_V(x) ((x) << ERR_DROPPED_DB_S) +#define ERR_DROPPED_DB_F ERR_DROPPED_DB_V(1U) + +#define ERR_DATA_CPL_ON_HIGH_QID1_S 17 +#define ERR_DATA_CPL_ON_HIGH_QID1_V(x) ((x) << ERR_DATA_CPL_ON_HIGH_QID1_S) +#define ERR_DATA_CPL_ON_HIGH_QID1_F ERR_DATA_CPL_ON_HIGH_QID1_V(1U) + +#define ERR_DATA_CPL_ON_HIGH_QID0_S 16 +#define ERR_DATA_CPL_ON_HIGH_QID0_V(x) ((x) << ERR_DATA_CPL_ON_HIGH_QID0_S) +#define ERR_DATA_CPL_ON_HIGH_QID0_F ERR_DATA_CPL_ON_HIGH_QID0_V(1U) + +#define ERR_BAD_DB_PIDX3_S 15 +#define ERR_BAD_DB_PIDX3_V(x) ((x) << ERR_BAD_DB_PIDX3_S) +#define ERR_BAD_DB_PIDX3_F ERR_BAD_DB_PIDX3_V(1U) + +#define ERR_BAD_DB_PIDX2_S 14 +#define ERR_BAD_DB_PIDX2_V(x) ((x) << ERR_BAD_DB_PIDX2_S) +#define ERR_BAD_DB_PIDX2_F ERR_BAD_DB_PIDX2_V(1U) + +#define ERR_BAD_DB_PIDX1_S 13 +#define ERR_BAD_DB_PIDX1_V(x) ((x) << ERR_BAD_DB_PIDX1_S) +#define ERR_BAD_DB_PIDX1_F ERR_BAD_DB_PIDX1_V(1U) + +#define ERR_BAD_DB_PIDX0_S 12 +#define ERR_BAD_DB_PIDX0_V(x) ((x) << ERR_BAD_DB_PIDX0_S) +#define ERR_BAD_DB_PIDX0_F ERR_BAD_DB_PIDX0_V(1U) + +#define ERR_ING_CTXT_PRIO_S 10 +#define ERR_ING_CTXT_PRIO_V(x) ((x) << ERR_ING_CTXT_PRIO_S) +#define ERR_ING_CTXT_PRIO_F ERR_ING_CTXT_PRIO_V(1U) + +#define ERR_EGR_CTXT_PRIO_S 9 +#define ERR_EGR_CTXT_PRIO_V(x) ((x) << ERR_EGR_CTXT_PRIO_S) +#define ERR_EGR_CTXT_PRIO_F ERR_EGR_CTXT_PRIO_V(1U) + +#define DBFIFO_HP_INT_S 8 +#define DBFIFO_HP_INT_V(x) ((x) << DBFIFO_HP_INT_S) +#define DBFIFO_HP_INT_F DBFIFO_HP_INT_V(1U) + +#define DBFIFO_LP_INT_S 7 +#define DBFIFO_LP_INT_V(x) ((x) << DBFIFO_LP_INT_S) +#define DBFIFO_LP_INT_F DBFIFO_LP_INT_V(1U) + +#define INGRESS_SIZE_ERR_S 5 +#define INGRESS_SIZE_ERR_V(x) ((x) << INGRESS_SIZE_ERR_S) +#define INGRESS_SIZE_ERR_F INGRESS_SIZE_ERR_V(1U) + +#define EGRESS_SIZE_ERR_S 4 +#define EGRESS_SIZE_ERR_V(x) ((x) << EGRESS_SIZE_ERR_S) +#define EGRESS_SIZE_ERR_F EGRESS_SIZE_ERR_V(1U) + +#define SGE_INT_ENABLE3_A 0x1040 +#define SGE_FL_BUFFER_SIZE0_A 0x1044 +#define SGE_FL_BUFFER_SIZE1_A 0x1048 +#define SGE_FL_BUFFER_SIZE2_A 0x104c +#define SGE_FL_BUFFER_SIZE3_A 0x1050 +#define SGE_FL_BUFFER_SIZE4_A 0x1054 +#define SGE_FL_BUFFER_SIZE5_A 0x1058 +#define SGE_FL_BUFFER_SIZE6_A 0x105c +#define SGE_FL_BUFFER_SIZE7_A 0x1060 +#define SGE_FL_BUFFER_SIZE8_A 0x1064 + +#define SGE_INGRESS_RX_THRESHOLD_A 0x10a0 + +#define THRESHOLD_0_S 24 +#define THRESHOLD_0_M 0x3fU +#define THRESHOLD_0_V(x) ((x) << THRESHOLD_0_S) +#define THRESHOLD_0_G(x) (((x) >> THRESHOLD_0_S) & THRESHOLD_0_M) + +#define THRESHOLD_1_S 16 +#define THRESHOLD_1_M 0x3fU +#define THRESHOLD_1_V(x) ((x) << THRESHOLD_1_S) +#define THRESHOLD_1_G(x) (((x) >> THRESHOLD_1_S) & THRESHOLD_1_M) + +#define THRESHOLD_2_S 8 +#define THRESHOLD_2_M 0x3fU +#define THRESHOLD_2_V(x) ((x) << THRESHOLD_2_S) +#define THRESHOLD_2_G(x) (((x) >> THRESHOLD_2_S) & THRESHOLD_2_M) + +#define THRESHOLD_3_S 0 +#define THRESHOLD_3_M 0x3fU +#define THRESHOLD_3_V(x) ((x) << THRESHOLD_3_S) +#define THRESHOLD_3_G(x) (((x) >> THRESHOLD_3_S) & THRESHOLD_3_M) + +#define SGE_CONM_CTRL_A 0x1094 + +#define EGRTHRESHOLD_S 8 +#define EGRTHRESHOLD_M 0x3fU +#define EGRTHRESHOLD_V(x) ((x) << EGRTHRESHOLD_S) +#define EGRTHRESHOLD_G(x) (((x) >> EGRTHRESHOLD_S) & EGRTHRESHOLD_M) + +#define EGRTHRESHOLDPACKING_S 14 +#define EGRTHRESHOLDPACKING_M 0x3fU +#define EGRTHRESHOLDPACKING_V(x) ((x) << EGRTHRESHOLDPACKING_S) +#define EGRTHRESHOLDPACKING_G(x) \ + (((x) >> EGRTHRESHOLDPACKING_S) & EGRTHRESHOLDPACKING_M) + +#define SGE_TIMESTAMP_LO_A 0x1098 +#define SGE_TIMESTAMP_HI_A 0x109c + +#define TSOP_S 28 +#define TSOP_M 0x3U +#define TSOP_V(x) ((x) << TSOP_S) +#define TSOP_G(x) (((x) >> TSOP_S) & TSOP_M) + +#define TSVAL_S 0 +#define TSVAL_M 0xfffffffU +#define TSVAL_V(x) ((x) << TSVAL_S) +#define TSVAL_G(x) (((x) >> TSVAL_S) & TSVAL_M) + +#define SGE_DBFIFO_STATUS_A 0x10a4 + +#define HP_INT_THRESH_S 28 +#define HP_INT_THRESH_M 0xfU +#define HP_INT_THRESH_V(x) ((x) << HP_INT_THRESH_S) + +#define LP_INT_THRESH_S 12 +#define LP_INT_THRESH_M 0xfU +#define LP_INT_THRESH_V(x) ((x) << LP_INT_THRESH_S) + +#define SGE_DOORBELL_CONTROL_A 0x10a8 + +#define NOCOALESCE_S 26 +#define NOCOALESCE_V(x) ((x) << NOCOALESCE_S) +#define NOCOALESCE_F NOCOALESCE_V(1U) + +#define ENABLE_DROP_S 13 +#define ENABLE_DROP_V(x) ((x) << ENABLE_DROP_S) +#define ENABLE_DROP_F ENABLE_DROP_V(1U) #define SGE_TIMER_VALUE_0_AND_1 0x10b8 #define TIMERVALUE0_MASK 0xffff0000U diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h new file mode 100644 index 0000000..7699686 --- /dev/null +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h @@ -0,0 +1,81 @@ +/* + * This file is part of the Chelsio T4 Ethernet driver for Linux. + * + * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * 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. + * + * 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. + */ + +#ifndef __T4_VALUES_H__ +#define __T4_VALUES_H__ + +/* This file contains definitions for various T4 register value hardware + * constants. The types of values encoded here are predominantly those for + * register fields which control "modal" behavior. For the most part, we do + * not include definitions for register fields which are simple numeric + * metrics, etc. + */ + +/* SGE register field values. + */ + +/* CONTROL1 register */ +#define RXPKTCPLMODE_SPLIT_X 1 + +#define INGPCIEBOUNDARY_SHIFT_X 5 +#define INGPCIEBOUNDARY_32B_X 0 + +#define INGPADBOUNDARY_SHIFT_X 5 + +/* CONTROL2 register */ +#define INGPACKBOUNDARY_SHIFT_X 5 +#define INGPACKBOUNDARY_16B_X 0 + +/* GTS register */ +#define SGE_TIMERREGS 6 + +/* T5 and later support a new BAR2-based doorbell mechanism for Egress Queues. + * The User Doorbells are each 128 bytes in length with a Simple Doorbell at + * offsets 8x and a Write Combining single 64-byte Egress Queue Unit + * (IDXSIZE_UNIT_X) Gather Buffer interface at offset 64. For Ingress Queues, + * we have a Going To Sleep register at offsets 8x+4. + * + * As noted above, we have many instances of the Simple Doorbell and Going To + * Sleep registers at offsets 8x and 8x+4, respectively. We want to use a + * non-64-byte aligned offset for the Simple Doorbell in order to attempt to + * avoid buffering of the writes to the Simple Doorbell and we want to use a + * non-contiguous offset for the Going To Sleep writes in order to avoid + * possible combining between them. + */ +#define SGE_UDB_SIZE 128 +#define SGE_UDB_KDOORBELL 8 +#define SGE_UDB_GTS 20 +#define SGE_UDB_WCDOORBELL 64 + +#endif /* __T4_VALUES_H__ */ diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index 2215d43..b645e33 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -380,9 +380,9 @@ static void qenable(struct sge_rspq *rspq) * enable interrupts. */ t4_write_reg(rspq->adapter, T4VF_SGE_BASE_ADDR + SGE_VF_GTS, - CIDXINC(0) | - SEINTARM(rspq->intr_params) | - INGRESSQID(rspq->cntxt_id)); + CIDXINC_V(0) | + SEINTARM_V(rspq->intr_params) | + INGRESSQID_V(rspq->cntxt_id)); } /* @@ -403,9 +403,9 @@ static void enable_rx(struct adapter *adapter) */ if (adapter->flags & USING_MSI) t4_write_reg(adapter, T4VF_SGE_BASE_ADDR + SGE_VF_GTS, - CIDXINC(0) | - SEINTARM(s->intrq.intr_params) | - INGRESSQID(s->intrq.cntxt_id)); + CIDXINC_V(0) | + SEINTARM_V(s->intrq.intr_params) | + INGRESSQID_V(s->intrq.cntxt_id)); } @@ -2306,14 +2306,10 @@ static int adap_init0(struct adapter *adapter) s->timer_val[5] = core_ticks_to_us(adapter, TIMERVALUE1_GET(sge_params->sge_timer_value_4_and_5)); - s->counter_val[0] = - THRESHOLD_0_GET(sge_params->sge_ingress_rx_threshold); - s->counter_val[1] = - THRESHOLD_1_GET(sge_params->sge_ingress_rx_threshold); - s->counter_val[2] = - THRESHOLD_2_GET(sge_params->sge_ingress_rx_threshold); - s->counter_val[3] = - THRESHOLD_3_GET(sge_params->sge_ingress_rx_threshold); + s->counter_val[0] = THRESHOLD_0_G(sge_params->sge_ingress_rx_threshold); + s->counter_val[1] = THRESHOLD_1_G(sge_params->sge_ingress_rx_threshold); + s->counter_val[2] = THRESHOLD_2_G(sge_params->sge_ingress_rx_threshold); + s->counter_val[3] = THRESHOLD_3_G(sge_params->sge_ingress_rx_threshold); /* * Grab our Virtual Interface resource allocation, extract the diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c index f7fd131..ef4da3e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c @@ -47,6 +47,7 @@ #include "t4vf_defs.h" #include "../cxgb4/t4_regs.h" +#include "../cxgb4/t4_values.h" #include "../cxgb4/t4fw_api.h" #include "../cxgb4/t4_msg.h" @@ -531,11 +532,11 @@ static inline void ring_fl_db(struct adapter *adapter, struct sge_fl *fl) */ if (fl->pend_cred >= FL_PER_EQ_UNIT) { if (is_t4(adapter->params.chip)) - val = PIDX(fl->pend_cred / FL_PER_EQ_UNIT); + val = PIDX_V(fl->pend_cred / FL_PER_EQ_UNIT); else - val = PIDX_T5(fl->pend_cred / FL_PER_EQ_UNIT) | - DBTYPE(1); - val |= DBPRIO(1); + val = PIDX_T5_V(fl->pend_cred / FL_PER_EQ_UNIT) | + DBTYPE_F; + val |= DBPRIO_F; /* Make sure all memory writes to the Free List queue are * committed before we tell the hardware about them. @@ -549,9 +550,9 @@ static inline void ring_fl_db(struct adapter *adapter, struct sge_fl *fl) if (unlikely(fl->bar2_addr == NULL)) { t4_write_reg(adapter, T4VF_SGE_BASE_ADDR + SGE_VF_KDOORBELL, - QID(fl->cntxt_id) | val); + QID_V(fl->cntxt_id) | val); } else { - writel(val | QID(fl->bar2_qid), + writel(val | QID_V(fl->bar2_qid), fl->bar2_addr + SGE_UDB_KDOORBELL); /* This Write memory Barrier will force the write to @@ -979,12 +980,12 @@ static inline void ring_tx_db(struct adapter *adapter, struct sge_txq *tq, * doorbell mechanism; otherwise use the new BAR2 mechanism. */ if (unlikely(tq->bar2_addr == NULL)) { - u32 val = PIDX(n); + u32 val = PIDX_V(n); t4_write_reg(adapter, T4VF_SGE_BASE_ADDR + SGE_VF_KDOORBELL, - QID(tq->cntxt_id) | val); + QID_V(tq->cntxt_id) | val); } else { - u32 val = PIDX_T5(n); + u32 val = PIDX_T5_V(n); /* T4 and later chips share the same PIDX field offset within * the doorbell, but T5 and later shrank the field in order to @@ -992,7 +993,7 @@ static inline void ring_tx_db(struct adapter *adapter, struct sge_txq *tq, * large in the first place (14 bits) so we just use the T5 * and later limits and warn if a Queue ID is too large. */ - WARN_ON(val & DBPRIO(1)); + WARN_ON(val & DBPRIO_F); /* If we're only writing a single Egress Unit and the BAR2 * Queue ID is 0, we can use the Write Combining Doorbell @@ -1023,7 +1024,7 @@ static inline void ring_tx_db(struct adapter *adapter, struct sge_txq *tq, count--; } } else - writel(val | QID(tq->bar2_qid), + writel(val | QID_V(tq->bar2_qid), tq->bar2_addr + SGE_UDB_KDOORBELL); /* This Write Memory Barrier will force the write to the User @@ -1875,13 +1876,13 @@ static int napi_rx_handler(struct napi_struct *napi, int budget) if (unlikely(work_done == 0)) rspq->unhandled_irqs++; - val = CIDXINC(work_done) | SEINTARM(intr_params); + val = CIDXINC_V(work_done) | SEINTARM_V(intr_params); if (is_t4(rspq->adapter->params.chip)) { t4_write_reg(rspq->adapter, T4VF_SGE_BASE_ADDR + SGE_VF_GTS, - val | INGRESSQID((u32)rspq->cntxt_id)); + val | INGRESSQID_V((u32)rspq->cntxt_id)); } else { - writel(val | INGRESSQID(rspq->bar2_qid), + writel(val | INGRESSQID_V(rspq->bar2_qid), rspq->bar2_addr + SGE_UDB_GTS); wmb(); } @@ -1975,12 +1976,12 @@ static unsigned int process_intrq(struct adapter *adapter) rspq_next(intrq); } - val = CIDXINC(work_done) | SEINTARM(intrq->intr_params); + val = CIDXINC_V(work_done) | SEINTARM_V(intrq->intr_params); if (is_t4(adapter->params.chip)) t4_write_reg(adapter, T4VF_SGE_BASE_ADDR + SGE_VF_GTS, - val | INGRESSQID(intrq->cntxt_id)); + val | INGRESSQID_V(intrq->cntxt_id)); else { - writel(val | INGRESSQID(intrq->bar2_qid), + writel(val | INGRESSQID_V(intrq->bar2_qid), intrq->bar2_addr + SGE_UDB_GTS); wmb(); } @@ -2583,7 +2584,7 @@ int t4vf_sge_init(struct adapter *adapter) fl0, fl1); return -EINVAL; } - if ((sge_params->sge_control & RXPKTCPLMODE_MASK) == 0) { + if ((sge_params->sge_control & RXPKTCPLMODE_F) == 0) { dev_err(adapter->pdev_dev, "bad SGE CPL MODE\n"); return -EINVAL; } @@ -2593,9 +2594,9 @@ int t4vf_sge_init(struct adapter *adapter) */ if (fl1) s->fl_pg_order = ilog2(fl1) - PAGE_SHIFT; - s->stat_len = ((sge_params->sge_control & EGRSTATUSPAGESIZE_MASK) + s->stat_len = ((sge_params->sge_control & EGRSTATUSPAGESIZE_F) ? 128 : 64); - s->pktshift = PKTSHIFT_GET(sge_params->sge_control); + s->pktshift = PKTSHIFT_G(sge_params->sge_control); /* T4 uses a single control field to specify both the PCIe Padding and * Packing Boundary. T5 introduced the ability to specify these @@ -2607,8 +2608,8 @@ int t4vf_sge_init(struct adapter *adapter) * end doing this because it would initialize the Padding Boundary and * leave the Packing Boundary initialized to 0 (16 bytes).) */ - ingpadboundary = 1 << (INGPADBOUNDARY_GET(sge_params->sge_control) + - X_INGPADBOUNDARY_SHIFT); + ingpadboundary = 1 << (INGPADBOUNDARY_G(sge_params->sge_control) + + INGPADBOUNDARY_SHIFT_X); if (is_t4(adapter->params.chip)) { s->fl_align = ingpadboundary; } else { @@ -2633,7 +2634,7 @@ int t4vf_sge_init(struct adapter *adapter) * Congestion Threshold is in units of 2 Free List pointers.) */ s->fl_starve_thres - = EGRTHRESHOLD_GET(sge_params->sge_congestion_control)*2 + 1; + = EGRTHRESHOLD_G(sge_params->sge_congestion_control)*2 + 1; /* * Set up tasklet timers. diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c index 21dc9a2..6929d1f 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c @@ -39,6 +39,7 @@ #include "t4vf_defs.h" #include "../cxgb4/t4_regs.h" +#include "../cxgb4/t4_values.h" #include "../cxgb4/t4fw_api.h" /* @@ -528,13 +529,13 @@ int t4vf_get_sge_params(struct adapter *adapter) int v; params[0] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_CONTROL)); + FW_PARAMS_PARAM_XYZ_V(SGE_CONTROL_A)); params[1] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_HOST_PAGE_SIZE)); + FW_PARAMS_PARAM_XYZ_V(SGE_HOST_PAGE_SIZE_A)); params[2] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_FL_BUFFER_SIZE0)); + FW_PARAMS_PARAM_XYZ_V(SGE_FL_BUFFER_SIZE0_A)); params[3] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_FL_BUFFER_SIZE1)); + FW_PARAMS_PARAM_XYZ_V(SGE_FL_BUFFER_SIZE1_A)); params[4] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | FW_PARAMS_PARAM_XYZ_V(SGE_TIMER_VALUE_0_AND_1)); params[5] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | @@ -576,9 +577,9 @@ int t4vf_get_sge_params(struct adapter *adapter) } params[0] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_INGRESS_RX_THRESHOLD)); + FW_PARAMS_PARAM_XYZ_V(SGE_INGRESS_RX_THRESHOLD_A)); params[1] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_CONM_CTRL)); + FW_PARAMS_PARAM_XYZ_V(SGE_CONM_CTRL_A)); v = t4vf_query_params(adapter, 2, params, vals); if (v) return v; @@ -628,10 +629,10 @@ int t4vf_get_sge_params(struct adapter *adapter) (QUEUESPERPAGEPF1_S - QUEUESPERPAGEPF0_S) * pf); sge_params->sge_vf_eq_qpp = ((sge_params->sge_egress_queues_per_page >> s_qpp) - & QUEUESPERPAGEPF0_MASK); + & QUEUESPERPAGEPF0_M); sge_params->sge_vf_iq_qpp = ((sge_params->sge_ingress_queues_per_page >> s_qpp) - & QUEUESPERPAGEPF0_MASK); + & QUEUESPERPAGEPF0_M); } return 0; diff --git a/drivers/scsi/csiostor/csio_hw.c b/drivers/scsi/csiostor/csio_hw.c index 9ab997e..890d93a 100644 --- a/drivers/scsi/csiostor/csio_hw.c +++ b/drivers/scsi/csiostor/csio_hw.c @@ -2256,15 +2256,15 @@ csio_hw_intr_enable(struct csio_hw *hw) pl &= (~SF); csio_wr_reg32(hw, pl, PL_INT_ENABLE); - csio_wr_reg32(hw, ERR_CPL_EXCEED_IQE_SIZE | - EGRESS_SIZE_ERR | ERR_INVALID_CIDX_INC | - ERR_CPL_OPCODE_0 | ERR_DROPPED_DB | - ERR_DATA_CPL_ON_HIGH_QID1 | - ERR_DATA_CPL_ON_HIGH_QID0 | ERR_BAD_DB_PIDX3 | - ERR_BAD_DB_PIDX2 | ERR_BAD_DB_PIDX1 | - ERR_BAD_DB_PIDX0 | ERR_ING_CTXT_PRIO | - ERR_EGR_CTXT_PRIO | INGRESS_SIZE_ERR, - SGE_INT_ENABLE3); + csio_wr_reg32(hw, ERR_CPL_EXCEED_IQE_SIZE_F | + EGRESS_SIZE_ERR_F | ERR_INVALID_CIDX_INC_F | + ERR_CPL_OPCODE_0_F | ERR_DROPPED_DB_F | + ERR_DATA_CPL_ON_HIGH_QID1_F | + ERR_DATA_CPL_ON_HIGH_QID0_F | ERR_BAD_DB_PIDX3_F | + ERR_BAD_DB_PIDX2_F | ERR_BAD_DB_PIDX1_F | + ERR_BAD_DB_PIDX0_F | ERR_ING_CTXT_PRIO_F | + ERR_EGR_CTXT_PRIO_F | INGRESS_SIZE_ERR_F, + SGE_INT_ENABLE3_A); csio_set_reg_field(hw, PL_INT_MAP0, 0, 1 << pf); } @@ -2300,7 +2300,7 @@ csio_hw_intr_disable(struct csio_hw *hw) void csio_hw_fatal_err(struct csio_hw *hw) { - csio_set_reg_field(hw, SGE_CONTROL, GLOBALENABLE, 0); + csio_set_reg_field(hw, SGE_CONTROL_A, GLOBALENABLE_F, 0); csio_hw_intr_disable(hw); /* Do not reset HW, we may need FW state for debugging */ @@ -2698,44 +2698,44 @@ static void csio_sge_intr_handler(struct csio_hw *hw) uint64_t v; static struct intr_info sge_intr_info[] = { - { ERR_CPL_EXCEED_IQE_SIZE, + { ERR_CPL_EXCEED_IQE_SIZE_F, "SGE received CPL exceeding IQE size", -1, 1 }, - { ERR_INVALID_CIDX_INC, + { ERR_INVALID_CIDX_INC_F, "SGE GTS CIDX increment too large", -1, 0 }, - { ERR_CPL_OPCODE_0, "SGE received 0-length CPL", -1, 0 }, - { ERR_DROPPED_DB, "SGE doorbell dropped", -1, 0 }, - { ERR_DATA_CPL_ON_HIGH_QID1 | ERR_DATA_CPL_ON_HIGH_QID0, + { ERR_CPL_OPCODE_0_F, "SGE received 0-length CPL", -1, 0 }, + { ERR_DROPPED_DB_F, "SGE doorbell dropped", -1, 0 }, + { ERR_DATA_CPL_ON_HIGH_QID1_F | ERR_DATA_CPL_ON_HIGH_QID0_F, "SGE IQID > 1023 received CPL for FL", -1, 0 }, - { ERR_BAD_DB_PIDX3, "SGE DBP 3 pidx increment too large", -1, + { ERR_BAD_DB_PIDX3_F, "SGE DBP 3 pidx increment too large", -1, 0 }, - { ERR_BAD_DB_PIDX2, "SGE DBP 2 pidx increment too large", -1, + { ERR_BAD_DB_PIDX2_F, "SGE DBP 2 pidx increment too large", -1, 0 }, - { ERR_BAD_DB_PIDX1, "SGE DBP 1 pidx increment too large", -1, + { ERR_BAD_DB_PIDX1_F, "SGE DBP 1 pidx increment too large", -1, 0 }, - { ERR_BAD_DB_PIDX0, "SGE DBP 0 pidx increment too large", -1, + { ERR_BAD_DB_PIDX0_F, "SGE DBP 0 pidx increment too large", -1, 0 }, - { ERR_ING_CTXT_PRIO, + { ERR_ING_CTXT_PRIO_F, "SGE too many priority ingress contexts", -1, 0 }, - { ERR_EGR_CTXT_PRIO, + { ERR_EGR_CTXT_PRIO_F, "SGE too many priority egress contexts", -1, 0 }, - { INGRESS_SIZE_ERR, "SGE illegal ingress QID", -1, 0 }, - { EGRESS_SIZE_ERR, "SGE illegal egress QID", -1, 0 }, + { INGRESS_SIZE_ERR_F, "SGE illegal ingress QID", -1, 0 }, + { EGRESS_SIZE_ERR_F, "SGE illegal egress QID", -1, 0 }, { 0, NULL, 0, 0 } }; - v = (uint64_t)csio_rd_reg32(hw, SGE_INT_CAUSE1) | - ((uint64_t)csio_rd_reg32(hw, SGE_INT_CAUSE2) << 32); + v = (uint64_t)csio_rd_reg32(hw, SGE_INT_CAUSE1_A) | + ((uint64_t)csio_rd_reg32(hw, SGE_INT_CAUSE2_A) << 32); if (v) { csio_fatal(hw, "SGE parity error (%#llx)\n", (unsigned long long)v); csio_wr_reg32(hw, (uint32_t)(v & 0xFFFFFFFF), - SGE_INT_CAUSE1); - csio_wr_reg32(hw, (uint32_t)(v >> 32), SGE_INT_CAUSE2); + SGE_INT_CAUSE1_A); + csio_wr_reg32(hw, (uint32_t)(v >> 32), SGE_INT_CAUSE2_A); } - v |= csio_handle_intr_status(hw, SGE_INT_CAUSE3, sge_intr_info); + v |= csio_handle_intr_status(hw, SGE_INT_CAUSE3_A, sge_intr_info); - if (csio_handle_intr_status(hw, SGE_INT_CAUSE3, sge_intr_info) || + if (csio_handle_intr_status(hw, SGE_INT_CAUSE3_A, sge_intr_info) || v != 0) csio_hw_fatal_err(hw); } diff --git a/drivers/scsi/csiostor/csio_hw_chip.h b/drivers/scsi/csiostor/csio_hw_chip.h index 4752fed..3bc9cf4 100644 --- a/drivers/scsi/csiostor/csio_hw_chip.h +++ b/drivers/scsi/csiostor/csio_hw_chip.h @@ -66,15 +66,15 @@ static inline int csio_is_t5(uint16_t chip) { PCI_VENDOR_ID_CHELSIO, (devid), PCI_ANY_ID, PCI_ANY_ID, 0, 0, (idx) } #define CSIO_HW_PIDX(hw, index) \ - (csio_is_t4(hw->chip_id) ? (PIDX(index)) : \ - (PIDX_T5(index) | DBTYPE(1U))) + (csio_is_t4(hw->chip_id) ? (PIDX_V(index)) : \ + (PIDX_T5_G(index) | DBTYPE_F)) #define CSIO_HW_LP_INT_THRESH(hw, val) \ - (csio_is_t4(hw->chip_id) ? (LP_INT_THRESH(val)) : \ + (csio_is_t4(hw->chip_id) ? (LP_INT_THRESH_V(val)) : \ (V_LP_INT_THRESH_T5(val))) #define CSIO_HW_M_LP_INT_THRESH(hw) \ - (csio_is_t4(hw->chip_id) ? (LP_INT_THRESH_MASK) : (M_LP_INT_THRESH_T5)) + (csio_is_t4(hw->chip_id) ? (LP_INT_THRESH_M) : (M_LP_INT_THRESH_T5)) #define CSIO_MAC_INT_CAUSE_REG(hw, port) \ (csio_is_t4(hw->chip_id) ? (PORT_REG(port, XGMAC_PORT_INT_CAUSE)) : \ diff --git a/drivers/scsi/csiostor/csio_wr.c b/drivers/scsi/csiostor/csio_wr.c index 773da14..221433f 100644 --- a/drivers/scsi/csiostor/csio_wr.c +++ b/drivers/scsi/csiostor/csio_wr.c @@ -51,12 +51,12 @@ int csio_intr_coalesce_time = 10; /* value:SGE_TIMER_VALUE_1 */ static int csio_sge_timer_reg = 1; #define CSIO_SET_FLBUF_SIZE(_hw, _reg, _val) \ - csio_wr_reg32((_hw), (_val), SGE_FL_BUFFER_SIZE##_reg) + csio_wr_reg32((_hw), (_val), SGE_FL_BUFFER_SIZE##_reg##_A) static void csio_get_flbuf_size(struct csio_hw *hw, struct csio_sge *sge, uint32_t reg) { - sge->sge_fl_buf_size[reg] = csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE0 + + sge->sge_fl_buf_size[reg] = csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE0_A + reg * sizeof(uint32_t)); } @@ -71,7 +71,7 @@ csio_wr_fl_bufsz(struct csio_sge *sge, struct csio_dma_buf *buf) static inline uint32_t csio_wr_qstat_pgsz(struct csio_hw *hw) { - return (hw->wrm.sge.sge_control & EGRSTATUSPAGESIZE(1)) ? 128 : 64; + return (hw->wrm.sge.sge_control & EGRSTATUSPAGESIZE_F) ? 128 : 64; } /* Ring freelist doorbell */ @@ -84,9 +84,9 @@ csio_wr_ring_fldb(struct csio_hw *hw, struct csio_q *flq) * 8 freelist buffer pointers (since each pointer is 8 bytes). */ if (flq->inc_idx >= 8) { - csio_wr_reg32(hw, DBPRIO(1) | QID(flq->un.fl.flid) | + csio_wr_reg32(hw, DBPRIO_F | QID_V(flq->un.fl.flid) | CSIO_HW_PIDX(hw, flq->inc_idx / 8), - MYPF_REG(SGE_PF_KDOORBELL)); + MYPF_REG(SGE_PF_KDOORBELL_A)); flq->inc_idx &= 7; } } @@ -95,10 +95,10 @@ csio_wr_ring_fldb(struct csio_hw *hw, struct csio_q *flq) static void csio_wr_sge_intr_enable(struct csio_hw *hw, uint16_t iqid) { - csio_wr_reg32(hw, CIDXINC(0) | - INGRESSQID(iqid) | - TIMERREG(X_TIMERREG_RESTART_COUNTER), - MYPF_REG(SGE_PF_GTS)); + csio_wr_reg32(hw, CIDXINC_V(0) | + INGRESSQID_V(iqid) | + TIMERREG_V(X_TIMERREG_RESTART_COUNTER), + MYPF_REG(SGE_PF_GTS_A)); } /* @@ -982,9 +982,9 @@ csio_wr_issue(struct csio_hw *hw, int qidx, bool prio) wmb(); /* Ring SGE Doorbell writing q->pidx into it */ - csio_wr_reg32(hw, DBPRIO(prio) | QID(q->un.eq.physeqid) | + csio_wr_reg32(hw, DBPRIO_V(prio) | QID_V(q->un.eq.physeqid) | CSIO_HW_PIDX(hw, q->inc_idx), - MYPF_REG(SGE_PF_KDOORBELL)); + MYPF_REG(SGE_PF_KDOORBELL_A)); q->inc_idx = 0; return 0; @@ -1242,10 +1242,10 @@ csio_wr_process_iq(struct csio_hw *hw, struct csio_q *q, restart: /* Now inform SGE about our incremental index value */ - csio_wr_reg32(hw, CIDXINC(q->inc_idx) | - INGRESSQID(q->un.iq.physiqid) | - TIMERREG(csio_sge_timer_reg), - MYPF_REG(SGE_PF_GTS)); + csio_wr_reg32(hw, CIDXINC_V(q->inc_idx) | + INGRESSQID_V(q->un.iq.physiqid) | + TIMERREG_V(csio_sge_timer_reg), + MYPF_REG(SGE_PF_GTS_A)); q->stats.n_tot_rsps += q->inc_idx; q->inc_idx = 0; @@ -1310,22 +1310,23 @@ csio_wr_fixup_host_params(struct csio_hw *hw) uint32_t ingpad = 0; uint32_t stat_len = clsz > 64 ? 128 : 64; - csio_wr_reg32(hw, HOSTPAGESIZEPF0(s_hps) | HOSTPAGESIZEPF1(s_hps) | - HOSTPAGESIZEPF2(s_hps) | HOSTPAGESIZEPF3(s_hps) | - HOSTPAGESIZEPF4(s_hps) | HOSTPAGESIZEPF5(s_hps) | - HOSTPAGESIZEPF6(s_hps) | HOSTPAGESIZEPF7(s_hps), - SGE_HOST_PAGE_SIZE); + csio_wr_reg32(hw, HOSTPAGESIZEPF0_V(s_hps) | HOSTPAGESIZEPF1_V(s_hps) | + HOSTPAGESIZEPF2_V(s_hps) | HOSTPAGESIZEPF3_V(s_hps) | + HOSTPAGESIZEPF4_V(s_hps) | HOSTPAGESIZEPF5_V(s_hps) | + HOSTPAGESIZEPF6_V(s_hps) | HOSTPAGESIZEPF7_V(s_hps), + SGE_HOST_PAGE_SIZE_A); sge->csio_fl_align = clsz < 32 ? 32 : clsz; ingpad = ilog2(sge->csio_fl_align) - 5; - csio_set_reg_field(hw, SGE_CONTROL, INGPADBOUNDARY_MASK | - EGRSTATUSPAGESIZE(1), - INGPADBOUNDARY(ingpad) | - EGRSTATUSPAGESIZE(stat_len != 64)); + csio_set_reg_field(hw, SGE_CONTROL_A, + INGPADBOUNDARY_V(INGPADBOUNDARY_M) | + EGRSTATUSPAGESIZE_F, + INGPADBOUNDARY_V(ingpad) | + EGRSTATUSPAGESIZE_V(stat_len != 64)); /* FL BUFFER SIZE#0 is Page size i,e already aligned to cache line */ - csio_wr_reg32(hw, PAGE_SIZE, SGE_FL_BUFFER_SIZE0); + csio_wr_reg32(hw, PAGE_SIZE, SGE_FL_BUFFER_SIZE0_A); /* * If using hard params, the following will get set correctly @@ -1333,20 +1334,21 @@ csio_wr_fixup_host_params(struct csio_hw *hw) */ if (hw->flags & CSIO_HWF_USING_SOFT_PARAMS) { csio_wr_reg32(hw, - (csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE2) + + (csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE2_A) + sge->csio_fl_align - 1) & ~(sge->csio_fl_align - 1), - SGE_FL_BUFFER_SIZE2); + SGE_FL_BUFFER_SIZE2_A); csio_wr_reg32(hw, - (csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE3) + + (csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE3_A) + sge->csio_fl_align - 1) & ~(sge->csio_fl_align - 1), - SGE_FL_BUFFER_SIZE3); + SGE_FL_BUFFER_SIZE3_A); } csio_wr_reg32(hw, HPZ0(PAGE_SHIFT - 12), ULP_RX_TDDP_PSZ); /* default value of rx_dma_offset of the NIC driver */ - csio_set_reg_field(hw, SGE_CONTROL, PKTSHIFT_MASK, - PKTSHIFT(CSIO_SGE_RX_DMA_OFFSET)); + csio_set_reg_field(hw, SGE_CONTROL_A, + PKTSHIFT_V(PKTSHIFT_M), + PKTSHIFT_V(CSIO_SGE_RX_DMA_OFFSET)); csio_hw_tp_wr_bits_indirect(hw, TP_INGRESS_CONFIG, CSUM_HAS_PSEUDO_HDR, 0); @@ -1384,9 +1386,9 @@ csio_wr_get_sge(struct csio_hw *hw) u32 timer_value_0_and_1, timer_value_2_and_3, timer_value_4_and_5; u32 ingress_rx_threshold; - sge->sge_control = csio_rd_reg32(hw, SGE_CONTROL); + sge->sge_control = csio_rd_reg32(hw, SGE_CONTROL_A); - ingpad = INGPADBOUNDARY_GET(sge->sge_control); + ingpad = INGPADBOUNDARY_G(sge->sge_control); switch (ingpad) { case X_INGPCIEBOUNDARY_32B: @@ -1427,11 +1429,11 @@ csio_wr_get_sge(struct csio_hw *hw) sge->timer_val[5] = (uint16_t)csio_core_ticks_to_us(hw, TIMERVALUE5_GET(timer_value_4_and_5)); - ingress_rx_threshold = csio_rd_reg32(hw, SGE_INGRESS_RX_THRESHOLD); - sge->counter_val[0] = THRESHOLD_0_GET(ingress_rx_threshold); - sge->counter_val[1] = THRESHOLD_1_GET(ingress_rx_threshold); - sge->counter_val[2] = THRESHOLD_2_GET(ingress_rx_threshold); - sge->counter_val[3] = THRESHOLD_3_GET(ingress_rx_threshold); + ingress_rx_threshold = csio_rd_reg32(hw, SGE_INGRESS_RX_THRESHOLD_A); + sge->counter_val[0] = THRESHOLD_0_G(ingress_rx_threshold); + sge->counter_val[1] = THRESHOLD_1_G(ingress_rx_threshold); + sge->counter_val[2] = THRESHOLD_2_G(ingress_rx_threshold); + sge->counter_val[3] = THRESHOLD_3_G(ingress_rx_threshold); csio_init_intr_coalesce_parms(hw); } @@ -1454,9 +1456,9 @@ csio_wr_set_sge(struct csio_hw *hw) * Set up our basic SGE mode to deliver CPL messages to our Ingress * Queue and Packet Date to the Free List. */ - csio_set_reg_field(hw, SGE_CONTROL, RXPKTCPLMODE(1), RXPKTCPLMODE(1)); + csio_set_reg_field(hw, SGE_CONTROL_A, RXPKTCPLMODE_F, RXPKTCPLMODE_F); - sge->sge_control = csio_rd_reg32(hw, SGE_CONTROL); + sge->sge_control = csio_rd_reg32(hw, SGE_CONTROL_A); /* sge->csio_fl_align is set up by csio_wr_fixup_host_params(). */ @@ -1464,22 +1466,24 @@ csio_wr_set_sge(struct csio_hw *hw) * Set up to drop DOORBELL writes when the DOORBELL FIFO overflows * and generate an interrupt when this occurs so we can recover. */ - csio_set_reg_field(hw, SGE_DBFIFO_STATUS, - HP_INT_THRESH(HP_INT_THRESH_MASK) | - CSIO_HW_LP_INT_THRESH(hw, CSIO_HW_M_LP_INT_THRESH(hw)), - HP_INT_THRESH(CSIO_SGE_DBFIFO_INT_THRESH) | - CSIO_HW_LP_INT_THRESH(hw, CSIO_SGE_DBFIFO_INT_THRESH)); + csio_set_reg_field(hw, SGE_DBFIFO_STATUS_A, + HP_INT_THRESH_V(HP_INT_THRESH_M) | + CSIO_HW_LP_INT_THRESH(hw, + CSIO_HW_M_LP_INT_THRESH(hw)), + HP_INT_THRESH_V(CSIO_SGE_DBFIFO_INT_THRESH) | + CSIO_HW_LP_INT_THRESH(hw, + CSIO_SGE_DBFIFO_INT_THRESH)); - csio_set_reg_field(hw, SGE_DOORBELL_CONTROL, ENABLE_DROP, - ENABLE_DROP); + csio_set_reg_field(hw, SGE_DOORBELL_CONTROL_A, ENABLE_DROP_F, + ENABLE_DROP_F); /* SGE_FL_BUFFER_SIZE0 is set up by csio_wr_fixup_host_params(). */ CSIO_SET_FLBUF_SIZE(hw, 1, CSIO_SGE_FLBUF_SIZE1); csio_wr_reg32(hw, (CSIO_SGE_FLBUF_SIZE2 + sge->csio_fl_align - 1) - & ~(sge->csio_fl_align - 1), SGE_FL_BUFFER_SIZE2); + & ~(sge->csio_fl_align - 1), SGE_FL_BUFFER_SIZE2_A); csio_wr_reg32(hw, (CSIO_SGE_FLBUF_SIZE3 + sge->csio_fl_align - 1) - & ~(sge->csio_fl_align - 1), SGE_FL_BUFFER_SIZE3); + & ~(sge->csio_fl_align - 1), SGE_FL_BUFFER_SIZE3_A); CSIO_SET_FLBUF_SIZE(hw, 4, CSIO_SGE_FLBUF_SIZE4); CSIO_SET_FLBUF_SIZE(hw, 5, CSIO_SGE_FLBUF_SIZE5); CSIO_SET_FLBUF_SIZE(hw, 6, CSIO_SGE_FLBUF_SIZE6); @@ -1502,11 +1506,11 @@ csio_wr_set_sge(struct csio_hw *hw) sge->counter_val[2] = CSIO_SGE_INT_CNT_VAL_2; sge->counter_val[3] = CSIO_SGE_INT_CNT_VAL_3; - csio_wr_reg32(hw, THRESHOLD_0(sge->counter_val[0]) | - THRESHOLD_1(sge->counter_val[1]) | - THRESHOLD_2(sge->counter_val[2]) | - THRESHOLD_3(sge->counter_val[3]), - SGE_INGRESS_RX_THRESHOLD); + csio_wr_reg32(hw, THRESHOLD_0_V(sge->counter_val[0]) | + THRESHOLD_1_V(sge->counter_val[1]) | + THRESHOLD_2_V(sge->counter_val[2]) | + THRESHOLD_3_V(sge->counter_val[3]), + SGE_INGRESS_RX_THRESHOLD_A); csio_wr_reg32(hw, TIMERVALUE0(csio_us_to_core_ticks(hw, sge->timer_val[0])) | -- cgit v0.10.2 From f061de42e6ae697dd22cd0acbfd50fa3c4ecb9b8 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Mon, 5 Jan 2015 16:30:44 +0530 Subject: cxgb4/cxgb4vf/csiostor: Cleanup SGE and PCI related register defines This patch cleansup remaining SGE related macros/register defines and all PCI related ones that are defined in t4_regs.h and the affected files. Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 5e0d57a..16c633f 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -1590,9 +1590,9 @@ static void get_stats(struct net_device *dev, struct ethtool_stats *stats, collect_sge_port_stats(adapter, pi, (struct queue_port_stats *)data); data += sizeof(struct queue_port_stats) / sizeof(u64); if (!is_t4(adapter->params.chip)) { - t4_write_reg(adapter, SGE_STAT_CFG, STATSOURCE_T5(7)); - val1 = t4_read_reg(adapter, SGE_STAT_TOTAL); - val2 = t4_read_reg(adapter, SGE_STAT_MATCH); + t4_write_reg(adapter, SGE_STAT_CFG_A, STATSOURCE_T5_V(7)); + val1 = t4_read_reg(adapter, SGE_STAT_TOTAL_A); + val2 = t4_read_reg(adapter, SGE_STAT_MATCH_A); *data = val1 - val2; data++; *data = val2; @@ -3601,14 +3601,14 @@ unsigned int cxgb4_dbfifo_count(const struct net_device *dev, int lpfifo) struct adapter *adap = netdev2adap(dev); u32 v1, v2, lp_count, hp_count; - v1 = t4_read_reg(adap, A_SGE_DBFIFO_STATUS); - v2 = t4_read_reg(adap, SGE_DBFIFO_STATUS2); + v1 = t4_read_reg(adap, SGE_DBFIFO_STATUS_A); + v2 = t4_read_reg(adap, SGE_DBFIFO_STATUS2_A); if (is_t4(adap->params.chip)) { - lp_count = G_LP_COUNT(v1); - hp_count = G_HP_COUNT(v1); + lp_count = LP_COUNT_G(v1); + hp_count = HP_COUNT_G(v1); } else { - lp_count = G_LP_COUNT_T5(v1); - hp_count = G_HP_COUNT_T5(v2); + lp_count = LP_COUNT_T5_G(v1); + hp_count = HP_COUNT_T5_G(v2); } return lpfifo ? lp_count : hp_count; } @@ -3667,14 +3667,14 @@ int cxgb4_flush_eq_cache(struct net_device *dev) int ret; ret = t4_fwaddrspace_write(adap, adap->mbox, - 0xe1000000 + A_SGE_CTXT_CMD, 0x20000000); + 0xe1000000 + SGE_CTXT_CMD_A, 0x20000000); return ret; } EXPORT_SYMBOL(cxgb4_flush_eq_cache); static int read_eq_indices(struct adapter *adap, u16 qid, u16 *pidx, u16 *cidx) { - u32 addr = t4_read_reg(adap, A_SGE_DBQ_CTXT_BADDR) + 24 * qid + 8; + u32 addr = t4_read_reg(adap, SGE_DBQ_CTXT_BADDR_A) + 24 * qid + 8; __be64 indices; int ret; @@ -3728,7 +3728,7 @@ void cxgb4_disable_db_coalescing(struct net_device *dev) struct adapter *adap; adap = netdev2adap(dev); - t4_set_reg_field(adap, A_SGE_DOORBELL_CONTROL, NOCOALESCE_F, + t4_set_reg_field(adap, SGE_DOORBELL_CONTROL_A, NOCOALESCE_F, NOCOALESCE_F); } EXPORT_SYMBOL(cxgb4_disable_db_coalescing); @@ -3738,7 +3738,7 @@ void cxgb4_enable_db_coalescing(struct net_device *dev) struct adapter *adap; adap = netdev2adap(dev); - t4_set_reg_field(adap, A_SGE_DOORBELL_CONTROL, NOCOALESCE_F, 0); + t4_set_reg_field(adap, SGE_DOORBELL_CONTROL_A, NOCOALESCE_F, 0); } EXPORT_SYMBOL(cxgb4_enable_db_coalescing); @@ -3877,14 +3877,14 @@ static void drain_db_fifo(struct adapter *adap, int usecs) u32 v1, v2, lp_count, hp_count; do { - v1 = t4_read_reg(adap, A_SGE_DBFIFO_STATUS); - v2 = t4_read_reg(adap, SGE_DBFIFO_STATUS2); + v1 = t4_read_reg(adap, SGE_DBFIFO_STATUS_A); + v2 = t4_read_reg(adap, SGE_DBFIFO_STATUS2_A); if (is_t4(adap->params.chip)) { - lp_count = G_LP_COUNT(v1); - hp_count = G_HP_COUNT(v1); + lp_count = LP_COUNT_G(v1); + hp_count = HP_COUNT_G(v1); } else { - lp_count = G_LP_COUNT_T5(v1); - hp_count = G_HP_COUNT_T5(v2); + lp_count = LP_COUNT_T5_G(v1); + hp_count = HP_COUNT_T5_G(v2); } if (lp_count == 0 && hp_count == 0) @@ -4044,7 +4044,7 @@ static void process_db_drop(struct work_struct *work) t4_set_reg_field(adap, 0x10b0, 1<<15, 1<<15); } - t4_set_reg_field(adap, A_SGE_DOORBELL_CONTROL, F_DROPPED_DB, 0); + t4_set_reg_field(adap, SGE_DOORBELL_CONTROL_A, DROPPED_DB_F, 0); } void t4_db_full(struct adapter *adap) @@ -4871,16 +4871,16 @@ static void setup_memwin(struct adapter *adap) mem_win2_base = MEMWIN2_BASE_T5; mem_win2_aperture = MEMWIN2_APERTURE_T5; } - t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 0), - mem_win0_base | BIR(0) | - WINDOW(ilog2(MEMWIN0_APERTURE) - 10)); - t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 1), - mem_win1_base | BIR(0) | - WINDOW(ilog2(MEMWIN1_APERTURE) - 10)); - t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 2), - mem_win2_base | BIR(0) | - WINDOW(ilog2(mem_win2_aperture) - 10)); - t4_read_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 2)); + t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, 0), + mem_win0_base | BIR_V(0) | + WINDOW_V(ilog2(MEMWIN0_APERTURE) - 10)); + t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, 1), + mem_win1_base | BIR_V(0) | + WINDOW_V(ilog2(MEMWIN1_APERTURE) - 10)); + t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, 2), + mem_win2_base | BIR_V(0) | + WINDOW_V(ilog2(mem_win2_aperture) - 10)); + t4_read_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, 2)); } static void setup_memwin_rdma(struct adapter *adap) @@ -4894,13 +4894,13 @@ static void setup_memwin_rdma(struct adapter *adap) start += OCQ_WIN_OFFSET(adap->pdev, &adap->vres); sz_kb = roundup_pow_of_two(adap->vres.ocq.size) >> 10; t4_write_reg(adap, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 3), - start | BIR(1) | WINDOW(ilog2(sz_kb))); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, 3), + start | BIR_V(1) | WINDOW_V(ilog2(sz_kb))); t4_write_reg(adap, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, 3), + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, 3), adap->vres.ocq.start); t4_read_reg(adap, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, 3)); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, 3)); } } diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index 4449fc7..d7c301c 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -2098,7 +2098,7 @@ static irqreturn_t t4_intr_intx(int irq, void *cookie) { struct adapter *adap = cookie; - t4_write_reg(adap, MYPF_REG(PCIE_PF_CLI), 0); + t4_write_reg(adap, MYPF_REG(PCIE_PF_CLI_A), 0); if (t4_slow_intr_handler(adap) | process_intrq(adap)) return IRQ_HANDLED; return IRQ_NONE; /* probably shared interrupt */ @@ -2145,9 +2145,9 @@ static void sge_rx_timer_cb(unsigned long data) } } - t4_write_reg(adap, SGE_DEBUG_INDEX, 13); - idma_same_state_cnt[0] = t4_read_reg(adap, SGE_DEBUG_DATA_HIGH); - idma_same_state_cnt[1] = t4_read_reg(adap, SGE_DEBUG_DATA_LOW); + t4_write_reg(adap, SGE_DEBUG_INDEX_A, 13); + idma_same_state_cnt[0] = t4_read_reg(adap, SGE_DEBUG_DATA_HIGH_A); + idma_same_state_cnt[1] = t4_read_reg(adap, SGE_DEBUG_DATA_LOW_A); for (i = 0; i < 2; i++) { u32 debug0, debug11; @@ -2191,12 +2191,12 @@ static void sge_rx_timer_cb(unsigned long data) /* Read and save the SGE IDMA State and Queue ID information. * We do this every time in case it changes across time ... */ - t4_write_reg(adap, SGE_DEBUG_INDEX, 0); - debug0 = t4_read_reg(adap, SGE_DEBUG_DATA_LOW); + t4_write_reg(adap, SGE_DEBUG_INDEX_A, 0); + debug0 = t4_read_reg(adap, SGE_DEBUG_DATA_LOW_A); s->idma_state[i] = (debug0 >> (i * 9)) & 0x3f; - t4_write_reg(adap, SGE_DEBUG_INDEX, 11); - debug11 = t4_read_reg(adap, SGE_DEBUG_DATA_LOW); + t4_write_reg(adap, SGE_DEBUG_INDEX_A, 11); + debug11 = t4_read_reg(adap, SGE_DEBUG_DATA_LOW_A); s->idma_qid[i] = (debug11 >> (i * 16)) & 0xffff; CH_WARN(adap, "SGE idma%u, queue%u, maybe stuck state%u %dsecs (debug0=%#x, debug11=%#x)\n", @@ -2826,21 +2826,21 @@ static int t4_sge_init_soft(struct adapter *adap) * Retrieve our RX interrupt holdoff timer values and counter * threshold values from the SGE parameters. */ - timer_value_0_and_1 = t4_read_reg(adap, SGE_TIMER_VALUE_0_AND_1); - timer_value_2_and_3 = t4_read_reg(adap, SGE_TIMER_VALUE_2_AND_3); - timer_value_4_and_5 = t4_read_reg(adap, SGE_TIMER_VALUE_4_AND_5); + timer_value_0_and_1 = t4_read_reg(adap, SGE_TIMER_VALUE_0_AND_1_A); + timer_value_2_and_3 = t4_read_reg(adap, SGE_TIMER_VALUE_2_AND_3_A); + timer_value_4_and_5 = t4_read_reg(adap, SGE_TIMER_VALUE_4_AND_5_A); s->timer_val[0] = core_ticks_to_us(adap, - TIMERVALUE0_GET(timer_value_0_and_1)); + TIMERVALUE0_G(timer_value_0_and_1)); s->timer_val[1] = core_ticks_to_us(adap, - TIMERVALUE1_GET(timer_value_0_and_1)); + TIMERVALUE1_G(timer_value_0_and_1)); s->timer_val[2] = core_ticks_to_us(adap, - TIMERVALUE2_GET(timer_value_2_and_3)); + TIMERVALUE2_G(timer_value_2_and_3)); s->timer_val[3] = core_ticks_to_us(adap, - TIMERVALUE3_GET(timer_value_2_and_3)); + TIMERVALUE3_G(timer_value_2_and_3)); s->timer_val[4] = core_ticks_to_us(adap, - TIMERVALUE4_GET(timer_value_4_and_5)); + TIMERVALUE4_G(timer_value_4_and_5)); s->timer_val[5] = core_ticks_to_us(adap, - TIMERVALUE5_GET(timer_value_4_and_5)); + TIMERVALUE5_G(timer_value_4_and_5)); ingress_rx_threshold = t4_read_reg(adap, SGE_INGRESS_RX_THRESHOLD_A); s->counter_val[0] = THRESHOLD_0_G(ingress_rx_threshold); @@ -2866,21 +2866,21 @@ static int t4_sge_init_hard(struct adapter *adap) * and generate an interrupt when this occurs so we can recover. */ if (is_t4(adap->params.chip)) { - t4_set_reg_field(adap, A_SGE_DBFIFO_STATUS, - V_HP_INT_THRESH(M_HP_INT_THRESH) | - V_LP_INT_THRESH(M_LP_INT_THRESH), - V_HP_INT_THRESH(dbfifo_int_thresh) | - V_LP_INT_THRESH(dbfifo_int_thresh)); + t4_set_reg_field(adap, SGE_DBFIFO_STATUS_A, + HP_INT_THRESH_V(HP_INT_THRESH_M) | + LP_INT_THRESH_V(LP_INT_THRESH_M), + HP_INT_THRESH_V(dbfifo_int_thresh) | + LP_INT_THRESH_V(dbfifo_int_thresh)); } else { - t4_set_reg_field(adap, A_SGE_DBFIFO_STATUS, - V_LP_INT_THRESH_T5(M_LP_INT_THRESH_T5), - V_LP_INT_THRESH_T5(dbfifo_int_thresh)); - t4_set_reg_field(adap, SGE_DBFIFO_STATUS2, - V_HP_INT_THRESH_T5(M_HP_INT_THRESH_T5), - V_HP_INT_THRESH_T5(dbfifo_int_thresh)); + t4_set_reg_field(adap, SGE_DBFIFO_STATUS_A, + LP_INT_THRESH_T5_V(LP_INT_THRESH_T5_M), + LP_INT_THRESH_T5_V(dbfifo_int_thresh)); + t4_set_reg_field(adap, SGE_DBFIFO_STATUS2_A, + HP_INT_THRESH_T5_V(HP_INT_THRESH_T5_M), + HP_INT_THRESH_T5_V(dbfifo_int_thresh)); } - t4_set_reg_field(adap, A_SGE_DOORBELL_CONTROL, F_ENABLE_DROP, - F_ENABLE_DROP); + t4_set_reg_field(adap, SGE_DOORBELL_CONTROL_A, ENABLE_DROP_F, + ENABLE_DROP_F); /* * SGE_FL_BUFFER_SIZE0 (RX_SMALL_PG_BUF) is set up by @@ -2905,15 +2905,15 @@ static int t4_sge_init_hard(struct adapter *adap) THRESHOLD_1_V(s->counter_val[1]) | THRESHOLD_2_V(s->counter_val[2]) | THRESHOLD_3_V(s->counter_val[3])); - t4_write_reg(adap, SGE_TIMER_VALUE_0_AND_1, - TIMERVALUE0(us_to_core_ticks(adap, s->timer_val[0])) | - TIMERVALUE1(us_to_core_ticks(adap, s->timer_val[1]))); - t4_write_reg(adap, SGE_TIMER_VALUE_2_AND_3, - TIMERVALUE2(us_to_core_ticks(adap, s->timer_val[2])) | - TIMERVALUE3(us_to_core_ticks(adap, s->timer_val[3]))); - t4_write_reg(adap, SGE_TIMER_VALUE_4_AND_5, - TIMERVALUE4(us_to_core_ticks(adap, s->timer_val[4])) | - TIMERVALUE5(us_to_core_ticks(adap, s->timer_val[5]))); + t4_write_reg(adap, SGE_TIMER_VALUE_0_AND_1_A, + TIMERVALUE0_V(us_to_core_ticks(adap, s->timer_val[0])) | + TIMERVALUE1_V(us_to_core_ticks(adap, s->timer_val[1]))); + t4_write_reg(adap, SGE_TIMER_VALUE_2_AND_3_A, + TIMERVALUE2_V(us_to_core_ticks(adap, s->timer_val[2])) | + TIMERVALUE3_V(us_to_core_ticks(adap, s->timer_val[3]))); + t4_write_reg(adap, SGE_TIMER_VALUE_4_AND_5_A, + TIMERVALUE4_V(us_to_core_ticks(adap, s->timer_val[4])) | + TIMERVALUE5_V(us_to_core_ticks(adap, s->timer_val[5]))); return 0; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index ac00cab..9f8cd56 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -150,20 +150,20 @@ void t4_write_indirect(struct adapter *adap, unsigned int addr_reg, */ void t4_hw_pci_read_cfg4(struct adapter *adap, int reg, u32 *val) { - u32 req = ENABLE | FUNCTION(adap->fn) | reg; + u32 req = ENABLE_F | FUNCTION_V(adap->fn) | REGISTER_V(reg); if (is_t4(adap->params.chip)) - req |= F_LOCALCFG; + req |= LOCALCFG_F; - t4_write_reg(adap, PCIE_CFG_SPACE_REQ, req); - *val = t4_read_reg(adap, PCIE_CFG_SPACE_DATA); + t4_write_reg(adap, PCIE_CFG_SPACE_REQ_A, req); + *val = t4_read_reg(adap, PCIE_CFG_SPACE_DATA_A); /* Reset ENABLE to 0 so reads of PCIE_CFG_SPACE_DATA won't cause a * Configuration Space read. (None of the other fields matter when * ENABLE is 0 so a simple register write is easier than a * read-modify-write via t4_set_reg_field().) */ - t4_write_reg(adap, PCIE_CFG_SPACE_REQ, 0); + t4_write_reg(adap, PCIE_CFG_SPACE_REQ_A, 0); } /* @@ -188,8 +188,8 @@ static void t4_report_fw_error(struct adapter *adap) }; u32 pcie_fw; - pcie_fw = t4_read_reg(adap, MA_PCIE_FW); - if (pcie_fw & PCIE_FW_ERR) + pcie_fw = t4_read_reg(adap, PCIE_FW_A); + if (pcie_fw & PCIE_FW_ERR_F) dev_err(adap->pdev_dev, "Firmware reports adapter error: %s\n", reason[PCIE_FW_EVAL_G(pcie_fw)]); } @@ -506,13 +506,13 @@ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, * the address is relative to BAR0. */ mem_reg = t4_read_reg(adap, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, win)); - mem_aperture = 1 << (GET_WINDOW(mem_reg) + 10); - mem_base = GET_PCIEOFST(mem_reg) << 10; + mem_aperture = 1 << (WINDOW_G(mem_reg) + WINDOW_SHIFT_X); + mem_base = PCIEOFST_G(mem_reg) << PCIEOFST_SHIFT_X; if (is_t4(adap->params.chip)) mem_base -= adap->t4_bar0; - win_pf = is_t4(adap->params.chip) ? 0 : V_PFNUM(adap->fn); + win_pf = is_t4(adap->params.chip) ? 0 : PFNUM_V(adap->fn); /* Calculate our initial PCI-E Memory Window Position and Offset into * that Window. @@ -525,10 +525,10 @@ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, * attempt to use the new value.) */ t4_write_reg(adap, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win), + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win), pos | win_pf); t4_read_reg(adap, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win)); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win)); /* Transfer data to/from the adapter as long as there's an integral * number of 32-bit transfers to complete. @@ -553,11 +553,11 @@ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, pos += mem_aperture; offset = 0; t4_write_reg(adap, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, - win), pos | win_pf); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, + win), pos | win_pf); t4_read_reg(adap, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, - win)); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, + win)); } } @@ -1366,95 +1366,97 @@ static int t4_handle_intr_status(struct adapter *adapter, unsigned int reg, static void pcie_intr_handler(struct adapter *adapter) { static const struct intr_info sysbus_intr_info[] = { - { RNPP, "RXNP array parity error", -1, 1 }, - { RPCP, "RXPC array parity error", -1, 1 }, - { RCIP, "RXCIF array parity error", -1, 1 }, - { RCCP, "Rx completions control array parity error", -1, 1 }, - { RFTP, "RXFT array parity error", -1, 1 }, + { RNPP_F, "RXNP array parity error", -1, 1 }, + { RPCP_F, "RXPC array parity error", -1, 1 }, + { RCIP_F, "RXCIF array parity error", -1, 1 }, + { RCCP_F, "Rx completions control array parity error", -1, 1 }, + { RFTP_F, "RXFT array parity error", -1, 1 }, { 0 } }; static const struct intr_info pcie_port_intr_info[] = { - { TPCP, "TXPC array parity error", -1, 1 }, - { TNPP, "TXNP array parity error", -1, 1 }, - { TFTP, "TXFT array parity error", -1, 1 }, - { TCAP, "TXCA array parity error", -1, 1 }, - { TCIP, "TXCIF array parity error", -1, 1 }, - { RCAP, "RXCA array parity error", -1, 1 }, - { OTDD, "outbound request TLP discarded", -1, 1 }, - { RDPE, "Rx data parity error", -1, 1 }, - { TDUE, "Tx uncorrectable data error", -1, 1 }, + { TPCP_F, "TXPC array parity error", -1, 1 }, + { TNPP_F, "TXNP array parity error", -1, 1 }, + { TFTP_F, "TXFT array parity error", -1, 1 }, + { TCAP_F, "TXCA array parity error", -1, 1 }, + { TCIP_F, "TXCIF array parity error", -1, 1 }, + { RCAP_F, "RXCA array parity error", -1, 1 }, + { OTDD_F, "outbound request TLP discarded", -1, 1 }, + { RDPE_F, "Rx data parity error", -1, 1 }, + { TDUE_F, "Tx uncorrectable data error", -1, 1 }, { 0 } }; static const struct intr_info pcie_intr_info[] = { - { MSIADDRLPERR, "MSI AddrL parity error", -1, 1 }, - { MSIADDRHPERR, "MSI AddrH parity error", -1, 1 }, - { MSIDATAPERR, "MSI data parity error", -1, 1 }, - { MSIXADDRLPERR, "MSI-X AddrL parity error", -1, 1 }, - { MSIXADDRHPERR, "MSI-X AddrH parity error", -1, 1 }, - { MSIXDATAPERR, "MSI-X data parity error", -1, 1 }, - { MSIXDIPERR, "MSI-X DI parity error", -1, 1 }, - { PIOCPLPERR, "PCI PIO completion FIFO parity error", -1, 1 }, - { PIOREQPERR, "PCI PIO request FIFO parity error", -1, 1 }, - { TARTAGPERR, "PCI PCI target tag FIFO parity error", -1, 1 }, - { CCNTPERR, "PCI CMD channel count parity error", -1, 1 }, - { CREQPERR, "PCI CMD channel request parity error", -1, 1 }, - { CRSPPERR, "PCI CMD channel response parity error", -1, 1 }, - { DCNTPERR, "PCI DMA channel count parity error", -1, 1 }, - { DREQPERR, "PCI DMA channel request parity error", -1, 1 }, - { DRSPPERR, "PCI DMA channel response parity error", -1, 1 }, - { HCNTPERR, "PCI HMA channel count parity error", -1, 1 }, - { HREQPERR, "PCI HMA channel request parity error", -1, 1 }, - { HRSPPERR, "PCI HMA channel response parity error", -1, 1 }, - { CFGSNPPERR, "PCI config snoop FIFO parity error", -1, 1 }, - { FIDPERR, "PCI FID parity error", -1, 1 }, - { INTXCLRPERR, "PCI INTx clear parity error", -1, 1 }, - { MATAGPERR, "PCI MA tag parity error", -1, 1 }, - { PIOTAGPERR, "PCI PIO tag parity error", -1, 1 }, - { RXCPLPERR, "PCI Rx completion parity error", -1, 1 }, - { RXWRPERR, "PCI Rx write parity error", -1, 1 }, - { RPLPERR, "PCI replay buffer parity error", -1, 1 }, - { PCIESINT, "PCI core secondary fault", -1, 1 }, - { PCIEPINT, "PCI core primary fault", -1, 1 }, - { UNXSPLCPLERR, "PCI unexpected split completion error", -1, 0 }, + { MSIADDRLPERR_F, "MSI AddrL parity error", -1, 1 }, + { MSIADDRHPERR_F, "MSI AddrH parity error", -1, 1 }, + { MSIDATAPERR_F, "MSI data parity error", -1, 1 }, + { MSIXADDRLPERR_F, "MSI-X AddrL parity error", -1, 1 }, + { MSIXADDRHPERR_F, "MSI-X AddrH parity error", -1, 1 }, + { MSIXDATAPERR_F, "MSI-X data parity error", -1, 1 }, + { MSIXDIPERR_F, "MSI-X DI parity error", -1, 1 }, + { PIOCPLPERR_F, "PCI PIO completion FIFO parity error", -1, 1 }, + { PIOREQPERR_F, "PCI PIO request FIFO parity error", -1, 1 }, + { TARTAGPERR_F, "PCI PCI target tag FIFO parity error", -1, 1 }, + { CCNTPERR_F, "PCI CMD channel count parity error", -1, 1 }, + { CREQPERR_F, "PCI CMD channel request parity error", -1, 1 }, + { CRSPPERR_F, "PCI CMD channel response parity error", -1, 1 }, + { DCNTPERR_F, "PCI DMA channel count parity error", -1, 1 }, + { DREQPERR_F, "PCI DMA channel request parity error", -1, 1 }, + { DRSPPERR_F, "PCI DMA channel response parity error", -1, 1 }, + { HCNTPERR_F, "PCI HMA channel count parity error", -1, 1 }, + { HREQPERR_F, "PCI HMA channel request parity error", -1, 1 }, + { HRSPPERR_F, "PCI HMA channel response parity error", -1, 1 }, + { CFGSNPPERR_F, "PCI config snoop FIFO parity error", -1, 1 }, + { FIDPERR_F, "PCI FID parity error", -1, 1 }, + { INTXCLRPERR_F, "PCI INTx clear parity error", -1, 1 }, + { MATAGPERR_F, "PCI MA tag parity error", -1, 1 }, + { PIOTAGPERR_F, "PCI PIO tag parity error", -1, 1 }, + { RXCPLPERR_F, "PCI Rx completion parity error", -1, 1 }, + { RXWRPERR_F, "PCI Rx write parity error", -1, 1 }, + { RPLPERR_F, "PCI replay buffer parity error", -1, 1 }, + { PCIESINT_F, "PCI core secondary fault", -1, 1 }, + { PCIEPINT_F, "PCI core primary fault", -1, 1 }, + { UNXSPLCPLERR_F, "PCI unexpected split completion error", + -1, 0 }, { 0 } }; static struct intr_info t5_pcie_intr_info[] = { - { MSTGRPPERR, "Master Response Read Queue parity error", + { MSTGRPPERR_F, "Master Response Read Queue parity error", -1, 1 }, - { MSTTIMEOUTPERR, "Master Timeout FIFO parity error", -1, 1 }, - { MSIXSTIPERR, "MSI-X STI SRAM parity error", -1, 1 }, - { MSIXADDRLPERR, "MSI-X AddrL parity error", -1, 1 }, - { MSIXADDRHPERR, "MSI-X AddrH parity error", -1, 1 }, - { MSIXDATAPERR, "MSI-X data parity error", -1, 1 }, - { MSIXDIPERR, "MSI-X DI parity error", -1, 1 }, - { PIOCPLGRPPERR, "PCI PIO completion Group FIFO parity error", + { MSTTIMEOUTPERR_F, "Master Timeout FIFO parity error", -1, 1 }, + { MSIXSTIPERR_F, "MSI-X STI SRAM parity error", -1, 1 }, + { MSIXADDRLPERR_F, "MSI-X AddrL parity error", -1, 1 }, + { MSIXADDRHPERR_F, "MSI-X AddrH parity error", -1, 1 }, + { MSIXDATAPERR_F, "MSI-X data parity error", -1, 1 }, + { MSIXDIPERR_F, "MSI-X DI parity error", -1, 1 }, + { PIOCPLGRPPERR_F, "PCI PIO completion Group FIFO parity error", -1, 1 }, - { PIOREQGRPPERR, "PCI PIO request Group FIFO parity error", + { PIOREQGRPPERR_F, "PCI PIO request Group FIFO parity error", -1, 1 }, - { TARTAGPERR, "PCI PCI target tag FIFO parity error", -1, 1 }, - { MSTTAGQPERR, "PCI master tag queue parity error", -1, 1 }, - { CREQPERR, "PCI CMD channel request parity error", -1, 1 }, - { CRSPPERR, "PCI CMD channel response parity error", -1, 1 }, - { DREQWRPERR, "PCI DMA channel write request parity error", + { TARTAGPERR_F, "PCI PCI target tag FIFO parity error", -1, 1 }, + { MSTTAGQPERR_F, "PCI master tag queue parity error", -1, 1 }, + { CREQPERR_F, "PCI CMD channel request parity error", -1, 1 }, + { CRSPPERR_F, "PCI CMD channel response parity error", -1, 1 }, + { DREQWRPERR_F, "PCI DMA channel write request parity error", -1, 1 }, - { DREQPERR, "PCI DMA channel request parity error", -1, 1 }, - { DRSPPERR, "PCI DMA channel response parity error", -1, 1 }, - { HREQWRPERR, "PCI HMA channel count parity error", -1, 1 }, - { HREQPERR, "PCI HMA channel request parity error", -1, 1 }, - { HRSPPERR, "PCI HMA channel response parity error", -1, 1 }, - { CFGSNPPERR, "PCI config snoop FIFO parity error", -1, 1 }, - { FIDPERR, "PCI FID parity error", -1, 1 }, - { VFIDPERR, "PCI INTx clear parity error", -1, 1 }, - { MAGRPPERR, "PCI MA group FIFO parity error", -1, 1 }, - { PIOTAGPERR, "PCI PIO tag parity error", -1, 1 }, - { IPRXHDRGRPPERR, "PCI IP Rx header group parity error", + { DREQPERR_F, "PCI DMA channel request parity error", -1, 1 }, + { DRSPPERR_F, "PCI DMA channel response parity error", -1, 1 }, + { HREQWRPERR_F, "PCI HMA channel count parity error", -1, 1 }, + { HREQPERR_F, "PCI HMA channel request parity error", -1, 1 }, + { HRSPPERR_F, "PCI HMA channel response parity error", -1, 1 }, + { CFGSNPPERR_F, "PCI config snoop FIFO parity error", -1, 1 }, + { FIDPERR_F, "PCI FID parity error", -1, 1 }, + { VFIDPERR_F, "PCI INTx clear parity error", -1, 1 }, + { MAGRPPERR_F, "PCI MA group FIFO parity error", -1, 1 }, + { PIOTAGPERR_F, "PCI PIO tag parity error", -1, 1 }, + { IPRXHDRGRPPERR_F, "PCI IP Rx header group parity error", -1, 1 }, - { IPRXDATAGRPPERR, "PCI IP Rx data group parity error", -1, 1 }, - { RPLPERR, "PCI IP replay buffer parity error", -1, 1 }, - { IPSOTPERR, "PCI IP SOT buffer parity error", -1, 1 }, - { TRGT1GRPPERR, "PCI TRGT1 group FIFOs parity error", -1, 1 }, - { READRSPERR, "Outbound read error", -1, 0 }, + { IPRXDATAGRPPERR_F, "PCI IP Rx data group parity error", + -1, 1 }, + { RPLPERR_F, "PCI IP replay buffer parity error", -1, 1 }, + { IPSOTPERR_F, "PCI IP SOT buffer parity error", -1, 1 }, + { TRGT1GRPPERR_F, "PCI TRGT1 group FIFOs parity error", -1, 1 }, + { READRSPERR_F, "Outbound read error", -1, 0 }, { 0 } }; @@ -1462,15 +1464,15 @@ static void pcie_intr_handler(struct adapter *adapter) if (is_t4(adapter->params.chip)) fat = t4_handle_intr_status(adapter, - PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS, - sysbus_intr_info) + + PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS_A, + sysbus_intr_info) + t4_handle_intr_status(adapter, - PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS, - pcie_port_intr_info) + - t4_handle_intr_status(adapter, PCIE_INT_CAUSE, + PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS_A, + pcie_port_intr_info) + + t4_handle_intr_status(adapter, PCIE_INT_CAUSE_A, pcie_intr_info); else - fat = t4_handle_intr_status(adapter, PCIE_INT_CAUSE, + fat = t4_handle_intr_status(adapter, PCIE_INT_CAUSE_A, t5_pcie_intr_info); if (fat) @@ -1590,7 +1592,7 @@ static void cim_intr_handler(struct adapter *adapter) int fat; - if (t4_read_reg(adapter, MA_PCIE_FW) & PCIE_FW_ERR) + if (t4_read_reg(adapter, PCIE_FW_A) & PCIE_FW_ERR_F) t4_report_fw_error(adapter); fat = t4_handle_intr_status(adapter, CIM_HOST_INT_CAUSE, @@ -2750,9 +2752,9 @@ void t4_sge_decode_idma_state(struct adapter *adapter, int state) "IDMA_FL_SEND_COMPLETION_TO_IMSG", }; static const u32 sge_regs[] = { - SGE_DEBUG_DATA_LOW_INDEX_2, - SGE_DEBUG_DATA_LOW_INDEX_3, - SGE_DEBUG_DATA_HIGH_INDEX_10, + SGE_DEBUG_DATA_LOW_INDEX_2_A, + SGE_DEBUG_DATA_LOW_INDEX_3_A, + SGE_DEBUG_DATA_HIGH_INDEX_10_A, }; const char **sge_idma_decode; int sge_idma_decode_nstates; @@ -2819,7 +2821,7 @@ retry: if (ret < 0) { if ((ret == -EBUSY || ret == -ETIMEDOUT) && retries-- > 0) goto retry; - if (t4_read_reg(adap, MA_PCIE_FW) & PCIE_FW_ERR) + if (t4_read_reg(adap, PCIE_FW_A) & PCIE_FW_ERR_F) t4_report_fw_error(adap); return ret; } @@ -2869,8 +2871,8 @@ retry: * timeout ... and then retry if we haven't exhausted * our retries ... */ - pcie_fw = t4_read_reg(adap, MA_PCIE_FW); - if (!(pcie_fw & (PCIE_FW_ERR|PCIE_FW_INIT))) { + pcie_fw = t4_read_reg(adap, PCIE_FW_A); + if (!(pcie_fw & (PCIE_FW_ERR_F|PCIE_FW_INIT_F))) { if (waiting <= 0) { if (retries-- > 0) goto retry; @@ -2885,9 +2887,9 @@ retry: * report errors preferentially. */ if (state) { - if (pcie_fw & PCIE_FW_ERR) + if (pcie_fw & PCIE_FW_ERR_F) *state = DEV_STATE_ERR; - else if (pcie_fw & PCIE_FW_INIT) + else if (pcie_fw & PCIE_FW_INIT_F) *state = DEV_STATE_INIT; } @@ -2897,7 +2899,7 @@ retry: * for our caller. */ if (master_mbox == PCIE_FW_MASTER_M && - (pcie_fw & PCIE_FW_MASTER_VLD)) + (pcie_fw & PCIE_FW_MASTER_VLD_F)) master_mbox = PCIE_FW_MASTER_G(pcie_fw); break; } @@ -3006,7 +3008,7 @@ static int t4_fw_halt(struct adapter *adap, unsigned int mbox, int force) */ if (ret == 0 || force) { t4_set_reg_field(adap, CIM_BOOT_CFG, UPCRST, UPCRST); - t4_set_reg_field(adap, PCIE_FW, PCIE_FW_HALT_F, + t4_set_reg_field(adap, PCIE_FW_A, PCIE_FW_HALT_F, PCIE_FW_HALT_F); } @@ -3046,7 +3048,7 @@ static int t4_fw_restart(struct adapter *adap, unsigned int mbox, int reset) * doing it automatically, we need to clear the PCIE_FW.HALT * bit. */ - t4_set_reg_field(adap, PCIE_FW, PCIE_FW_HALT_F, 0); + t4_set_reg_field(adap, PCIE_FW_A, PCIE_FW_HALT_F, 0); /* * If we've been given a valid mailbox, first try to get the @@ -3070,7 +3072,7 @@ static int t4_fw_restart(struct adapter *adap, unsigned int mbox, int reset) t4_set_reg_field(adap, CIM_BOOT_CFG, UPCRST, 0); for (ms = 0; ms < FW_CMD_MAX_TIMEOUT; ) { - if (!(t4_read_reg(adap, PCIE_FW) & PCIE_FW_HALT_F)) + if (!(t4_read_reg(adap, PCIE_FW_A) & PCIE_FW_HALT_F)) return 0; msleep(100); ms += 100; @@ -4146,7 +4148,7 @@ int t4_init_sge_params(struct adapter *adapter) (QUEUESPERPAGEPF1_S - QUEUESPERPAGEPF0_S) * adapter->fn); qpp = t4_read_reg(adapter, SGE_EGRESS_QUEUES_PER_PAGE_PF_A); sge_params->eq_qpp = ((qpp >> s_qpp) & QUEUESPERPAGEPF0_M); - qpp = t4_read_reg(adapter, SGE_INGRESS_QUEUES_PER_PAGE_PF); + qpp = t4_read_reg(adapter, SGE_INGRESS_QUEUES_PER_PAGE_PF_A); sge_params->iq_qpp = ((qpp >> s_qpp) & QUEUESPERPAGEPF0_M); return 0; diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index 29c0911..9b73824 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -369,196 +369,424 @@ #define ENABLE_DROP_V(x) ((x) << ENABLE_DROP_S) #define ENABLE_DROP_F ENABLE_DROP_V(1U) -#define SGE_TIMER_VALUE_0_AND_1 0x10b8 -#define TIMERVALUE0_MASK 0xffff0000U -#define TIMERVALUE0_SHIFT 16 -#define TIMERVALUE0(x) ((x) << TIMERVALUE0_SHIFT) -#define TIMERVALUE0_GET(x) (((x) & TIMERVALUE0_MASK) >> TIMERVALUE0_SHIFT) -#define TIMERVALUE1_MASK 0x0000ffffU -#define TIMERVALUE1_SHIFT 0 -#define TIMERVALUE1(x) ((x) << TIMERVALUE1_SHIFT) -#define TIMERVALUE1_GET(x) (((x) & TIMERVALUE1_MASK) >> TIMERVALUE1_SHIFT) - -#define SGE_TIMER_VALUE_2_AND_3 0x10bc -#define TIMERVALUE2_MASK 0xffff0000U -#define TIMERVALUE2_SHIFT 16 -#define TIMERVALUE2(x) ((x) << TIMERVALUE2_SHIFT) -#define TIMERVALUE2_GET(x) (((x) & TIMERVALUE2_MASK) >> TIMERVALUE2_SHIFT) -#define TIMERVALUE3_MASK 0x0000ffffU -#define TIMERVALUE3_SHIFT 0 -#define TIMERVALUE3(x) ((x) << TIMERVALUE3_SHIFT) -#define TIMERVALUE3_GET(x) (((x) & TIMERVALUE3_MASK) >> TIMERVALUE3_SHIFT) - -#define SGE_TIMER_VALUE_4_AND_5 0x10c0 -#define TIMERVALUE4_MASK 0xffff0000U -#define TIMERVALUE4_SHIFT 16 -#define TIMERVALUE4(x) ((x) << TIMERVALUE4_SHIFT) -#define TIMERVALUE4_GET(x) (((x) & TIMERVALUE4_MASK) >> TIMERVALUE4_SHIFT) -#define TIMERVALUE5_MASK 0x0000ffffU -#define TIMERVALUE5_SHIFT 0 -#define TIMERVALUE5(x) ((x) << TIMERVALUE5_SHIFT) -#define TIMERVALUE5_GET(x) (((x) & TIMERVALUE5_MASK) >> TIMERVALUE5_SHIFT) - -#define SGE_DEBUG_INDEX 0x10cc -#define SGE_DEBUG_DATA_HIGH 0x10d0 -#define SGE_DEBUG_DATA_LOW 0x10d4 -#define SGE_DEBUG_DATA_LOW_INDEX_2 0x12c8 -#define SGE_DEBUG_DATA_LOW_INDEX_3 0x12cc -#define SGE_DEBUG_DATA_HIGH_INDEX_10 0x12a8 -#define SGE_INGRESS_QUEUES_PER_PAGE_PF 0x10f4 +#define SGE_TIMER_VALUE_0_AND_1_A 0x10b8 + +#define TIMERVALUE0_S 16 +#define TIMERVALUE0_M 0xffffU +#define TIMERVALUE0_V(x) ((x) << TIMERVALUE0_S) +#define TIMERVALUE0_G(x) (((x) >> TIMERVALUE0_S) & TIMERVALUE0_M) + +#define TIMERVALUE1_S 0 +#define TIMERVALUE1_M 0xffffU +#define TIMERVALUE1_V(x) ((x) << TIMERVALUE1_S) +#define TIMERVALUE1_G(x) (((x) >> TIMERVALUE1_S) & TIMERVALUE1_M) + +#define SGE_TIMER_VALUE_2_AND_3_A 0x10bc + +#define TIMERVALUE2_S 16 +#define TIMERVALUE2_M 0xffffU +#define TIMERVALUE2_V(x) ((x) << TIMERVALUE2_S) +#define TIMERVALUE2_G(x) (((x) >> TIMERVALUE2_S) & TIMERVALUE2_M) + +#define TIMERVALUE3_S 0 +#define TIMERVALUE3_M 0xffffU +#define TIMERVALUE3_V(x) ((x) << TIMERVALUE3_S) +#define TIMERVALUE3_G(x) (((x) >> TIMERVALUE3_S) & TIMERVALUE3_M) + +#define SGE_TIMER_VALUE_4_AND_5_A 0x10c0 + +#define TIMERVALUE4_S 16 +#define TIMERVALUE4_M 0xffffU +#define TIMERVALUE4_V(x) ((x) << TIMERVALUE4_S) +#define TIMERVALUE4_G(x) (((x) >> TIMERVALUE4_S) & TIMERVALUE4_M) + +#define TIMERVALUE5_S 0 +#define TIMERVALUE5_M 0xffffU +#define TIMERVALUE5_V(x) ((x) << TIMERVALUE5_S) +#define TIMERVALUE5_G(x) (((x) >> TIMERVALUE5_S) & TIMERVALUE5_M) + +#define SGE_DEBUG_INDEX_A 0x10cc +#define SGE_DEBUG_DATA_HIGH_A 0x10d0 +#define SGE_DEBUG_DATA_LOW_A 0x10d4 + +#define SGE_DEBUG_DATA_LOW_INDEX_2_A 0x12c8 +#define SGE_DEBUG_DATA_LOW_INDEX_3_A 0x12cc +#define SGE_DEBUG_DATA_HIGH_INDEX_10_A 0x12a8 + +#define SGE_INGRESS_QUEUES_PER_PAGE_PF_A 0x10f4 #define SGE_INGRESS_QUEUES_PER_PAGE_VF_A 0x10f8 -#define S_HP_INT_THRESH 28 -#define M_HP_INT_THRESH 0xfU -#define V_HP_INT_THRESH(x) ((x) << S_HP_INT_THRESH) -#define S_LP_INT_THRESH_T5 18 -#define V_LP_INT_THRESH_T5(x) ((x) << S_LP_INT_THRESH_T5) -#define M_LP_COUNT_T5 0x3ffffU -#define G_LP_COUNT_T5(x) (((x) >> S_LP_COUNT) & M_LP_COUNT_T5) -#define M_HP_COUNT 0x7ffU -#define S_HP_COUNT 16 -#define G_HP_COUNT(x) (((x) >> S_HP_COUNT) & M_HP_COUNT) -#define S_LP_INT_THRESH 12 -#define M_LP_INT_THRESH 0xfU -#define M_LP_INT_THRESH_T5 0xfffU -#define V_LP_INT_THRESH(x) ((x) << S_LP_INT_THRESH) -#define M_LP_COUNT 0x7ffU -#define S_LP_COUNT 0 -#define G_LP_COUNT(x) (((x) >> S_LP_COUNT) & M_LP_COUNT) -#define A_SGE_DBFIFO_STATUS 0x10a4 - -#define SGE_STAT_TOTAL 0x10e4 -#define SGE_STAT_MATCH 0x10e8 - -#define SGE_STAT_CFG 0x10ec -#define S_STATSOURCE_T5 9 -#define STATSOURCE_T5(x) ((x) << S_STATSOURCE_T5) - -#define SGE_DBFIFO_STATUS2 0x1118 -#define M_HP_COUNT_T5 0x3ffU -#define G_HP_COUNT_T5(x) ((x) & M_HP_COUNT_T5) -#define S_HP_INT_THRESH_T5 10 -#define M_HP_INT_THRESH_T5 0xfU -#define V_HP_INT_THRESH_T5(x) ((x) << S_HP_INT_THRESH_T5) - -#define S_ENABLE_DROP 13 -#define V_ENABLE_DROP(x) ((x) << S_ENABLE_DROP) -#define F_ENABLE_DROP V_ENABLE_DROP(1U) -#define S_DROPPED_DB 0 -#define V_DROPPED_DB(x) ((x) << S_DROPPED_DB) -#define F_DROPPED_DB V_DROPPED_DB(1U) -#define A_SGE_DOORBELL_CONTROL 0x10a8 - -#define A_SGE_CTXT_CMD 0x11fc -#define A_SGE_DBQ_CTXT_BADDR 0x1084 - -#define PCIE_PF_CFG 0x40 -#define AIVEC(x) ((x) << 4) -#define AIVEC_MASK 0x3ffU - -#define PCIE_PF_CLI 0x44 -#define PCIE_INT_CAUSE 0x3004 -#define UNXSPLCPLERR 0x20000000U -#define PCIEPINT 0x10000000U -#define PCIESINT 0x08000000U -#define RPLPERR 0x04000000U -#define RXWRPERR 0x02000000U -#define RXCPLPERR 0x01000000U -#define PIOTAGPERR 0x00800000U -#define MATAGPERR 0x00400000U -#define INTXCLRPERR 0x00200000U -#define FIDPERR 0x00100000U -#define CFGSNPPERR 0x00080000U -#define HRSPPERR 0x00040000U -#define HREQPERR 0x00020000U -#define HCNTPERR 0x00010000U -#define DRSPPERR 0x00008000U -#define DREQPERR 0x00004000U -#define DCNTPERR 0x00002000U -#define CRSPPERR 0x00001000U -#define CREQPERR 0x00000800U -#define CCNTPERR 0x00000400U -#define TARTAGPERR 0x00000200U -#define PIOREQPERR 0x00000100U -#define PIOCPLPERR 0x00000080U -#define MSIXDIPERR 0x00000040U -#define MSIXDATAPERR 0x00000020U -#define MSIXADDRHPERR 0x00000010U -#define MSIXADDRLPERR 0x00000008U -#define MSIDATAPERR 0x00000004U -#define MSIADDRHPERR 0x00000002U -#define MSIADDRLPERR 0x00000001U - -#define READRSPERR 0x20000000U -#define TRGT1GRPPERR 0x10000000U -#define IPSOTPERR 0x08000000U -#define IPRXDATAGRPPERR 0x02000000U -#define IPRXHDRGRPPERR 0x01000000U -#define MAGRPPERR 0x00400000U -#define VFIDPERR 0x00200000U -#define HREQWRPERR 0x00010000U -#define DREQWRPERR 0x00002000U -#define MSTTAGQPERR 0x00000400U -#define PIOREQGRPPERR 0x00000100U -#define PIOCPLGRPPERR 0x00000080U -#define MSIXSTIPERR 0x00000004U -#define MSTTIMEOUTPERR 0x00000002U -#define MSTGRPPERR 0x00000001U - -#define PCIE_NONFAT_ERR 0x3010 -#define PCIE_CFG_SPACE_REQ 0x3060 -#define PCIE_CFG_SPACE_DATA 0x3064 -#define PCIE_MEM_ACCESS_BASE_WIN 0x3068 -#define S_PCIEOFST 10 -#define M_PCIEOFST 0x3fffffU -#define GET_PCIEOFST(x) (((x) >> S_PCIEOFST) & M_PCIEOFST) -#define PCIEOFST_MASK 0xfffffc00U -#define BIR_MASK 0x00000300U -#define BIR_SHIFT 8 -#define BIR(x) ((x) << BIR_SHIFT) -#define WINDOW_MASK 0x000000ffU -#define WINDOW_SHIFT 0 -#define WINDOW(x) ((x) << WINDOW_SHIFT) -#define GET_WINDOW(x) (((x) >> WINDOW_SHIFT) & WINDOW_MASK) -#define PCIE_MEM_ACCESS_OFFSET 0x306c -#define ENABLE (1U << 30) -#define FUNCTION(x) ((x) << 12) -#define F_LOCALCFG (1U << 28) - -#define S_PFNUM 0 -#define V_PFNUM(x) ((x) << S_PFNUM) - -#define PCIE_FW 0x30b8 -#define PCIE_FW_ERR 0x80000000U -#define PCIE_FW_INIT 0x40000000U -#define PCIE_FW_HALT 0x20000000U -#define PCIE_FW_MASTER_VLD 0x00008000U -#define PCIE_FW_MASTER(x) ((x) << 12) -#define PCIE_FW_MASTER_MASK 0x7 -#define PCIE_FW_MASTER_GET(x) (((x) >> 12) & PCIE_FW_MASTER_MASK) - -#define PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS 0x5908 -#define RNPP 0x80000000U -#define RPCP 0x20000000U -#define RCIP 0x08000000U -#define RCCP 0x04000000U -#define RFTP 0x00800000U -#define PTRP 0x00100000U - -#define PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS 0x59a4 -#define TPCP 0x40000000U -#define TNPP 0x20000000U -#define TFTP 0x10000000U -#define TCAP 0x08000000U -#define TCIP 0x04000000U -#define RCAP 0x02000000U -#define PLUP 0x00800000U -#define PLDN 0x00400000U -#define OTDD 0x00200000U -#define GTRP 0x00100000U -#define RDPE 0x00040000U -#define TDCE 0x00020000U -#define TDUE 0x00010000U +#define HP_INT_THRESH_S 28 +#define HP_INT_THRESH_M 0xfU +#define HP_INT_THRESH_V(x) ((x) << HP_INT_THRESH_S) + +#define HP_COUNT_S 16 +#define HP_COUNT_M 0x7ffU +#define HP_COUNT_G(x) (((x) >> HP_COUNT_S) & HP_COUNT_M) + +#define LP_INT_THRESH_S 12 +#define LP_INT_THRESH_M 0xfU +#define LP_INT_THRESH_V(x) ((x) << LP_INT_THRESH_S) + +#define LP_COUNT_S 0 +#define LP_COUNT_M 0x7ffU +#define LP_COUNT_G(x) (((x) >> LP_COUNT_S) & LP_COUNT_M) + +#define LP_INT_THRESH_T5_S 18 +#define LP_INT_THRESH_T5_M 0xfffU +#define LP_INT_THRESH_T5_V(x) ((x) << LP_INT_THRESH_T5_S) + +#define LP_COUNT_T5_S 0 +#define LP_COUNT_T5_M 0x3ffffU +#define LP_COUNT_T5_G(x) (((x) >> LP_COUNT_T5_S) & LP_COUNT_T5_M) + +#define SGE_DOORBELL_CONTROL_A 0x10a8 + +#define SGE_STAT_TOTAL_A 0x10e4 +#define SGE_STAT_MATCH_A 0x10e8 +#define SGE_STAT_CFG_A 0x10ec + +#define STATSOURCE_T5_S 9 +#define STATSOURCE_T5_V(x) ((x) << STATSOURCE_T5_S) + +#define SGE_DBFIFO_STATUS2_A 0x1118 + +#define HP_INT_THRESH_T5_S 10 +#define HP_INT_THRESH_T5_M 0xfU +#define HP_INT_THRESH_T5_V(x) ((x) << HP_INT_THRESH_T5_S) + +#define HP_COUNT_T5_S 0 +#define HP_COUNT_T5_M 0x3ffU +#define HP_COUNT_T5_G(x) (((x) >> HP_COUNT_T5_S) & HP_COUNT_T5_M) + +#define ENABLE_DROP_S 13 +#define ENABLE_DROP_V(x) ((x) << ENABLE_DROP_S) +#define ENABLE_DROP_F ENABLE_DROP_V(1U) + +#define DROPPED_DB_S 0 +#define DROPPED_DB_V(x) ((x) << DROPPED_DB_S) +#define DROPPED_DB_F DROPPED_DB_V(1U) + +#define SGE_CTXT_CMD_A 0x11fc +#define SGE_DBQ_CTXT_BADDR_A 0x1084 + +/* registers for module PCIE */ +#define PCIE_PF_CFG_A 0x40 + +#define AIVEC_S 4 +#define AIVEC_M 0x3ffU +#define AIVEC_V(x) ((x) << AIVEC_S) + +#define PCIE_PF_CLI_A 0x44 +#define PCIE_INT_CAUSE_A 0x3004 + +#define UNXSPLCPLERR_S 29 +#define UNXSPLCPLERR_V(x) ((x) << UNXSPLCPLERR_S) +#define UNXSPLCPLERR_F UNXSPLCPLERR_V(1U) + +#define PCIEPINT_S 28 +#define PCIEPINT_V(x) ((x) << PCIEPINT_S) +#define PCIEPINT_F PCIEPINT_V(1U) + +#define PCIESINT_S 27 +#define PCIESINT_V(x) ((x) << PCIESINT_S) +#define PCIESINT_F PCIESINT_V(1U) + +#define RPLPERR_S 26 +#define RPLPERR_V(x) ((x) << RPLPERR_S) +#define RPLPERR_F RPLPERR_V(1U) + +#define RXWRPERR_S 25 +#define RXWRPERR_V(x) ((x) << RXWRPERR_S) +#define RXWRPERR_F RXWRPERR_V(1U) + +#define RXCPLPERR_S 24 +#define RXCPLPERR_V(x) ((x) << RXCPLPERR_S) +#define RXCPLPERR_F RXCPLPERR_V(1U) + +#define PIOTAGPERR_S 23 +#define PIOTAGPERR_V(x) ((x) << PIOTAGPERR_S) +#define PIOTAGPERR_F PIOTAGPERR_V(1U) + +#define MATAGPERR_S 22 +#define MATAGPERR_V(x) ((x) << MATAGPERR_S) +#define MATAGPERR_F MATAGPERR_V(1U) + +#define INTXCLRPERR_S 21 +#define INTXCLRPERR_V(x) ((x) << INTXCLRPERR_S) +#define INTXCLRPERR_F INTXCLRPERR_V(1U) + +#define FIDPERR_S 20 +#define FIDPERR_V(x) ((x) << FIDPERR_S) +#define FIDPERR_F FIDPERR_V(1U) + +#define CFGSNPPERR_S 19 +#define CFGSNPPERR_V(x) ((x) << CFGSNPPERR_S) +#define CFGSNPPERR_F CFGSNPPERR_V(1U) + +#define HRSPPERR_S 18 +#define HRSPPERR_V(x) ((x) << HRSPPERR_S) +#define HRSPPERR_F HRSPPERR_V(1U) + +#define HREQPERR_S 17 +#define HREQPERR_V(x) ((x) << HREQPERR_S) +#define HREQPERR_F HREQPERR_V(1U) + +#define HCNTPERR_S 16 +#define HCNTPERR_V(x) ((x) << HCNTPERR_S) +#define HCNTPERR_F HCNTPERR_V(1U) + +#define DRSPPERR_S 15 +#define DRSPPERR_V(x) ((x) << DRSPPERR_S) +#define DRSPPERR_F DRSPPERR_V(1U) + +#define DREQPERR_S 14 +#define DREQPERR_V(x) ((x) << DREQPERR_S) +#define DREQPERR_F DREQPERR_V(1U) + +#define DCNTPERR_S 13 +#define DCNTPERR_V(x) ((x) << DCNTPERR_S) +#define DCNTPERR_F DCNTPERR_V(1U) + +#define CRSPPERR_S 12 +#define CRSPPERR_V(x) ((x) << CRSPPERR_S) +#define CRSPPERR_F CRSPPERR_V(1U) + +#define CREQPERR_S 11 +#define CREQPERR_V(x) ((x) << CREQPERR_S) +#define CREQPERR_F CREQPERR_V(1U) + +#define CCNTPERR_S 10 +#define CCNTPERR_V(x) ((x) << CCNTPERR_S) +#define CCNTPERR_F CCNTPERR_V(1U) + +#define TARTAGPERR_S 9 +#define TARTAGPERR_V(x) ((x) << TARTAGPERR_S) +#define TARTAGPERR_F TARTAGPERR_V(1U) + +#define PIOREQPERR_S 8 +#define PIOREQPERR_V(x) ((x) << PIOREQPERR_S) +#define PIOREQPERR_F PIOREQPERR_V(1U) + +#define PIOCPLPERR_S 7 +#define PIOCPLPERR_V(x) ((x) << PIOCPLPERR_S) +#define PIOCPLPERR_F PIOCPLPERR_V(1U) + +#define MSIXDIPERR_S 6 +#define MSIXDIPERR_V(x) ((x) << MSIXDIPERR_S) +#define MSIXDIPERR_F MSIXDIPERR_V(1U) + +#define MSIXDATAPERR_S 5 +#define MSIXDATAPERR_V(x) ((x) << MSIXDATAPERR_S) +#define MSIXDATAPERR_F MSIXDATAPERR_V(1U) + +#define MSIXADDRHPERR_S 4 +#define MSIXADDRHPERR_V(x) ((x) << MSIXADDRHPERR_S) +#define MSIXADDRHPERR_F MSIXADDRHPERR_V(1U) + +#define MSIXADDRLPERR_S 3 +#define MSIXADDRLPERR_V(x) ((x) << MSIXADDRLPERR_S) +#define MSIXADDRLPERR_F MSIXADDRLPERR_V(1U) + +#define MSIDATAPERR_S 2 +#define MSIDATAPERR_V(x) ((x) << MSIDATAPERR_S) +#define MSIDATAPERR_F MSIDATAPERR_V(1U) + +#define MSIADDRHPERR_S 1 +#define MSIADDRHPERR_V(x) ((x) << MSIADDRHPERR_S) +#define MSIADDRHPERR_F MSIADDRHPERR_V(1U) + +#define MSIADDRLPERR_S 0 +#define MSIADDRLPERR_V(x) ((x) << MSIADDRLPERR_S) +#define MSIADDRLPERR_F MSIADDRLPERR_V(1U) + +#define READRSPERR_S 29 +#define READRSPERR_V(x) ((x) << READRSPERR_S) +#define READRSPERR_F READRSPERR_V(1U) + +#define TRGT1GRPPERR_S 28 +#define TRGT1GRPPERR_V(x) ((x) << TRGT1GRPPERR_S) +#define TRGT1GRPPERR_F TRGT1GRPPERR_V(1U) + +#define IPSOTPERR_S 27 +#define IPSOTPERR_V(x) ((x) << IPSOTPERR_S) +#define IPSOTPERR_F IPSOTPERR_V(1U) + +#define IPRETRYPERR_S 26 +#define IPRETRYPERR_V(x) ((x) << IPRETRYPERR_S) +#define IPRETRYPERR_F IPRETRYPERR_V(1U) + +#define IPRXDATAGRPPERR_S 25 +#define IPRXDATAGRPPERR_V(x) ((x) << IPRXDATAGRPPERR_S) +#define IPRXDATAGRPPERR_F IPRXDATAGRPPERR_V(1U) + +#define IPRXHDRGRPPERR_S 24 +#define IPRXHDRGRPPERR_V(x) ((x) << IPRXHDRGRPPERR_S) +#define IPRXHDRGRPPERR_F IPRXHDRGRPPERR_V(1U) + +#define MAGRPPERR_S 22 +#define MAGRPPERR_V(x) ((x) << MAGRPPERR_S) +#define MAGRPPERR_F MAGRPPERR_V(1U) + +#define VFIDPERR_S 21 +#define VFIDPERR_V(x) ((x) << VFIDPERR_S) +#define VFIDPERR_F VFIDPERR_V(1U) + +#define HREQWRPERR_S 16 +#define HREQWRPERR_V(x) ((x) << HREQWRPERR_S) +#define HREQWRPERR_F HREQWRPERR_V(1U) + +#define DREQWRPERR_S 13 +#define DREQWRPERR_V(x) ((x) << DREQWRPERR_S) +#define DREQWRPERR_F DREQWRPERR_V(1U) + +#define CREQRDPERR_S 11 +#define CREQRDPERR_V(x) ((x) << CREQRDPERR_S) +#define CREQRDPERR_F CREQRDPERR_V(1U) + +#define MSTTAGQPERR_S 10 +#define MSTTAGQPERR_V(x) ((x) << MSTTAGQPERR_S) +#define MSTTAGQPERR_F MSTTAGQPERR_V(1U) + +#define PIOREQGRPPERR_S 8 +#define PIOREQGRPPERR_V(x) ((x) << PIOREQGRPPERR_S) +#define PIOREQGRPPERR_F PIOREQGRPPERR_V(1U) + +#define PIOCPLGRPPERR_S 7 +#define PIOCPLGRPPERR_V(x) ((x) << PIOCPLGRPPERR_S) +#define PIOCPLGRPPERR_F PIOCPLGRPPERR_V(1U) + +#define MSIXSTIPERR_S 2 +#define MSIXSTIPERR_V(x) ((x) << MSIXSTIPERR_S) +#define MSIXSTIPERR_F MSIXSTIPERR_V(1U) + +#define MSTTIMEOUTPERR_S 1 +#define MSTTIMEOUTPERR_V(x) ((x) << MSTTIMEOUTPERR_S) +#define MSTTIMEOUTPERR_F MSTTIMEOUTPERR_V(1U) + +#define MSTGRPPERR_S 0 +#define MSTGRPPERR_V(x) ((x) << MSTGRPPERR_S) +#define MSTGRPPERR_F MSTGRPPERR_V(1U) + +#define PCIE_NONFAT_ERR_A 0x3010 +#define PCIE_CFG_SPACE_REQ_A 0x3060 +#define PCIE_CFG_SPACE_DATA_A 0x3064 +#define PCIE_MEM_ACCESS_BASE_WIN_A 0x3068 + +#define PCIEOFST_S 10 +#define PCIEOFST_M 0x3fffffU +#define PCIEOFST_G(x) (((x) >> PCIEOFST_S) & PCIEOFST_M) + +#define BIR_S 8 +#define BIR_M 0x3U +#define BIR_V(x) ((x) << BIR_S) +#define BIR_G(x) (((x) >> BIR_S) & BIR_M) + +#define WINDOW_S 0 +#define WINDOW_M 0xffU +#define WINDOW_V(x) ((x) << WINDOW_S) +#define WINDOW_G(x) (((x) >> WINDOW_S) & WINDOW_M) + +#define PCIE_MEM_ACCESS_OFFSET_A 0x306c + +#define ENABLE_S 30 +#define ENABLE_V(x) ((x) << ENABLE_S) +#define ENABLE_F ENABLE_V(1U) + +#define LOCALCFG_S 28 +#define LOCALCFG_V(x) ((x) << LOCALCFG_S) +#define LOCALCFG_F LOCALCFG_V(1U) + +#define FUNCTION_S 12 +#define FUNCTION_V(x) ((x) << FUNCTION_S) + +#define REGISTER_S 0 +#define REGISTER_V(x) ((x) << REGISTER_S) + +#define PFNUM_S 0 +#define PFNUM_V(x) ((x) << PFNUM_S) + +#define PCIE_FW_A 0x30b8 + +#define PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS_A 0x5908 + +#define RNPP_S 31 +#define RNPP_V(x) ((x) << RNPP_S) +#define RNPP_F RNPP_V(1U) + +#define RPCP_S 29 +#define RPCP_V(x) ((x) << RPCP_S) +#define RPCP_F RPCP_V(1U) + +#define RCIP_S 27 +#define RCIP_V(x) ((x) << RCIP_S) +#define RCIP_F RCIP_V(1U) + +#define RCCP_S 26 +#define RCCP_V(x) ((x) << RCCP_S) +#define RCCP_F RCCP_V(1U) + +#define RFTP_S 23 +#define RFTP_V(x) ((x) << RFTP_S) +#define RFTP_F RFTP_V(1U) + +#define PTRP_S 20 +#define PTRP_V(x) ((x) << PTRP_S) +#define PTRP_F PTRP_V(1U) + +#define PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS_A 0x59a4 + +#define TPCP_S 30 +#define TPCP_V(x) ((x) << TPCP_S) +#define TPCP_F TPCP_V(1U) + +#define TNPP_S 29 +#define TNPP_V(x) ((x) << TNPP_S) +#define TNPP_F TNPP_V(1U) + +#define TFTP_S 28 +#define TFTP_V(x) ((x) << TFTP_S) +#define TFTP_F TFTP_V(1U) + +#define TCAP_S 27 +#define TCAP_V(x) ((x) << TCAP_S) +#define TCAP_F TCAP_V(1U) + +#define TCIP_S 26 +#define TCIP_V(x) ((x) << TCIP_S) +#define TCIP_F TCIP_V(1U) + +#define RCAP_S 25 +#define RCAP_V(x) ((x) << RCAP_S) +#define RCAP_F RCAP_V(1U) + +#define PLUP_S 23 +#define PLUP_V(x) ((x) << PLUP_S) +#define PLUP_F PLUP_V(1U) + +#define PLDN_S 22 +#define PLDN_V(x) ((x) << PLDN_S) +#define PLDN_F PLDN_V(1U) + +#define OTDD_S 21 +#define OTDD_V(x) ((x) << OTDD_S) +#define OTDD_F OTDD_V(1U) + +#define GTRP_S 20 +#define GTRP_V(x) ((x) << GTRP_S) +#define GTRP_F GTRP_V(1U) + +#define RDPE_S 18 +#define RDPE_V(x) ((x) << RDPE_S) +#define RDPE_F RDPE_V(1U) + +#define TDCE_S 17 +#define TDCE_V(x) ((x) << TDCE_S) +#define TDCE_F TDCE_V(1U) + +#define TDUE_S 16 +#define TDUE_V(x) ((x) << TDUE_S) +#define TDUE_F TDUE_V(1U) #define MC_INT_CAUSE 0x7518 #define MC_P_INT_CAUSE 0x41318 @@ -663,7 +891,6 @@ #define MEM_WRAP_CLIENT_NUM_MASK 0x0000000fU #define MEM_WRAP_CLIENT_NUM_SHIFT 0 #define MEM_WRAP_CLIENT_NUM_GET(x) (((x) & MEM_WRAP_CLIENT_NUM_MASK) >> MEM_WRAP_CLIENT_NUM_SHIFT) -#define MA_PCIE_FW 0x30b8 #define MA_PARITY_ERROR_STATUS 0x77f4 #define MA_PARITY_ERROR_STATUS2 0x7804 diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h index 7699686..ecf7459 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h @@ -78,4 +78,8 @@ #define SGE_UDB_GTS 20 #define SGE_UDB_WCDOORBELL 64 +/* PCI-E definitions */ +#define WINDOW_SHIFT_X 10 +#define PCIEOFST_SHIFT_X 10 + #endif /* __T4_VALUES_H__ */ diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index b645e33..aa9f9cd 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -2294,17 +2294,17 @@ static int adap_init0(struct adapter *adapter) * threshold values from the SGE parameters. */ s->timer_val[0] = core_ticks_to_us(adapter, - TIMERVALUE0_GET(sge_params->sge_timer_value_0_and_1)); + TIMERVALUE0_G(sge_params->sge_timer_value_0_and_1)); s->timer_val[1] = core_ticks_to_us(adapter, - TIMERVALUE1_GET(sge_params->sge_timer_value_0_and_1)); + TIMERVALUE1_G(sge_params->sge_timer_value_0_and_1)); s->timer_val[2] = core_ticks_to_us(adapter, - TIMERVALUE0_GET(sge_params->sge_timer_value_2_and_3)); + TIMERVALUE0_G(sge_params->sge_timer_value_2_and_3)); s->timer_val[3] = core_ticks_to_us(adapter, - TIMERVALUE1_GET(sge_params->sge_timer_value_2_and_3)); + TIMERVALUE1_G(sge_params->sge_timer_value_2_and_3)); s->timer_val[4] = core_ticks_to_us(adapter, - TIMERVALUE0_GET(sge_params->sge_timer_value_4_and_5)); + TIMERVALUE0_G(sge_params->sge_timer_value_4_and_5)); s->timer_val[5] = core_ticks_to_us(adapter, - TIMERVALUE1_GET(sge_params->sge_timer_value_4_and_5)); + TIMERVALUE1_G(sge_params->sge_timer_value_4_and_5)); s->counter_val[0] = THRESHOLD_0_G(sge_params->sge_ingress_rx_threshold); s->counter_val[1] = THRESHOLD_1_G(sge_params->sge_ingress_rx_threshold); diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c index 6929d1f..fa60f71 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c @@ -537,11 +537,11 @@ int t4vf_get_sge_params(struct adapter *adapter) params[3] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | FW_PARAMS_PARAM_XYZ_V(SGE_FL_BUFFER_SIZE1_A)); params[4] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_TIMER_VALUE_0_AND_1)); + FW_PARAMS_PARAM_XYZ_V(SGE_TIMER_VALUE_0_AND_1_A)); params[5] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_TIMER_VALUE_2_AND_3)); + FW_PARAMS_PARAM_XYZ_V(SGE_TIMER_VALUE_2_AND_3_A)); params[6] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_TIMER_VALUE_4_AND_5)); + FW_PARAMS_PARAM_XYZ_V(SGE_TIMER_VALUE_4_AND_5_A)); v = t4vf_query_params(adapter, 7, params, vals); if (v) return v; diff --git a/drivers/scsi/csiostor/csio_hw.c b/drivers/scsi/csiostor/csio_hw.c index 890d93a..04ec586 100644 --- a/drivers/scsi/csiostor/csio_hw.c +++ b/drivers/scsi/csiostor/csio_hw.c @@ -959,8 +959,8 @@ retry: * timeout ... and then retry if we haven't exhausted * our retries ... */ - pcie_fw = csio_rd_reg32(hw, PCIE_FW); - if (!(pcie_fw & (PCIE_FW_ERR|PCIE_FW_INIT))) { + pcie_fw = csio_rd_reg32(hw, PCIE_FW_A); + if (!(pcie_fw & (PCIE_FW_ERR_F|PCIE_FW_INIT_F))) { if (waiting <= 0) { if (retries-- > 0) goto retry; @@ -976,10 +976,10 @@ retry: * report errors preferentially. */ if (state) { - if (pcie_fw & PCIE_FW_ERR) { + if (pcie_fw & PCIE_FW_ERR_F) { *state = CSIO_DEV_STATE_ERR; rv = -ETIMEDOUT; - } else if (pcie_fw & PCIE_FW_INIT) + } else if (pcie_fw & PCIE_FW_INIT_F) *state = CSIO_DEV_STATE_INIT; } @@ -988,9 +988,9 @@ retry: * there's not a valid Master PF, grab its identity * for our caller. */ - if (mpfn == PCIE_FW_MASTER_MASK && - (pcie_fw & PCIE_FW_MASTER_VLD)) - mpfn = PCIE_FW_MASTER_GET(pcie_fw); + if (mpfn == PCIE_FW_MASTER_M && + (pcie_fw & PCIE_FW_MASTER_VLD_F)) + mpfn = PCIE_FW_MASTER_G(pcie_fw); break; } hw->flags &= ~CSIO_HWF_MASTER; @@ -1156,7 +1156,7 @@ csio_hw_fw_halt(struct csio_hw *hw, uint32_t mbox, int32_t force) * If a legitimate mailbox is provided, issue a RESET command * with a HALT indication. */ - if (mbox <= PCIE_FW_MASTER_MASK) { + if (mbox <= PCIE_FW_MASTER_M) { struct csio_mb *mbp; mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC); @@ -1194,7 +1194,8 @@ csio_hw_fw_halt(struct csio_hw *hw, uint32_t mbox, int32_t force) */ if (retval == 0 || force) { csio_set_reg_field(hw, CIM_BOOT_CFG, UPCRST, UPCRST); - csio_set_reg_field(hw, PCIE_FW, PCIE_FW_HALT, PCIE_FW_HALT); + csio_set_reg_field(hw, PCIE_FW_A, PCIE_FW_HALT_F, + PCIE_FW_HALT_F); } /* @@ -1234,7 +1235,7 @@ csio_hw_fw_restart(struct csio_hw *hw, uint32_t mbox, int32_t reset) * doing it automatically, we need to clear the PCIE_FW.HALT * bit. */ - csio_set_reg_field(hw, PCIE_FW, PCIE_FW_HALT, 0); + csio_set_reg_field(hw, PCIE_FW_A, PCIE_FW_HALT_F, 0); /* * If we've been given a valid mailbox, first try to get the @@ -1243,7 +1244,7 @@ csio_hw_fw_restart(struct csio_hw *hw, uint32_t mbox, int32_t reset) * valid mailbox or the RESET command failed, fall back to * hitting the chip with a hammer. */ - if (mbox <= PCIE_FW_MASTER_MASK) { + if (mbox <= PCIE_FW_MASTER_M) { csio_set_reg_field(hw, CIM_BOOT_CFG, UPCRST, 0); msleep(100); if (csio_do_reset(hw, true) == 0) @@ -1257,7 +1258,7 @@ csio_hw_fw_restart(struct csio_hw *hw, uint32_t mbox, int32_t reset) csio_set_reg_field(hw, CIM_BOOT_CFG, UPCRST, 0); for (ms = 0; ms < FW_CMD_MAX_TIMEOUT; ) { - if (!(csio_rd_reg32(hw, PCIE_FW) & PCIE_FW_HALT)) + if (!(csio_rd_reg32(hw, PCIE_FW_A) & PCIE_FW_HALT_F)) return 0; msleep(100); ms += 100; @@ -2237,11 +2238,11 @@ csio_hw_intr_enable(struct csio_hw *hw) * by FW, so do nothing for INTX. */ if (hw->intr_mode == CSIO_IM_MSIX) - csio_set_reg_field(hw, MYPF_REG(PCIE_PF_CFG), - AIVEC(AIVEC_MASK), vec); + csio_set_reg_field(hw, MYPF_REG(PCIE_PF_CFG_A), + AIVEC_V(AIVEC_M), vec); else if (hw->intr_mode == CSIO_IM_MSI) - csio_set_reg_field(hw, MYPF_REG(PCIE_PF_CFG), - AIVEC(AIVEC_MASK), 0); + csio_set_reg_field(hw, MYPF_REG(PCIE_PF_CFG_A), + AIVEC_V(AIVEC_M), 0); csio_wr_reg32(hw, PF_INTR_MASK, MYPF_REG(PL_PF_INT_ENABLE)); diff --git a/drivers/scsi/csiostor/csio_hw_chip.h b/drivers/scsi/csiostor/csio_hw_chip.h index 3bc9cf4..0198662 100644 --- a/drivers/scsi/csiostor/csio_hw_chip.h +++ b/drivers/scsi/csiostor/csio_hw_chip.h @@ -71,10 +71,10 @@ static inline int csio_is_t5(uint16_t chip) #define CSIO_HW_LP_INT_THRESH(hw, val) \ (csio_is_t4(hw->chip_id) ? (LP_INT_THRESH_V(val)) : \ - (V_LP_INT_THRESH_T5(val))) + (LP_INT_THRESH_T5_V(val))) #define CSIO_HW_M_LP_INT_THRESH(hw) \ - (csio_is_t4(hw->chip_id) ? (LP_INT_THRESH_M) : (M_LP_INT_THRESH_T5)) + (csio_is_t4(hw->chip_id) ? (LP_INT_THRESH_M) : (LP_INT_THRESH_T5_M)) #define CSIO_MAC_INT_CAUSE_REG(hw, port) \ (csio_is_t4(hw->chip_id) ? (PORT_REG(port, XGMAC_PORT_INT_CAUSE)) : \ diff --git a/drivers/scsi/csiostor/csio_hw_t4.c b/drivers/scsi/csiostor/csio_hw_t4.c index 95d8318..d0dac67 100644 --- a/drivers/scsi/csiostor/csio_hw_t4.c +++ b/drivers/scsi/csiostor/csio_hw_t4.c @@ -96,11 +96,11 @@ csio_t4_set_mem_win(struct csio_hw *hw, uint32_t win) * back MA register to ensure that changes propagate before we attempt * to use the new values.) */ - csio_wr_reg32(hw, mem_win_base | BIR(0) | - WINDOW(ilog2(MEMWIN_APERTURE) - 10), - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win)); + csio_wr_reg32(hw, mem_win_base | BIR_V(0) | + WINDOW_V(ilog2(MEMWIN_APERTURE) - 10), + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, win)); csio_rd_reg32(hw, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win)); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, win)); return 0; } @@ -111,69 +111,69 @@ static void csio_t4_pcie_intr_handler(struct csio_hw *hw) { static struct intr_info sysbus_intr_info[] = { - { RNPP, "RXNP array parity error", -1, 1 }, - { RPCP, "RXPC array parity error", -1, 1 }, - { RCIP, "RXCIF array parity error", -1, 1 }, - { RCCP, "Rx completions control array parity error", -1, 1 }, - { RFTP, "RXFT array parity error", -1, 1 }, + { RNPP_F, "RXNP array parity error", -1, 1 }, + { RPCP_F, "RXPC array parity error", -1, 1 }, + { RCIP_F, "RXCIF array parity error", -1, 1 }, + { RCCP_F, "Rx completions control array parity error", -1, 1 }, + { RFTP_F, "RXFT array parity error", -1, 1 }, { 0, NULL, 0, 0 } }; static struct intr_info pcie_port_intr_info[] = { - { TPCP, "TXPC array parity error", -1, 1 }, - { TNPP, "TXNP array parity error", -1, 1 }, - { TFTP, "TXFT array parity error", -1, 1 }, - { TCAP, "TXCA array parity error", -1, 1 }, - { TCIP, "TXCIF array parity error", -1, 1 }, - { RCAP, "RXCA array parity error", -1, 1 }, - { OTDD, "outbound request TLP discarded", -1, 1 }, - { RDPE, "Rx data parity error", -1, 1 }, - { TDUE, "Tx uncorrectable data error", -1, 1 }, + { TPCP_F, "TXPC array parity error", -1, 1 }, + { TNPP_F, "TXNP array parity error", -1, 1 }, + { TFTP_F, "TXFT array parity error", -1, 1 }, + { TCAP_F, "TXCA array parity error", -1, 1 }, + { TCIP_F, "TXCIF array parity error", -1, 1 }, + { RCAP_F, "RXCA array parity error", -1, 1 }, + { OTDD_F, "outbound request TLP discarded", -1, 1 }, + { RDPE_F, "Rx data parity error", -1, 1 }, + { TDUE_F, "Tx uncorrectable data error", -1, 1 }, { 0, NULL, 0, 0 } }; static struct intr_info pcie_intr_info[] = { - { MSIADDRLPERR, "MSI AddrL parity error", -1, 1 }, - { MSIADDRHPERR, "MSI AddrH parity error", -1, 1 }, - { MSIDATAPERR, "MSI data parity error", -1, 1 }, - { MSIXADDRLPERR, "MSI-X AddrL parity error", -1, 1 }, - { MSIXADDRHPERR, "MSI-X AddrH parity error", -1, 1 }, - { MSIXDATAPERR, "MSI-X data parity error", -1, 1 }, - { MSIXDIPERR, "MSI-X DI parity error", -1, 1 }, - { PIOCPLPERR, "PCI PIO completion FIFO parity error", -1, 1 }, - { PIOREQPERR, "PCI PIO request FIFO parity error", -1, 1 }, - { TARTAGPERR, "PCI PCI target tag FIFO parity error", -1, 1 }, - { CCNTPERR, "PCI CMD channel count parity error", -1, 1 }, - { CREQPERR, "PCI CMD channel request parity error", -1, 1 }, - { CRSPPERR, "PCI CMD channel response parity error", -1, 1 }, - { DCNTPERR, "PCI DMA channel count parity error", -1, 1 }, - { DREQPERR, "PCI DMA channel request parity error", -1, 1 }, - { DRSPPERR, "PCI DMA channel response parity error", -1, 1 }, - { HCNTPERR, "PCI HMA channel count parity error", -1, 1 }, - { HREQPERR, "PCI HMA channel request parity error", -1, 1 }, - { HRSPPERR, "PCI HMA channel response parity error", -1, 1 }, - { CFGSNPPERR, "PCI config snoop FIFO parity error", -1, 1 }, - { FIDPERR, "PCI FID parity error", -1, 1 }, - { INTXCLRPERR, "PCI INTx clear parity error", -1, 1 }, - { MATAGPERR, "PCI MA tag parity error", -1, 1 }, - { PIOTAGPERR, "PCI PIO tag parity error", -1, 1 }, - { RXCPLPERR, "PCI Rx completion parity error", -1, 1 }, - { RXWRPERR, "PCI Rx write parity error", -1, 1 }, - { RPLPERR, "PCI replay buffer parity error", -1, 1 }, - { PCIESINT, "PCI core secondary fault", -1, 1 }, - { PCIEPINT, "PCI core primary fault", -1, 1 }, - { UNXSPLCPLERR, "PCI unexpected split completion error", -1, + { MSIADDRLPERR_F, "MSI AddrL parity error", -1, 1 }, + { MSIADDRHPERR_F, "MSI AddrH parity error", -1, 1 }, + { MSIDATAPERR_F, "MSI data parity error", -1, 1 }, + { MSIXADDRLPERR_F, "MSI-X AddrL parity error", -1, 1 }, + { MSIXADDRHPERR_F, "MSI-X AddrH parity error", -1, 1 }, + { MSIXDATAPERR_F, "MSI-X data parity error", -1, 1 }, + { MSIXDIPERR_F, "MSI-X DI parity error", -1, 1 }, + { PIOCPLPERR_F, "PCI PIO completion FIFO parity error", -1, 1 }, + { PIOREQPERR_F, "PCI PIO request FIFO parity error", -1, 1 }, + { TARTAGPERR_F, "PCI PCI target tag FIFO parity error", -1, 1 }, + { CCNTPERR_F, "PCI CMD channel count parity error", -1, 1 }, + { CREQPERR_F, "PCI CMD channel request parity error", -1, 1 }, + { CRSPPERR_F, "PCI CMD channel response parity error", -1, 1 }, + { DCNTPERR_F, "PCI DMA channel count parity error", -1, 1 }, + { DREQPERR_F, "PCI DMA channel request parity error", -1, 1 }, + { DRSPPERR_F, "PCI DMA channel response parity error", -1, 1 }, + { HCNTPERR_F, "PCI HMA channel count parity error", -1, 1 }, + { HREQPERR_F, "PCI HMA channel request parity error", -1, 1 }, + { HRSPPERR_F, "PCI HMA channel response parity error", -1, 1 }, + { CFGSNPPERR_F, "PCI config snoop FIFO parity error", -1, 1 }, + { FIDPERR_F, "PCI FID parity error", -1, 1 }, + { INTXCLRPERR_F, "PCI INTx clear parity error", -1, 1 }, + { MATAGPERR_F, "PCI MA tag parity error", -1, 1 }, + { PIOTAGPERR_F, "PCI PIO tag parity error", -1, 1 }, + { RXCPLPERR_F, "PCI Rx completion parity error", -1, 1 }, + { RXWRPERR_F, "PCI Rx write parity error", -1, 1 }, + { RPLPERR_F, "PCI replay buffer parity error", -1, 1 }, + { PCIESINT_F, "PCI core secondary fault", -1, 1 }, + { PCIEPINT_F, "PCI core primary fault", -1, 1 }, + { UNXSPLCPLERR_F, "PCI unexpected split completion error", -1, 0 }, { 0, NULL, 0, 0 } }; int fat; fat = csio_handle_intr_status(hw, - PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS, + PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS_A, sysbus_intr_info) + csio_handle_intr_status(hw, - PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS, + PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS_A, pcie_port_intr_info) + - csio_handle_intr_status(hw, PCIE_INT_CAUSE, pcie_intr_info); + csio_handle_intr_status(hw, PCIE_INT_CAUSE_A, pcie_intr_info); if (fat) csio_hw_fatal_err(hw); } @@ -329,9 +329,9 @@ csio_t4_memory_rw(struct csio_hw *hw, u32 win, int mtype, u32 addr, * the address is relative to BAR0. */ mem_reg = csio_rd_reg32(hw, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win)); - mem_aperture = 1 << (WINDOW(mem_reg) + 10); - mem_base = GET_PCIEOFST(mem_reg) << 10; + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, win)); + mem_aperture = 1 << (WINDOW_V(mem_reg) + 10); + mem_base = PCIEOFST_G(mem_reg) << 10; bar0 = csio_t4_read_pcie_cfg4(hw, PCI_BASE_ADDRESS_0); bar0 &= PCI_BASE_ADDRESS_MEM_MASK; @@ -356,9 +356,9 @@ csio_t4_memory_rw(struct csio_hw *hw, u32 win, int mtype, u32 addr, * before we attempt to use the new value. */ csio_wr_reg32(hw, pos, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win)); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win)); csio_rd_reg32(hw, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win)); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win)); while (offset < mem_aperture && len > 0) { if (dir) diff --git a/drivers/scsi/csiostor/csio_hw_t5.c b/drivers/scsi/csiostor/csio_hw_t5.c index 66e180a..99a7dca 100644 --- a/drivers/scsi/csiostor/csio_hw_t5.c +++ b/drivers/scsi/csiostor/csio_hw_t5.c @@ -56,11 +56,11 @@ csio_t5_set_mem_win(struct csio_hw *hw, uint32_t win) * back MA register to ensure that changes propagate before we attempt * to use the new values.) */ - csio_wr_reg32(hw, mem_win_base | BIR(0) | - WINDOW(ilog2(MEMWIN_APERTURE) - 10), - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win)); + csio_wr_reg32(hw, mem_win_base | BIR_V(0) | + WINDOW_V(ilog2(MEMWIN_APERTURE) - 10), + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, win)); csio_rd_reg32(hw, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win)); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, win)); return 0; } @@ -72,74 +72,74 @@ static void csio_t5_pcie_intr_handler(struct csio_hw *hw) { static struct intr_info sysbus_intr_info[] = { - { RNPP, "RXNP array parity error", -1, 1 }, - { RPCP, "RXPC array parity error", -1, 1 }, - { RCIP, "RXCIF array parity error", -1, 1 }, - { RCCP, "Rx completions control array parity error", -1, 1 }, - { RFTP, "RXFT array parity error", -1, 1 }, + { RNPP_F, "RXNP array parity error", -1, 1 }, + { RPCP_F, "RXPC array parity error", -1, 1 }, + { RCIP_F, "RXCIF array parity error", -1, 1 }, + { RCCP_F, "Rx completions control array parity error", -1, 1 }, + { RFTP_F, "RXFT array parity error", -1, 1 }, { 0, NULL, 0, 0 } }; static struct intr_info pcie_port_intr_info[] = { - { TPCP, "TXPC array parity error", -1, 1 }, - { TNPP, "TXNP array parity error", -1, 1 }, - { TFTP, "TXFT array parity error", -1, 1 }, - { TCAP, "TXCA array parity error", -1, 1 }, - { TCIP, "TXCIF array parity error", -1, 1 }, - { RCAP, "RXCA array parity error", -1, 1 }, - { OTDD, "outbound request TLP discarded", -1, 1 }, - { RDPE, "Rx data parity error", -1, 1 }, - { TDUE, "Tx uncorrectable data error", -1, 1 }, + { TPCP_F, "TXPC array parity error", -1, 1 }, + { TNPP_F, "TXNP array parity error", -1, 1 }, + { TFTP_F, "TXFT array parity error", -1, 1 }, + { TCAP_F, "TXCA array parity error", -1, 1 }, + { TCIP_F, "TXCIF array parity error", -1, 1 }, + { RCAP_F, "RXCA array parity error", -1, 1 }, + { OTDD_F, "outbound request TLP discarded", -1, 1 }, + { RDPE_F, "Rx data parity error", -1, 1 }, + { TDUE_F, "Tx uncorrectable data error", -1, 1 }, { 0, NULL, 0, 0 } }; static struct intr_info pcie_intr_info[] = { - { MSTGRPPERR, "Master Response Read Queue parity error", + { MSTGRPPERR_F, "Master Response Read Queue parity error", -1, 1 }, - { MSTTIMEOUTPERR, "Master Timeout FIFO parity error", -1, 1 }, - { MSIXSTIPERR, "MSI-X STI SRAM parity error", -1, 1 }, - { MSIXADDRLPERR, "MSI-X AddrL parity error", -1, 1 }, - { MSIXADDRHPERR, "MSI-X AddrH parity error", -1, 1 }, - { MSIXDATAPERR, "MSI-X data parity error", -1, 1 }, - { MSIXDIPERR, "MSI-X DI parity error", -1, 1 }, - { PIOCPLGRPPERR, "PCI PIO completion Group FIFO parity error", + { MSTTIMEOUTPERR_F, "Master Timeout FIFO parity error", -1, 1 }, + { MSIXSTIPERR_F, "MSI-X STI SRAM parity error", -1, 1 }, + { MSIXADDRLPERR_F, "MSI-X AddrL parity error", -1, 1 }, + { MSIXADDRHPERR_F, "MSI-X AddrH parity error", -1, 1 }, + { MSIXDATAPERR_F, "MSI-X data parity error", -1, 1 }, + { MSIXDIPERR_F, "MSI-X DI parity error", -1, 1 }, + { PIOCPLGRPPERR_F, "PCI PIO completion Group FIFO parity error", -1, 1 }, - { PIOREQGRPPERR, "PCI PIO request Group FIFO parity error", + { PIOREQGRPPERR_F, "PCI PIO request Group FIFO parity error", -1, 1 }, - { TARTAGPERR, "PCI PCI target tag FIFO parity error", -1, 1 }, - { MSTTAGQPERR, "PCI master tag queue parity error", -1, 1 }, - { CREQPERR, "PCI CMD channel request parity error", -1, 1 }, - { CRSPPERR, "PCI CMD channel response parity error", -1, 1 }, - { DREQWRPERR, "PCI DMA channel write request parity error", + { TARTAGPERR_F, "PCI PCI target tag FIFO parity error", -1, 1 }, + { MSTTAGQPERR_F, "PCI master tag queue parity error", -1, 1 }, + { CREQPERR_F, "PCI CMD channel request parity error", -1, 1 }, + { CRSPPERR_F, "PCI CMD channel response parity error", -1, 1 }, + { DREQWRPERR_F, "PCI DMA channel write request parity error", -1, 1 }, - { DREQPERR, "PCI DMA channel request parity error", -1, 1 }, - { DRSPPERR, "PCI DMA channel response parity error", -1, 1 }, - { HREQWRPERR, "PCI HMA channel count parity error", -1, 1 }, - { HREQPERR, "PCI HMA channel request parity error", -1, 1 }, - { HRSPPERR, "PCI HMA channel response parity error", -1, 1 }, - { CFGSNPPERR, "PCI config snoop FIFO parity error", -1, 1 }, - { FIDPERR, "PCI FID parity error", -1, 1 }, - { VFIDPERR, "PCI INTx clear parity error", -1, 1 }, - { MAGRPPERR, "PCI MA group FIFO parity error", -1, 1 }, - { PIOTAGPERR, "PCI PIO tag parity error", -1, 1 }, - { IPRXHDRGRPPERR, "PCI IP Rx header group parity error", + { DREQPERR_F, "PCI DMA channel request parity error", -1, 1 }, + { DRSPPERR_F, "PCI DMA channel response parity error", -1, 1 }, + { HREQWRPERR_F, "PCI HMA channel count parity error", -1, 1 }, + { HREQPERR_F, "PCI HMA channel request parity error", -1, 1 }, + { HRSPPERR_F, "PCI HMA channel response parity error", -1, 1 }, + { CFGSNPPERR_F, "PCI config snoop FIFO parity error", -1, 1 }, + { FIDPERR_F, "PCI FID parity error", -1, 1 }, + { VFIDPERR_F, "PCI INTx clear parity error", -1, 1 }, + { MAGRPPERR_F, "PCI MA group FIFO parity error", -1, 1 }, + { PIOTAGPERR_F, "PCI PIO tag parity error", -1, 1 }, + { IPRXHDRGRPPERR_F, "PCI IP Rx header group parity error", -1, 1 }, - { IPRXDATAGRPPERR, "PCI IP Rx data group parity error", + { IPRXDATAGRPPERR_F, "PCI IP Rx data group parity error", -1, 1 }, - { RPLPERR, "PCI IP replay buffer parity error", -1, 1 }, - { IPSOTPERR, "PCI IP SOT buffer parity error", -1, 1 }, - { TRGT1GRPPERR, "PCI TRGT1 group FIFOs parity error", -1, 1 }, - { READRSPERR, "Outbound read error", -1, 0 }, + { RPLPERR_F, "PCI IP replay buffer parity error", -1, 1 }, + { IPSOTPERR_F, "PCI IP SOT buffer parity error", -1, 1 }, + { TRGT1GRPPERR_F, "PCI TRGT1 group FIFOs parity error", -1, 1 }, + { READRSPERR_F, "Outbound read error", -1, 0 }, { 0, NULL, 0, 0 } }; int fat; fat = csio_handle_intr_status(hw, - PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS, + PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS_A, sysbus_intr_info) + csio_handle_intr_status(hw, - PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS, + PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS_A, pcie_port_intr_info) + - csio_handle_intr_status(hw, PCIE_INT_CAUSE, pcie_intr_info); + csio_handle_intr_status(hw, PCIE_INT_CAUSE_A, pcie_intr_info); if (fat) csio_hw_fatal_err(hw); } @@ -320,13 +320,13 @@ csio_t5_memory_rw(struct csio_hw *hw, u32 win, int mtype, u32 addr, * the address is relative to BAR0. */ mem_reg = csio_rd_reg32(hw, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win)); - mem_aperture = 1 << (WINDOW(mem_reg) + 10); - mem_base = GET_PCIEOFST(mem_reg) << 10; + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, win)); + mem_aperture = 1 << (WINDOW_V(mem_reg) + 10); + mem_base = PCIEOFST_G(mem_reg) << 10; start = addr & ~(mem_aperture-1); offset = addr - start; - win_pf = V_PFNUM(hw->pfn); + win_pf = PFNUM_V(hw->pfn); csio_dbg(hw, "csio_t5_memory_rw: mem_reg: 0x%x, mem_aperture: 0x%x\n", mem_reg, mem_aperture); @@ -344,9 +344,9 @@ csio_t5_memory_rw(struct csio_hw *hw, u32 win, int mtype, u32 addr, * before we attempt to use the new value. */ csio_wr_reg32(hw, pos | win_pf, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win)); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win)); csio_rd_reg32(hw, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win)); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win)); while (offset < mem_aperture && len > 0) { if (dir) diff --git a/drivers/scsi/csiostor/csio_isr.c b/drivers/scsi/csiostor/csio_isr.c index a8c748a..2fb71c6 100644 --- a/drivers/scsi/csiostor/csio_isr.c +++ b/drivers/scsi/csiostor/csio_isr.c @@ -317,7 +317,7 @@ csio_fcoe_isr(int irq, void *dev_id) /* Disable the interrupt for this PCI function. */ if (hw->intr_mode == CSIO_IM_INTX) - csio_wr_reg32(hw, 0, MYPF_REG(PCIE_PF_CLI)); + csio_wr_reg32(hw, 0, MYPF_REG(PCIE_PF_CLI_A)); /* * The read in the following function will flush the diff --git a/drivers/scsi/csiostor/csio_wr.c b/drivers/scsi/csiostor/csio_wr.c index 221433f..12697c6 100644 --- a/drivers/scsi/csiostor/csio_wr.c +++ b/drivers/scsi/csiostor/csio_wr.c @@ -1412,22 +1412,22 @@ csio_wr_get_sge(struct csio_hw *hw) for (i = 0; i < CSIO_SGE_FL_SIZE_REGS; i++) csio_get_flbuf_size(hw, sge, i); - timer_value_0_and_1 = csio_rd_reg32(hw, SGE_TIMER_VALUE_0_AND_1); - timer_value_2_and_3 = csio_rd_reg32(hw, SGE_TIMER_VALUE_2_AND_3); - timer_value_4_and_5 = csio_rd_reg32(hw, SGE_TIMER_VALUE_4_AND_5); + timer_value_0_and_1 = csio_rd_reg32(hw, SGE_TIMER_VALUE_0_AND_1_A); + timer_value_2_and_3 = csio_rd_reg32(hw, SGE_TIMER_VALUE_2_AND_3_A); + timer_value_4_and_5 = csio_rd_reg32(hw, SGE_TIMER_VALUE_4_AND_5_A); sge->timer_val[0] = (uint16_t)csio_core_ticks_to_us(hw, - TIMERVALUE0_GET(timer_value_0_and_1)); + TIMERVALUE0_G(timer_value_0_and_1)); sge->timer_val[1] = (uint16_t)csio_core_ticks_to_us(hw, - TIMERVALUE1_GET(timer_value_0_and_1)); + TIMERVALUE1_G(timer_value_0_and_1)); sge->timer_val[2] = (uint16_t)csio_core_ticks_to_us(hw, - TIMERVALUE2_GET(timer_value_2_and_3)); + TIMERVALUE2_G(timer_value_2_and_3)); sge->timer_val[3] = (uint16_t)csio_core_ticks_to_us(hw, - TIMERVALUE3_GET(timer_value_2_and_3)); + TIMERVALUE3_G(timer_value_2_and_3)); sge->timer_val[4] = (uint16_t)csio_core_ticks_to_us(hw, - TIMERVALUE4_GET(timer_value_4_and_5)); + TIMERVALUE4_G(timer_value_4_and_5)); sge->timer_val[5] = (uint16_t)csio_core_ticks_to_us(hw, - TIMERVALUE5_GET(timer_value_4_and_5)); + TIMERVALUE5_G(timer_value_4_and_5)); ingress_rx_threshold = csio_rd_reg32(hw, SGE_INGRESS_RX_THRESHOLD_A); sge->counter_val[0] = THRESHOLD_0_G(ingress_rx_threshold); @@ -1513,19 +1513,19 @@ csio_wr_set_sge(struct csio_hw *hw) SGE_INGRESS_RX_THRESHOLD_A); csio_wr_reg32(hw, - TIMERVALUE0(csio_us_to_core_ticks(hw, sge->timer_val[0])) | - TIMERVALUE1(csio_us_to_core_ticks(hw, sge->timer_val[1])), - SGE_TIMER_VALUE_0_AND_1); + TIMERVALUE0_V(csio_us_to_core_ticks(hw, sge->timer_val[0])) | + TIMERVALUE1_V(csio_us_to_core_ticks(hw, sge->timer_val[1])), + SGE_TIMER_VALUE_0_AND_1_A); csio_wr_reg32(hw, - TIMERVALUE2(csio_us_to_core_ticks(hw, sge->timer_val[2])) | - TIMERVALUE3(csio_us_to_core_ticks(hw, sge->timer_val[3])), - SGE_TIMER_VALUE_2_AND_3); + TIMERVALUE2_V(csio_us_to_core_ticks(hw, sge->timer_val[2])) | + TIMERVALUE3_V(csio_us_to_core_ticks(hw, sge->timer_val[3])), + SGE_TIMER_VALUE_2_AND_3_A); csio_wr_reg32(hw, - TIMERVALUE4(csio_us_to_core_ticks(hw, sge->timer_val[4])) | - TIMERVALUE5(csio_us_to_core_ticks(hw, sge->timer_val[5])), - SGE_TIMER_VALUE_4_AND_5); + TIMERVALUE4_V(csio_us_to_core_ticks(hw, sge->timer_val[4])) | + TIMERVALUE5_V(csio_us_to_core_ticks(hw, sge->timer_val[5])), + SGE_TIMER_VALUE_4_AND_5_A); csio_init_intr_coalesce_parms(hw); } -- cgit v0.10.2 From 89c3a86cc7e5e86b841dbd0c2a67ae7899e62fdb Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Mon, 5 Jan 2015 16:30:45 +0530 Subject: cxgb4/cxg4vf/csiostor: Cleanup MC, MA and CIM related register defines This patch cleanups all MC, MA and CIM related macros/register defines that are defined in t4_regs.h and the affected files. Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 9f8cd56..c9777e0 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -265,8 +265,8 @@ int t4_wr_mbox_meat(struct adapter *adap, int mbox, const void *cmd, int size, u64 res; int i, ms, delay_idx; const __be64 *p = cmd; - u32 data_reg = PF_REG(mbox, CIM_PF_MAILBOX_DATA); - u32 ctl_reg = PF_REG(mbox, CIM_PF_MAILBOX_CTRL); + u32 data_reg = PF_REG(mbox, CIM_PF_MAILBOX_DATA_A); + u32 ctl_reg = PF_REG(mbox, CIM_PF_MAILBOX_CTRL_A); if ((size & 15) || size > MBOX_LEN) return -EINVAL; @@ -278,9 +278,9 @@ int t4_wr_mbox_meat(struct adapter *adap, int mbox, const void *cmd, int size, if (adap->pdev->error_state != pci_channel_io_normal) return -EIO; - v = MBOWNER_GET(t4_read_reg(adap, ctl_reg)); + v = MBOWNER_G(t4_read_reg(adap, ctl_reg)); for (i = 0; v == MBOX_OWNER_NONE && i < 3; i++) - v = MBOWNER_GET(t4_read_reg(adap, ctl_reg)); + v = MBOWNER_G(t4_read_reg(adap, ctl_reg)); if (v != MBOX_OWNER_DRV) return v ? -EBUSY : -ETIMEDOUT; @@ -288,7 +288,7 @@ int t4_wr_mbox_meat(struct adapter *adap, int mbox, const void *cmd, int size, for (i = 0; i < size; i += 8) t4_write_reg64(adap, data_reg + i, be64_to_cpu(*p++)); - t4_write_reg(adap, ctl_reg, MBMSGVALID | MBOWNER(MBOX_OWNER_FW)); + t4_write_reg(adap, ctl_reg, MBMSGVALID_F | MBOWNER_V(MBOX_OWNER_FW)); t4_read_reg(adap, ctl_reg); /* flush write */ delay_idx = 0; @@ -304,8 +304,8 @@ int t4_wr_mbox_meat(struct adapter *adap, int mbox, const void *cmd, int size, mdelay(ms); v = t4_read_reg(adap, ctl_reg); - if (MBOWNER_GET(v) == MBOX_OWNER_DRV) { - if (!(v & MBMSGVALID)) { + if (MBOWNER_G(v) == MBOX_OWNER_DRV) { + if (!(v & MBMSGVALID_F)) { t4_write_reg(adap, ctl_reg, 0); continue; } @@ -351,27 +351,27 @@ int t4_mc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *ecc) u32 mc_bist_status_rdata, mc_bist_data_pattern; if (is_t4(adap->params.chip)) { - mc_bist_cmd = MC_BIST_CMD; - mc_bist_cmd_addr = MC_BIST_CMD_ADDR; - mc_bist_cmd_len = MC_BIST_CMD_LEN; - mc_bist_status_rdata = MC_BIST_STATUS_RDATA; - mc_bist_data_pattern = MC_BIST_DATA_PATTERN; + mc_bist_cmd = MC_BIST_CMD_A; + mc_bist_cmd_addr = MC_BIST_CMD_ADDR_A; + mc_bist_cmd_len = MC_BIST_CMD_LEN_A; + mc_bist_status_rdata = MC_BIST_STATUS_RDATA_A; + mc_bist_data_pattern = MC_BIST_DATA_PATTERN_A; } else { - mc_bist_cmd = MC_REG(MC_P_BIST_CMD, idx); - mc_bist_cmd_addr = MC_REG(MC_P_BIST_CMD_ADDR, idx); - mc_bist_cmd_len = MC_REG(MC_P_BIST_CMD_LEN, idx); - mc_bist_status_rdata = MC_REG(MC_P_BIST_STATUS_RDATA, idx); - mc_bist_data_pattern = MC_REG(MC_P_BIST_DATA_PATTERN, idx); + mc_bist_cmd = MC_REG(MC_P_BIST_CMD_A, idx); + mc_bist_cmd_addr = MC_REG(MC_P_BIST_CMD_ADDR_A, idx); + mc_bist_cmd_len = MC_REG(MC_P_BIST_CMD_LEN_A, idx); + mc_bist_status_rdata = MC_REG(MC_P_BIST_STATUS_RDATA_A, idx); + mc_bist_data_pattern = MC_REG(MC_P_BIST_DATA_PATTERN_A, idx); } - if (t4_read_reg(adap, mc_bist_cmd) & START_BIST) + if (t4_read_reg(adap, mc_bist_cmd) & START_BIST_F) return -EBUSY; t4_write_reg(adap, mc_bist_cmd_addr, addr & ~0x3fU); t4_write_reg(adap, mc_bist_cmd_len, 64); t4_write_reg(adap, mc_bist_data_pattern, 0xc); - t4_write_reg(adap, mc_bist_cmd, BIST_OPCODE(1) | START_BIST | - BIST_CMD_GAP(1)); - i = t4_wait_op_done(adap, mc_bist_cmd, START_BIST, 0, 10, 1); + t4_write_reg(adap, mc_bist_cmd, BIST_OPCODE_V(1) | START_BIST_F | + BIST_CMD_GAP_V(1)); + i = t4_wait_op_done(adap, mc_bist_cmd, START_BIST_F, 0, 10, 1); if (i) return i; @@ -404,31 +404,31 @@ int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *ecc) u32 edc_bist_cmd_data_pattern, edc_bist_status_rdata; if (is_t4(adap->params.chip)) { - edc_bist_cmd = EDC_REG(EDC_BIST_CMD, idx); - edc_bist_cmd_addr = EDC_REG(EDC_BIST_CMD_ADDR, idx); - edc_bist_cmd_len = EDC_REG(EDC_BIST_CMD_LEN, idx); - edc_bist_cmd_data_pattern = EDC_REG(EDC_BIST_DATA_PATTERN, - idx); - edc_bist_status_rdata = EDC_REG(EDC_BIST_STATUS_RDATA, + edc_bist_cmd = EDC_REG(EDC_BIST_CMD_A, idx); + edc_bist_cmd_addr = EDC_REG(EDC_BIST_CMD_ADDR_A, idx); + edc_bist_cmd_len = EDC_REG(EDC_BIST_CMD_LEN_A, idx); + edc_bist_cmd_data_pattern = EDC_REG(EDC_BIST_DATA_PATTERN_A, idx); + edc_bist_status_rdata = EDC_REG(EDC_BIST_STATUS_RDATA_A, + idx); } else { - edc_bist_cmd = EDC_REG_T5(EDC_H_BIST_CMD, idx); - edc_bist_cmd_addr = EDC_REG_T5(EDC_H_BIST_CMD_ADDR, idx); - edc_bist_cmd_len = EDC_REG_T5(EDC_H_BIST_CMD_LEN, idx); + edc_bist_cmd = EDC_REG_T5(EDC_H_BIST_CMD_A, idx); + edc_bist_cmd_addr = EDC_REG_T5(EDC_H_BIST_CMD_ADDR_A, idx); + edc_bist_cmd_len = EDC_REG_T5(EDC_H_BIST_CMD_LEN_A, idx); edc_bist_cmd_data_pattern = - EDC_REG_T5(EDC_H_BIST_DATA_PATTERN, idx); + EDC_REG_T5(EDC_H_BIST_DATA_PATTERN_A, idx); edc_bist_status_rdata = - EDC_REG_T5(EDC_H_BIST_STATUS_RDATA, idx); + EDC_REG_T5(EDC_H_BIST_STATUS_RDATA_A, idx); } - if (t4_read_reg(adap, edc_bist_cmd) & START_BIST) + if (t4_read_reg(adap, edc_bist_cmd) & START_BIST_F) return -EBUSY; t4_write_reg(adap, edc_bist_cmd_addr, addr & ~0x3fU); t4_write_reg(adap, edc_bist_cmd_len, 64); t4_write_reg(adap, edc_bist_cmd_data_pattern, 0xc); t4_write_reg(adap, edc_bist_cmd, - BIST_OPCODE(1) | BIST_CMD_GAP(1) | START_BIST); - i = t4_wait_op_done(adap, edc_bist_cmd, START_BIST, 0, 10, 1); + BIST_OPCODE_V(1) | BIST_CMD_GAP_V(1) | START_BIST_F); + i = t4_wait_op_done(adap, edc_bist_cmd, START_BIST_F, 0, 10, 1); if (i) return i; @@ -1543,50 +1543,55 @@ static void sge_intr_handler(struct adapter *adapter) t4_fatal_err(adapter); } +#define CIM_OBQ_INTR (OBQULP0PARERR_F | OBQULP1PARERR_F | OBQULP2PARERR_F |\ + OBQULP3PARERR_F | OBQSGEPARERR_F | OBQNCSIPARERR_F) +#define CIM_IBQ_INTR (IBQTP0PARERR_F | IBQTP1PARERR_F | IBQULPPARERR_F |\ + IBQSGEHIPARERR_F | IBQSGELOPARERR_F | IBQNCSIPARERR_F) + /* * CIM interrupt handler. */ static void cim_intr_handler(struct adapter *adapter) { static const struct intr_info cim_intr_info[] = { - { PREFDROPINT, "CIM control register prefetch drop", -1, 1 }, - { OBQPARERR, "CIM OBQ parity error", -1, 1 }, - { IBQPARERR, "CIM IBQ parity error", -1, 1 }, - { MBUPPARERR, "CIM mailbox uP parity error", -1, 1 }, - { MBHOSTPARERR, "CIM mailbox host parity error", -1, 1 }, - { TIEQINPARERRINT, "CIM TIEQ outgoing parity error", -1, 1 }, - { TIEQOUTPARERRINT, "CIM TIEQ incoming parity error", -1, 1 }, + { PREFDROPINT_F, "CIM control register prefetch drop", -1, 1 }, + { CIM_OBQ_INTR, "CIM OBQ parity error", -1, 1 }, + { CIM_IBQ_INTR, "CIM IBQ parity error", -1, 1 }, + { MBUPPARERR_F, "CIM mailbox uP parity error", -1, 1 }, + { MBHOSTPARERR_F, "CIM mailbox host parity error", -1, 1 }, + { TIEQINPARERRINT_F, "CIM TIEQ outgoing parity error", -1, 1 }, + { TIEQOUTPARERRINT_F, "CIM TIEQ incoming parity error", -1, 1 }, { 0 } }; static const struct intr_info cim_upintr_info[] = { - { RSVDSPACEINT, "CIM reserved space access", -1, 1 }, - { ILLTRANSINT, "CIM illegal transaction", -1, 1 }, - { ILLWRINT, "CIM illegal write", -1, 1 }, - { ILLRDINT, "CIM illegal read", -1, 1 }, - { ILLRDBEINT, "CIM illegal read BE", -1, 1 }, - { ILLWRBEINT, "CIM illegal write BE", -1, 1 }, - { SGLRDBOOTINT, "CIM single read from boot space", -1, 1 }, - { SGLWRBOOTINT, "CIM single write to boot space", -1, 1 }, - { BLKWRBOOTINT, "CIM block write to boot space", -1, 1 }, - { SGLRDFLASHINT, "CIM single read from flash space", -1, 1 }, - { SGLWRFLASHINT, "CIM single write to flash space", -1, 1 }, - { BLKWRFLASHINT, "CIM block write to flash space", -1, 1 }, - { SGLRDEEPROMINT, "CIM single EEPROM read", -1, 1 }, - { SGLWREEPROMINT, "CIM single EEPROM write", -1, 1 }, - { BLKRDEEPROMINT, "CIM block EEPROM read", -1, 1 }, - { BLKWREEPROMINT, "CIM block EEPROM write", -1, 1 }, - { SGLRDCTLINT , "CIM single read from CTL space", -1, 1 }, - { SGLWRCTLINT , "CIM single write to CTL space", -1, 1 }, - { BLKRDCTLINT , "CIM block read from CTL space", -1, 1 }, - { BLKWRCTLINT , "CIM block write to CTL space", -1, 1 }, - { SGLRDPLINT , "CIM single read from PL space", -1, 1 }, - { SGLWRPLINT , "CIM single write to PL space", -1, 1 }, - { BLKRDPLINT , "CIM block read from PL space", -1, 1 }, - { BLKWRPLINT , "CIM block write to PL space", -1, 1 }, - { REQOVRLOOKUPINT , "CIM request FIFO overwrite", -1, 1 }, - { RSPOVRLOOKUPINT , "CIM response FIFO overwrite", -1, 1 }, - { TIMEOUTINT , "CIM PIF timeout", -1, 1 }, - { TIMEOUTMAINT , "CIM PIF MA timeout", -1, 1 }, + { RSVDSPACEINT_F, "CIM reserved space access", -1, 1 }, + { ILLTRANSINT_F, "CIM illegal transaction", -1, 1 }, + { ILLWRINT_F, "CIM illegal write", -1, 1 }, + { ILLRDINT_F, "CIM illegal read", -1, 1 }, + { ILLRDBEINT_F, "CIM illegal read BE", -1, 1 }, + { ILLWRBEINT_F, "CIM illegal write BE", -1, 1 }, + { SGLRDBOOTINT_F, "CIM single read from boot space", -1, 1 }, + { SGLWRBOOTINT_F, "CIM single write to boot space", -1, 1 }, + { BLKWRBOOTINT_F, "CIM block write to boot space", -1, 1 }, + { SGLRDFLASHINT_F, "CIM single read from flash space", -1, 1 }, + { SGLWRFLASHINT_F, "CIM single write to flash space", -1, 1 }, + { BLKWRFLASHINT_F, "CIM block write to flash space", -1, 1 }, + { SGLRDEEPROMINT_F, "CIM single EEPROM read", -1, 1 }, + { SGLWREEPROMINT_F, "CIM single EEPROM write", -1, 1 }, + { BLKRDEEPROMINT_F, "CIM block EEPROM read", -1, 1 }, + { BLKWREEPROMINT_F, "CIM block EEPROM write", -1, 1 }, + { SGLRDCTLINT_F, "CIM single read from CTL space", -1, 1 }, + { SGLWRCTLINT_F, "CIM single write to CTL space", -1, 1 }, + { BLKRDCTLINT_F, "CIM block read from CTL space", -1, 1 }, + { BLKWRCTLINT_F, "CIM block write to CTL space", -1, 1 }, + { SGLRDPLINT_F, "CIM single read from PL space", -1, 1 }, + { SGLWRPLINT_F, "CIM single write to PL space", -1, 1 }, + { BLKRDPLINT_F, "CIM block read from PL space", -1, 1 }, + { BLKWRPLINT_F, "CIM block write to PL space", -1, 1 }, + { REQOVRLOOKUPINT_F, "CIM request FIFO overwrite", -1, 1 }, + { RSPOVRLOOKUPINT_F, "CIM response FIFO overwrite", -1, 1 }, + { TIMEOUTINT_F, "CIM PIF timeout", -1, 1 }, + { TIMEOUTMAINT_F, "CIM PIF MA timeout", -1, 1 }, { 0 } }; @@ -1595,9 +1600,9 @@ static void cim_intr_handler(struct adapter *adapter) if (t4_read_reg(adapter, PCIE_FW_A) & PCIE_FW_ERR_F) t4_report_fw_error(adapter); - fat = t4_handle_intr_status(adapter, CIM_HOST_INT_CAUSE, + fat = t4_handle_intr_status(adapter, CIM_HOST_INT_CAUSE_A, cim_intr_info) + - t4_handle_intr_status(adapter, CIM_HOST_UPACC_INT_CAUSE, + t4_handle_intr_status(adapter, CIM_HOST_UPACC_INT_CAUSE_A, cim_upintr_info); if (fat) t4_fatal_err(adapter); @@ -1786,7 +1791,8 @@ static void mps_intr_handler(struct adapter *adapter) t4_fatal_err(adapter); } -#define MEM_INT_MASK (PERR_INT_CAUSE | ECC_CE_INT_CAUSE | ECC_UE_INT_CAUSE) +#define MEM_INT_MASK (PERR_INT_CAUSE_F | ECC_CE_INT_CAUSE_F | \ + ECC_UE_INT_CAUSE_F) /* * EDC/MC interrupt handler. @@ -1798,40 +1804,40 @@ static void mem_intr_handler(struct adapter *adapter, int idx) unsigned int addr, cnt_addr, v; if (idx <= MEM_EDC1) { - addr = EDC_REG(EDC_INT_CAUSE, idx); - cnt_addr = EDC_REG(EDC_ECC_STATUS, idx); + addr = EDC_REG(EDC_INT_CAUSE_A, idx); + cnt_addr = EDC_REG(EDC_ECC_STATUS_A, idx); } else if (idx == MEM_MC) { if (is_t4(adapter->params.chip)) { - addr = MC_INT_CAUSE; - cnt_addr = MC_ECC_STATUS; + addr = MC_INT_CAUSE_A; + cnt_addr = MC_ECC_STATUS_A; } else { - addr = MC_P_INT_CAUSE; - cnt_addr = MC_P_ECC_STATUS; + addr = MC_P_INT_CAUSE_A; + cnt_addr = MC_P_ECC_STATUS_A; } } else { - addr = MC_REG(MC_P_INT_CAUSE, 1); - cnt_addr = MC_REG(MC_P_ECC_STATUS, 1); + addr = MC_REG(MC_P_INT_CAUSE_A, 1); + cnt_addr = MC_REG(MC_P_ECC_STATUS_A, 1); } v = t4_read_reg(adapter, addr) & MEM_INT_MASK; - if (v & PERR_INT_CAUSE) + if (v & PERR_INT_CAUSE_F) dev_alert(adapter->pdev_dev, "%s FIFO parity error\n", name[idx]); - if (v & ECC_CE_INT_CAUSE) { - u32 cnt = ECC_CECNT_GET(t4_read_reg(adapter, cnt_addr)); + if (v & ECC_CE_INT_CAUSE_F) { + u32 cnt = ECC_CECNT_G(t4_read_reg(adapter, cnt_addr)); - t4_write_reg(adapter, cnt_addr, ECC_CECNT_MASK); + t4_write_reg(adapter, cnt_addr, ECC_CECNT_V(ECC_CECNT_M)); if (printk_ratelimit()) dev_warn(adapter->pdev_dev, "%u %s correctable ECC data error%s\n", cnt, name[idx], cnt > 1 ? "s" : ""); } - if (v & ECC_UE_INT_CAUSE) + if (v & ECC_UE_INT_CAUSE_F) dev_alert(adapter->pdev_dev, "%s uncorrectable ECC data error\n", name[idx]); t4_write_reg(adapter, addr, v); - if (v & (PERR_INT_CAUSE | ECC_UE_INT_CAUSE)) + if (v & (PERR_INT_CAUSE_F | ECC_UE_INT_CAUSE_F)) t4_fatal_err(adapter); } @@ -1840,26 +1846,26 @@ static void mem_intr_handler(struct adapter *adapter, int idx) */ static void ma_intr_handler(struct adapter *adap) { - u32 v, status = t4_read_reg(adap, MA_INT_CAUSE); + u32 v, status = t4_read_reg(adap, MA_INT_CAUSE_A); - if (status & MEM_PERR_INT_CAUSE) { + if (status & MEM_PERR_INT_CAUSE_F) { dev_alert(adap->pdev_dev, "MA parity error, parity status %#x\n", - t4_read_reg(adap, MA_PARITY_ERROR_STATUS)); + t4_read_reg(adap, MA_PARITY_ERROR_STATUS1_A)); if (is_t5(adap->params.chip)) dev_alert(adap->pdev_dev, "MA parity error, parity status %#x\n", t4_read_reg(adap, - MA_PARITY_ERROR_STATUS2)); + MA_PARITY_ERROR_STATUS2_A)); } - if (status & MEM_WRAP_INT_CAUSE) { - v = t4_read_reg(adap, MA_INT_WRAP_STATUS); + if (status & MEM_WRAP_INT_CAUSE_F) { + v = t4_read_reg(adap, MA_INT_WRAP_STATUS_A); dev_alert(adap->pdev_dev, "MA address wrap-around error by " "client %u to address %#x\n", - MEM_WRAP_CLIENT_NUM_GET(v), - MEM_WRAP_ADDRESS_GET(v) << 4); + MEM_WRAP_CLIENT_NUM_G(v), + MEM_WRAP_ADDRESS_G(v) << 4); } - t4_write_reg(adap, MA_INT_CAUSE, status); + t4_write_reg(adap, MA_INT_CAUSE_A, status); t4_fatal_err(adap); } @@ -3007,7 +3013,7 @@ static int t4_fw_halt(struct adapter *adap, unsigned int mbox, int force) * rather than a RESET ... if it's new enough to understand that ... */ if (ret == 0 || force) { - t4_set_reg_field(adap, CIM_BOOT_CFG, UPCRST, UPCRST); + t4_set_reg_field(adap, CIM_BOOT_CFG_A, UPCRST_F, UPCRST_F); t4_set_reg_field(adap, PCIE_FW_A, PCIE_FW_HALT_F, PCIE_FW_HALT_F); } @@ -3058,7 +3064,7 @@ static int t4_fw_restart(struct adapter *adap, unsigned int mbox, int reset) * hitting the chip with a hammer. */ if (mbox <= PCIE_FW_MASTER_M) { - t4_set_reg_field(adap, CIM_BOOT_CFG, UPCRST, 0); + t4_set_reg_field(adap, CIM_BOOT_CFG_A, UPCRST_F, 0); msleep(100); if (t4_fw_reset(adap, mbox, PIORST | PIORSTMODE) == 0) @@ -3070,7 +3076,7 @@ static int t4_fw_restart(struct adapter *adap, unsigned int mbox, int reset) } else { int ms; - t4_set_reg_field(adap, CIM_BOOT_CFG, UPCRST, 0); + t4_set_reg_field(adap, CIM_BOOT_CFG_A, UPCRST_F, 0); for (ms = 0; ms < FW_CMD_MAX_TIMEOUT; ) { if (!(t4_read_reg(adap, PCIE_FW_A) & PCIE_FW_HALT_F)) return 0; @@ -3973,7 +3979,7 @@ static int get_flash_params(struct adapter *adap) return -EINVAL; adap->params.sf_size = 1 << info; adap->params.sf_fw_start = - t4_read_reg(adap, CIM_BOOT_CFG) & BOOTADDR_MASK; + t4_read_reg(adap, CIM_BOOT_CFG_A) & BOOTADDR_M; if (adap->params.sf_size < FLASH_MIN_SIZE) dev_warn(adap->pdev_dev, "WARNING!!! FLASH size %#x < %#x!!!\n", diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index 9b73824..4b66818 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -788,41 +788,54 @@ #define TDUE_V(x) ((x) << TDUE_S) #define TDUE_F TDUE_V(1U) -#define MC_INT_CAUSE 0x7518 -#define MC_P_INT_CAUSE 0x41318 -#define ECC_UE_INT_CAUSE 0x00000004U -#define ECC_CE_INT_CAUSE 0x00000002U -#define PERR_INT_CAUSE 0x00000001U - -#define MC_ECC_STATUS 0x751c -#define MC_P_ECC_STATUS 0x4131c -#define ECC_CECNT_MASK 0xffff0000U -#define ECC_CECNT_SHIFT 16 -#define ECC_CECNT(x) ((x) << ECC_CECNT_SHIFT) -#define ECC_CECNT_GET(x) (((x) & ECC_CECNT_MASK) >> ECC_CECNT_SHIFT) -#define ECC_UECNT_MASK 0x0000ffffU -#define ECC_UECNT_SHIFT 0 -#define ECC_UECNT(x) ((x) << ECC_UECNT_SHIFT) -#define ECC_UECNT_GET(x) (((x) & ECC_UECNT_MASK) >> ECC_UECNT_SHIFT) - -#define MC_BIST_CMD 0x7600 -#define START_BIST 0x80000000U -#define BIST_CMD_GAP_MASK 0x0000ff00U -#define BIST_CMD_GAP_SHIFT 8 -#define BIST_CMD_GAP(x) ((x) << BIST_CMD_GAP_SHIFT) -#define BIST_OPCODE_MASK 0x00000003U -#define BIST_OPCODE_SHIFT 0 -#define BIST_OPCODE(x) ((x) << BIST_OPCODE_SHIFT) - -#define MC_BIST_CMD_ADDR 0x7604 -#define MC_BIST_CMD_LEN 0x7608 -#define MC_BIST_DATA_PATTERN 0x760c -#define BIST_DATA_TYPE_MASK 0x0000000fU -#define BIST_DATA_TYPE_SHIFT 0 -#define BIST_DATA_TYPE(x) ((x) << BIST_DATA_TYPE_SHIFT) - -#define MC_BIST_STATUS_RDATA 0x7688 +/* registers for module MC */ +#define MC_INT_CAUSE_A 0x7518 +#define MC_P_INT_CAUSE_A 0x41318 +#define ECC_UE_INT_CAUSE_S 2 +#define ECC_UE_INT_CAUSE_V(x) ((x) << ECC_UE_INT_CAUSE_S) +#define ECC_UE_INT_CAUSE_F ECC_UE_INT_CAUSE_V(1U) + +#define ECC_CE_INT_CAUSE_S 1 +#define ECC_CE_INT_CAUSE_V(x) ((x) << ECC_CE_INT_CAUSE_S) +#define ECC_CE_INT_CAUSE_F ECC_CE_INT_CAUSE_V(1U) + +#define PERR_INT_CAUSE_S 0 +#define PERR_INT_CAUSE_V(x) ((x) << PERR_INT_CAUSE_S) +#define PERR_INT_CAUSE_F PERR_INT_CAUSE_V(1U) + +#define MC_ECC_STATUS_A 0x751c +#define MC_P_ECC_STATUS_A 0x4131c + +#define ECC_CECNT_S 16 +#define ECC_CECNT_M 0xffffU +#define ECC_CECNT_V(x) ((x) << ECC_CECNT_S) +#define ECC_CECNT_G(x) (((x) >> ECC_CECNT_S) & ECC_CECNT_M) + +#define ECC_UECNT_S 0 +#define ECC_UECNT_M 0xffffU +#define ECC_UECNT_V(x) ((x) << ECC_UECNT_S) +#define ECC_UECNT_G(x) (((x) >> ECC_UECNT_S) & ECC_UECNT_M) + +#define MC_BIST_CMD_A 0x7600 + +#define START_BIST_S 31 +#define START_BIST_V(x) ((x) << START_BIST_S) +#define START_BIST_F START_BIST_V(1U) + +#define BIST_CMD_GAP_S 8 +#define BIST_CMD_GAP_V(x) ((x) << BIST_CMD_GAP_S) + +#define BIST_OPCODE_S 0 +#define BIST_OPCODE_V(x) ((x) << BIST_OPCODE_S) + +#define MC_BIST_CMD_ADDR_A 0x7604 +#define MC_BIST_CMD_LEN_A 0x7608 +#define MC_BIST_DATA_PATTERN_A 0x760c + +#define MC_BIST_STATUS_RDATA_A 0x7688 + +/* registers for module MA */ #define MA_EDRAM0_BAR_A 0x77c0 #define EDRAM0_SIZE_S 0 @@ -880,109 +893,294 @@ #define EXT_MEM0_ENABLE_V(x) ((x) << EXT_MEM0_ENABLE_S) #define EXT_MEM0_ENABLE_F EXT_MEM0_ENABLE_V(1U) -#define MA_INT_CAUSE 0x77e0 -#define MEM_PERR_INT_CAUSE 0x00000002U -#define MEM_WRAP_INT_CAUSE 0x00000001U - -#define MA_INT_WRAP_STATUS 0x77e4 -#define MEM_WRAP_ADDRESS_MASK 0xfffffff0U -#define MEM_WRAP_ADDRESS_SHIFT 4 -#define MEM_WRAP_ADDRESS_GET(x) (((x) & MEM_WRAP_ADDRESS_MASK) >> MEM_WRAP_ADDRESS_SHIFT) -#define MEM_WRAP_CLIENT_NUM_MASK 0x0000000fU -#define MEM_WRAP_CLIENT_NUM_SHIFT 0 -#define MEM_WRAP_CLIENT_NUM_GET(x) (((x) & MEM_WRAP_CLIENT_NUM_MASK) >> MEM_WRAP_CLIENT_NUM_SHIFT) -#define MA_PARITY_ERROR_STATUS 0x77f4 -#define MA_PARITY_ERROR_STATUS2 0x7804 - -#define EDC_0_BASE_ADDR 0x7900 - -#define EDC_BIST_CMD 0x7904 -#define EDC_BIST_CMD_ADDR 0x7908 -#define EDC_BIST_CMD_LEN 0x790c -#define EDC_BIST_DATA_PATTERN 0x7910 -#define EDC_BIST_STATUS_RDATA 0x7928 -#define EDC_INT_CAUSE 0x7978 -#define ECC_UE_PAR 0x00000020U -#define ECC_CE_PAR 0x00000010U -#define PERR_PAR_CAUSE 0x00000008U - -#define EDC_ECC_STATUS 0x797c - -#define EDC_1_BASE_ADDR 0x7980 - -#define CIM_BOOT_CFG 0x7b00 -#define BOOTADDR_MASK 0xffffff00U -#define UPCRST 0x1U - -#define CIM_PF_MAILBOX_DATA 0x240 -#define CIM_PF_MAILBOX_CTRL 0x280 -#define MBMSGVALID 0x00000008U -#define MBINTREQ 0x00000004U -#define MBOWNER_MASK 0x00000003U -#define MBOWNER_SHIFT 0 -#define MBOWNER(x) ((x) << MBOWNER_SHIFT) -#define MBOWNER_GET(x) (((x) & MBOWNER_MASK) >> MBOWNER_SHIFT) - -#define CIM_PF_HOST_INT_ENABLE 0x288 -#define MBMSGRDYINTEN(x) ((x) << 19) - -#define CIM_PF_HOST_INT_CAUSE 0x28c -#define MBMSGRDYINT 0x00080000U - -#define CIM_HOST_INT_CAUSE 0x7b2c -#define TIEQOUTPARERRINT 0x00100000U -#define TIEQINPARERRINT 0x00080000U -#define MBHOSTPARERR 0x00040000U -#define MBUPPARERR 0x00020000U -#define IBQPARERR 0x0001f800U -#define IBQTP0PARERR 0x00010000U -#define IBQTP1PARERR 0x00008000U -#define IBQULPPARERR 0x00004000U -#define IBQSGELOPARERR 0x00002000U -#define IBQSGEHIPARERR 0x00001000U -#define IBQNCSIPARERR 0x00000800U -#define OBQPARERR 0x000007e0U -#define OBQULP0PARERR 0x00000400U -#define OBQULP1PARERR 0x00000200U -#define OBQULP2PARERR 0x00000100U -#define OBQULP3PARERR 0x00000080U -#define OBQSGEPARERR 0x00000040U -#define OBQNCSIPARERR 0x00000020U -#define PREFDROPINT 0x00000002U -#define UPACCNONZERO 0x00000001U - -#define CIM_HOST_UPACC_INT_CAUSE 0x7b34 -#define EEPROMWRINT 0x40000000U -#define TIMEOUTMAINT 0x20000000U -#define TIMEOUTINT 0x10000000U -#define RSPOVRLOOKUPINT 0x08000000U -#define REQOVRLOOKUPINT 0x04000000U -#define BLKWRPLINT 0x02000000U -#define BLKRDPLINT 0x01000000U -#define SGLWRPLINT 0x00800000U -#define SGLRDPLINT 0x00400000U -#define BLKWRCTLINT 0x00200000U -#define BLKRDCTLINT 0x00100000U -#define SGLWRCTLINT 0x00080000U -#define SGLRDCTLINT 0x00040000U -#define BLKWREEPROMINT 0x00020000U -#define BLKRDEEPROMINT 0x00010000U -#define SGLWREEPROMINT 0x00008000U -#define SGLRDEEPROMINT 0x00004000U -#define BLKWRFLASHINT 0x00002000U -#define BLKRDFLASHINT 0x00001000U -#define SGLWRFLASHINT 0x00000800U -#define SGLRDFLASHINT 0x00000400U -#define BLKWRBOOTINT 0x00000200U -#define BLKRDBOOTINT 0x00000100U -#define SGLWRBOOTINT 0x00000080U -#define SGLRDBOOTINT 0x00000040U -#define ILLWRBEINT 0x00000020U -#define ILLRDBEINT 0x00000010U -#define ILLRDINT 0x00000008U -#define ILLWRINT 0x00000004U -#define ILLTRANSINT 0x00000002U -#define RSVDSPACEINT 0x00000001U +#define MA_INT_CAUSE_A 0x77e0 + +#define MEM_PERR_INT_CAUSE_S 1 +#define MEM_PERR_INT_CAUSE_V(x) ((x) << MEM_PERR_INT_CAUSE_S) +#define MEM_PERR_INT_CAUSE_F MEM_PERR_INT_CAUSE_V(1U) + +#define MEM_WRAP_INT_CAUSE_S 0 +#define MEM_WRAP_INT_CAUSE_V(x) ((x) << MEM_WRAP_INT_CAUSE_S) +#define MEM_WRAP_INT_CAUSE_F MEM_WRAP_INT_CAUSE_V(1U) + +#define MA_INT_WRAP_STATUS_A 0x77e4 + +#define MEM_WRAP_ADDRESS_S 4 +#define MEM_WRAP_ADDRESS_M 0xfffffffU +#define MEM_WRAP_ADDRESS_G(x) (((x) >> MEM_WRAP_ADDRESS_S) & MEM_WRAP_ADDRESS_M) + +#define MEM_WRAP_CLIENT_NUM_S 0 +#define MEM_WRAP_CLIENT_NUM_M 0xfU +#define MEM_WRAP_CLIENT_NUM_G(x) \ + (((x) >> MEM_WRAP_CLIENT_NUM_S) & MEM_WRAP_CLIENT_NUM_M) + +#define MA_PARITY_ERROR_STATUS_A 0x77f4 +#define MA_PARITY_ERROR_STATUS1_A 0x77f4 +#define MA_PARITY_ERROR_STATUS2_A 0x7804 + +/* registers for module EDC_0 */ +#define EDC_0_BASE_ADDR 0x7900 + +#define EDC_BIST_CMD_A 0x7904 +#define EDC_BIST_CMD_ADDR_A 0x7908 +#define EDC_BIST_CMD_LEN_A 0x790c +#define EDC_BIST_DATA_PATTERN_A 0x7910 +#define EDC_BIST_STATUS_RDATA_A 0x7928 +#define EDC_INT_CAUSE_A 0x7978 + +#define ECC_UE_PAR_S 5 +#define ECC_UE_PAR_V(x) ((x) << ECC_UE_PAR_S) +#define ECC_UE_PAR_F ECC_UE_PAR_V(1U) + +#define ECC_CE_PAR_S 4 +#define ECC_CE_PAR_V(x) ((x) << ECC_CE_PAR_S) +#define ECC_CE_PAR_F ECC_CE_PAR_V(1U) + +#define PERR_PAR_CAUSE_S 3 +#define PERR_PAR_CAUSE_V(x) ((x) << PERR_PAR_CAUSE_S) +#define PERR_PAR_CAUSE_F PERR_PAR_CAUSE_V(1U) + +#define EDC_ECC_STATUS_A 0x797c + +/* registers for module EDC_1 */ +#define EDC_1_BASE_ADDR 0x7980 + +/* registers for module CIM */ +#define CIM_BOOT_CFG_A 0x7b00 + +#define BOOTADDR_M 0xffffff00U + +#define UPCRST_S 0 +#define UPCRST_V(x) ((x) << UPCRST_S) +#define UPCRST_F UPCRST_V(1U) + +#define CIM_PF_MAILBOX_DATA_A 0x240 +#define CIM_PF_MAILBOX_CTRL_A 0x280 + +#define MBMSGVALID_S 3 +#define MBMSGVALID_V(x) ((x) << MBMSGVALID_S) +#define MBMSGVALID_F MBMSGVALID_V(1U) + +#define MBINTREQ_S 2 +#define MBINTREQ_V(x) ((x) << MBINTREQ_S) +#define MBINTREQ_F MBINTREQ_V(1U) + +#define MBOWNER_S 0 +#define MBOWNER_M 0x3U +#define MBOWNER_V(x) ((x) << MBOWNER_S) +#define MBOWNER_G(x) (((x) >> MBOWNER_S) & MBOWNER_M) + +#define CIM_PF_HOST_INT_ENABLE_A 0x288 + +#define MBMSGRDYINTEN_S 19 +#define MBMSGRDYINTEN_V(x) ((x) << MBMSGRDYINTEN_S) +#define MBMSGRDYINTEN_F MBMSGRDYINTEN_V(1U) + +#define CIM_PF_HOST_INT_CAUSE_A 0x28c + +#define MBMSGRDYINT_S 19 +#define MBMSGRDYINT_V(x) ((x) << MBMSGRDYINT_S) +#define MBMSGRDYINT_F MBMSGRDYINT_V(1U) + +#define CIM_HOST_INT_CAUSE_A 0x7b2c + +#define TIEQOUTPARERRINT_S 20 +#define TIEQOUTPARERRINT_V(x) ((x) << TIEQOUTPARERRINT_S) +#define TIEQOUTPARERRINT_F TIEQOUTPARERRINT_V(1U) + +#define TIEQINPARERRINT_S 19 +#define TIEQINPARERRINT_V(x) ((x) << TIEQINPARERRINT_S) +#define TIEQINPARERRINT_F TIEQINPARERRINT_V(1U) + +#define PREFDROPINT_S 1 +#define PREFDROPINT_V(x) ((x) << PREFDROPINT_S) +#define PREFDROPINT_F PREFDROPINT_V(1U) + +#define UPACCNONZERO_S 0 +#define UPACCNONZERO_V(x) ((x) << UPACCNONZERO_S) +#define UPACCNONZERO_F UPACCNONZERO_V(1U) + +#define MBHOSTPARERR_S 18 +#define MBHOSTPARERR_V(x) ((x) << MBHOSTPARERR_S) +#define MBHOSTPARERR_F MBHOSTPARERR_V(1U) + +#define MBUPPARERR_S 17 +#define MBUPPARERR_V(x) ((x) << MBUPPARERR_S) +#define MBUPPARERR_F MBUPPARERR_V(1U) + +#define IBQTP0PARERR_S 16 +#define IBQTP0PARERR_V(x) ((x) << IBQTP0PARERR_S) +#define IBQTP0PARERR_F IBQTP0PARERR_V(1U) + +#define IBQTP1PARERR_S 15 +#define IBQTP1PARERR_V(x) ((x) << IBQTP1PARERR_S) +#define IBQTP1PARERR_F IBQTP1PARERR_V(1U) + +#define IBQULPPARERR_S 14 +#define IBQULPPARERR_V(x) ((x) << IBQULPPARERR_S) +#define IBQULPPARERR_F IBQULPPARERR_V(1U) + +#define IBQSGELOPARERR_S 13 +#define IBQSGELOPARERR_V(x) ((x) << IBQSGELOPARERR_S) +#define IBQSGELOPARERR_F IBQSGELOPARERR_V(1U) + +#define IBQSGEHIPARERR_S 12 +#define IBQSGEHIPARERR_V(x) ((x) << IBQSGEHIPARERR_S) +#define IBQSGEHIPARERR_F IBQSGEHIPARERR_V(1U) + +#define IBQNCSIPARERR_S 11 +#define IBQNCSIPARERR_V(x) ((x) << IBQNCSIPARERR_S) +#define IBQNCSIPARERR_F IBQNCSIPARERR_V(1U) + +#define OBQULP0PARERR_S 10 +#define OBQULP0PARERR_V(x) ((x) << OBQULP0PARERR_S) +#define OBQULP0PARERR_F OBQULP0PARERR_V(1U) + +#define OBQULP1PARERR_S 9 +#define OBQULP1PARERR_V(x) ((x) << OBQULP1PARERR_S) +#define OBQULP1PARERR_F OBQULP1PARERR_V(1U) + +#define OBQULP2PARERR_S 8 +#define OBQULP2PARERR_V(x) ((x) << OBQULP2PARERR_S) +#define OBQULP2PARERR_F OBQULP2PARERR_V(1U) + +#define OBQULP3PARERR_S 7 +#define OBQULP3PARERR_V(x) ((x) << OBQULP3PARERR_S) +#define OBQULP3PARERR_F OBQULP3PARERR_V(1U) + +#define OBQSGEPARERR_S 6 +#define OBQSGEPARERR_V(x) ((x) << OBQSGEPARERR_S) +#define OBQSGEPARERR_F OBQSGEPARERR_V(1U) + +#define OBQNCSIPARERR_S 5 +#define OBQNCSIPARERR_V(x) ((x) << OBQNCSIPARERR_S) +#define OBQNCSIPARERR_F OBQNCSIPARERR_V(1U) + +#define CIM_HOST_UPACC_INT_CAUSE_A 0x7b34 + +#define EEPROMWRINT_S 30 +#define EEPROMWRINT_V(x) ((x) << EEPROMWRINT_S) +#define EEPROMWRINT_F EEPROMWRINT_V(1U) + +#define TIMEOUTMAINT_S 29 +#define TIMEOUTMAINT_V(x) ((x) << TIMEOUTMAINT_S) +#define TIMEOUTMAINT_F TIMEOUTMAINT_V(1U) + +#define TIMEOUTINT_S 28 +#define TIMEOUTINT_V(x) ((x) << TIMEOUTINT_S) +#define TIMEOUTINT_F TIMEOUTINT_V(1U) + +#define RSPOVRLOOKUPINT_S 27 +#define RSPOVRLOOKUPINT_V(x) ((x) << RSPOVRLOOKUPINT_S) +#define RSPOVRLOOKUPINT_F RSPOVRLOOKUPINT_V(1U) + +#define REQOVRLOOKUPINT_S 26 +#define REQOVRLOOKUPINT_V(x) ((x) << REQOVRLOOKUPINT_S) +#define REQOVRLOOKUPINT_F REQOVRLOOKUPINT_V(1U) + +#define BLKWRPLINT_S 25 +#define BLKWRPLINT_V(x) ((x) << BLKWRPLINT_S) +#define BLKWRPLINT_F BLKWRPLINT_V(1U) + +#define BLKRDPLINT_S 24 +#define BLKRDPLINT_V(x) ((x) << BLKRDPLINT_S) +#define BLKRDPLINT_F BLKRDPLINT_V(1U) + +#define SGLWRPLINT_S 23 +#define SGLWRPLINT_V(x) ((x) << SGLWRPLINT_S) +#define SGLWRPLINT_F SGLWRPLINT_V(1U) + +#define SGLRDPLINT_S 22 +#define SGLRDPLINT_V(x) ((x) << SGLRDPLINT_S) +#define SGLRDPLINT_F SGLRDPLINT_V(1U) + +#define BLKWRCTLINT_S 21 +#define BLKWRCTLINT_V(x) ((x) << BLKWRCTLINT_S) +#define BLKWRCTLINT_F BLKWRCTLINT_V(1U) + +#define BLKRDCTLINT_S 20 +#define BLKRDCTLINT_V(x) ((x) << BLKRDCTLINT_S) +#define BLKRDCTLINT_F BLKRDCTLINT_V(1U) + +#define SGLWRCTLINT_S 19 +#define SGLWRCTLINT_V(x) ((x) << SGLWRCTLINT_S) +#define SGLWRCTLINT_F SGLWRCTLINT_V(1U) + +#define SGLRDCTLINT_S 18 +#define SGLRDCTLINT_V(x) ((x) << SGLRDCTLINT_S) +#define SGLRDCTLINT_F SGLRDCTLINT_V(1U) + +#define BLKWREEPROMINT_S 17 +#define BLKWREEPROMINT_V(x) ((x) << BLKWREEPROMINT_S) +#define BLKWREEPROMINT_F BLKWREEPROMINT_V(1U) + +#define BLKRDEEPROMINT_S 16 +#define BLKRDEEPROMINT_V(x) ((x) << BLKRDEEPROMINT_S) +#define BLKRDEEPROMINT_F BLKRDEEPROMINT_V(1U) + +#define SGLWREEPROMINT_S 15 +#define SGLWREEPROMINT_V(x) ((x) << SGLWREEPROMINT_S) +#define SGLWREEPROMINT_F SGLWREEPROMINT_V(1U) + +#define SGLRDEEPROMINT_S 14 +#define SGLRDEEPROMINT_V(x) ((x) << SGLRDEEPROMINT_S) +#define SGLRDEEPROMINT_F SGLRDEEPROMINT_V(1U) + +#define BLKWRFLASHINT_S 13 +#define BLKWRFLASHINT_V(x) ((x) << BLKWRFLASHINT_S) +#define BLKWRFLASHINT_F BLKWRFLASHINT_V(1U) + +#define BLKRDFLASHINT_S 12 +#define BLKRDFLASHINT_V(x) ((x) << BLKRDFLASHINT_S) +#define BLKRDFLASHINT_F BLKRDFLASHINT_V(1U) + +#define SGLWRFLASHINT_S 11 +#define SGLWRFLASHINT_V(x) ((x) << SGLWRFLASHINT_S) +#define SGLWRFLASHINT_F SGLWRFLASHINT_V(1U) + +#define SGLRDFLASHINT_S 10 +#define SGLRDFLASHINT_V(x) ((x) << SGLRDFLASHINT_S) +#define SGLRDFLASHINT_F SGLRDFLASHINT_V(1U) + +#define BLKWRBOOTINT_S 9 +#define BLKWRBOOTINT_V(x) ((x) << BLKWRBOOTINT_S) +#define BLKWRBOOTINT_F BLKWRBOOTINT_V(1U) + +#define BLKRDBOOTINT_S 8 +#define BLKRDBOOTINT_V(x) ((x) << BLKRDBOOTINT_S) +#define BLKRDBOOTINT_F BLKRDBOOTINT_V(1U) + +#define SGLWRBOOTINT_S 7 +#define SGLWRBOOTINT_V(x) ((x) << SGLWRBOOTINT_S) +#define SGLWRBOOTINT_F SGLWRBOOTINT_V(1U) + +#define SGLRDBOOTINT_S 6 +#define SGLRDBOOTINT_V(x) ((x) << SGLRDBOOTINT_S) +#define SGLRDBOOTINT_F SGLRDBOOTINT_V(1U) + +#define ILLWRBEINT_S 5 +#define ILLWRBEINT_V(x) ((x) << ILLWRBEINT_S) +#define ILLWRBEINT_F ILLWRBEINT_V(1U) + +#define ILLRDBEINT_S 4 +#define ILLRDBEINT_V(x) ((x) << ILLRDBEINT_S) +#define ILLRDBEINT_F ILLRDBEINT_V(1U) + +#define ILLRDINT_S 3 +#define ILLRDINT_V(x) ((x) << ILLRDINT_S) +#define ILLRDINT_F ILLRDINT_V(1U) + +#define ILLWRINT_S 2 +#define ILLWRINT_V(x) ((x) << ILLWRINT_S) +#define ILLWRINT_F ILLWRINT_V(1U) + +#define ILLTRANSINT_S 1 +#define ILLTRANSINT_V(x) ((x) << ILLTRANSINT_S) +#define ILLTRANSINT_F ILLTRANSINT_V(1U) + +#define RSVDSPACEINT_S 0 +#define RSVDSPACEINT_V(x) ((x) << RSVDSPACEINT_S) +#define RSVDSPACEINT_F RSVDSPACEINT_V(1U) #define TP_OUT_CONFIG 0x7d04 #define VLANEXTENABLE_MASK 0x0000f000U @@ -1634,19 +1832,22 @@ #define MC_STRIDE (MC_1_BASE_ADDR - MC_0_BASE_ADDR) #define MC_REG(reg, idx) (reg + MC_STRIDE * idx) -#define MC_P_BIST_CMD 0x41400 -#define MC_P_BIST_CMD_ADDR 0x41404 -#define MC_P_BIST_CMD_LEN 0x41408 -#define MC_P_BIST_DATA_PATTERN 0x4140c -#define MC_P_BIST_STATUS_RDATA 0x41488 -#define EDC_T50_BASE_ADDR 0x50000 -#define EDC_H_BIST_CMD 0x50004 -#define EDC_H_BIST_CMD_ADDR 0x50008 -#define EDC_H_BIST_CMD_LEN 0x5000c -#define EDC_H_BIST_DATA_PATTERN 0x50010 -#define EDC_H_BIST_STATUS_RDATA 0x50028 - -#define EDC_T51_BASE_ADDR 0x50800 +#define MC_P_BIST_CMD_A 0x41400 +#define MC_P_BIST_CMD_ADDR_A 0x41404 +#define MC_P_BIST_CMD_LEN_A 0x41408 +#define MC_P_BIST_DATA_PATTERN_A 0x4140c +#define MC_P_BIST_STATUS_RDATA_A 0x41488 + +#define EDC_T50_BASE_ADDR 0x50000 + +#define EDC_H_BIST_CMD_A 0x50004 +#define EDC_H_BIST_CMD_ADDR_A 0x50008 +#define EDC_H_BIST_CMD_LEN_A 0x5000c +#define EDC_H_BIST_DATA_PATTERN_A 0x50010 +#define EDC_H_BIST_STATUS_RDATA_A 0x50028 + +#define EDC_T51_BASE_ADDR 0x50800 + #define EDC_STRIDE_T5 (EDC_T51_BASE_ADDR - EDC_T50_BASE_ADDR) #define EDC_REG_T5(reg, idx) (reg + EDC_STRIDE_T5 * idx) diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h index c7b127d..b516b12 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h @@ -64,8 +64,8 @@ * Mailbox Data in the fixed CIM PF map and the programmable VF map must * match. However, it's a useful convention ... */ -#if T4VF_MBDATA_BASE_ADDR != CIM_PF_MAILBOX_DATA -#error T4VF_MBDATA_BASE_ADDR must match CIM_PF_MAILBOX_DATA! +#if T4VF_MBDATA_BASE_ADDR != CIM_PF_MAILBOX_DATA_A +#error T4VF_MBDATA_BASE_ADDR must match CIM_PF_MAILBOX_DATA_A! #endif /* diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c index fa60f71..5e83c18 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c @@ -138,9 +138,9 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size, * Loop trying to get ownership of the mailbox. Return an error * if we can't gain ownership. */ - v = MBOWNER_GET(t4_read_reg(adapter, mbox_ctl)); + v = MBOWNER_G(t4_read_reg(adapter, mbox_ctl)); for (i = 0; v == MBOX_OWNER_NONE && i < 3; i++) - v = MBOWNER_GET(t4_read_reg(adapter, mbox_ctl)); + v = MBOWNER_G(t4_read_reg(adapter, mbox_ctl)); if (v != MBOX_OWNER_DRV) return v == MBOX_OWNER_FW ? -EBUSY : -ETIMEDOUT; @@ -162,7 +162,7 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size, t4_read_reg(adapter, mbox_data); /* flush write */ t4_write_reg(adapter, mbox_ctl, - MBMSGVALID | MBOWNER(MBOX_OWNER_FW)); + MBMSGVALID_F | MBOWNER_V(MBOX_OWNER_FW)); t4_read_reg(adapter, mbox_ctl); /* flush write */ /* @@ -184,14 +184,14 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size, * If we're the owner, see if this is the reply we wanted. */ v = t4_read_reg(adapter, mbox_ctl); - if (MBOWNER_GET(v) == MBOX_OWNER_DRV) { + if (MBOWNER_G(v) == MBOX_OWNER_DRV) { /* * If the Message Valid bit isn't on, revoke ownership * of the mailbox and continue waiting for our reply. */ - if ((v & MBMSGVALID) == 0) { + if ((v & MBMSGVALID_F) == 0) { t4_write_reg(adapter, mbox_ctl, - MBOWNER(MBOX_OWNER_NONE)); + MBOWNER_V(MBOX_OWNER_NONE)); continue; } @@ -217,7 +217,7 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size, & FW_CMD_REQUEST_F) != 0); } t4_write_reg(adapter, mbox_ctl, - MBOWNER(MBOX_OWNER_NONE)); + MBOWNER_V(MBOX_OWNER_NONE)); return -FW_CMD_RETVAL_G(v); } } diff --git a/drivers/scsi/csiostor/csio_hw.c b/drivers/scsi/csiostor/csio_hw.c index 04ec586..257dc8d 100644 --- a/drivers/scsi/csiostor/csio_hw.c +++ b/drivers/scsi/csiostor/csio_hw.c @@ -1193,7 +1193,7 @@ csio_hw_fw_halt(struct csio_hw *hw, uint32_t mbox, int32_t force) * rather than a RESET ... if it's new enough to understand that ... */ if (retval == 0 || force) { - csio_set_reg_field(hw, CIM_BOOT_CFG, UPCRST, UPCRST); + csio_set_reg_field(hw, CIM_BOOT_CFG_A, UPCRST_F, UPCRST_F); csio_set_reg_field(hw, PCIE_FW_A, PCIE_FW_HALT_F, PCIE_FW_HALT_F); } @@ -1245,7 +1245,7 @@ csio_hw_fw_restart(struct csio_hw *hw, uint32_t mbox, int32_t reset) * hitting the chip with a hammer. */ if (mbox <= PCIE_FW_MASTER_M) { - csio_set_reg_field(hw, CIM_BOOT_CFG, UPCRST, 0); + csio_set_reg_field(hw, CIM_BOOT_CFG_A, UPCRST_F, 0); msleep(100); if (csio_do_reset(hw, true) == 0) return 0; @@ -1256,7 +1256,7 @@ csio_hw_fw_restart(struct csio_hw *hw, uint32_t mbox, int32_t reset) } else { int ms; - csio_set_reg_field(hw, CIM_BOOT_CFG, UPCRST, 0); + csio_set_reg_field(hw, CIM_BOOT_CFG_A, UPCRST_F, 0); for (ms = 0; ms < FW_CMD_MAX_TIMEOUT; ) { if (!(csio_rd_reg32(hw, PCIE_FW_A) & PCIE_FW_HALT_F)) return 0; @@ -2741,10 +2741,10 @@ static void csio_sge_intr_handler(struct csio_hw *hw) csio_hw_fatal_err(hw); } -#define CIM_OBQ_INTR (OBQULP0PARERR | OBQULP1PARERR | OBQULP2PARERR |\ - OBQULP3PARERR | OBQSGEPARERR | OBQNCSIPARERR) -#define CIM_IBQ_INTR (IBQTP0PARERR | IBQTP1PARERR | IBQULPPARERR |\ - IBQSGEHIPARERR | IBQSGELOPARERR | IBQNCSIPARERR) +#define CIM_OBQ_INTR (OBQULP0PARERR_F | OBQULP1PARERR_F | OBQULP2PARERR_F |\ + OBQULP3PARERR_F | OBQSGEPARERR_F | OBQNCSIPARERR_F) +#define CIM_IBQ_INTR (IBQTP0PARERR_F | IBQTP1PARERR_F | IBQULPPARERR_F |\ + IBQSGEHIPARERR_F | IBQSGELOPARERR_F | IBQNCSIPARERR_F) /* * CIM interrupt handler. @@ -2752,53 +2752,53 @@ static void csio_sge_intr_handler(struct csio_hw *hw) static void csio_cim_intr_handler(struct csio_hw *hw) { static struct intr_info cim_intr_info[] = { - { PREFDROPINT, "CIM control register prefetch drop", -1, 1 }, + { PREFDROPINT_F, "CIM control register prefetch drop", -1, 1 }, { CIM_OBQ_INTR, "CIM OBQ parity error", -1, 1 }, { CIM_IBQ_INTR, "CIM IBQ parity error", -1, 1 }, - { MBUPPARERR, "CIM mailbox uP parity error", -1, 1 }, - { MBHOSTPARERR, "CIM mailbox host parity error", -1, 1 }, - { TIEQINPARERRINT, "CIM TIEQ outgoing parity error", -1, 1 }, - { TIEQOUTPARERRINT, "CIM TIEQ incoming parity error", -1, 1 }, + { MBUPPARERR_F, "CIM mailbox uP parity error", -1, 1 }, + { MBHOSTPARERR_F, "CIM mailbox host parity error", -1, 1 }, + { TIEQINPARERRINT_F, "CIM TIEQ outgoing parity error", -1, 1 }, + { TIEQOUTPARERRINT_F, "CIM TIEQ incoming parity error", -1, 1 }, { 0, NULL, 0, 0 } }; static struct intr_info cim_upintr_info[] = { - { RSVDSPACEINT, "CIM reserved space access", -1, 1 }, - { ILLTRANSINT, "CIM illegal transaction", -1, 1 }, - { ILLWRINT, "CIM illegal write", -1, 1 }, - { ILLRDINT, "CIM illegal read", -1, 1 }, - { ILLRDBEINT, "CIM illegal read BE", -1, 1 }, - { ILLWRBEINT, "CIM illegal write BE", -1, 1 }, - { SGLRDBOOTINT, "CIM single read from boot space", -1, 1 }, - { SGLWRBOOTINT, "CIM single write to boot space", -1, 1 }, - { BLKWRBOOTINT, "CIM block write to boot space", -1, 1 }, - { SGLRDFLASHINT, "CIM single read from flash space", -1, 1 }, - { SGLWRFLASHINT, "CIM single write to flash space", -1, 1 }, - { BLKWRFLASHINT, "CIM block write to flash space", -1, 1 }, - { SGLRDEEPROMINT, "CIM single EEPROM read", -1, 1 }, - { SGLWREEPROMINT, "CIM single EEPROM write", -1, 1 }, - { BLKRDEEPROMINT, "CIM block EEPROM read", -1, 1 }, - { BLKWREEPROMINT, "CIM block EEPROM write", -1, 1 }, - { SGLRDCTLINT , "CIM single read from CTL space", -1, 1 }, - { SGLWRCTLINT , "CIM single write to CTL space", -1, 1 }, - { BLKRDCTLINT , "CIM block read from CTL space", -1, 1 }, - { BLKWRCTLINT , "CIM block write to CTL space", -1, 1 }, - { SGLRDPLINT , "CIM single read from PL space", -1, 1 }, - { SGLWRPLINT , "CIM single write to PL space", -1, 1 }, - { BLKRDPLINT , "CIM block read from PL space", -1, 1 }, - { BLKWRPLINT , "CIM block write to PL space", -1, 1 }, - { REQOVRLOOKUPINT , "CIM request FIFO overwrite", -1, 1 }, - { RSPOVRLOOKUPINT , "CIM response FIFO overwrite", -1, 1 }, - { TIMEOUTINT , "CIM PIF timeout", -1, 1 }, - { TIMEOUTMAINT , "CIM PIF MA timeout", -1, 1 }, + { RSVDSPACEINT_F, "CIM reserved space access", -1, 1 }, + { ILLTRANSINT_F, "CIM illegal transaction", -1, 1 }, + { ILLWRINT_F, "CIM illegal write", -1, 1 }, + { ILLRDINT_F, "CIM illegal read", -1, 1 }, + { ILLRDBEINT_F, "CIM illegal read BE", -1, 1 }, + { ILLWRBEINT_F, "CIM illegal write BE", -1, 1 }, + { SGLRDBOOTINT_F, "CIM single read from boot space", -1, 1 }, + { SGLWRBOOTINT_F, "CIM single write to boot space", -1, 1 }, + { BLKWRBOOTINT_F, "CIM block write to boot space", -1, 1 }, + { SGLRDFLASHINT_F, "CIM single read from flash space", -1, 1 }, + { SGLWRFLASHINT_F, "CIM single write to flash space", -1, 1 }, + { BLKWRFLASHINT_F, "CIM block write to flash space", -1, 1 }, + { SGLRDEEPROMINT_F, "CIM single EEPROM read", -1, 1 }, + { SGLWREEPROMINT_F, "CIM single EEPROM write", -1, 1 }, + { BLKRDEEPROMINT_F, "CIM block EEPROM read", -1, 1 }, + { BLKWREEPROMINT_F, "CIM block EEPROM write", -1, 1 }, + { SGLRDCTLINT_F, "CIM single read from CTL space", -1, 1 }, + { SGLWRCTLINT_F, "CIM single write to CTL space", -1, 1 }, + { BLKRDCTLINT_F, "CIM block read from CTL space", -1, 1 }, + { BLKWRCTLINT_F, "CIM block write to CTL space", -1, 1 }, + { SGLRDPLINT_F, "CIM single read from PL space", -1, 1 }, + { SGLWRPLINT_F, "CIM single write to PL space", -1, 1 }, + { BLKRDPLINT_F, "CIM block read from PL space", -1, 1 }, + { BLKWRPLINT_F, "CIM block write to PL space", -1, 1 }, + { REQOVRLOOKUPINT_F, "CIM request FIFO overwrite", -1, 1 }, + { RSPOVRLOOKUPINT_F, "CIM response FIFO overwrite", -1, 1 }, + { TIMEOUTINT_F, "CIM PIF timeout", -1, 1 }, + { TIMEOUTMAINT_F, "CIM PIF MA timeout", -1, 1 }, { 0, NULL, 0, 0 } }; int fat; - fat = csio_handle_intr_status(hw, CIM_HOST_INT_CAUSE, - cim_intr_info) + - csio_handle_intr_status(hw, CIM_HOST_UPACC_INT_CAUSE, - cim_upintr_info); + fat = csio_handle_intr_status(hw, CIM_HOST_INT_CAUSE_A, + cim_intr_info) + + csio_handle_intr_status(hw, CIM_HOST_UPACC_INT_CAUSE_A, + cim_upintr_info); if (fat) csio_hw_fatal_err(hw); } @@ -2987,7 +2987,8 @@ static void csio_mps_intr_handler(struct csio_hw *hw) csio_hw_fatal_err(hw); } -#define MEM_INT_MASK (PERR_INT_CAUSE | ECC_CE_INT_CAUSE | ECC_UE_INT_CAUSE) +#define MEM_INT_MASK (PERR_INT_CAUSE_F | ECC_CE_INT_CAUSE_F | \ + ECC_UE_INT_CAUSE_F) /* * EDC/MC interrupt handler. @@ -2999,28 +3000,28 @@ static void csio_mem_intr_handler(struct csio_hw *hw, int idx) unsigned int addr, cnt_addr, v; if (idx <= MEM_EDC1) { - addr = EDC_REG(EDC_INT_CAUSE, idx); - cnt_addr = EDC_REG(EDC_ECC_STATUS, idx); + addr = EDC_REG(EDC_INT_CAUSE_A, idx); + cnt_addr = EDC_REG(EDC_ECC_STATUS_A, idx); } else { - addr = MC_INT_CAUSE; - cnt_addr = MC_ECC_STATUS; + addr = MC_INT_CAUSE_A; + cnt_addr = MC_ECC_STATUS_A; } v = csio_rd_reg32(hw, addr) & MEM_INT_MASK; - if (v & PERR_INT_CAUSE) + if (v & PERR_INT_CAUSE_F) csio_fatal(hw, "%s FIFO parity error\n", name[idx]); - if (v & ECC_CE_INT_CAUSE) { - uint32_t cnt = ECC_CECNT_GET(csio_rd_reg32(hw, cnt_addr)); + if (v & ECC_CE_INT_CAUSE_F) { + uint32_t cnt = ECC_CECNT_G(csio_rd_reg32(hw, cnt_addr)); - csio_wr_reg32(hw, ECC_CECNT_MASK, cnt_addr); + csio_wr_reg32(hw, ECC_CECNT_V(ECC_CECNT_M), cnt_addr); csio_warn(hw, "%u %s correctable ECC data error%s\n", cnt, name[idx], cnt > 1 ? "s" : ""); } - if (v & ECC_UE_INT_CAUSE) + if (v & ECC_UE_INT_CAUSE_F) csio_fatal(hw, "%s uncorrectable ECC data error\n", name[idx]); csio_wr_reg32(hw, v, addr); - if (v & (PERR_INT_CAUSE | ECC_UE_INT_CAUSE)) + if (v & (PERR_INT_CAUSE_F | ECC_UE_INT_CAUSE_F)) csio_hw_fatal_err(hw); } @@ -3029,18 +3030,18 @@ static void csio_mem_intr_handler(struct csio_hw *hw, int idx) */ static void csio_ma_intr_handler(struct csio_hw *hw) { - uint32_t v, status = csio_rd_reg32(hw, MA_INT_CAUSE); + uint32_t v, status = csio_rd_reg32(hw, MA_INT_CAUSE_A); - if (status & MEM_PERR_INT_CAUSE) + if (status & MEM_PERR_INT_CAUSE_F) csio_fatal(hw, "MA parity error, parity status %#x\n", - csio_rd_reg32(hw, MA_PARITY_ERROR_STATUS)); - if (status & MEM_WRAP_INT_CAUSE) { - v = csio_rd_reg32(hw, MA_INT_WRAP_STATUS); + csio_rd_reg32(hw, MA_PARITY_ERROR_STATUS_A)); + if (status & MEM_WRAP_INT_CAUSE_F) { + v = csio_rd_reg32(hw, MA_INT_WRAP_STATUS_A); csio_fatal(hw, "MA address wrap-around error by client %u to address %#x\n", - MEM_WRAP_CLIENT_NUM_GET(v), MEM_WRAP_ADDRESS_GET(v) << 4); + MEM_WRAP_CLIENT_NUM_G(v), MEM_WRAP_ADDRESS_G(v) << 4); } - csio_wr_reg32(hw, status, MA_INT_CAUSE); + csio_wr_reg32(hw, status, MA_INT_CAUSE_A); csio_hw_fatal_err(hw); } diff --git a/drivers/scsi/csiostor/csio_hw_t4.c b/drivers/scsi/csiostor/csio_hw_t4.c index d0dac67..14884e4 100644 --- a/drivers/scsi/csiostor/csio_hw_t4.c +++ b/drivers/scsi/csiostor/csio_hw_t4.c @@ -209,19 +209,19 @@ csio_t4_mc_read(struct csio_hw *hw, int idx, uint32_t addr, __be32 *data, { int i; - if (csio_rd_reg32(hw, MC_BIST_CMD) & START_BIST) + if (csio_rd_reg32(hw, MC_BIST_CMD_A) & START_BIST_F) return -EBUSY; - csio_wr_reg32(hw, addr & ~0x3fU, MC_BIST_CMD_ADDR); - csio_wr_reg32(hw, 64, MC_BIST_CMD_LEN); - csio_wr_reg32(hw, 0xc, MC_BIST_DATA_PATTERN); - csio_wr_reg32(hw, BIST_OPCODE(1) | START_BIST | BIST_CMD_GAP(1), - MC_BIST_CMD); - i = csio_hw_wait_op_done_val(hw, MC_BIST_CMD, START_BIST, + csio_wr_reg32(hw, addr & ~0x3fU, MC_BIST_CMD_ADDR_A); + csio_wr_reg32(hw, 64, MC_BIST_CMD_LEN_A); + csio_wr_reg32(hw, 0xc, MC_BIST_DATA_PATTERN_A); + csio_wr_reg32(hw, BIST_OPCODE_V(1) | START_BIST_F | BIST_CMD_GAP_V(1), + MC_BIST_CMD_A); + i = csio_hw_wait_op_done_val(hw, MC_BIST_CMD_A, START_BIST_F, 0, 10, 1, NULL); if (i) return i; -#define MC_DATA(i) MC_BIST_STATUS_REG(MC_BIST_STATUS_RDATA, i) +#define MC_DATA(i) MC_BIST_STATUS_REG(MC_BIST_STATUS_RDATA_A, i) for (i = 15; i >= 0; i--) *data++ = htonl(csio_rd_reg32(hw, MC_DATA(i))); @@ -250,19 +250,19 @@ csio_t4_edc_read(struct csio_hw *hw, int idx, uint32_t addr, __be32 *data, int i; idx *= EDC_STRIDE; - if (csio_rd_reg32(hw, EDC_BIST_CMD + idx) & START_BIST) + if (csio_rd_reg32(hw, EDC_BIST_CMD_A + idx) & START_BIST_F) return -EBUSY; - csio_wr_reg32(hw, addr & ~0x3fU, EDC_BIST_CMD_ADDR + idx); - csio_wr_reg32(hw, 64, EDC_BIST_CMD_LEN + idx); - csio_wr_reg32(hw, 0xc, EDC_BIST_DATA_PATTERN + idx); - csio_wr_reg32(hw, BIST_OPCODE(1) | BIST_CMD_GAP(1) | START_BIST, - EDC_BIST_CMD + idx); - i = csio_hw_wait_op_done_val(hw, EDC_BIST_CMD + idx, START_BIST, + csio_wr_reg32(hw, addr & ~0x3fU, EDC_BIST_CMD_ADDR_A + idx); + csio_wr_reg32(hw, 64, EDC_BIST_CMD_LEN_A + idx); + csio_wr_reg32(hw, 0xc, EDC_BIST_DATA_PATTERN_A + idx); + csio_wr_reg32(hw, BIST_OPCODE_V(1) | BIST_CMD_GAP_V(1) | START_BIST_F, + EDC_BIST_CMD_A + idx); + i = csio_hw_wait_op_done_val(hw, EDC_BIST_CMD_A + idx, START_BIST_F, 0, 10, 1, NULL); if (i) return i; -#define EDC_DATA(i) (EDC_BIST_STATUS_REG(EDC_BIST_STATUS_RDATA, i) + idx) +#define EDC_DATA(i) (EDC_BIST_STATUS_REG(EDC_BIST_STATUS_RDATA_A, i) + idx) for (i = 15; i >= 0; i--) *data++ = htonl(csio_rd_reg32(hw, EDC_DATA(i))); diff --git a/drivers/scsi/csiostor/csio_hw_t5.c b/drivers/scsi/csiostor/csio_hw_t5.c index 99a7dca..3267f4f 100644 --- a/drivers/scsi/csiostor/csio_hw_t5.c +++ b/drivers/scsi/csiostor/csio_hw_t5.c @@ -177,25 +177,25 @@ csio_t5_mc_read(struct csio_hw *hw, int idx, uint32_t addr, __be32 *data, uint32_t mc_bist_cmd_reg, mc_bist_cmd_addr_reg, mc_bist_cmd_len_reg; uint32_t mc_bist_status_rdata_reg, mc_bist_data_pattern_reg; - mc_bist_cmd_reg = MC_REG(MC_P_BIST_CMD, idx); - mc_bist_cmd_addr_reg = MC_REG(MC_P_BIST_CMD_ADDR, idx); - mc_bist_cmd_len_reg = MC_REG(MC_P_BIST_CMD_LEN, idx); - mc_bist_status_rdata_reg = MC_REG(MC_P_BIST_STATUS_RDATA, idx); - mc_bist_data_pattern_reg = MC_REG(MC_P_BIST_DATA_PATTERN, idx); + mc_bist_cmd_reg = MC_REG(MC_P_BIST_CMD_A, idx); + mc_bist_cmd_addr_reg = MC_REG(MC_P_BIST_CMD_ADDR_A, idx); + mc_bist_cmd_len_reg = MC_REG(MC_P_BIST_CMD_LEN_A, idx); + mc_bist_status_rdata_reg = MC_REG(MC_P_BIST_STATUS_RDATA_A, idx); + mc_bist_data_pattern_reg = MC_REG(MC_P_BIST_DATA_PATTERN_A, idx); - if (csio_rd_reg32(hw, mc_bist_cmd_reg) & START_BIST) + if (csio_rd_reg32(hw, mc_bist_cmd_reg) & START_BIST_F) return -EBUSY; csio_wr_reg32(hw, addr & ~0x3fU, mc_bist_cmd_addr_reg); csio_wr_reg32(hw, 64, mc_bist_cmd_len_reg); csio_wr_reg32(hw, 0xc, mc_bist_data_pattern_reg); - csio_wr_reg32(hw, BIST_OPCODE(1) | START_BIST | BIST_CMD_GAP(1), + csio_wr_reg32(hw, BIST_OPCODE_V(1) | START_BIST_F | BIST_CMD_GAP_V(1), mc_bist_cmd_reg); - i = csio_hw_wait_op_done_val(hw, mc_bist_cmd_reg, START_BIST, + i = csio_hw_wait_op_done_val(hw, mc_bist_cmd_reg, START_BIST_F, 0, 10, 1, NULL); if (i) return i; -#define MC_DATA(i) MC_BIST_STATUS_REG(MC_BIST_STATUS_RDATA, i) +#define MC_DATA(i) MC_BIST_STATUS_REG(MC_BIST_STATUS_RDATA_A, i) for (i = 15; i >= 0; i--) *data++ = htonl(csio_rd_reg32(hw, MC_DATA(i))); @@ -231,27 +231,27 @@ csio_t5_edc_read(struct csio_hw *hw, int idx, uint32_t addr, __be32 *data, #define EDC_STRIDE_T5 (EDC_T51_BASE_ADDR - EDC_T50_BASE_ADDR) #define EDC_REG_T5(reg, idx) (reg + EDC_STRIDE_T5 * idx) - edc_bist_cmd_reg = EDC_REG_T5(EDC_H_BIST_CMD, idx); - edc_bist_cmd_addr_reg = EDC_REG_T5(EDC_H_BIST_CMD_ADDR, idx); - edc_bist_cmd_len_reg = EDC_REG_T5(EDC_H_BIST_CMD_LEN, idx); - edc_bist_cmd_data_pattern = EDC_REG_T5(EDC_H_BIST_DATA_PATTERN, idx); - edc_bist_status_rdata_reg = EDC_REG_T5(EDC_H_BIST_STATUS_RDATA, idx); + edc_bist_cmd_reg = EDC_REG_T5(EDC_H_BIST_CMD_A, idx); + edc_bist_cmd_addr_reg = EDC_REG_T5(EDC_H_BIST_CMD_ADDR_A, idx); + edc_bist_cmd_len_reg = EDC_REG_T5(EDC_H_BIST_CMD_LEN_A, idx); + edc_bist_cmd_data_pattern = EDC_REG_T5(EDC_H_BIST_DATA_PATTERN_A, idx); + edc_bist_status_rdata_reg = EDC_REG_T5(EDC_H_BIST_STATUS_RDATA_A, idx); #undef EDC_REG_T5 #undef EDC_STRIDE_T5 - if (csio_rd_reg32(hw, edc_bist_cmd_reg) & START_BIST) + if (csio_rd_reg32(hw, edc_bist_cmd_reg) & START_BIST_F) return -EBUSY; csio_wr_reg32(hw, addr & ~0x3fU, edc_bist_cmd_addr_reg); csio_wr_reg32(hw, 64, edc_bist_cmd_len_reg); csio_wr_reg32(hw, 0xc, edc_bist_cmd_data_pattern); - csio_wr_reg32(hw, BIST_OPCODE(1) | START_BIST | BIST_CMD_GAP(1), + csio_wr_reg32(hw, BIST_OPCODE_V(1) | START_BIST_F | BIST_CMD_GAP_V(1), edc_bist_cmd_reg); - i = csio_hw_wait_op_done_val(hw, edc_bist_cmd_reg, START_BIST, + i = csio_hw_wait_op_done_val(hw, edc_bist_cmd_reg, START_BIST_F, 0, 10, 1, NULL); if (i) return i; -#define EDC_DATA(i) (EDC_BIST_STATUS_REG(EDC_BIST_STATUS_RDATA, i) + idx) +#define EDC_DATA(i) (EDC_BIST_STATUS_REG(EDC_BIST_STATUS_RDATA_A, i) + idx) for (i = 15; i >= 0; i--) *data++ = htonl(csio_rd_reg32(hw, EDC_DATA(i))); diff --git a/drivers/scsi/csiostor/csio_mb.c b/drivers/scsi/csiostor/csio_mb.c index 08c265c..cb53985 100644 --- a/drivers/scsi/csiostor/csio_mb.c +++ b/drivers/scsi/csiostor/csio_mb.c @@ -1104,8 +1104,8 @@ csio_mb_process_portparams_rsp(struct csio_hw *hw, void csio_mb_intr_enable(struct csio_hw *hw) { - csio_wr_reg32(hw, MBMSGRDYINTEN(1), MYPF_REG(CIM_PF_HOST_INT_ENABLE)); - csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_ENABLE)); + csio_wr_reg32(hw, MBMSGRDYINTEN_F, MYPF_REG(CIM_PF_HOST_INT_ENABLE_A)); + csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_ENABLE_A)); } /* @@ -1117,8 +1117,9 @@ csio_mb_intr_enable(struct csio_hw *hw) void csio_mb_intr_disable(struct csio_hw *hw) { - csio_wr_reg32(hw, MBMSGRDYINTEN(0), MYPF_REG(CIM_PF_HOST_INT_ENABLE)); - csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_ENABLE)); + csio_wr_reg32(hw, MBMSGRDYINTEN_V(0), + MYPF_REG(CIM_PF_HOST_INT_ENABLE_A)); + csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_ENABLE_A)); } static void @@ -1153,8 +1154,8 @@ csio_mb_debug_cmd_handler(struct csio_hw *hw) { int i; __be64 cmd[CSIO_MB_MAX_REGS]; - uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL); - uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA); + uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL_A); + uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA_A); int size = sizeof(struct fw_debug_cmd); /* Copy mailbox data */ @@ -1164,8 +1165,8 @@ csio_mb_debug_cmd_handler(struct csio_hw *hw) csio_mb_dump_fw_dbg(hw, cmd); /* Notify FW of mailbox by setting owner as UP */ - csio_wr_reg32(hw, MBMSGVALID | MBINTREQ | MBOWNER(CSIO_MBOWNER_FW), - ctl_reg); + csio_wr_reg32(hw, MBMSGVALID_F | MBINTREQ_F | + MBOWNER_V(CSIO_MBOWNER_FW), ctl_reg); csio_rd_reg32(hw, ctl_reg); wmb(); @@ -1187,8 +1188,8 @@ csio_mb_issue(struct csio_hw *hw, struct csio_mb *mbp) __be64 *cmd = mbp->mb; __be64 hdr; struct csio_mbm *mbm = &hw->mbm; - uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL); - uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA); + uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL_A); + uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA_A); int size = mbp->mb_size; int rv = -EINVAL; struct fw_cmd_hdr *fw_hdr; @@ -1224,12 +1225,12 @@ csio_mb_issue(struct csio_hw *hw, struct csio_mb *mbp) } /* Now get ownership of mailbox */ - owner = MBOWNER_GET(csio_rd_reg32(hw, ctl_reg)); + owner = MBOWNER_G(csio_rd_reg32(hw, ctl_reg)); if (!csio_mb_is_host_owner(owner)) { for (i = 0; (owner == CSIO_MBOWNER_NONE) && (i < 3); i++) - owner = MBOWNER_GET(csio_rd_reg32(hw, ctl_reg)); + owner = MBOWNER_G(csio_rd_reg32(hw, ctl_reg)); /* * Mailbox unavailable. In immediate mode, fail the command. * In other modes, enqueue the request. @@ -1271,10 +1272,10 @@ csio_mb_issue(struct csio_hw *hw, struct csio_mb *mbp) if (mbp->mb_cbfn != NULL) { mbm->mcurrent = mbp; mod_timer(&mbm->timer, jiffies + msecs_to_jiffies(mbp->tmo)); - csio_wr_reg32(hw, MBMSGVALID | MBINTREQ | - MBOWNER(CSIO_MBOWNER_FW), ctl_reg); + csio_wr_reg32(hw, MBMSGVALID_F | MBINTREQ_F | + MBOWNER_V(CSIO_MBOWNER_FW), ctl_reg); } else - csio_wr_reg32(hw, MBMSGVALID | MBOWNER(CSIO_MBOWNER_FW), + csio_wr_reg32(hw, MBMSGVALID_F | MBOWNER_V(CSIO_MBOWNER_FW), ctl_reg); /* Flush posted writes */ @@ -1294,9 +1295,9 @@ csio_mb_issue(struct csio_hw *hw, struct csio_mb *mbp) /* Check for response */ ctl = csio_rd_reg32(hw, ctl_reg); - if (csio_mb_is_host_owner(MBOWNER_GET(ctl))) { + if (csio_mb_is_host_owner(MBOWNER_G(ctl))) { - if (!(ctl & MBMSGVALID)) { + if (!(ctl & MBMSGVALID_F)) { csio_wr_reg32(hw, 0, ctl_reg); continue; } @@ -1457,16 +1458,16 @@ csio_mb_isr_handler(struct csio_hw *hw) __be64 *cmd; uint32_t ctl, cim_cause, pl_cause; int i; - uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL); - uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA); + uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL_A); + uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA_A); int size; __be64 hdr; struct fw_cmd_hdr *fw_hdr; pl_cause = csio_rd_reg32(hw, MYPF_REG(PL_PF_INT_CAUSE)); - cim_cause = csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_CAUSE)); + cim_cause = csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_CAUSE_A)); - if (!(pl_cause & PFCIM) || !(cim_cause & MBMSGRDYINT)) { + if (!(pl_cause & PFCIM) || !(cim_cause & MBMSGRDYINT_F)) { CSIO_INC_STATS(hw, n_mbint_unexp); return -EINVAL; } @@ -1477,16 +1478,16 @@ csio_mb_isr_handler(struct csio_hw *hw) * the upper level cause register. In other words, CIM-cause * first followed by PL-Cause next. */ - csio_wr_reg32(hw, MBMSGRDYINT, MYPF_REG(CIM_PF_HOST_INT_CAUSE)); + csio_wr_reg32(hw, MBMSGRDYINT_F, MYPF_REG(CIM_PF_HOST_INT_CAUSE_A)); csio_wr_reg32(hw, PFCIM, MYPF_REG(PL_PF_INT_CAUSE)); ctl = csio_rd_reg32(hw, ctl_reg); - if (csio_mb_is_host_owner(MBOWNER_GET(ctl))) { + if (csio_mb_is_host_owner(MBOWNER_G(ctl))) { CSIO_DUMP_MB(hw, hw->pfn, data_reg); - if (!(ctl & MBMSGVALID)) { + if (!(ctl & MBMSGVALID_F)) { csio_warn(hw, "Stray mailbox interrupt recvd," " mailbox data not valid\n"); -- cgit v0.10.2 From 837e4a42bbb5c41ce555bcd544a9c24c28134e24 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Mon, 5 Jan 2015 16:30:46 +0530 Subject: cxgb4/csiostor: Cleanup TP, MPS and TCAM related register defines This patch cleanups all TP, MPS and TCAM related macros/register defines that are defined in t4_regs.h and the affected files Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 16c633f..53ad8d3 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -359,8 +359,8 @@ MODULE_PARM_DESC(select_queue, */ enum { TP_VLAN_PRI_MAP_DEFAULT = HW_TPL_FR_MT_PR_IV_P_FC, - TP_VLAN_PRI_MAP_FIRST = FCOE_SHIFT, - TP_VLAN_PRI_MAP_LAST = FRAGMENTATION_SHIFT, + TP_VLAN_PRI_MAP_FIRST = FCOE_S, + TP_VLAN_PRI_MAP_LAST = FRAGMENTATION_S, }; static unsigned int tp_vlan_pri_map = TP_VLAN_PRI_MAP_DEFAULT; @@ -1177,10 +1177,10 @@ freeout: t4_free_sge_resources(adap); } t4_write_reg(adap, is_t4(adap->params.chip) ? - MPS_TRC_RSS_CONTROL : - MPS_T5_TRC_RSS_CONTROL, - RSSCONTROL(netdev2pinfo(adap->port[0])->tx_chan) | - QUEUENUMBER(s->ethrxq[0].rspq.abs_id)); + MPS_TRC_RSS_CONTROL_A : + MPS_T5_TRC_RSS_CONTROL_A, + RSSCONTROL_V(netdev2pinfo(adap->port[0])->tx_chan) | + QUEUENUMBER_V(s->ethrxq[0].rspq.abs_id)); return 0; } @@ -4094,7 +4094,7 @@ static void uld_attach(struct adapter *adap, unsigned int uld) lli.nports = adap->params.nports; lli.wr_cred = adap->params.ofldq_wr_cred; lli.adapter_type = adap->params.chip; - lli.iscsi_iolen = MAXRXDATA_GET(t4_read_reg(adap, TP_PARA_REG2)); + lli.iscsi_iolen = MAXRXDATA_G(t4_read_reg(adap, TP_PARA_REG2_A)); lli.cclk_ps = 1000000000 / adap->params.vpd.cclk; lli.udb_density = 1 << adap->params.sge.eq_qpp; lli.ucq_density = 1 << adap->params.sge.iq_qpp; @@ -4949,11 +4949,11 @@ static int adap_init1(struct adapter *adap, struct fw_caps_config_cmd *c) t4_sge_init(adap); /* tweak some settings */ - t4_write_reg(adap, TP_SHIFT_CNT, 0x64f8849); + t4_write_reg(adap, TP_SHIFT_CNT_A, 0x64f8849); t4_write_reg(adap, ULP_RX_TDDP_PSZ, HPZ0(PAGE_SHIFT - 12)); - t4_write_reg(adap, TP_PIO_ADDR, TP_INGRESS_CONFIG); - v = t4_read_reg(adap, TP_PIO_DATA); - t4_write_reg(adap, TP_PIO_DATA, v & ~CSUM_HAS_PSEUDO_HDR); + t4_write_reg(adap, TP_PIO_ADDR_A, TP_INGRESS_CONFIG_A); + v = t4_read_reg(adap, TP_PIO_DATA_A); + t4_write_reg(adap, TP_PIO_DATA_A, v & ~CSUM_HAS_PSEUDO_HDR_F); /* first 4 Tx modulation queues point to consecutive Tx channels */ adap->params.tp.tx_modq_map = 0xE4; @@ -4962,11 +4962,11 @@ static int adap_init1(struct adapter *adap, struct fw_caps_config_cmd *c) /* associate each Tx modulation queue with consecutive Tx channels */ v = 0x84218421; - t4_write_indirect(adap, TP_PIO_ADDR, TP_PIO_DATA, + t4_write_indirect(adap, TP_PIO_ADDR_A, TP_PIO_DATA_A, &v, 1, A_TP_TX_SCHED_HDR); - t4_write_indirect(adap, TP_PIO_ADDR, TP_PIO_DATA, + t4_write_indirect(adap, TP_PIO_ADDR_A, TP_PIO_DATA_A, &v, 1, A_TP_TX_SCHED_FIFO); - t4_write_indirect(adap, TP_PIO_ADDR, TP_PIO_DATA, + t4_write_indirect(adap, TP_PIO_ADDR_A, TP_PIO_DATA_A, &v, 1, A_TP_TX_SCHED_PCMD); #define T4_TX_MODQ_10G_WEIGHT_DEFAULT 16 /* in KB units */ @@ -5034,8 +5034,8 @@ static int adap_init0_tweaks(struct adapter *adapter) * Don't include the "IP Pseudo Header" in CPL_RX_PKT checksums: Linux * adds the pseudo header itself. */ - t4_tp_wr_bits_indirect(adapter, TP_INGRESS_CONFIG, - CSUM_HAS_PSEUDO_HDR, 0); + t4_tp_wr_bits_indirect(adapter, TP_INGRESS_CONFIG_A, + CSUM_HAS_PSEUDO_HDR_F, 0); return 0; } @@ -5401,34 +5401,34 @@ static int adap_init0_no_config(struct adapter *adapter, int reset) case 0: /* compressed filter field not enabled */ break; - case FCOE_MASK: + case FCOE_F: bits += 1; break; - case PORT_MASK: + case PORT_F: bits += 3; break; - case VNIC_ID_MASK: + case VNIC_F: bits += 17; break; - case VLAN_MASK: + case VLAN_F: bits += 17; break; - case TOS_MASK: + case TOS_F: bits += 8; break; - case PROTOCOL_MASK: + case PROTOCOL_F: bits += 8; break; - case ETHERTYPE_MASK: + case ETHERTYPE_F: bits += 16; break; - case MACMATCH_MASK: + case MACMATCH_F: bits += 9; break; - case MPSHITTYPE_MASK: + case MPSHITTYPE_F: bits += 3; break; - case FRAGMENTATION_MASK: + case FRAGMENTATION_F: bits += 1; break; } @@ -5442,8 +5442,8 @@ static int adap_init0_no_config(struct adapter *adapter, int reset) } } v = tp_vlan_pri_map; - t4_write_indirect(adapter, TP_PIO_ADDR, TP_PIO_DATA, - &v, 1, TP_VLAN_PRI_MAP); + t4_write_indirect(adapter, TP_PIO_ADDR_A, TP_PIO_DATA_A, + &v, 1, TP_VLAN_PRI_MAP_A); /* * We need Five Tuple Lookup mode to be set in TP_GLOBAL_CONFIG order @@ -5456,17 +5456,17 @@ static int adap_init0_no_config(struct adapter *adapter, int reset) * performance impact). */ if (tp_vlan_pri_map) - t4_set_reg_field(adapter, TP_GLOBAL_CONFIG, - FIVETUPLELOOKUP_MASK, - FIVETUPLELOOKUP_MASK); + t4_set_reg_field(adapter, TP_GLOBAL_CONFIG_A, + FIVETUPLELOOKUP_V(FIVETUPLELOOKUP_M), + FIVETUPLELOOKUP_V(FIVETUPLELOOKUP_M)); /* * Tweak some settings. */ - t4_write_reg(adapter, TP_SHIFT_CNT, SYNSHIFTMAX(6) | - RXTSHIFTMAXR1(4) | RXTSHIFTMAXR2(15) | - PERSHIFTBACKOFFMAX(8) | PERSHIFTMAX(8) | - KEEPALIVEMAXR1(4) | KEEPALIVEMAXR2(9)); + t4_write_reg(adapter, TP_SHIFT_CNT_A, SYNSHIFTMAX_V(6) | + RXTSHIFTMAXR1_V(4) | RXTSHIFTMAXR2_V(15) | + PERSHIFTBACKOFFMAX_V(8) | PERSHIFTMAX_V(8) | + KEEPALIVEMAXR1_V(4) | KEEPALIVEMAXR2_V(9)); /* * Get basic stuff going by issuing the Firmware Initialize command. diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index c9777e0..cf0bf79 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -1486,11 +1486,11 @@ static void tp_intr_handler(struct adapter *adapter) { static const struct intr_info tp_intr_info[] = { { 0x3fffffff, "TP parity error", -1, 1 }, - { FLMTXFLSTEMPTY, "TP out of Tx pages", -1, 1 }, + { FLMTXFLSTEMPTY_F, "TP out of Tx pages", -1, 1 }, { 0 } }; - if (t4_handle_intr_status(adapter, TP_INT_CAUSE, tp_intr_info)) + if (t4_handle_intr_status(adapter, TP_INT_CAUSE_A, tp_intr_info)) t4_fatal_err(adapter); } @@ -1629,19 +1629,19 @@ static void ulprx_intr_handler(struct adapter *adapter) static void ulptx_intr_handler(struct adapter *adapter) { static const struct intr_info ulptx_intr_info[] = { - { PBL_BOUND_ERR_CH3, "ULPTX channel 3 PBL out of bounds", -1, + { PBL_BOUND_ERR_CH3_F, "ULPTX channel 3 PBL out of bounds", -1, 0 }, - { PBL_BOUND_ERR_CH2, "ULPTX channel 2 PBL out of bounds", -1, + { PBL_BOUND_ERR_CH2_F, "ULPTX channel 2 PBL out of bounds", -1, 0 }, - { PBL_BOUND_ERR_CH1, "ULPTX channel 1 PBL out of bounds", -1, + { PBL_BOUND_ERR_CH1_F, "ULPTX channel 1 PBL out of bounds", -1, 0 }, - { PBL_BOUND_ERR_CH0, "ULPTX channel 0 PBL out of bounds", -1, + { PBL_BOUND_ERR_CH0_F, "ULPTX channel 0 PBL out of bounds", -1, 0 }, { 0xfffffff, "ULPTX parity error", -1, 1 }, { 0 } }; - if (t4_handle_intr_status(adapter, ULP_TX_INT_CAUSE, ulptx_intr_info)) + if (t4_handle_intr_status(adapter, ULP_TX_INT_CAUSE_A, ulptx_intr_info)) t4_fatal_err(adapter); } @@ -1651,19 +1651,20 @@ static void ulptx_intr_handler(struct adapter *adapter) static void pmtx_intr_handler(struct adapter *adapter) { static const struct intr_info pmtx_intr_info[] = { - { PCMD_LEN_OVFL0, "PMTX channel 0 pcmd too large", -1, 1 }, - { PCMD_LEN_OVFL1, "PMTX channel 1 pcmd too large", -1, 1 }, - { PCMD_LEN_OVFL2, "PMTX channel 2 pcmd too large", -1, 1 }, - { ZERO_C_CMD_ERROR, "PMTX 0-length pcmd", -1, 1 }, - { PMTX_FRAMING_ERROR, "PMTX framing error", -1, 1 }, - { OESPI_PAR_ERROR, "PMTX oespi parity error", -1, 1 }, - { DB_OPTIONS_PAR_ERROR, "PMTX db_options parity error", -1, 1 }, - { ICSPI_PAR_ERROR, "PMTX icspi parity error", -1, 1 }, - { C_PCMD_PAR_ERROR, "PMTX c_pcmd parity error", -1, 1}, + { PCMD_LEN_OVFL0_F, "PMTX channel 0 pcmd too large", -1, 1 }, + { PCMD_LEN_OVFL1_F, "PMTX channel 1 pcmd too large", -1, 1 }, + { PCMD_LEN_OVFL2_F, "PMTX channel 2 pcmd too large", -1, 1 }, + { ZERO_C_CMD_ERROR_F, "PMTX 0-length pcmd", -1, 1 }, + { PMTX_FRAMING_ERROR_F, "PMTX framing error", -1, 1 }, + { OESPI_PAR_ERROR_F, "PMTX oespi parity error", -1, 1 }, + { DB_OPTIONS_PAR_ERROR_F, "PMTX db_options parity error", + -1, 1 }, + { ICSPI_PAR_ERROR_F, "PMTX icspi parity error", -1, 1 }, + { PMTX_C_PCMD_PAR_ERROR_F, "PMTX c_pcmd parity error", -1, 1}, { 0 } }; - if (t4_handle_intr_status(adapter, PM_TX_INT_CAUSE, pmtx_intr_info)) + if (t4_handle_intr_status(adapter, PM_TX_INT_CAUSE_A, pmtx_intr_info)) t4_fatal_err(adapter); } @@ -1673,16 +1674,17 @@ static void pmtx_intr_handler(struct adapter *adapter) static void pmrx_intr_handler(struct adapter *adapter) { static const struct intr_info pmrx_intr_info[] = { - { ZERO_E_CMD_ERROR, "PMRX 0-length pcmd", -1, 1 }, - { PMRX_FRAMING_ERROR, "PMRX framing error", -1, 1 }, - { OCSPI_PAR_ERROR, "PMRX ocspi parity error", -1, 1 }, - { DB_OPTIONS_PAR_ERROR, "PMRX db_options parity error", -1, 1 }, - { IESPI_PAR_ERROR, "PMRX iespi parity error", -1, 1 }, - { E_PCMD_PAR_ERROR, "PMRX e_pcmd parity error", -1, 1}, + { ZERO_E_CMD_ERROR_F, "PMRX 0-length pcmd", -1, 1 }, + { PMRX_FRAMING_ERROR_F, "PMRX framing error", -1, 1 }, + { OCSPI_PAR_ERROR_F, "PMRX ocspi parity error", -1, 1 }, + { DB_OPTIONS_PAR_ERROR_F, "PMRX db_options parity error", + -1, 1 }, + { IESPI_PAR_ERROR_F, "PMRX iespi parity error", -1, 1 }, + { PMRX_E_PCMD_PAR_ERROR_F, "PMRX e_pcmd parity error", -1, 1}, { 0 } }; - if (t4_handle_intr_status(adapter, PM_RX_INT_CAUSE, pmrx_intr_info)) + if (t4_handle_intr_status(adapter, PM_RX_INT_CAUSE_A, pmrx_intr_info)) t4_fatal_err(adapter); } @@ -1733,19 +1735,22 @@ static void mps_intr_handler(struct adapter *adapter) { 0 } }; static const struct intr_info mps_tx_intr_info[] = { - { TPFIFO, "MPS Tx TP FIFO parity error", -1, 1 }, - { NCSIFIFO, "MPS Tx NC-SI FIFO parity error", -1, 1 }, - { TXDATAFIFO, "MPS Tx data FIFO parity error", -1, 1 }, - { TXDESCFIFO, "MPS Tx desc FIFO parity error", -1, 1 }, - { BUBBLE, "MPS Tx underflow", -1, 1 }, - { SECNTERR, "MPS Tx SOP/EOP error", -1, 1 }, - { FRMERR, "MPS Tx framing error", -1, 1 }, + { TPFIFO_V(TPFIFO_M), "MPS Tx TP FIFO parity error", -1, 1 }, + { NCSIFIFO_F, "MPS Tx NC-SI FIFO parity error", -1, 1 }, + { TXDATAFIFO_V(TXDATAFIFO_M), "MPS Tx data FIFO parity error", + -1, 1 }, + { TXDESCFIFO_V(TXDESCFIFO_M), "MPS Tx desc FIFO parity error", + -1, 1 }, + { BUBBLE_F, "MPS Tx underflow", -1, 1 }, + { SECNTERR_F, "MPS Tx SOP/EOP error", -1, 1 }, + { FRMERR_F, "MPS Tx framing error", -1, 1 }, { 0 } }; static const struct intr_info mps_trc_intr_info[] = { - { FILTMEM, "MPS TRC filter parity error", -1, 1 }, - { PKTFIFO, "MPS TRC packet FIFO parity error", -1, 1 }, - { MISCPERR, "MPS TRC misc parity error", -1, 1 }, + { FILTMEM_V(FILTMEM_M), "MPS TRC filter parity error", -1, 1 }, + { PKTFIFO_V(PKTFIFO_M), "MPS TRC packet FIFO parity error", + -1, 1 }, + { MISCPERR_F, "MPS TRC misc parity error", -1, 1 }, { 0 } }; static const struct intr_info mps_stat_sram_intr_info[] = { @@ -1761,32 +1766,31 @@ static void mps_intr_handler(struct adapter *adapter) { 0 } }; static const struct intr_info mps_cls_intr_info[] = { - { MATCHSRAM, "MPS match SRAM parity error", -1, 1 }, - { MATCHTCAM, "MPS match TCAM parity error", -1, 1 }, - { HASHSRAM, "MPS hash SRAM parity error", -1, 1 }, + { MATCHSRAM_F, "MPS match SRAM parity error", -1, 1 }, + { MATCHTCAM_F, "MPS match TCAM parity error", -1, 1 }, + { HASHSRAM_F, "MPS hash SRAM parity error", -1, 1 }, { 0 } }; int fat; - fat = t4_handle_intr_status(adapter, MPS_RX_PERR_INT_CAUSE, + fat = t4_handle_intr_status(adapter, MPS_RX_PERR_INT_CAUSE_A, mps_rx_intr_info) + - t4_handle_intr_status(adapter, MPS_TX_INT_CAUSE, + t4_handle_intr_status(adapter, MPS_TX_INT_CAUSE_A, mps_tx_intr_info) + - t4_handle_intr_status(adapter, MPS_TRC_INT_CAUSE, + t4_handle_intr_status(adapter, MPS_TRC_INT_CAUSE_A, mps_trc_intr_info) + - t4_handle_intr_status(adapter, MPS_STAT_PERR_INT_CAUSE_SRAM, + t4_handle_intr_status(adapter, MPS_STAT_PERR_INT_CAUSE_SRAM_A, mps_stat_sram_intr_info) + - t4_handle_intr_status(adapter, MPS_STAT_PERR_INT_CAUSE_TX_FIFO, + t4_handle_intr_status(adapter, MPS_STAT_PERR_INT_CAUSE_TX_FIFO_A, mps_stat_tx_intr_info) + - t4_handle_intr_status(adapter, MPS_STAT_PERR_INT_CAUSE_RX_FIFO, + t4_handle_intr_status(adapter, MPS_STAT_PERR_INT_CAUSE_RX_FIFO_A, mps_stat_rx_intr_info) + - t4_handle_intr_status(adapter, MPS_CLS_INT_CAUSE, + t4_handle_intr_status(adapter, MPS_CLS_INT_CAUSE_A, mps_cls_intr_info); - t4_write_reg(adapter, MPS_INT_CAUSE, CLSINT | TRCINT | - RXINT | TXINT | STATINT); - t4_read_reg(adapter, MPS_INT_CAUSE); /* flush */ + t4_write_reg(adapter, MPS_INT_CAUSE_A, 0); + t4_read_reg(adapter, MPS_INT_CAUSE_A); /* flush */ if (fat) t4_fatal_err(adapter); } @@ -2187,23 +2191,23 @@ int t4_config_glbl_rss(struct adapter *adapter, int mbox, unsigned int mode, void t4_tp_get_tcp_stats(struct adapter *adap, struct tp_tcp_stats *v4, struct tp_tcp_stats *v6) { - u32 val[TP_MIB_TCP_RXT_SEG_LO - TP_MIB_TCP_OUT_RST + 1]; + u32 val[TP_MIB_TCP_RXT_SEG_LO_A - TP_MIB_TCP_OUT_RST_A + 1]; -#define STAT_IDX(x) ((TP_MIB_TCP_##x) - TP_MIB_TCP_OUT_RST) +#define STAT_IDX(x) ((TP_MIB_TCP_##x##_A) - TP_MIB_TCP_OUT_RST_A) #define STAT(x) val[STAT_IDX(x)] #define STAT64(x) (((u64)STAT(x##_HI) << 32) | STAT(x##_LO)) if (v4) { - t4_read_indirect(adap, TP_MIB_INDEX, TP_MIB_DATA, val, - ARRAY_SIZE(val), TP_MIB_TCP_OUT_RST); + t4_read_indirect(adap, TP_MIB_INDEX_A, TP_MIB_DATA_A, val, + ARRAY_SIZE(val), TP_MIB_TCP_OUT_RST_A); v4->tcpOutRsts = STAT(OUT_RST); v4->tcpInSegs = STAT64(IN_SEG); v4->tcpOutSegs = STAT64(OUT_SEG); v4->tcpRetransSegs = STAT64(RXT_SEG); } if (v6) { - t4_read_indirect(adap, TP_MIB_INDEX, TP_MIB_DATA, val, - ARRAY_SIZE(val), TP_MIB_TCP_V6OUT_RST); + t4_read_indirect(adap, TP_MIB_INDEX_A, TP_MIB_DATA_A, val, + ARRAY_SIZE(val), TP_MIB_TCP_V6OUT_RST_A); v6->tcpOutRsts = STAT(OUT_RST); v6->tcpInSegs = STAT64(IN_SEG); v6->tcpOutSegs = STAT64(OUT_SEG); @@ -2228,12 +2232,12 @@ void t4_read_mtu_tbl(struct adapter *adap, u16 *mtus, u8 *mtu_log) int i; for (i = 0; i < NMTUS; ++i) { - t4_write_reg(adap, TP_MTU_TABLE, - MTUINDEX(0xff) | MTUVALUE(i)); - v = t4_read_reg(adap, TP_MTU_TABLE); - mtus[i] = MTUVALUE_GET(v); + t4_write_reg(adap, TP_MTU_TABLE_A, + MTUINDEX_V(0xff) | MTUVALUE_V(i)); + v = t4_read_reg(adap, TP_MTU_TABLE_A); + mtus[i] = MTUVALUE_G(v); if (mtu_log) - mtu_log[i] = MTUWIDTH_GET(v); + mtu_log[i] = MTUWIDTH_G(v); } } @@ -2249,9 +2253,9 @@ void t4_read_mtu_tbl(struct adapter *adap, u16 *mtus, u8 *mtu_log) void t4_tp_wr_bits_indirect(struct adapter *adap, unsigned int addr, unsigned int mask, unsigned int val) { - t4_write_reg(adap, TP_PIO_ADDR, addr); - val |= t4_read_reg(adap, TP_PIO_DATA) & ~mask; - t4_write_reg(adap, TP_PIO_DATA, val); + t4_write_reg(adap, TP_PIO_ADDR_A, addr); + val |= t4_read_reg(adap, TP_PIO_DATA_A) & ~mask; + t4_write_reg(adap, TP_PIO_DATA_A, val); } /** @@ -2330,8 +2334,8 @@ void t4_load_mtus(struct adapter *adap, const unsigned short *mtus, if (!(mtu & ((1 << log2) >> 2))) /* round */ log2--; - t4_write_reg(adap, TP_MTU_TABLE, MTUINDEX(i) | - MTUWIDTH(log2) | MTUVALUE(mtu)); + t4_write_reg(adap, TP_MTU_TABLE_A, MTUINDEX_V(i) | + MTUWIDTH_V(log2) | MTUVALUE_V(mtu)); for (w = 0; w < NCCTRL_WIN; ++w) { unsigned int inc; @@ -2339,7 +2343,7 @@ void t4_load_mtus(struct adapter *adap, const unsigned short *mtus, inc = max(((mtu - 40) * alpha[w]) / avg_pkts[w], CC_MIN_INCR); - t4_write_reg(adap, TP_CCTRL_TABLE, (i << 21) | + t4_write_reg(adap, TP_CCTRL_TABLE_A, (i << 21) | (w << 16) | (beta[w] << 13) | inc); } } @@ -2356,7 +2360,7 @@ void t4_load_mtus(struct adapter *adap, const unsigned short *mtus, */ static unsigned int get_mps_bg_map(struct adapter *adap, int idx) { - u32 n = NUMPORTS_GET(t4_read_reg(adap, MPS_CMN_CTL)); + u32 n = NUMPORTS_G(t4_read_reg(adap, MPS_CMN_CTL_A)); if (n == 0) return idx == 0 ? 0xf : 0; @@ -2498,7 +2502,7 @@ void t4_wol_magic_enable(struct adapter *adap, unsigned int port, } else { mag_id_reg_l = T5_PORT_REG(port, MAC_PORT_MAGIC_MACID_LO); mag_id_reg_h = T5_PORT_REG(port, MAC_PORT_MAGIC_MACID_HI); - port_cfg_reg = T5_PORT_REG(port, MAC_PORT_CFG2); + port_cfg_reg = T5_PORT_REG(port, MAC_PORT_CFG2_A); } if (addr) { @@ -2536,7 +2540,7 @@ int t4_wol_pat_enable(struct adapter *adap, unsigned int port, unsigned int map, if (is_t4(adap->params.chip)) port_cfg_reg = PORT_REG(port, XGMAC_PORT_CFG2); else - port_cfg_reg = T5_PORT_REG(port, MAC_PORT_CFG2); + port_cfg_reg = T5_PORT_REG(port, MAC_PORT_CFG2_A); if (!enable) { t4_set_reg_field(adap, port_cfg_reg, PATEN, 0); @@ -2547,7 +2551,7 @@ int t4_wol_pat_enable(struct adapter *adap, unsigned int port, unsigned int map, #define EPIO_REG(name) \ (is_t4(adap->params.chip) ? PORT_REG(port, XGMAC_PORT_EPIO_##name) : \ - T5_PORT_REG(port, MAC_PORT_EPIO_##name)) + T5_PORT_REG(port, MAC_PORT_EPIO_##name##_A)) t4_write_reg(adap, EPIO_REG(DATA1), mask0 >> 32); t4_write_reg(adap, EPIO_REG(DATA2), mask1); @@ -4171,9 +4175,9 @@ int t4_init_tp_params(struct adapter *adap) int chan; u32 v; - v = t4_read_reg(adap, TP_TIMER_RESOLUTION); - adap->params.tp.tre = TIMERRESOLUTION_GET(v); - adap->params.tp.dack_re = DELAYEDACKRESOLUTION_GET(v); + v = t4_read_reg(adap, TP_TIMER_RESOLUTION_A); + adap->params.tp.tre = TIMERRESOLUTION_G(v); + adap->params.tp.dack_re = DELAYEDACKRESOLUTION_G(v); /* MODQ_REQ_MAP defaults to setting queues 0-3 to chan 0-3 */ for (chan = 0; chan < NCHAN; chan++) @@ -4182,12 +4186,12 @@ int t4_init_tp_params(struct adapter *adap) /* Cache the adapter's Compressed Filter Mode and global Incress * Configuration. */ - t4_read_indirect(adap, TP_PIO_ADDR, TP_PIO_DATA, + t4_read_indirect(adap, TP_PIO_ADDR_A, TP_PIO_DATA_A, &adap->params.tp.vlan_pri_map, 1, - TP_VLAN_PRI_MAP); - t4_read_indirect(adap, TP_PIO_ADDR, TP_PIO_DATA, + TP_VLAN_PRI_MAP_A); + t4_read_indirect(adap, TP_PIO_ADDR_A, TP_PIO_DATA_A, &adap->params.tp.ingress_config, 1, - TP_INGRESS_CONFIG); + TP_INGRESS_CONFIG_A); /* Now that we have TP_VLAN_PRI_MAP cached, we can calculate the field * shift positions of several elements of the Compressed Filter Tuple diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index 4b66818..ec0addc 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -1182,158 +1182,258 @@ #define RSVDSPACEINT_V(x) ((x) << RSVDSPACEINT_S) #define RSVDSPACEINT_F RSVDSPACEINT_V(1U) -#define TP_OUT_CONFIG 0x7d04 -#define VLANEXTENABLE_MASK 0x0000f000U -#define VLANEXTENABLE_SHIFT 12 - -#define TP_GLOBAL_CONFIG 0x7d08 -#define FIVETUPLELOOKUP_SHIFT 17 -#define FIVETUPLELOOKUP_MASK 0x00060000U -#define FIVETUPLELOOKUP(x) ((x) << FIVETUPLELOOKUP_SHIFT) -#define FIVETUPLELOOKUP_GET(x) (((x) & FIVETUPLELOOKUP_MASK) >> \ - FIVETUPLELOOKUP_SHIFT) - -#define TP_PARA_REG2 0x7d68 -#define MAXRXDATA_MASK 0xffff0000U -#define MAXRXDATA_SHIFT 16 -#define MAXRXDATA_GET(x) (((x) & MAXRXDATA_MASK) >> MAXRXDATA_SHIFT) - -#define TP_TIMER_RESOLUTION 0x7d90 -#define TIMERRESOLUTION_MASK 0x00ff0000U -#define TIMERRESOLUTION_SHIFT 16 -#define TIMERRESOLUTION_GET(x) (((x) & TIMERRESOLUTION_MASK) >> TIMERRESOLUTION_SHIFT) -#define DELAYEDACKRESOLUTION_MASK 0x000000ffU -#define DELAYEDACKRESOLUTION_SHIFT 0 -#define DELAYEDACKRESOLUTION_GET(x) \ - (((x) & DELAYEDACKRESOLUTION_MASK) >> DELAYEDACKRESOLUTION_SHIFT) - -#define TP_SHIFT_CNT 0x7dc0 -#define SYNSHIFTMAX_SHIFT 24 -#define SYNSHIFTMAX_MASK 0xff000000U -#define SYNSHIFTMAX(x) ((x) << SYNSHIFTMAX_SHIFT) -#define SYNSHIFTMAX_GET(x) (((x) & SYNSHIFTMAX_MASK) >> \ - SYNSHIFTMAX_SHIFT) -#define RXTSHIFTMAXR1_SHIFT 20 -#define RXTSHIFTMAXR1_MASK 0x00f00000U -#define RXTSHIFTMAXR1(x) ((x) << RXTSHIFTMAXR1_SHIFT) -#define RXTSHIFTMAXR1_GET(x) (((x) & RXTSHIFTMAXR1_MASK) >> \ - RXTSHIFTMAXR1_SHIFT) -#define RXTSHIFTMAXR2_SHIFT 16 -#define RXTSHIFTMAXR2_MASK 0x000f0000U -#define RXTSHIFTMAXR2(x) ((x) << RXTSHIFTMAXR2_SHIFT) -#define RXTSHIFTMAXR2_GET(x) (((x) & RXTSHIFTMAXR2_MASK) >> \ - RXTSHIFTMAXR2_SHIFT) -#define PERSHIFTBACKOFFMAX_SHIFT 12 -#define PERSHIFTBACKOFFMAX_MASK 0x0000f000U -#define PERSHIFTBACKOFFMAX(x) ((x) << PERSHIFTBACKOFFMAX_SHIFT) -#define PERSHIFTBACKOFFMAX_GET(x) (((x) & PERSHIFTBACKOFFMAX_MASK) >> \ - PERSHIFTBACKOFFMAX_SHIFT) -#define PERSHIFTMAX_SHIFT 8 -#define PERSHIFTMAX_MASK 0x00000f00U -#define PERSHIFTMAX(x) ((x) << PERSHIFTMAX_SHIFT) -#define PERSHIFTMAX_GET(x) (((x) & PERSHIFTMAX_MASK) >> \ - PERSHIFTMAX_SHIFT) -#define KEEPALIVEMAXR1_SHIFT 4 -#define KEEPALIVEMAXR1_MASK 0x000000f0U -#define KEEPALIVEMAXR1(x) ((x) << KEEPALIVEMAXR1_SHIFT) -#define KEEPALIVEMAXR1_GET(x) (((x) & KEEPALIVEMAXR1_MASK) >> \ - KEEPALIVEMAXR1_SHIFT) -#define KEEPALIVEMAXR2_SHIFT 0 -#define KEEPALIVEMAXR2_MASK 0x0000000fU -#define KEEPALIVEMAXR2(x) ((x) << KEEPALIVEMAXR2_SHIFT) -#define KEEPALIVEMAXR2_GET(x) (((x) & KEEPALIVEMAXR2_MASK) >> \ - KEEPALIVEMAXR2_SHIFT) - -#define TP_CCTRL_TABLE 0x7ddc -#define TP_MTU_TABLE 0x7de4 -#define MTUINDEX_MASK 0xff000000U -#define MTUINDEX_SHIFT 24 -#define MTUINDEX(x) ((x) << MTUINDEX_SHIFT) -#define MTUWIDTH_MASK 0x000f0000U -#define MTUWIDTH_SHIFT 16 -#define MTUWIDTH(x) ((x) << MTUWIDTH_SHIFT) -#define MTUWIDTH_GET(x) (((x) & MTUWIDTH_MASK) >> MTUWIDTH_SHIFT) -#define MTUVALUE_MASK 0x00003fffU -#define MTUVALUE_SHIFT 0 -#define MTUVALUE(x) ((x) << MTUVALUE_SHIFT) -#define MTUVALUE_GET(x) (((x) & MTUVALUE_MASK) >> MTUVALUE_SHIFT) - -#define TP_RSS_LKP_TABLE 0x7dec -#define LKPTBLROWVLD 0x80000000U -#define LKPTBLQUEUE1_MASK 0x000ffc00U -#define LKPTBLQUEUE1_SHIFT 10 -#define LKPTBLQUEUE1(x) ((x) << LKPTBLQUEUE1_SHIFT) -#define LKPTBLQUEUE1_GET(x) (((x) & LKPTBLQUEUE1_MASK) >> LKPTBLQUEUE1_SHIFT) -#define LKPTBLQUEUE0_MASK 0x000003ffU -#define LKPTBLQUEUE0_SHIFT 0 -#define LKPTBLQUEUE0(x) ((x) << LKPTBLQUEUE0_SHIFT) -#define LKPTBLQUEUE0_GET(x) (((x) & LKPTBLQUEUE0_MASK) >> LKPTBLQUEUE0_SHIFT) - -#define TP_PIO_ADDR 0x7e40 -#define TP_PIO_DATA 0x7e44 -#define TP_MIB_INDEX 0x7e50 -#define TP_MIB_DATA 0x7e54 -#define TP_INT_CAUSE 0x7e74 -#define FLMTXFLSTEMPTY 0x40000000U - -#define TP_VLAN_PRI_MAP 0x140 -#define FRAGMENTATION_SHIFT 9 -#define FRAGMENTATION_MASK 0x00000200U -#define MPSHITTYPE_MASK 0x00000100U -#define MACMATCH_MASK 0x00000080U -#define ETHERTYPE_MASK 0x00000040U -#define PROTOCOL_MASK 0x00000020U -#define TOS_MASK 0x00000010U -#define VLAN_MASK 0x00000008U -#define VNIC_ID_MASK 0x00000004U -#define PORT_MASK 0x00000002U -#define FCOE_SHIFT 0 -#define FCOE_MASK 0x00000001U - -#define TP_INGRESS_CONFIG 0x141 -#define VNIC 0x00000800U -#define CSUM_HAS_PSEUDO_HDR 0x00000400U -#define RM_OVLAN 0x00000200U -#define LOOKUPEVERYPKT 0x00000100U - -#define TP_MIB_MAC_IN_ERR_0 0x0 -#define TP_MIB_TCP_OUT_RST 0xc -#define TP_MIB_TCP_IN_SEG_HI 0x10 -#define TP_MIB_TCP_IN_SEG_LO 0x11 -#define TP_MIB_TCP_OUT_SEG_HI 0x12 -#define TP_MIB_TCP_OUT_SEG_LO 0x13 -#define TP_MIB_TCP_RXT_SEG_HI 0x14 -#define TP_MIB_TCP_RXT_SEG_LO 0x15 -#define TP_MIB_TNL_CNG_DROP_0 0x18 -#define TP_MIB_TCP_V6IN_ERR_0 0x28 -#define TP_MIB_TCP_V6OUT_RST 0x2c -#define TP_MIB_OFD_ARP_DROP 0x36 -#define TP_MIB_TNL_DROP_0 0x44 -#define TP_MIB_OFD_VLN_DROP_0 0x58 - -#define ULP_TX_INT_CAUSE 0x8dcc -#define PBL_BOUND_ERR_CH3 0x80000000U -#define PBL_BOUND_ERR_CH2 0x40000000U -#define PBL_BOUND_ERR_CH1 0x20000000U -#define PBL_BOUND_ERR_CH0 0x10000000U - -#define PM_RX_INT_CAUSE 0x8fdc -#define ZERO_E_CMD_ERROR 0x00400000U -#define PMRX_FRAMING_ERROR 0x003ffff0U -#define OCSPI_PAR_ERROR 0x00000008U -#define DB_OPTIONS_PAR_ERROR 0x00000004U -#define IESPI_PAR_ERROR 0x00000002U -#define E_PCMD_PAR_ERROR 0x00000001U - -#define PM_TX_INT_CAUSE 0x8ffc -#define PCMD_LEN_OVFL0 0x80000000U -#define PCMD_LEN_OVFL1 0x40000000U -#define PCMD_LEN_OVFL2 0x20000000U -#define ZERO_C_CMD_ERROR 0x10000000U -#define PMTX_FRAMING_ERROR 0x0ffffff0U -#define OESPI_PAR_ERROR 0x00000008U -#define ICSPI_PAR_ERROR 0x00000002U -#define C_PCMD_PAR_ERROR 0x00000001U +/* registers for module TP */ +#define TP_OUT_CONFIG_A 0x7d04 +#define TP_GLOBAL_CONFIG_A 0x7d08 + +#define FIVETUPLELOOKUP_S 17 +#define FIVETUPLELOOKUP_M 0x3U +#define FIVETUPLELOOKUP_V(x) ((x) << FIVETUPLELOOKUP_S) +#define FIVETUPLELOOKUP_G(x) (((x) >> FIVETUPLELOOKUP_S) & FIVETUPLELOOKUP_M) + +#define TP_PARA_REG2_A 0x7d68 + +#define MAXRXDATA_S 16 +#define MAXRXDATA_M 0xffffU +#define MAXRXDATA_G(x) (((x) >> MAXRXDATA_S) & MAXRXDATA_M) + +#define TP_TIMER_RESOLUTION_A 0x7d90 + +#define TIMERRESOLUTION_S 16 +#define TIMERRESOLUTION_M 0xffU +#define TIMERRESOLUTION_G(x) (((x) >> TIMERRESOLUTION_S) & TIMERRESOLUTION_M) + +#define DELAYEDACKRESOLUTION_S 0 +#define DELAYEDACKRESOLUTION_M 0xffU +#define DELAYEDACKRESOLUTION_G(x) \ + (((x) >> DELAYEDACKRESOLUTION_S) & DELAYEDACKRESOLUTION_M) + +#define TP_SHIFT_CNT_A 0x7dc0 + +#define SYNSHIFTMAX_S 24 +#define SYNSHIFTMAX_M 0xffU +#define SYNSHIFTMAX_V(x) ((x) << SYNSHIFTMAX_S) +#define SYNSHIFTMAX_G(x) (((x) >> SYNSHIFTMAX_S) & SYNSHIFTMAX_M) + +#define RXTSHIFTMAXR1_S 20 +#define RXTSHIFTMAXR1_M 0xfU +#define RXTSHIFTMAXR1_V(x) ((x) << RXTSHIFTMAXR1_S) +#define RXTSHIFTMAXR1_G(x) (((x) >> RXTSHIFTMAXR1_S) & RXTSHIFTMAXR1_M) + +#define RXTSHIFTMAXR2_S 16 +#define RXTSHIFTMAXR2_M 0xfU +#define RXTSHIFTMAXR2_V(x) ((x) << RXTSHIFTMAXR2_S) +#define RXTSHIFTMAXR2_G(x) (((x) >> RXTSHIFTMAXR2_S) & RXTSHIFTMAXR2_M) + +#define PERSHIFTBACKOFFMAX_S 12 +#define PERSHIFTBACKOFFMAX_M 0xfU +#define PERSHIFTBACKOFFMAX_V(x) ((x) << PERSHIFTBACKOFFMAX_S) +#define PERSHIFTBACKOFFMAX_G(x) \ + (((x) >> PERSHIFTBACKOFFMAX_S) & PERSHIFTBACKOFFMAX_M) + +#define PERSHIFTMAX_S 8 +#define PERSHIFTMAX_M 0xfU +#define PERSHIFTMAX_V(x) ((x) << PERSHIFTMAX_S) +#define PERSHIFTMAX_G(x) (((x) >> PERSHIFTMAX_S) & PERSHIFTMAX_M) + +#define KEEPALIVEMAXR1_S 4 +#define KEEPALIVEMAXR1_M 0xfU +#define KEEPALIVEMAXR1_V(x) ((x) << KEEPALIVEMAXR1_S) +#define KEEPALIVEMAXR1_G(x) (((x) >> KEEPALIVEMAXR1_S) & KEEPALIVEMAXR1_M) + +#define KEEPALIVEMAXR2_S 0 +#define KEEPALIVEMAXR2_M 0xfU +#define KEEPALIVEMAXR2_V(x) ((x) << KEEPALIVEMAXR2_S) +#define KEEPALIVEMAXR2_G(x) (((x) >> KEEPALIVEMAXR2_S) & KEEPALIVEMAXR2_M) + +#define TP_CCTRL_TABLE_A 0x7ddc +#define TP_MTU_TABLE_A 0x7de4 + +#define MTUINDEX_S 24 +#define MTUINDEX_V(x) ((x) << MTUINDEX_S) + +#define MTUWIDTH_S 16 +#define MTUWIDTH_M 0xfU +#define MTUWIDTH_V(x) ((x) << MTUWIDTH_S) +#define MTUWIDTH_G(x) (((x) >> MTUWIDTH_S) & MTUWIDTH_M) + +#define MTUVALUE_S 0 +#define MTUVALUE_M 0x3fffU +#define MTUVALUE_V(x) ((x) << MTUVALUE_S) +#define MTUVALUE_G(x) (((x) >> MTUVALUE_S) & MTUVALUE_M) + +#define TP_RSS_LKP_TABLE_A 0x7dec + +#define LKPTBLROWVLD_S 31 +#define LKPTBLROWVLD_V(x) ((x) << LKPTBLROWVLD_S) +#define LKPTBLROWVLD_F LKPTBLROWVLD_V(1U) + +#define LKPTBLQUEUE1_S 10 +#define LKPTBLQUEUE1_M 0x3ffU +#define LKPTBLQUEUE1_G(x) (((x) >> LKPTBLQUEUE1_S) & LKPTBLQUEUE1_M) + +#define LKPTBLQUEUE0_S 0 +#define LKPTBLQUEUE0_M 0x3ffU +#define LKPTBLQUEUE0_G(x) (((x) >> LKPTBLQUEUE0_S) & LKPTBLQUEUE0_M) + +#define TP_PIO_ADDR_A 0x7e40 +#define TP_PIO_DATA_A 0x7e44 +#define TP_MIB_INDEX_A 0x7e50 +#define TP_MIB_DATA_A 0x7e54 +#define TP_INT_CAUSE_A 0x7e74 + +#define FLMTXFLSTEMPTY_S 30 +#define FLMTXFLSTEMPTY_V(x) ((x) << FLMTXFLSTEMPTY_S) +#define FLMTXFLSTEMPTY_F FLMTXFLSTEMPTY_V(1U) + +#define TP_VLAN_PRI_MAP_A 0x140 + +#define FRAGMENTATION_S 9 +#define FRAGMENTATION_V(x) ((x) << FRAGMENTATION_S) +#define FRAGMENTATION_F FRAGMENTATION_V(1U) + +#define MPSHITTYPE_S 8 +#define MPSHITTYPE_V(x) ((x) << MPSHITTYPE_S) +#define MPSHITTYPE_F MPSHITTYPE_V(1U) + +#define MACMATCH_S 7 +#define MACMATCH_V(x) ((x) << MACMATCH_S) +#define MACMATCH_F MACMATCH_V(1U) + +#define ETHERTYPE_S 6 +#define ETHERTYPE_V(x) ((x) << ETHERTYPE_S) +#define ETHERTYPE_F ETHERTYPE_V(1U) + +#define PROTOCOL_S 5 +#define PROTOCOL_V(x) ((x) << PROTOCOL_S) +#define PROTOCOL_F PROTOCOL_V(1U) + +#define TOS_S 4 +#define TOS_V(x) ((x) << TOS_S) +#define TOS_F TOS_V(1U) + +#define VLAN_S 3 +#define VLAN_V(x) ((x) << VLAN_S) +#define VLAN_F VLAN_V(1U) + +#define VNIC_ID_S 2 +#define VNIC_ID_V(x) ((x) << VNIC_ID_S) +#define VNIC_ID_F VNIC_ID_V(1U) + +#define PORT_S 1 +#define PORT_V(x) ((x) << PORT_S) +#define PORT_F PORT_V(1U) + +#define FCOE_S 0 +#define FCOE_V(x) ((x) << FCOE_S) +#define FCOE_F FCOE_V(1U) + +#define FILTERMODE_S 15 +#define FILTERMODE_V(x) ((x) << FILTERMODE_S) +#define FILTERMODE_F FILTERMODE_V(1U) + +#define FCOEMASK_S 14 +#define FCOEMASK_V(x) ((x) << FCOEMASK_S) +#define FCOEMASK_F FCOEMASK_V(1U) + +#define TP_INGRESS_CONFIG_A 0x141 + +#define VNIC_S 11 +#define VNIC_V(x) ((x) << VNIC_S) +#define VNIC_F VNIC_V(1U) + +#define CSUM_HAS_PSEUDO_HDR_S 10 +#define CSUM_HAS_PSEUDO_HDR_V(x) ((x) << CSUM_HAS_PSEUDO_HDR_S) +#define CSUM_HAS_PSEUDO_HDR_F CSUM_HAS_PSEUDO_HDR_V(1U) + +#define TP_MIB_MAC_IN_ERR_0_A 0x0 +#define TP_MIB_TCP_OUT_RST_A 0xc +#define TP_MIB_TCP_IN_SEG_HI_A 0x10 +#define TP_MIB_TCP_IN_SEG_LO_A 0x11 +#define TP_MIB_TCP_OUT_SEG_HI_A 0x12 +#define TP_MIB_TCP_OUT_SEG_LO_A 0x13 +#define TP_MIB_TCP_RXT_SEG_HI_A 0x14 +#define TP_MIB_TCP_RXT_SEG_LO_A 0x15 +#define TP_MIB_TNL_CNG_DROP_0_A 0x18 +#define TP_MIB_TCP_V6IN_ERR_0_A 0x28 +#define TP_MIB_TCP_V6OUT_RST_A 0x2c +#define TP_MIB_OFD_ARP_DROP_A 0x36 +#define TP_MIB_TNL_DROP_0_A 0x44 +#define TP_MIB_OFD_VLN_DROP_0_A 0x58 + +#define ULP_TX_INT_CAUSE_A 0x8dcc + +#define PBL_BOUND_ERR_CH3_S 31 +#define PBL_BOUND_ERR_CH3_V(x) ((x) << PBL_BOUND_ERR_CH3_S) +#define PBL_BOUND_ERR_CH3_F PBL_BOUND_ERR_CH3_V(1U) + +#define PBL_BOUND_ERR_CH2_S 30 +#define PBL_BOUND_ERR_CH2_V(x) ((x) << PBL_BOUND_ERR_CH2_S) +#define PBL_BOUND_ERR_CH2_F PBL_BOUND_ERR_CH2_V(1U) + +#define PBL_BOUND_ERR_CH1_S 29 +#define PBL_BOUND_ERR_CH1_V(x) ((x) << PBL_BOUND_ERR_CH1_S) +#define PBL_BOUND_ERR_CH1_F PBL_BOUND_ERR_CH1_V(1U) + +#define PBL_BOUND_ERR_CH0_S 28 +#define PBL_BOUND_ERR_CH0_V(x) ((x) << PBL_BOUND_ERR_CH0_S) +#define PBL_BOUND_ERR_CH0_F PBL_BOUND_ERR_CH0_V(1U) + +#define PM_RX_INT_CAUSE_A 0x8fdc + +#define PMRX_FRAMING_ERROR_F 0x003ffff0U + +#define ZERO_E_CMD_ERROR_S 22 +#define ZERO_E_CMD_ERROR_V(x) ((x) << ZERO_E_CMD_ERROR_S) +#define ZERO_E_CMD_ERROR_F ZERO_E_CMD_ERROR_V(1U) + +#define OCSPI_PAR_ERROR_S 3 +#define OCSPI_PAR_ERROR_V(x) ((x) << OCSPI_PAR_ERROR_S) +#define OCSPI_PAR_ERROR_F OCSPI_PAR_ERROR_V(1U) + +#define DB_OPTIONS_PAR_ERROR_S 2 +#define DB_OPTIONS_PAR_ERROR_V(x) ((x) << DB_OPTIONS_PAR_ERROR_S) +#define DB_OPTIONS_PAR_ERROR_F DB_OPTIONS_PAR_ERROR_V(1U) + +#define IESPI_PAR_ERROR_S 1 +#define IESPI_PAR_ERROR_V(x) ((x) << IESPI_PAR_ERROR_S) +#define IESPI_PAR_ERROR_F IESPI_PAR_ERROR_V(1U) + +#define PMRX_E_PCMD_PAR_ERROR_S 0 +#define PMRX_E_PCMD_PAR_ERROR_V(x) ((x) << PMRX_E_PCMD_PAR_ERROR_S) +#define PMRX_E_PCMD_PAR_ERROR_F PMRX_E_PCMD_PAR_ERROR_V(1U) + +#define PM_TX_INT_CAUSE_A 0x8ffc + +#define PCMD_LEN_OVFL0_S 31 +#define PCMD_LEN_OVFL0_V(x) ((x) << PCMD_LEN_OVFL0_S) +#define PCMD_LEN_OVFL0_F PCMD_LEN_OVFL0_V(1U) + +#define PCMD_LEN_OVFL1_S 30 +#define PCMD_LEN_OVFL1_V(x) ((x) << PCMD_LEN_OVFL1_S) +#define PCMD_LEN_OVFL1_F PCMD_LEN_OVFL1_V(1U) + +#define PCMD_LEN_OVFL2_S 29 +#define PCMD_LEN_OVFL2_V(x) ((x) << PCMD_LEN_OVFL2_S) +#define PCMD_LEN_OVFL2_F PCMD_LEN_OVFL2_V(1U) + +#define ZERO_C_CMD_ERROR_S 28 +#define ZERO_C_CMD_ERROR_V(x) ((x) << ZERO_C_CMD_ERROR_S) +#define ZERO_C_CMD_ERROR_F ZERO_C_CMD_ERROR_V(1U) + +#define PMTX_FRAMING_ERROR_F 0x0ffffff0U + +#define OESPI_PAR_ERROR_S 3 +#define OESPI_PAR_ERROR_V(x) ((x) << OESPI_PAR_ERROR_S) +#define OESPI_PAR_ERROR_F OESPI_PAR_ERROR_V(1U) + +#define ICSPI_PAR_ERROR_S 1 +#define ICSPI_PAR_ERROR_V(x) ((x) << ICSPI_PAR_ERROR_S) +#define ICSPI_PAR_ERROR_F ICSPI_PAR_ERROR_V(1U) + +#define PMTX_C_PCMD_PAR_ERROR_S 0 +#define PMTX_C_PCMD_PAR_ERROR_V(x) ((x) << PMTX_C_PCMD_PAR_ERROR_S) +#define PMTX_C_PCMD_PAR_ERROR_F PMTX_C_PCMD_PAR_ERROR_V(1U) #define MPS_PORT_STAT_TX_PORT_BYTES_L 0x400 #define MPS_PORT_STAT_TX_PORT_BYTES_H 0x404 @@ -1462,41 +1562,57 @@ #define MPS_PORT_STAT_RX_PORT_PPP7_H 0x60c #define MPS_PORT_STAT_RX_PORT_LESS_64B_L 0x610 #define MPS_PORT_STAT_RX_PORT_LESS_64B_H 0x614 -#define MAC_PORT_CFG2 0x818 #define MAC_PORT_MAGIC_MACID_LO 0x824 #define MAC_PORT_MAGIC_MACID_HI 0x828 -#define MAC_PORT_EPIO_DATA0 0x8c0 -#define MAC_PORT_EPIO_DATA1 0x8c4 -#define MAC_PORT_EPIO_DATA2 0x8c8 -#define MAC_PORT_EPIO_DATA3 0x8cc -#define MAC_PORT_EPIO_OP 0x8d0 - -#define MPS_CMN_CTL 0x9000 -#define NUMPORTS_MASK 0x00000003U -#define NUMPORTS_SHIFT 0 -#define NUMPORTS_GET(x) (((x) & NUMPORTS_MASK) >> NUMPORTS_SHIFT) - -#define MPS_INT_CAUSE 0x9008 -#define STATINT 0x00000020U -#define TXINT 0x00000010U -#define RXINT 0x00000008U -#define TRCINT 0x00000004U -#define CLSINT 0x00000002U -#define PLINT 0x00000001U - -#define MPS_TX_INT_CAUSE 0x9408 -#define PORTERR 0x00010000U -#define FRMERR 0x00008000U -#define SECNTERR 0x00004000U -#define BUBBLE 0x00002000U -#define TXDESCFIFO 0x00001e00U -#define TXDATAFIFO 0x000001e0U -#define NCSIFIFO 0x00000010U -#define TPFIFO 0x0000000fU - -#define MPS_STAT_PERR_INT_CAUSE_SRAM 0x9614 -#define MPS_STAT_PERR_INT_CAUSE_TX_FIFO 0x9620 -#define MPS_STAT_PERR_INT_CAUSE_RX_FIFO 0x962c + +#define MAC_PORT_EPIO_DATA0_A 0x8c0 +#define MAC_PORT_EPIO_DATA1_A 0x8c4 +#define MAC_PORT_EPIO_DATA2_A 0x8c8 +#define MAC_PORT_EPIO_DATA3_A 0x8cc +#define MAC_PORT_EPIO_OP_A 0x8d0 + +#define MAC_PORT_CFG2_A 0x818 + +#define MPS_CMN_CTL_A 0x9000 + +#define NUMPORTS_S 0 +#define NUMPORTS_M 0x3U +#define NUMPORTS_G(x) (((x) >> NUMPORTS_S) & NUMPORTS_M) + +#define MPS_INT_CAUSE_A 0x9008 +#define MPS_TX_INT_CAUSE_A 0x9408 + +#define FRMERR_S 15 +#define FRMERR_V(x) ((x) << FRMERR_S) +#define FRMERR_F FRMERR_V(1U) + +#define SECNTERR_S 14 +#define SECNTERR_V(x) ((x) << SECNTERR_S) +#define SECNTERR_F SECNTERR_V(1U) + +#define BUBBLE_S 13 +#define BUBBLE_V(x) ((x) << BUBBLE_S) +#define BUBBLE_F BUBBLE_V(1U) + +#define TXDESCFIFO_S 9 +#define TXDESCFIFO_M 0xfU +#define TXDESCFIFO_V(x) ((x) << TXDESCFIFO_S) + +#define TXDATAFIFO_S 5 +#define TXDATAFIFO_M 0xfU +#define TXDATAFIFO_V(x) ((x) << TXDATAFIFO_S) + +#define NCSIFIFO_S 4 +#define NCSIFIFO_V(x) ((x) << NCSIFIFO_S) +#define NCSIFIFO_F NCSIFIFO_V(1U) + +#define TPFIFO_S 0 +#define TPFIFO_M 0xfU +#define TPFIFO_V(x) ((x) << TPFIFO_S) + +#define MPS_STAT_PERR_INT_CAUSE_SRAM_A 0x9614 +#define MPS_STAT_PERR_INT_CAUSE_TX_FIFO_A 0x9620 +#define MPS_STAT_PERR_INT_CAUSE_RX_FIFO_A 0x962c #define MPS_STAT_RX_BG_0_MAC_DROP_FRAME_L 0x9640 #define MPS_STAT_RX_BG_0_MAC_DROP_FRAME_H 0x9644 @@ -1530,66 +1646,67 @@ #define MPS_STAT_RX_BG_2_LB_TRUNC_FRAME_H 0x96b4 #define MPS_STAT_RX_BG_3_LB_TRUNC_FRAME_L 0x96b8 #define MPS_STAT_RX_BG_3_LB_TRUNC_FRAME_H 0x96bc -#define MPS_TRC_CFG 0x9800 -#define TRCFIFOEMPTY 0x00000010U -#define TRCIGNOREDROPINPUT 0x00000008U -#define TRCKEEPDUPLICATES 0x00000004U -#define TRCEN 0x00000002U -#define TRCMULTIFILTER 0x00000001U - -#define MPS_TRC_RSS_CONTROL 0x9808 -#define MPS_T5_TRC_RSS_CONTROL 0xa00c -#define RSSCONTROL_MASK 0x00ff0000U -#define RSSCONTROL_SHIFT 16 -#define RSSCONTROL(x) ((x) << RSSCONTROL_SHIFT) -#define QUEUENUMBER_MASK 0x0000ffffU -#define QUEUENUMBER_SHIFT 0 -#define QUEUENUMBER(x) ((x) << QUEUENUMBER_SHIFT) - -#define MPS_TRC_FILTER_MATCH_CTL_A 0x9810 -#define TFINVERTMATCH 0x01000000U -#define TFPKTTOOLARGE 0x00800000U -#define TFEN 0x00400000U -#define TFPORT_MASK 0x003c0000U -#define TFPORT_SHIFT 18 -#define TFPORT(x) ((x) << TFPORT_SHIFT) -#define TFPORT_GET(x) (((x) & TFPORT_MASK) >> TFPORT_SHIFT) -#define TFDROP 0x00020000U -#define TFSOPEOPERR 0x00010000U -#define TFLENGTH_MASK 0x00001f00U -#define TFLENGTH_SHIFT 8 -#define TFLENGTH(x) ((x) << TFLENGTH_SHIFT) -#define TFLENGTH_GET(x) (((x) & TFLENGTH_MASK) >> TFLENGTH_SHIFT) -#define TFOFFSET_MASK 0x0000001fU -#define TFOFFSET_SHIFT 0 -#define TFOFFSET(x) ((x) << TFOFFSET_SHIFT) -#define TFOFFSET_GET(x) (((x) & TFOFFSET_MASK) >> TFOFFSET_SHIFT) - -#define MPS_TRC_FILTER_MATCH_CTL_B 0x9820 -#define TFMINPKTSIZE_MASK 0x01ff0000U -#define TFMINPKTSIZE_SHIFT 16 -#define TFMINPKTSIZE(x) ((x) << TFMINPKTSIZE_SHIFT) -#define TFMINPKTSIZE_GET(x) (((x) & TFMINPKTSIZE_MASK) >> TFMINPKTSIZE_SHIFT) -#define TFCAPTUREMAX_MASK 0x00003fffU -#define TFCAPTUREMAX_SHIFT 0 -#define TFCAPTUREMAX(x) ((x) << TFCAPTUREMAX_SHIFT) -#define TFCAPTUREMAX_GET(x) (((x) & TFCAPTUREMAX_MASK) >> TFCAPTUREMAX_SHIFT) - -#define MPS_TRC_INT_CAUSE 0x985c -#define MISCPERR 0x00000100U -#define PKTFIFO 0x000000f0U -#define FILTMEM 0x0000000fU - -#define MPS_TRC_FILTER0_MATCH 0x9c00 -#define MPS_TRC_FILTER0_DONT_CARE 0x9c80 -#define MPS_TRC_FILTER1_MATCH 0x9d00 -#define MPS_CLS_INT_CAUSE 0xd028 -#define PLERRENB 0x00000008U -#define HASHSRAM 0x00000004U -#define MATCHTCAM 0x00000002U -#define MATCHSRAM 0x00000001U - -#define MPS_RX_PERR_INT_CAUSE 0x11074 + +#define MPS_TRC_CFG_A 0x9800 + +#define TRCFIFOEMPTY_S 4 +#define TRCFIFOEMPTY_V(x) ((x) << TRCFIFOEMPTY_S) +#define TRCFIFOEMPTY_F TRCFIFOEMPTY_V(1U) + +#define TRCIGNOREDROPINPUT_S 3 +#define TRCIGNOREDROPINPUT_V(x) ((x) << TRCIGNOREDROPINPUT_S) +#define TRCIGNOREDROPINPUT_F TRCIGNOREDROPINPUT_V(1U) + +#define TRCKEEPDUPLICATES_S 2 +#define TRCKEEPDUPLICATES_V(x) ((x) << TRCKEEPDUPLICATES_S) +#define TRCKEEPDUPLICATES_F TRCKEEPDUPLICATES_V(1U) + +#define TRCEN_S 1 +#define TRCEN_V(x) ((x) << TRCEN_S) +#define TRCEN_F TRCEN_V(1U) + +#define TRCMULTIFILTER_S 0 +#define TRCMULTIFILTER_V(x) ((x) << TRCMULTIFILTER_S) +#define TRCMULTIFILTER_F TRCMULTIFILTER_V(1U) + +#define MPS_TRC_RSS_CONTROL_A 0x9808 +#define MPS_T5_TRC_RSS_CONTROL_A 0xa00c + +#define RSSCONTROL_S 16 +#define RSSCONTROL_V(x) ((x) << RSSCONTROL_S) + +#define QUEUENUMBER_S 0 +#define QUEUENUMBER_V(x) ((x) << QUEUENUMBER_S) + +#define MPS_TRC_INT_CAUSE_A 0x985c + +#define MISCPERR_S 8 +#define MISCPERR_V(x) ((x) << MISCPERR_S) +#define MISCPERR_F MISCPERR_V(1U) + +#define PKTFIFO_S 4 +#define PKTFIFO_M 0xfU +#define PKTFIFO_V(x) ((x) << PKTFIFO_S) + +#define FILTMEM_S 0 +#define FILTMEM_M 0xfU +#define FILTMEM_V(x) ((x) << FILTMEM_S) + +#define MPS_CLS_INT_CAUSE_A 0xd028 + +#define HASHSRAM_S 2 +#define HASHSRAM_V(x) ((x) << HASHSRAM_S) +#define HASHSRAM_F HASHSRAM_V(1U) + +#define MATCHTCAM_S 1 +#define MATCHTCAM_V(x) ((x) << MATCHTCAM_S) +#define MATCHTCAM_F MATCHTCAM_V(1U) + +#define MATCHSRAM_S 0 +#define MATCHSRAM_V(x) ((x) << MATCHSRAM_S) +#define MATCHSRAM_F MATCHSRAM_V(1U) + +#define MPS_RX_PERR_INT_CAUSE_A 0x11074 #define CPL_INTR_CAUSE 0x19054 #define CIM_OP_MAP_PERR 0x00000020U diff --git a/drivers/scsi/csiostor/csio_hw.c b/drivers/scsi/csiostor/csio_hw.c index 257dc8d..c641931 100644 --- a/drivers/scsi/csiostor/csio_hw.c +++ b/drivers/scsi/csiostor/csio_hw.c @@ -188,9 +188,9 @@ void csio_hw_tp_wr_bits_indirect(struct csio_hw *hw, unsigned int addr, unsigned int mask, unsigned int val) { - csio_wr_reg32(hw, addr, TP_PIO_ADDR); - val |= csio_rd_reg32(hw, TP_PIO_DATA) & ~mask; - csio_wr_reg32(hw, val, TP_PIO_DATA); + csio_wr_reg32(hw, addr, TP_PIO_ADDR_A); + val |= csio_rd_reg32(hw, TP_PIO_DATA_A) & ~mask; + csio_wr_reg32(hw, val, TP_PIO_DATA_A); } void @@ -2683,11 +2683,11 @@ static void csio_tp_intr_handler(struct csio_hw *hw) { static struct intr_info tp_intr_info[] = { { 0x3fffffff, "TP parity error", -1, 1 }, - { FLMTXFLSTEMPTY, "TP out of Tx pages", -1, 1 }, + { FLMTXFLSTEMPTY_F, "TP out of Tx pages", -1, 1 }, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, TP_INT_CAUSE, tp_intr_info)) + if (csio_handle_intr_status(hw, TP_INT_CAUSE_A, tp_intr_info)) csio_hw_fatal_err(hw); } @@ -2824,19 +2824,19 @@ static void csio_ulprx_intr_handler(struct csio_hw *hw) static void csio_ulptx_intr_handler(struct csio_hw *hw) { static struct intr_info ulptx_intr_info[] = { - { PBL_BOUND_ERR_CH3, "ULPTX channel 3 PBL out of bounds", -1, + { PBL_BOUND_ERR_CH3_F, "ULPTX channel 3 PBL out of bounds", -1, 0 }, - { PBL_BOUND_ERR_CH2, "ULPTX channel 2 PBL out of bounds", -1, + { PBL_BOUND_ERR_CH2_F, "ULPTX channel 2 PBL out of bounds", -1, 0 }, - { PBL_BOUND_ERR_CH1, "ULPTX channel 1 PBL out of bounds", -1, + { PBL_BOUND_ERR_CH1_F, "ULPTX channel 1 PBL out of bounds", -1, 0 }, - { PBL_BOUND_ERR_CH0, "ULPTX channel 0 PBL out of bounds", -1, + { PBL_BOUND_ERR_CH0_F, "ULPTX channel 0 PBL out of bounds", -1, 0 }, { 0xfffffff, "ULPTX parity error", -1, 1 }, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, ULP_TX_INT_CAUSE, ulptx_intr_info)) + if (csio_handle_intr_status(hw, ULP_TX_INT_CAUSE_A, ulptx_intr_info)) csio_hw_fatal_err(hw); } @@ -2846,20 +2846,20 @@ static void csio_ulptx_intr_handler(struct csio_hw *hw) static void csio_pmtx_intr_handler(struct csio_hw *hw) { static struct intr_info pmtx_intr_info[] = { - { PCMD_LEN_OVFL0, "PMTX channel 0 pcmd too large", -1, 1 }, - { PCMD_LEN_OVFL1, "PMTX channel 1 pcmd too large", -1, 1 }, - { PCMD_LEN_OVFL2, "PMTX channel 2 pcmd too large", -1, 1 }, - { ZERO_C_CMD_ERROR, "PMTX 0-length pcmd", -1, 1 }, + { PCMD_LEN_OVFL0_F, "PMTX channel 0 pcmd too large", -1, 1 }, + { PCMD_LEN_OVFL1_F, "PMTX channel 1 pcmd too large", -1, 1 }, + { PCMD_LEN_OVFL2_F, "PMTX channel 2 pcmd too large", -1, 1 }, + { ZERO_C_CMD_ERROR_F, "PMTX 0-length pcmd", -1, 1 }, { 0xffffff0, "PMTX framing error", -1, 1 }, - { OESPI_PAR_ERROR, "PMTX oespi parity error", -1, 1 }, - { DB_OPTIONS_PAR_ERROR, "PMTX db_options parity error", -1, + { OESPI_PAR_ERROR_F, "PMTX oespi parity error", -1, 1 }, + { DB_OPTIONS_PAR_ERROR_F, "PMTX db_options parity error", -1, 1 }, - { ICSPI_PAR_ERROR, "PMTX icspi parity error", -1, 1 }, - { C_PCMD_PAR_ERROR, "PMTX c_pcmd parity error", -1, 1}, + { ICSPI_PAR_ERROR_F, "PMTX icspi parity error", -1, 1 }, + { PMTX_C_PCMD_PAR_ERROR_F, "PMTX c_pcmd parity error", -1, 1}, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, PM_TX_INT_CAUSE, pmtx_intr_info)) + if (csio_handle_intr_status(hw, PM_TX_INT_CAUSE_A, pmtx_intr_info)) csio_hw_fatal_err(hw); } @@ -2869,17 +2869,17 @@ static void csio_pmtx_intr_handler(struct csio_hw *hw) static void csio_pmrx_intr_handler(struct csio_hw *hw) { static struct intr_info pmrx_intr_info[] = { - { ZERO_E_CMD_ERROR, "PMRX 0-length pcmd", -1, 1 }, + { ZERO_E_CMD_ERROR_F, "PMRX 0-length pcmd", -1, 1 }, { 0x3ffff0, "PMRX framing error", -1, 1 }, - { OCSPI_PAR_ERROR, "PMRX ocspi parity error", -1, 1 }, - { DB_OPTIONS_PAR_ERROR, "PMRX db_options parity error", -1, + { OCSPI_PAR_ERROR_F, "PMRX ocspi parity error", -1, 1 }, + { DB_OPTIONS_PAR_ERROR_F, "PMRX db_options parity error", -1, 1 }, - { IESPI_PAR_ERROR, "PMRX iespi parity error", -1, 1 }, - { E_PCMD_PAR_ERROR, "PMRX e_pcmd parity error", -1, 1}, + { IESPI_PAR_ERROR_F, "PMRX iespi parity error", -1, 1 }, + { PMRX_E_PCMD_PAR_ERROR_F, "PMRX e_pcmd parity error", -1, 1}, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, PM_RX_INT_CAUSE, pmrx_intr_info)) + if (csio_handle_intr_status(hw, PM_RX_INT_CAUSE_A, pmrx_intr_info)) csio_hw_fatal_err(hw); } @@ -2930,19 +2930,22 @@ static void csio_mps_intr_handler(struct csio_hw *hw) { 0, NULL, 0, 0 } }; static struct intr_info mps_tx_intr_info[] = { - { TPFIFO, "MPS Tx TP FIFO parity error", -1, 1 }, - { NCSIFIFO, "MPS Tx NC-SI FIFO parity error", -1, 1 }, - { TXDATAFIFO, "MPS Tx data FIFO parity error", -1, 1 }, - { TXDESCFIFO, "MPS Tx desc FIFO parity error", -1, 1 }, - { BUBBLE, "MPS Tx underflow", -1, 1 }, - { SECNTERR, "MPS Tx SOP/EOP error", -1, 1 }, - { FRMERR, "MPS Tx framing error", -1, 1 }, + { TPFIFO_V(TPFIFO_M), "MPS Tx TP FIFO parity error", -1, 1 }, + { NCSIFIFO_F, "MPS Tx NC-SI FIFO parity error", -1, 1 }, + { TXDATAFIFO_V(TXDATAFIFO_M), "MPS Tx data FIFO parity error", + -1, 1 }, + { TXDESCFIFO_V(TXDESCFIFO_M), "MPS Tx desc FIFO parity error", + -1, 1 }, + { BUBBLE_F, "MPS Tx underflow", -1, 1 }, + { SECNTERR_F, "MPS Tx SOP/EOP error", -1, 1 }, + { FRMERR_F, "MPS Tx framing error", -1, 1 }, { 0, NULL, 0, 0 } }; static struct intr_info mps_trc_intr_info[] = { - { FILTMEM, "MPS TRC filter parity error", -1, 1 }, - { PKTFIFO, "MPS TRC packet FIFO parity error", -1, 1 }, - { MISCPERR, "MPS TRC misc parity error", -1, 1 }, + { FILTMEM_V(FILTMEM_M), "MPS TRC filter parity error", -1, 1 }, + { PKTFIFO_V(PKTFIFO_M), "MPS TRC packet FIFO parity error", + -1, 1 }, + { MISCPERR_F, "MPS TRC misc parity error", -1, 1 }, { 0, NULL, 0, 0 } }; static struct intr_info mps_stat_sram_intr_info[] = { @@ -2958,31 +2961,31 @@ static void csio_mps_intr_handler(struct csio_hw *hw) { 0, NULL, 0, 0 } }; static struct intr_info mps_cls_intr_info[] = { - { MATCHSRAM, "MPS match SRAM parity error", -1, 1 }, - { MATCHTCAM, "MPS match TCAM parity error", -1, 1 }, - { HASHSRAM, "MPS hash SRAM parity error", -1, 1 }, + { MATCHSRAM_F, "MPS match SRAM parity error", -1, 1 }, + { MATCHTCAM_F, "MPS match TCAM parity error", -1, 1 }, + { HASHSRAM_F, "MPS hash SRAM parity error", -1, 1 }, { 0, NULL, 0, 0 } }; int fat; - fat = csio_handle_intr_status(hw, MPS_RX_PERR_INT_CAUSE, - mps_rx_intr_info) + - csio_handle_intr_status(hw, MPS_TX_INT_CAUSE, - mps_tx_intr_info) + - csio_handle_intr_status(hw, MPS_TRC_INT_CAUSE, - mps_trc_intr_info) + - csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_SRAM, - mps_stat_sram_intr_info) + - csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_TX_FIFO, - mps_stat_tx_intr_info) + - csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_RX_FIFO, - mps_stat_rx_intr_info) + - csio_handle_intr_status(hw, MPS_CLS_INT_CAUSE, - mps_cls_intr_info); - - csio_wr_reg32(hw, 0, MPS_INT_CAUSE); - csio_rd_reg32(hw, MPS_INT_CAUSE); /* flush */ + fat = csio_handle_intr_status(hw, MPS_RX_PERR_INT_CAUSE_A, + mps_rx_intr_info) + + csio_handle_intr_status(hw, MPS_TX_INT_CAUSE_A, + mps_tx_intr_info) + + csio_handle_intr_status(hw, MPS_TRC_INT_CAUSE_A, + mps_trc_intr_info) + + csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_SRAM_A, + mps_stat_sram_intr_info) + + csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_TX_FIFO_A, + mps_stat_tx_intr_info) + + csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_RX_FIFO_A, + mps_stat_rx_intr_info) + + csio_handle_intr_status(hw, MPS_CLS_INT_CAUSE_A, + mps_cls_intr_info); + + csio_wr_reg32(hw, 0, MPS_INT_CAUSE_A); + csio_rd_reg32(hw, MPS_INT_CAUSE_A); /* flush */ if (fat) csio_hw_fatal_err(hw); } diff --git a/drivers/scsi/csiostor/csio_wr.c b/drivers/scsi/csiostor/csio_wr.c index 12697c6..e22503b 100644 --- a/drivers/scsi/csiostor/csio_wr.c +++ b/drivers/scsi/csiostor/csio_wr.c @@ -1350,8 +1350,8 @@ csio_wr_fixup_host_params(struct csio_hw *hw) PKTSHIFT_V(PKTSHIFT_M), PKTSHIFT_V(CSIO_SGE_RX_DMA_OFFSET)); - csio_hw_tp_wr_bits_indirect(hw, TP_INGRESS_CONFIG, - CSUM_HAS_PSEUDO_HDR, 0); + csio_hw_tp_wr_bits_indirect(hw, TP_INGRESS_CONFIG_A, + CSUM_HAS_PSEUDO_HDR_F, 0); } static void -- cgit v0.10.2 From 0d8043389bf3abc86016995bfe3d3314dd5b3db7 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Mon, 5 Jan 2015 16:30:47 +0530 Subject: cxgb4/cxgb4vf/csiostor: Cleanup PL, XGMAC, SF and MC related register defines This patch cleanups all PL, XGMAC and SF related macros/register defines that are defined in t4_regs.h and the affected files Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 53ad8d3..04e675b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -834,11 +834,11 @@ static void disable_msi(struct adapter *adapter) static irqreturn_t t4_nondata_intr(int irq, void *cookie) { struct adapter *adap = cookie; + u32 v = t4_read_reg(adap, MYPF_REG(PL_PF_INT_CAUSE_A)); - u32 v = t4_read_reg(adap, MYPF_REG(PL_PF_INT_CAUSE)); - if (v & PFSW) { + if (v & PFSW_F) { adap->swintr = 1; - t4_write_reg(adap, MYPF_REG(PL_PF_INT_CAUSE), v); + t4_write_reg(adap, MYPF_REG(PL_PF_INT_CAUSE_A), v); } t4_slow_intr_handler(adap); return IRQ_HANDLED; @@ -3654,10 +3654,10 @@ void cxgb4_iscsi_init(struct net_device *dev, unsigned int tag_mask, { struct adapter *adap = netdev2adap(dev); - t4_write_reg(adap, ULP_RX_ISCSI_TAGMASK, tag_mask); - t4_write_reg(adap, ULP_RX_ISCSI_PSZ, HPZ0(pgsz_order[0]) | - HPZ1(pgsz_order[1]) | HPZ2(pgsz_order[2]) | - HPZ3(pgsz_order[3])); + t4_write_reg(adap, ULP_RX_ISCSI_TAGMASK_A, tag_mask); + t4_write_reg(adap, ULP_RX_ISCSI_PSZ_A, HPZ0_V(pgsz_order[0]) | + HPZ1_V(pgsz_order[1]) | HPZ2_V(pgsz_order[2]) | + HPZ3_V(pgsz_order[3])); } EXPORT_SYMBOL(cxgb4_iscsi_init); @@ -4580,13 +4580,13 @@ int cxgb4_create_server_filter(const struct net_device *dev, unsigned int stid, f->fs.val.lip[i] = val[i]; f->fs.mask.lip[i] = ~0; } - if (adap->params.tp.vlan_pri_map & F_PORT) { + if (adap->params.tp.vlan_pri_map & PORT_F) { f->fs.val.iport = port; f->fs.mask.iport = mask; } } - if (adap->params.tp.vlan_pri_map & F_PROTOCOL) { + if (adap->params.tp.vlan_pri_map & PROTOCOL_F) { f->fs.val.proto = IPPROTO_TCP; f->fs.mask.proto = ~0; } @@ -4950,37 +4950,37 @@ static int adap_init1(struct adapter *adap, struct fw_caps_config_cmd *c) /* tweak some settings */ t4_write_reg(adap, TP_SHIFT_CNT_A, 0x64f8849); - t4_write_reg(adap, ULP_RX_TDDP_PSZ, HPZ0(PAGE_SHIFT - 12)); + t4_write_reg(adap, ULP_RX_TDDP_PSZ_A, HPZ0_V(PAGE_SHIFT - 12)); t4_write_reg(adap, TP_PIO_ADDR_A, TP_INGRESS_CONFIG_A); v = t4_read_reg(adap, TP_PIO_DATA_A); t4_write_reg(adap, TP_PIO_DATA_A, v & ~CSUM_HAS_PSEUDO_HDR_F); /* first 4 Tx modulation queues point to consecutive Tx channels */ adap->params.tp.tx_modq_map = 0xE4; - t4_write_reg(adap, A_TP_TX_MOD_QUEUE_REQ_MAP, - V_TX_MOD_QUEUE_REQ_MAP(adap->params.tp.tx_modq_map)); + t4_write_reg(adap, TP_TX_MOD_QUEUE_REQ_MAP_A, + TX_MOD_QUEUE_REQ_MAP_V(adap->params.tp.tx_modq_map)); /* associate each Tx modulation queue with consecutive Tx channels */ v = 0x84218421; t4_write_indirect(adap, TP_PIO_ADDR_A, TP_PIO_DATA_A, - &v, 1, A_TP_TX_SCHED_HDR); + &v, 1, TP_TX_SCHED_HDR_A); t4_write_indirect(adap, TP_PIO_ADDR_A, TP_PIO_DATA_A, - &v, 1, A_TP_TX_SCHED_FIFO); + &v, 1, TP_TX_SCHED_FIFO_A); t4_write_indirect(adap, TP_PIO_ADDR_A, TP_PIO_DATA_A, - &v, 1, A_TP_TX_SCHED_PCMD); + &v, 1, TP_TX_SCHED_PCMD_A); #define T4_TX_MODQ_10G_WEIGHT_DEFAULT 16 /* in KB units */ if (is_offload(adap)) { - t4_write_reg(adap, A_TP_TX_MOD_QUEUE_WEIGHT0, - V_TX_MODQ_WEIGHT0(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | - V_TX_MODQ_WEIGHT1(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | - V_TX_MODQ_WEIGHT2(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | - V_TX_MODQ_WEIGHT3(T4_TX_MODQ_10G_WEIGHT_DEFAULT)); - t4_write_reg(adap, A_TP_TX_MOD_CHANNEL_WEIGHT, - V_TX_MODQ_WEIGHT0(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | - V_TX_MODQ_WEIGHT1(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | - V_TX_MODQ_WEIGHT2(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | - V_TX_MODQ_WEIGHT3(T4_TX_MODQ_10G_WEIGHT_DEFAULT)); + t4_write_reg(adap, TP_TX_MOD_QUEUE_WEIGHT0_A, + TX_MODQ_WEIGHT0_V(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | + TX_MODQ_WEIGHT1_V(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | + TX_MODQ_WEIGHT2_V(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | + TX_MODQ_WEIGHT3_V(T4_TX_MODQ_10G_WEIGHT_DEFAULT)); + t4_write_reg(adap, TP_TX_MOD_CHANNEL_WEIGHT_A, + TX_MODQ_WEIGHT0_V(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | + TX_MODQ_WEIGHT1_V(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | + TX_MODQ_WEIGHT2_V(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | + TX_MODQ_WEIGHT3_V(T4_TX_MODQ_10G_WEIGHT_DEFAULT)); } /* get basic stuff going */ @@ -5059,7 +5059,7 @@ static int adap_init0_config(struct adapter *adapter, int reset) */ if (reset) { ret = t4_fw_reset(adapter, adapter->mbox, - PIORSTMODE | PIORST); + PIORSTMODE_F | PIORST_F); if (ret < 0) goto bye; } @@ -5264,7 +5264,7 @@ static int adap_init0_no_config(struct adapter *adapter, int reset) */ if (reset) { ret = t4_fw_reset(adapter, adapter->mbox, - PIORSTMODE | PIORST); + PIORSTMODE_F | PIORST_F); if (ret < 0) goto bye; } @@ -6413,7 +6413,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_unmap_bar0; /* We control everything through one PF */ - func = SOURCEPF_GET(readl(regs + PL_WHOAMI)); + func = SOURCEPF_G(readl(regs + PL_WHOAMI_A)); if (func != ent->driver_data) { iounmap(regs); pci_disable_device(pdev); diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.c b/drivers/net/ethernet/chelsio/cxgb4/l2t.c index a047baa..5ae1445 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/l2t.c +++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.c @@ -46,6 +46,7 @@ #include "t4_msg.h" #include "t4fw_api.h" #include "t4_regs.h" +#include "t4_values.h" #define VLAN_NONE 0xfff @@ -425,7 +426,7 @@ u64 cxgb4_select_ntuple(struct net_device *dev, * in the Compressed Filter Tuple. */ if (tp->vlan_shift >= 0 && l2t->vlan != VLAN_NONE) - ntuple |= (u64)(F_FT_VLAN_VLD | l2t->vlan) << tp->vlan_shift; + ntuple |= (u64)(FT_VLAN_VLD_F | l2t->vlan) << tp->vlan_shift; if (tp->port_shift >= 0) ntuple |= (u64)l2t->lport << tp->port_shift; @@ -439,9 +440,9 @@ u64 cxgb4_select_ntuple(struct net_device *dev, u32 pf = FW_VIID_PFN_G(viid); u32 vld = FW_VIID_VIVLD_G(viid); - ntuple |= (u64)(V_FT_VNID_ID_VF(vf) | - V_FT_VNID_ID_PF(pf) | - V_FT_VNID_ID_VLD(vld)) << tp->vnic_shift; + ntuple |= (u64)(FT_VNID_ID_VF_V(vf) | + FT_VNID_ID_PF_V(pf) | + FT_VNID_ID_VLD_V(vld)) << tp->vnic_shift; } return ntuple; diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index cf0bf79..3776279 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -761,14 +761,13 @@ static int sf1_read(struct adapter *adapter, unsigned int byte_cnt, int cont, if (!byte_cnt || byte_cnt > 4) return -EINVAL; - if (t4_read_reg(adapter, SF_OP) & SF_BUSY) + if (t4_read_reg(adapter, SF_OP_A) & SF_BUSY_F) return -EBUSY; - cont = cont ? SF_CONT : 0; - lock = lock ? SF_LOCK : 0; - t4_write_reg(adapter, SF_OP, lock | cont | BYTECNT(byte_cnt - 1)); - ret = t4_wait_op_done(adapter, SF_OP, SF_BUSY, 0, SF_ATTEMPTS, 5); + t4_write_reg(adapter, SF_OP_A, SF_LOCK_V(lock) | + SF_CONT_V(cont) | BYTECNT_V(byte_cnt - 1)); + ret = t4_wait_op_done(adapter, SF_OP_A, SF_BUSY_F, 0, SF_ATTEMPTS, 5); if (!ret) - *valp = t4_read_reg(adapter, SF_DATA); + *valp = t4_read_reg(adapter, SF_DATA_A); return ret; } @@ -789,14 +788,12 @@ static int sf1_write(struct adapter *adapter, unsigned int byte_cnt, int cont, { if (!byte_cnt || byte_cnt > 4) return -EINVAL; - if (t4_read_reg(adapter, SF_OP) & SF_BUSY) + if (t4_read_reg(adapter, SF_OP_A) & SF_BUSY_F) return -EBUSY; - cont = cont ? SF_CONT : 0; - lock = lock ? SF_LOCK : 0; - t4_write_reg(adapter, SF_DATA, val); - t4_write_reg(adapter, SF_OP, lock | - cont | BYTECNT(byte_cnt - 1) | OP_WR); - return t4_wait_op_done(adapter, SF_OP, SF_BUSY, 0, SF_ATTEMPTS, 5); + t4_write_reg(adapter, SF_DATA_A, val); + t4_write_reg(adapter, SF_OP_A, SF_LOCK_V(lock) | + SF_CONT_V(cont) | BYTECNT_V(byte_cnt - 1) | OP_V(1)); + return t4_wait_op_done(adapter, SF_OP_A, SF_BUSY_F, 0, SF_ATTEMPTS, 5); } /** @@ -855,7 +852,7 @@ static int t4_read_flash(struct adapter *adapter, unsigned int addr, for ( ; nwords; nwords--, data++) { ret = sf1_read(adapter, 4, nwords > 1, nwords == 1, data); if (nwords == 1) - t4_write_reg(adapter, SF_OP, 0); /* unlock SF */ + t4_write_reg(adapter, SF_OP_A, 0); /* unlock SF */ if (ret) return ret; if (byte_oriented) @@ -903,7 +900,7 @@ static int t4_write_flash(struct adapter *adapter, unsigned int addr, if (ret) goto unlock; - t4_write_reg(adapter, SF_OP, 0); /* unlock SF */ + t4_write_reg(adapter, SF_OP_A, 0); /* unlock SF */ /* Read the page to verify the write succeeded */ ret = t4_read_flash(adapter, addr & ~0xff, ARRAY_SIZE(buf), buf, 1); @@ -919,7 +916,7 @@ static int t4_write_flash(struct adapter *adapter, unsigned int addr, return 0; unlock: - t4_write_reg(adapter, SF_OP, 0); /* unlock SF */ + t4_write_reg(adapter, SF_OP_A, 0); /* unlock SF */ return ret; } @@ -1114,7 +1111,7 @@ static int t4_flash_erase_sectors(struct adapter *adapter, int start, int end) } start++; } - t4_write_reg(adapter, SF_OP, 0); /* unlock SF */ + t4_write_reg(adapter, SF_OP_A, 0); /* unlock SF */ return ret; } @@ -1619,7 +1616,7 @@ static void ulprx_intr_handler(struct adapter *adapter) { 0 } }; - if (t4_handle_intr_status(adapter, ULP_RX_INT_CAUSE, ulprx_intr_info)) + if (t4_handle_intr_status(adapter, ULP_RX_INT_CAUSE_A, ulprx_intr_info)) t4_fatal_err(adapter); } @@ -1694,16 +1691,16 @@ static void pmrx_intr_handler(struct adapter *adapter) static void cplsw_intr_handler(struct adapter *adapter) { static const struct intr_info cplsw_intr_info[] = { - { CIM_OP_MAP_PERR, "CPLSW CIM op_map parity error", -1, 1 }, - { CIM_OVFL_ERROR, "CPLSW CIM overflow", -1, 1 }, - { TP_FRAMING_ERROR, "CPLSW TP framing error", -1, 1 }, - { SGE_FRAMING_ERROR, "CPLSW SGE framing error", -1, 1 }, - { CIM_FRAMING_ERROR, "CPLSW CIM framing error", -1, 1 }, - { ZERO_SWITCH_ERROR, "CPLSW no-switch error", -1, 1 }, + { CIM_OP_MAP_PERR_F, "CPLSW CIM op_map parity error", -1, 1 }, + { CIM_OVFL_ERROR_F, "CPLSW CIM overflow", -1, 1 }, + { TP_FRAMING_ERROR_F, "CPLSW TP framing error", -1, 1 }, + { SGE_FRAMING_ERROR_F, "CPLSW SGE framing error", -1, 1 }, + { CIM_FRAMING_ERROR_F, "CPLSW CIM framing error", -1, 1 }, + { ZERO_SWITCH_ERROR_F, "CPLSW no-switch error", -1, 1 }, { 0 } }; - if (t4_handle_intr_status(adapter, CPL_INTR_CAUSE, cplsw_intr_info)) + if (t4_handle_intr_status(adapter, CPL_INTR_CAUSE_A, cplsw_intr_info)) t4_fatal_err(adapter); } @@ -1713,15 +1710,15 @@ static void cplsw_intr_handler(struct adapter *adapter) static void le_intr_handler(struct adapter *adap) { static const struct intr_info le_intr_info[] = { - { LIPMISS, "LE LIP miss", -1, 0 }, - { LIP0, "LE 0 LIP error", -1, 0 }, - { PARITYERR, "LE parity error", -1, 1 }, - { UNKNOWNCMD, "LE unknown command", -1, 1 }, - { REQQPARERR, "LE request queue parity error", -1, 1 }, + { LIPMISS_F, "LE LIP miss", -1, 0 }, + { LIP0_F, "LE 0 LIP error", -1, 0 }, + { PARITYERR_F, "LE parity error", -1, 1 }, + { UNKNOWNCMD_F, "LE unknown command", -1, 1 }, + { REQQPARERR_F, "LE request queue parity error", -1, 1 }, { 0 } }; - if (t4_handle_intr_status(adap, LE_DB_INT_CAUSE, le_intr_info)) + if (t4_handle_intr_status(adap, LE_DB_INT_CAUSE_A, le_intr_info)) t4_fatal_err(adap); } @@ -1879,13 +1876,13 @@ static void ma_intr_handler(struct adapter *adap) static void smb_intr_handler(struct adapter *adap) { static const struct intr_info smb_intr_info[] = { - { MSTTXFIFOPARINT, "SMB master Tx FIFO parity error", -1, 1 }, - { MSTRXFIFOPARINT, "SMB master Rx FIFO parity error", -1, 1 }, - { SLVFIFOPARINT, "SMB slave FIFO parity error", -1, 1 }, + { MSTTXFIFOPARINT_F, "SMB master Tx FIFO parity error", -1, 1 }, + { MSTRXFIFOPARINT_F, "SMB master Rx FIFO parity error", -1, 1 }, + { SLVFIFOPARINT_F, "SMB slave FIFO parity error", -1, 1 }, { 0 } }; - if (t4_handle_intr_status(adap, SMB_INT_CAUSE, smb_intr_info)) + if (t4_handle_intr_status(adap, SMB_INT_CAUSE_A, smb_intr_info)) t4_fatal_err(adap); } @@ -1895,14 +1892,14 @@ static void smb_intr_handler(struct adapter *adap) static void ncsi_intr_handler(struct adapter *adap) { static const struct intr_info ncsi_intr_info[] = { - { CIM_DM_PRTY_ERR, "NC-SI CIM parity error", -1, 1 }, - { MPS_DM_PRTY_ERR, "NC-SI MPS parity error", -1, 1 }, - { TXFIFO_PRTY_ERR, "NC-SI Tx FIFO parity error", -1, 1 }, - { RXFIFO_PRTY_ERR, "NC-SI Rx FIFO parity error", -1, 1 }, + { CIM_DM_PRTY_ERR_F, "NC-SI CIM parity error", -1, 1 }, + { MPS_DM_PRTY_ERR_F, "NC-SI MPS parity error", -1, 1 }, + { TXFIFO_PRTY_ERR_F, "NC-SI Tx FIFO parity error", -1, 1 }, + { RXFIFO_PRTY_ERR_F, "NC-SI Rx FIFO parity error", -1, 1 }, { 0 } }; - if (t4_handle_intr_status(adap, NCSI_INT_CAUSE, ncsi_intr_info)) + if (t4_handle_intr_status(adap, NCSI_INT_CAUSE_A, ncsi_intr_info)) t4_fatal_err(adap); } @@ -1914,23 +1911,23 @@ static void xgmac_intr_handler(struct adapter *adap, int port) u32 v, int_cause_reg; if (is_t4(adap->params.chip)) - int_cause_reg = PORT_REG(port, XGMAC_PORT_INT_CAUSE); + int_cause_reg = PORT_REG(port, XGMAC_PORT_INT_CAUSE_A); else - int_cause_reg = T5_PORT_REG(port, MAC_PORT_INT_CAUSE); + int_cause_reg = T5_PORT_REG(port, MAC_PORT_INT_CAUSE_A); v = t4_read_reg(adap, int_cause_reg); - v &= TXFIFO_PRTY_ERR | RXFIFO_PRTY_ERR; + v &= TXFIFO_PRTY_ERR_F | RXFIFO_PRTY_ERR_F; if (!v) return; - if (v & TXFIFO_PRTY_ERR) + if (v & TXFIFO_PRTY_ERR_F) dev_alert(adap->pdev_dev, "XGMAC %d Tx FIFO parity error\n", port); - if (v & RXFIFO_PRTY_ERR) + if (v & RXFIFO_PRTY_ERR_F) dev_alert(adap->pdev_dev, "XGMAC %d Rx FIFO parity error\n", port); - t4_write_reg(adap, PORT_REG(port, XGMAC_PORT_INT_CAUSE), v); + t4_write_reg(adap, PORT_REG(port, XGMAC_PORT_INT_CAUSE_A), v); t4_fatal_err(adap); } @@ -1940,19 +1937,19 @@ static void xgmac_intr_handler(struct adapter *adap, int port) static void pl_intr_handler(struct adapter *adap) { static const struct intr_info pl_intr_info[] = { - { FATALPERR, "T4 fatal parity error", -1, 1 }, - { PERRVFID, "PL VFID_MAP parity error", -1, 1 }, + { FATALPERR_F, "T4 fatal parity error", -1, 1 }, + { PERRVFID_F, "PL VFID_MAP parity error", -1, 1 }, { 0 } }; - if (t4_handle_intr_status(adap, PL_PL_INT_CAUSE, pl_intr_info)) + if (t4_handle_intr_status(adap, PL_PL_INT_CAUSE_A, pl_intr_info)) t4_fatal_err(adap); } -#define PF_INTR_MASK (PFSW) -#define GLBL_INTR_MASK (CIM | MPS | PL | PCIE | MC | EDC0 | \ - EDC1 | LE | TP | MA | PM_TX | PM_RX | ULP_RX | \ - CPL_SWITCH | SGE | ULP_TX) +#define PF_INTR_MASK (PFSW_F) +#define GLBL_INTR_MASK (CIM_F | MPS_F | PL_F | PCIE_F | MC_F | EDC0_F | \ + EDC1_F | LE_F | TP_F | MA_F | PM_TX_F | PM_RX_F | ULP_RX_F | \ + CPL_SWITCH_F | SGE_F | ULP_TX_F) /** * t4_slow_intr_handler - control path interrupt handler @@ -1964,60 +1961,60 @@ static void pl_intr_handler(struct adapter *adap) */ int t4_slow_intr_handler(struct adapter *adapter) { - u32 cause = t4_read_reg(adapter, PL_INT_CAUSE); + u32 cause = t4_read_reg(adapter, PL_INT_CAUSE_A); if (!(cause & GLBL_INTR_MASK)) return 0; - if (cause & CIM) + if (cause & CIM_F) cim_intr_handler(adapter); - if (cause & MPS) + if (cause & MPS_F) mps_intr_handler(adapter); - if (cause & NCSI) + if (cause & NCSI_F) ncsi_intr_handler(adapter); - if (cause & PL) + if (cause & PL_F) pl_intr_handler(adapter); - if (cause & SMB) + if (cause & SMB_F) smb_intr_handler(adapter); - if (cause & XGMAC0) + if (cause & XGMAC0_F) xgmac_intr_handler(adapter, 0); - if (cause & XGMAC1) + if (cause & XGMAC1_F) xgmac_intr_handler(adapter, 1); - if (cause & XGMAC_KR0) + if (cause & XGMAC_KR0_F) xgmac_intr_handler(adapter, 2); - if (cause & XGMAC_KR1) + if (cause & XGMAC_KR1_F) xgmac_intr_handler(adapter, 3); - if (cause & PCIE) + if (cause & PCIE_F) pcie_intr_handler(adapter); - if (cause & MC) + if (cause & MC_F) mem_intr_handler(adapter, MEM_MC); - if (!is_t4(adapter->params.chip) && (cause & MC1)) + if (!is_t4(adapter->params.chip) && (cause & MC1_S)) mem_intr_handler(adapter, MEM_MC1); - if (cause & EDC0) + if (cause & EDC0_F) mem_intr_handler(adapter, MEM_EDC0); - if (cause & EDC1) + if (cause & EDC1_F) mem_intr_handler(adapter, MEM_EDC1); - if (cause & LE) + if (cause & LE_F) le_intr_handler(adapter); - if (cause & TP) + if (cause & TP_F) tp_intr_handler(adapter); - if (cause & MA) + if (cause & MA_F) ma_intr_handler(adapter); - if (cause & PM_TX) + if (cause & PM_TX_F) pmtx_intr_handler(adapter); - if (cause & PM_RX) + if (cause & PM_RX_F) pmrx_intr_handler(adapter); - if (cause & ULP_RX) + if (cause & ULP_RX_F) ulprx_intr_handler(adapter); - if (cause & CPL_SWITCH) + if (cause & CPL_SWITCH_F) cplsw_intr_handler(adapter); - if (cause & SGE) + if (cause & SGE_F) sge_intr_handler(adapter); - if (cause & ULP_TX) + if (cause & ULP_TX_F) ulptx_intr_handler(adapter); /* Clear the interrupts just processed for which we are the master. */ - t4_write_reg(adapter, PL_INT_CAUSE, cause & GLBL_INTR_MASK); - (void) t4_read_reg(adapter, PL_INT_CAUSE); /* flush */ + t4_write_reg(adapter, PL_INT_CAUSE_A, cause & GLBL_INTR_MASK); + (void)t4_read_reg(adapter, PL_INT_CAUSE_A); /* flush */ return 1; } @@ -2036,7 +2033,7 @@ int t4_slow_intr_handler(struct adapter *adapter) */ void t4_intr_enable(struct adapter *adapter) { - u32 pf = SOURCEPF_GET(t4_read_reg(adapter, PL_WHOAMI)); + u32 pf = SOURCEPF_G(t4_read_reg(adapter, PL_WHOAMI_A)); t4_write_reg(adapter, SGE_INT_ENABLE3_A, ERR_CPL_EXCEED_IQE_SIZE_F | ERR_INVALID_CIDX_INC_F | ERR_CPL_OPCODE_0_F | @@ -2047,8 +2044,8 @@ void t4_intr_enable(struct adapter *adapter) ERR_EGR_CTXT_PRIO_F | INGRESS_SIZE_ERR_F | DBFIFO_HP_INT_F | DBFIFO_LP_INT_F | EGRESS_SIZE_ERR_F); - t4_write_reg(adapter, MYPF_REG(PL_PF_INT_ENABLE), PF_INTR_MASK); - t4_set_reg_field(adapter, PL_INT_MAP0, 0, 1 << pf); + t4_write_reg(adapter, MYPF_REG(PL_PF_INT_ENABLE_A), PF_INTR_MASK); + t4_set_reg_field(adapter, PL_INT_MAP0_A, 0, 1 << pf); } /** @@ -2061,10 +2058,10 @@ void t4_intr_enable(struct adapter *adapter) */ void t4_intr_disable(struct adapter *adapter) { - u32 pf = SOURCEPF_GET(t4_read_reg(adapter, PL_WHOAMI)); + u32 pf = SOURCEPF_G(t4_read_reg(adapter, PL_WHOAMI_A)); - t4_write_reg(adapter, MYPF_REG(PL_PF_INT_ENABLE), 0); - t4_set_reg_field(adapter, PL_INT_MAP0, 1 << pf, 0); + t4_write_reg(adapter, MYPF_REG(PL_PF_INT_ENABLE_A), 0); + t4_set_reg_field(adapter, PL_INT_MAP0_A, 1 << pf, 0); } /** @@ -2498,7 +2495,7 @@ void t4_wol_magic_enable(struct adapter *adap, unsigned int port, if (is_t4(adap->params.chip)) { mag_id_reg_l = PORT_REG(port, XGMAC_PORT_MAGIC_MACID_LO); mag_id_reg_h = PORT_REG(port, XGMAC_PORT_MAGIC_MACID_HI); - port_cfg_reg = PORT_REG(port, XGMAC_PORT_CFG2); + port_cfg_reg = PORT_REG(port, XGMAC_PORT_CFG2_A); } else { mag_id_reg_l = T5_PORT_REG(port, MAC_PORT_MAGIC_MACID_LO); mag_id_reg_h = T5_PORT_REG(port, MAC_PORT_MAGIC_MACID_HI); @@ -2512,8 +2509,8 @@ void t4_wol_magic_enable(struct adapter *adap, unsigned int port, t4_write_reg(adap, mag_id_reg_h, (addr[0] << 8) | addr[1]); } - t4_set_reg_field(adap, port_cfg_reg, MAGICEN, - addr ? MAGICEN : 0); + t4_set_reg_field(adap, port_cfg_reg, MAGICEN_F, + addr ? MAGICEN_F : 0); } /** @@ -2538,20 +2535,21 @@ int t4_wol_pat_enable(struct adapter *adap, unsigned int port, unsigned int map, u32 port_cfg_reg; if (is_t4(adap->params.chip)) - port_cfg_reg = PORT_REG(port, XGMAC_PORT_CFG2); + port_cfg_reg = PORT_REG(port, XGMAC_PORT_CFG2_A); else port_cfg_reg = T5_PORT_REG(port, MAC_PORT_CFG2_A); if (!enable) { - t4_set_reg_field(adap, port_cfg_reg, PATEN, 0); + t4_set_reg_field(adap, port_cfg_reg, PATEN_F, 0); return 0; } if (map > 0xff) return -EINVAL; #define EPIO_REG(name) \ - (is_t4(adap->params.chip) ? PORT_REG(port, XGMAC_PORT_EPIO_##name) : \ - T5_PORT_REG(port, MAC_PORT_EPIO_##name##_A)) + (is_t4(adap->params.chip) ? \ + PORT_REG(port, XGMAC_PORT_EPIO_##name##_A) : \ + T5_PORT_REG(port, MAC_PORT_EPIO_##name##_A)) t4_write_reg(adap, EPIO_REG(DATA1), mask0 >> 32); t4_write_reg(adap, EPIO_REG(DATA2), mask1); @@ -2563,21 +2561,21 @@ int t4_wol_pat_enable(struct adapter *adap, unsigned int port, unsigned int map, /* write byte masks */ t4_write_reg(adap, EPIO_REG(DATA0), mask0); - t4_write_reg(adap, EPIO_REG(OP), ADDRESS(i) | EPIOWR); + t4_write_reg(adap, EPIO_REG(OP), ADDRESS_V(i) | EPIOWR_F); t4_read_reg(adap, EPIO_REG(OP)); /* flush */ - if (t4_read_reg(adap, EPIO_REG(OP)) & SF_BUSY) + if (t4_read_reg(adap, EPIO_REG(OP)) & SF_BUSY_F) return -ETIMEDOUT; /* write CRC */ t4_write_reg(adap, EPIO_REG(DATA0), crc); - t4_write_reg(adap, EPIO_REG(OP), ADDRESS(i + 32) | EPIOWR); + t4_write_reg(adap, EPIO_REG(OP), ADDRESS_V(i + 32) | EPIOWR_F); t4_read_reg(adap, EPIO_REG(OP)); /* flush */ - if (t4_read_reg(adap, EPIO_REG(OP)) & SF_BUSY) + if (t4_read_reg(adap, EPIO_REG(OP)) & SF_BUSY_F) return -ETIMEDOUT; } #undef EPIO_REG - t4_set_reg_field(adap, PORT_REG(port, XGMAC_PORT_CFG2), 0, PATEN); + t4_set_reg_field(adap, PORT_REG(port, XGMAC_PORT_CFG2_A), 0, PATEN_F); return 0; } @@ -2998,7 +2996,7 @@ static int t4_fw_halt(struct adapter *adap, unsigned int mbox, int force) memset(&c, 0, sizeof(c)); INIT_CMD(c, RESET, WRITE); - c.val = htonl(PIORST | PIORSTMODE); + c.val = htonl(PIORST_F | PIORSTMODE_F); c.halt_pkd = htonl(FW_RESET_CMD_HALT_F); ret = t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); } @@ -3071,11 +3069,11 @@ static int t4_fw_restart(struct adapter *adap, unsigned int mbox, int reset) t4_set_reg_field(adap, CIM_BOOT_CFG_A, UPCRST_F, 0); msleep(100); if (t4_fw_reset(adap, mbox, - PIORST | PIORSTMODE) == 0) + PIORST_F | PIORSTMODE_F) == 0) return 0; } - t4_write_reg(adap, PL_RST, PIORST | PIORSTMODE); + t4_write_reg(adap, PL_RST_A, PIORST_F | PIORSTMODE_F); msleep(2000); } else { int ms; @@ -3246,7 +3244,7 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size, (t4_read_reg(adap, SGE_FL_BUFFER_SIZE3_A) + fl_align-1) & ~(fl_align-1)); - t4_write_reg(adap, ULP_RX_TDDP_PSZ, HPZ0(page_shift - 12)); + t4_write_reg(adap, ULP_RX_TDDP_PSZ_A, HPZ0_V(page_shift - 12)); return 0; } @@ -3931,12 +3929,12 @@ int t4_wait_dev_ready(void __iomem *regs) { u32 whoami; - whoami = readl(regs + PL_WHOAMI); + whoami = readl(regs + PL_WHOAMI_A); if (whoami != 0xffffffff && whoami != CIM_PF_NOACCESS) return 0; msleep(500); - whoami = readl(regs + PL_WHOAMI); + whoami = readl(regs + PL_WHOAMI_A); return (whoami != 0xffffffff && whoami != CIM_PF_NOACCESS ? 0 : -EIO); } @@ -3960,7 +3958,7 @@ static int get_flash_params(struct adapter *adap) ret = sf1_write(adap, 1, 1, 0, SF_RD_ID); if (!ret) ret = sf1_read(adap, 3, 0, 1, &info); - t4_write_reg(adap, SF_OP, 0); /* unlock SF */ + t4_write_reg(adap, SF_OP_A, 0); /* unlock SF */ if (ret) return ret; @@ -4007,7 +4005,7 @@ int t4_prep_adapter(struct adapter *adapter) u32 pl_rev; get_pci_mode(adapter, &adapter->params.pci); - pl_rev = G_REV(t4_read_reg(adapter, PL_REV)); + pl_rev = REV_G(t4_read_reg(adapter, PL_REV_A)); ret = get_flash_params(adapter); if (ret < 0) { @@ -4197,16 +4195,16 @@ int t4_init_tp_params(struct adapter *adap) * shift positions of several elements of the Compressed Filter Tuple * for this adapter which we need frequently ... */ - adap->params.tp.vlan_shift = t4_filter_field_shift(adap, F_VLAN); - adap->params.tp.vnic_shift = t4_filter_field_shift(adap, F_VNIC_ID); - adap->params.tp.port_shift = t4_filter_field_shift(adap, F_PORT); + adap->params.tp.vlan_shift = t4_filter_field_shift(adap, VLAN_F); + adap->params.tp.vnic_shift = t4_filter_field_shift(adap, VNIC_ID_F); + adap->params.tp.port_shift = t4_filter_field_shift(adap, PORT_F); adap->params.tp.protocol_shift = t4_filter_field_shift(adap, - F_PROTOCOL); + PROTOCOL_F); /* If TP_INGRESS_CONFIG.VNID == 0, then TP_VLAN_PRI_MAP.VNIC_ID * represents the presense of an Outer VLAN instead of a VNIC ID. */ - if ((adap->params.tp.ingress_config & F_VNIC) == 0) + if ((adap->params.tp.ingress_config & VNIC_F) == 0) adap->params.tp.vnic_shift = -1; return 0; @@ -4232,35 +4230,35 @@ int t4_filter_field_shift(const struct adapter *adap, int filter_sel) for (sel = 1, field_shift = 0; sel < filter_sel; sel <<= 1) { switch (filter_mode & sel) { - case F_FCOE: - field_shift += W_FT_FCOE; + case FCOE_F: + field_shift += FT_FCOE_W; break; - case F_PORT: - field_shift += W_FT_PORT; + case PORT_F: + field_shift += FT_PORT_W; break; - case F_VNIC_ID: - field_shift += W_FT_VNIC_ID; + case VNIC_ID_F: + field_shift += FT_VNIC_ID_W; break; - case F_VLAN: - field_shift += W_FT_VLAN; + case VLAN_F: + field_shift += FT_VLAN_W; break; - case F_TOS: - field_shift += W_FT_TOS; + case TOS_F: + field_shift += FT_TOS_W; break; - case F_PROTOCOL: - field_shift += W_FT_PROTOCOL; + case PROTOCOL_F: + field_shift += FT_PROTOCOL_W; break; - case F_ETHERTYPE: - field_shift += W_FT_ETHERTYPE; + case ETHERTYPE_F: + field_shift += FT_ETHERTYPE_W; break; - case F_MACMATCH: - field_shift += W_FT_MACMATCH; + case MACMATCH_F: + field_shift += FT_MACMATCH_W; break; - case F_MPSHITTYPE: - field_shift += W_FT_MPSHITTYPE; + case MPSHITTYPE_F: + field_shift += FT_MPSHITTYPE_W; break; - case F_FRAGMENTATION: - field_shift += W_FT_FRAGMENTATION; + case FRAGMENTATION_F: + field_shift += FT_FRAGMENTATION_W; break; } } diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index ec0addc..4077227 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -1708,233 +1708,323 @@ #define MPS_RX_PERR_INT_CAUSE_A 0x11074 -#define CPL_INTR_CAUSE 0x19054 -#define CIM_OP_MAP_PERR 0x00000020U -#define CIM_OVFL_ERROR 0x00000010U -#define TP_FRAMING_ERROR 0x00000008U -#define SGE_FRAMING_ERROR 0x00000004U -#define CIM_FRAMING_ERROR 0x00000002U -#define ZERO_SWITCH_ERROR 0x00000001U - -#define SMB_INT_CAUSE 0x19090 -#define MSTTXFIFOPARINT 0x00200000U -#define MSTRXFIFOPARINT 0x00100000U -#define SLVFIFOPARINT 0x00080000U - -#define ULP_RX_INT_CAUSE 0x19158 -#define ULP_RX_ISCSI_TAGMASK 0x19164 -#define ULP_RX_ISCSI_PSZ 0x19168 -#define HPZ3_MASK 0x0f000000U -#define HPZ3_SHIFT 24 -#define HPZ3(x) ((x) << HPZ3_SHIFT) -#define HPZ2_MASK 0x000f0000U -#define HPZ2_SHIFT 16 -#define HPZ2(x) ((x) << HPZ2_SHIFT) -#define HPZ1_MASK 0x00000f00U -#define HPZ1_SHIFT 8 -#define HPZ1(x) ((x) << HPZ1_SHIFT) -#define HPZ0_MASK 0x0000000fU -#define HPZ0_SHIFT 0 -#define HPZ0(x) ((x) << HPZ0_SHIFT) - -#define ULP_RX_TDDP_PSZ 0x19178 - -#define SF_DATA 0x193f8 -#define SF_OP 0x193fc -#define SF_BUSY 0x80000000U -#define SF_LOCK 0x00000010U -#define SF_CONT 0x00000008U -#define BYTECNT_MASK 0x00000006U -#define BYTECNT_SHIFT 1 -#define BYTECNT(x) ((x) << BYTECNT_SHIFT) -#define OP_WR 0x00000001U - -#define PL_PF_INT_CAUSE 0x3c0 -#define PFSW 0x00000008U -#define PFSGE 0x00000004U -#define PFCIM 0x00000002U -#define PFMPS 0x00000001U - -#define PL_PF_INT_ENABLE 0x3c4 -#define PL_PF_CTL 0x3c8 -#define SWINT 0x00000001U - -#define PL_WHOAMI 0x19400 -#define SOURCEPF_MASK 0x00000700U -#define SOURCEPF_SHIFT 8 -#define SOURCEPF(x) ((x) << SOURCEPF_SHIFT) -#define SOURCEPF_GET(x) (((x) & SOURCEPF_MASK) >> SOURCEPF_SHIFT) -#define ISVF 0x00000080U -#define VFID_MASK 0x0000007fU -#define VFID_SHIFT 0 -#define VFID(x) ((x) << VFID_SHIFT) -#define VFID_GET(x) (((x) & VFID_MASK) >> VFID_SHIFT) - -#define PL_INT_CAUSE 0x1940c -#define ULP_TX 0x08000000U -#define SGE 0x04000000U -#define HMA 0x02000000U -#define CPL_SWITCH 0x01000000U -#define ULP_RX 0x00800000U -#define PM_RX 0x00400000U -#define PM_TX 0x00200000U -#define MA 0x00100000U -#define TP 0x00080000U -#define LE 0x00040000U -#define EDC1 0x00020000U -#define EDC0 0x00010000U -#define MC 0x00008000U -#define PCIE 0x00004000U -#define PMU 0x00002000U -#define XGMAC_KR1 0x00001000U -#define XGMAC_KR0 0x00000800U -#define XGMAC1 0x00000400U -#define XGMAC0 0x00000200U -#define SMB 0x00000100U -#define SF 0x00000080U -#define PL 0x00000040U -#define NCSI 0x00000020U -#define MPS 0x00000010U -#define MI 0x00000008U -#define DBG 0x00000004U -#define I2CM 0x00000002U -#define CIM 0x00000001U - -#define MC1 0x31 -#define PL_INT_ENABLE 0x19410 -#define PL_INT_MAP0 0x19414 -#define PL_RST 0x19428 -#define PIORST 0x00000002U -#define PIORSTMODE 0x00000001U - -#define PL_PL_INT_CAUSE 0x19430 -#define FATALPERR 0x00000010U -#define PERRVFID 0x00000001U - -#define PL_REV 0x1943c - -#define S_REV 0 -#define M_REV 0xfU -#define V_REV(x) ((x) << S_REV) -#define G_REV(x) (((x) >> S_REV) & M_REV) - -#define LE_DB_CONFIG 0x19c04 -#define HASHEN 0x00100000U - -#define LE_DB_SERVER_INDEX 0x19c18 -#define LE_DB_ACT_CNT_IPV4 0x19c20 -#define LE_DB_ACT_CNT_IPV6 0x19c24 - -#define LE_DB_INT_CAUSE 0x19c3c -#define REQQPARERR 0x00010000U -#define UNKNOWNCMD 0x00008000U -#define PARITYERR 0x00000040U -#define LIPMISS 0x00000020U -#define LIP0 0x00000010U - -#define LE_DB_TID_HASHBASE 0x19df8 - -#define NCSI_INT_CAUSE 0x1a0d8 -#define CIM_DM_PRTY_ERR 0x00000100U -#define MPS_DM_PRTY_ERR 0x00000080U -#define TXFIFO_PRTY_ERR 0x00000002U -#define RXFIFO_PRTY_ERR 0x00000001U - -#define XGMAC_PORT_CFG2 0x1018 -#define PATEN 0x00040000U -#define MAGICEN 0x00020000U +#define CPL_INTR_CAUSE_A 0x19054 -#define XGMAC_PORT_MAGIC_MACID_LO 0x1024 -#define XGMAC_PORT_MAGIC_MACID_HI 0x1028 +#define CIM_OP_MAP_PERR_S 5 +#define CIM_OP_MAP_PERR_V(x) ((x) << CIM_OP_MAP_PERR_S) +#define CIM_OP_MAP_PERR_F CIM_OP_MAP_PERR_V(1U) + +#define CIM_OVFL_ERROR_S 4 +#define CIM_OVFL_ERROR_V(x) ((x) << CIM_OVFL_ERROR_S) +#define CIM_OVFL_ERROR_F CIM_OVFL_ERROR_V(1U) + +#define TP_FRAMING_ERROR_S 3 +#define TP_FRAMING_ERROR_V(x) ((x) << TP_FRAMING_ERROR_S) +#define TP_FRAMING_ERROR_F TP_FRAMING_ERROR_V(1U) + +#define SGE_FRAMING_ERROR_S 2 +#define SGE_FRAMING_ERROR_V(x) ((x) << SGE_FRAMING_ERROR_S) +#define SGE_FRAMING_ERROR_F SGE_FRAMING_ERROR_V(1U) + +#define CIM_FRAMING_ERROR_S 1 +#define CIM_FRAMING_ERROR_V(x) ((x) << CIM_FRAMING_ERROR_S) +#define CIM_FRAMING_ERROR_F CIM_FRAMING_ERROR_V(1U) + +#define ZERO_SWITCH_ERROR_S 0 +#define ZERO_SWITCH_ERROR_V(x) ((x) << ZERO_SWITCH_ERROR_S) +#define ZERO_SWITCH_ERROR_F ZERO_SWITCH_ERROR_V(1U) + +#define SMB_INT_CAUSE_A 0x19090 + +#define MSTTXFIFOPARINT_S 21 +#define MSTTXFIFOPARINT_V(x) ((x) << MSTTXFIFOPARINT_S) +#define MSTTXFIFOPARINT_F MSTTXFIFOPARINT_V(1U) + +#define MSTRXFIFOPARINT_S 20 +#define MSTRXFIFOPARINT_V(x) ((x) << MSTRXFIFOPARINT_S) +#define MSTRXFIFOPARINT_F MSTRXFIFOPARINT_V(1U) + +#define SLVFIFOPARINT_S 19 +#define SLVFIFOPARINT_V(x) ((x) << SLVFIFOPARINT_S) +#define SLVFIFOPARINT_F SLVFIFOPARINT_V(1U) + +#define ULP_RX_INT_CAUSE_A 0x19158 +#define ULP_RX_ISCSI_TAGMASK_A 0x19164 +#define ULP_RX_ISCSI_PSZ_A 0x19168 + +#define HPZ3_S 24 +#define HPZ3_V(x) ((x) << HPZ3_S) + +#define HPZ2_S 16 +#define HPZ2_V(x) ((x) << HPZ2_S) + +#define HPZ1_S 8 +#define HPZ1_V(x) ((x) << HPZ1_S) + +#define HPZ0_S 0 +#define HPZ0_V(x) ((x) << HPZ0_S) + +#define ULP_RX_TDDP_PSZ_A 0x19178 + +/* registers for module SF */ +#define SF_DATA_A 0x193f8 +#define SF_OP_A 0x193fc + +#define SF_BUSY_S 31 +#define SF_BUSY_V(x) ((x) << SF_BUSY_S) +#define SF_BUSY_F SF_BUSY_V(1U) + +#define SF_LOCK_S 4 +#define SF_LOCK_V(x) ((x) << SF_LOCK_S) +#define SF_LOCK_F SF_LOCK_V(1U) + +#define SF_CONT_S 3 +#define SF_CONT_V(x) ((x) << SF_CONT_S) +#define SF_CONT_F SF_CONT_V(1U) + +#define BYTECNT_S 1 +#define BYTECNT_V(x) ((x) << BYTECNT_S) + +#define OP_S 0 +#define OP_V(x) ((x) << OP_S) +#define OP_F OP_V(1U) + +#define PL_PF_INT_CAUSE_A 0x3c0 + +#define PFSW_S 3 +#define PFSW_V(x) ((x) << PFSW_S) +#define PFSW_F PFSW_V(1U) + +#define PFCIM_S 1 +#define PFCIM_V(x) ((x) << PFCIM_S) +#define PFCIM_F PFCIM_V(1U) + +#define PL_PF_INT_ENABLE_A 0x3c4 +#define PL_PF_CTL_A 0x3c8 + +#define PL_WHOAMI_A 0x19400 + +#define SOURCEPF_S 8 +#define SOURCEPF_M 0x7U +#define SOURCEPF_G(x) (((x) >> SOURCEPF_S) & SOURCEPF_M) + +#define PL_INT_CAUSE_A 0x1940c + +#define ULP_TX_S 27 +#define ULP_TX_V(x) ((x) << ULP_TX_S) +#define ULP_TX_F ULP_TX_V(1U) + +#define SGE_S 26 +#define SGE_V(x) ((x) << SGE_S) +#define SGE_F SGE_V(1U) + +#define CPL_SWITCH_S 24 +#define CPL_SWITCH_V(x) ((x) << CPL_SWITCH_S) +#define CPL_SWITCH_F CPL_SWITCH_V(1U) + +#define ULP_RX_S 23 +#define ULP_RX_V(x) ((x) << ULP_RX_S) +#define ULP_RX_F ULP_RX_V(1U) + +#define PM_RX_S 22 +#define PM_RX_V(x) ((x) << PM_RX_S) +#define PM_RX_F PM_RX_V(1U) + +#define PM_TX_S 21 +#define PM_TX_V(x) ((x) << PM_TX_S) +#define PM_TX_F PM_TX_V(1U) + +#define MA_S 20 +#define MA_V(x) ((x) << MA_S) +#define MA_F MA_V(1U) -#define XGMAC_PORT_EPIO_DATA0 0x10c0 -#define XGMAC_PORT_EPIO_DATA1 0x10c4 -#define XGMAC_PORT_EPIO_DATA2 0x10c8 -#define XGMAC_PORT_EPIO_DATA3 0x10cc -#define XGMAC_PORT_EPIO_OP 0x10d0 -#define EPIOWR 0x00000100U -#define ADDRESS_MASK 0x000000ffU -#define ADDRESS_SHIFT 0 -#define ADDRESS(x) ((x) << ADDRESS_SHIFT) +#define TP_S 19 +#define TP_V(x) ((x) << TP_S) +#define TP_F TP_V(1U) -#define MAC_PORT_INT_CAUSE 0x8dc -#define XGMAC_PORT_INT_CAUSE 0x10dc +#define LE_S 18 +#define LE_V(x) ((x) << LE_S) +#define LE_F LE_V(1U) -#define A_TP_TX_MOD_QUEUE_REQ_MAP 0x7e28 +#define EDC1_S 17 +#define EDC1_V(x) ((x) << EDC1_S) +#define EDC1_F EDC1_V(1U) -#define A_TP_TX_MOD_CHANNEL_WEIGHT 0x7e34 +#define EDC0_S 16 +#define EDC0_V(x) ((x) << EDC0_S) +#define EDC0_F EDC0_V(1U) -#define S_TX_MOD_QUEUE_REQ_MAP 0 -#define M_TX_MOD_QUEUE_REQ_MAP 0xffffU -#define V_TX_MOD_QUEUE_REQ_MAP(x) ((x) << S_TX_MOD_QUEUE_REQ_MAP) +#define MC_S 15 +#define MC_V(x) ((x) << MC_S) +#define MC_F MC_V(1U) -#define A_TP_TX_MOD_QUEUE_WEIGHT0 0x7e30 +#define PCIE_S 14 +#define PCIE_V(x) ((x) << PCIE_S) +#define PCIE_F PCIE_V(1U) -#define S_TX_MODQ_WEIGHT3 24 -#define M_TX_MODQ_WEIGHT3 0xffU -#define V_TX_MODQ_WEIGHT3(x) ((x) << S_TX_MODQ_WEIGHT3) +#define XGMAC_KR1_S 12 +#define XGMAC_KR1_V(x) ((x) << XGMAC_KR1_S) +#define XGMAC_KR1_F XGMAC_KR1_V(1U) -#define S_TX_MODQ_WEIGHT2 16 -#define M_TX_MODQ_WEIGHT2 0xffU -#define V_TX_MODQ_WEIGHT2(x) ((x) << S_TX_MODQ_WEIGHT2) +#define XGMAC_KR0_S 11 +#define XGMAC_KR0_V(x) ((x) << XGMAC_KR0_S) +#define XGMAC_KR0_F XGMAC_KR0_V(1U) -#define S_TX_MODQ_WEIGHT1 8 -#define M_TX_MODQ_WEIGHT1 0xffU -#define V_TX_MODQ_WEIGHT1(x) ((x) << S_TX_MODQ_WEIGHT1) +#define XGMAC1_S 10 +#define XGMAC1_V(x) ((x) << XGMAC1_S) +#define XGMAC1_F XGMAC1_V(1U) -#define S_TX_MODQ_WEIGHT0 0 -#define M_TX_MODQ_WEIGHT0 0xffU -#define V_TX_MODQ_WEIGHT0(x) ((x) << S_TX_MODQ_WEIGHT0) +#define XGMAC0_S 9 +#define XGMAC0_V(x) ((x) << XGMAC0_S) +#define XGMAC0_F XGMAC0_V(1U) -#define A_TP_TX_SCHED_HDR 0x23 +#define SMB_S 8 +#define SMB_V(x) ((x) << SMB_S) +#define SMB_F SMB_V(1U) -#define A_TP_TX_SCHED_FIFO 0x24 +#define SF_S 7 +#define SF_V(x) ((x) << SF_S) +#define SF_F SF_V(1U) -#define A_TP_TX_SCHED_PCMD 0x25 +#define PL_S 6 +#define PL_V(x) ((x) << PL_S) +#define PL_F PL_V(1U) -#define S_VNIC 11 -#define V_VNIC(x) ((x) << S_VNIC) -#define F_VNIC V_VNIC(1U) +#define NCSI_S 5 +#define NCSI_V(x) ((x) << NCSI_S) +#define NCSI_F NCSI_V(1U) -#define S_FRAGMENTATION 9 -#define V_FRAGMENTATION(x) ((x) << S_FRAGMENTATION) -#define F_FRAGMENTATION V_FRAGMENTATION(1U) +#define MPS_S 4 +#define MPS_V(x) ((x) << MPS_S) +#define MPS_F MPS_V(1U) -#define S_MPSHITTYPE 8 -#define V_MPSHITTYPE(x) ((x) << S_MPSHITTYPE) -#define F_MPSHITTYPE V_MPSHITTYPE(1U) +#define CIM_S 0 +#define CIM_V(x) ((x) << CIM_S) +#define CIM_F CIM_V(1U) -#define S_MACMATCH 7 -#define V_MACMATCH(x) ((x) << S_MACMATCH) -#define F_MACMATCH V_MACMATCH(1U) +#define MC1_S 31 -#define S_ETHERTYPE 6 -#define V_ETHERTYPE(x) ((x) << S_ETHERTYPE) -#define F_ETHERTYPE V_ETHERTYPE(1U) +#define PL_INT_ENABLE_A 0x19410 +#define PL_INT_MAP0_A 0x19414 +#define PL_RST_A 0x19428 -#define S_PROTOCOL 5 -#define V_PROTOCOL(x) ((x) << S_PROTOCOL) -#define F_PROTOCOL V_PROTOCOL(1U) +#define PIORST_S 1 +#define PIORST_V(x) ((x) << PIORST_S) +#define PIORST_F PIORST_V(1U) -#define S_TOS 4 -#define V_TOS(x) ((x) << S_TOS) -#define F_TOS V_TOS(1U) +#define PIORSTMODE_S 0 +#define PIORSTMODE_V(x) ((x) << PIORSTMODE_S) +#define PIORSTMODE_F PIORSTMODE_V(1U) -#define S_VLAN 3 -#define V_VLAN(x) ((x) << S_VLAN) -#define F_VLAN V_VLAN(1U) +#define PL_PL_INT_CAUSE_A 0x19430 -#define S_VNIC_ID 2 -#define V_VNIC_ID(x) ((x) << S_VNIC_ID) -#define F_VNIC_ID V_VNIC_ID(1U) +#define FATALPERR_S 4 +#define FATALPERR_V(x) ((x) << FATALPERR_S) +#define FATALPERR_F FATALPERR_V(1U) -#define S_PORT 1 -#define V_PORT(x) ((x) << S_PORT) -#define F_PORT V_PORT(1U) +#define PERRVFID_S 0 +#define PERRVFID_V(x) ((x) << PERRVFID_S) +#define PERRVFID_F PERRVFID_V(1U) -#define S_FCOE 0 -#define V_FCOE(x) ((x) << S_FCOE) -#define F_FCOE V_FCOE(1U) +#define PL_REV_A 0x1943c + +#define REV_S 0 +#define REV_M 0xfU +#define REV_V(x) ((x) << REV_S) +#define REV_G(x) (((x) >> REV_S) & REV_M) + +#define LE_DB_INT_CAUSE_A 0x19c3c + +#define REQQPARERR_S 16 +#define REQQPARERR_V(x) ((x) << REQQPARERR_S) +#define REQQPARERR_F REQQPARERR_V(1U) + +#define UNKNOWNCMD_S 15 +#define UNKNOWNCMD_V(x) ((x) << UNKNOWNCMD_S) +#define UNKNOWNCMD_F UNKNOWNCMD_V(1U) + +#define PARITYERR_S 6 +#define PARITYERR_V(x) ((x) << PARITYERR_S) +#define PARITYERR_F PARITYERR_V(1U) + +#define LIPMISS_S 5 +#define LIPMISS_V(x) ((x) << LIPMISS_S) +#define LIPMISS_F LIPMISS_V(1U) + +#define LIP0_S 4 +#define LIP0_V(x) ((x) << LIP0_S) +#define LIP0_F LIP0_V(1U) + +#define NCSI_INT_CAUSE_A 0x1a0d8 + +#define CIM_DM_PRTY_ERR_S 8 +#define CIM_DM_PRTY_ERR_V(x) ((x) << CIM_DM_PRTY_ERR_S) +#define CIM_DM_PRTY_ERR_F CIM_DM_PRTY_ERR_V(1U) + +#define MPS_DM_PRTY_ERR_S 7 +#define MPS_DM_PRTY_ERR_V(x) ((x) << MPS_DM_PRTY_ERR_S) +#define MPS_DM_PRTY_ERR_F MPS_DM_PRTY_ERR_V(1U) + +#define TXFIFO_PRTY_ERR_S 1 +#define TXFIFO_PRTY_ERR_V(x) ((x) << TXFIFO_PRTY_ERR_S) +#define TXFIFO_PRTY_ERR_F TXFIFO_PRTY_ERR_V(1U) + +#define RXFIFO_PRTY_ERR_S 0 +#define RXFIFO_PRTY_ERR_V(x) ((x) << RXFIFO_PRTY_ERR_S) +#define RXFIFO_PRTY_ERR_F RXFIFO_PRTY_ERR_V(1U) + +#define XGMAC_PORT_CFG2_A 0x1018 + +#define PATEN_S 18 +#define PATEN_V(x) ((x) << PATEN_S) +#define PATEN_F PATEN_V(1U) + +#define MAGICEN_S 17 +#define MAGICEN_V(x) ((x) << MAGICEN_S) +#define MAGICEN_F MAGICEN_V(1U) + +#define XGMAC_PORT_MAGIC_MACID_LO 0x1024 +#define XGMAC_PORT_MAGIC_MACID_HI 0x1028 + +#define XGMAC_PORT_EPIO_DATA0_A 0x10c0 +#define XGMAC_PORT_EPIO_DATA1_A 0x10c4 +#define XGMAC_PORT_EPIO_DATA2_A 0x10c8 +#define XGMAC_PORT_EPIO_DATA3_A 0x10cc +#define XGMAC_PORT_EPIO_OP_A 0x10d0 + +#define EPIOWR_S 8 +#define EPIOWR_V(x) ((x) << EPIOWR_S) +#define EPIOWR_F EPIOWR_V(1U) + +#define ADDRESS_S 0 +#define ADDRESS_V(x) ((x) << ADDRESS_S) + +#define MAC_PORT_INT_CAUSE_A 0x8dc +#define XGMAC_PORT_INT_CAUSE_A 0x10dc + +#define TP_TX_MOD_QUEUE_REQ_MAP_A 0x7e28 + +#define TP_TX_MOD_QUEUE_WEIGHT0_A 0x7e30 +#define TP_TX_MOD_CHANNEL_WEIGHT_A 0x7e34 + +#define TX_MOD_QUEUE_REQ_MAP_S 0 +#define TX_MOD_QUEUE_REQ_MAP_V(x) ((x) << TX_MOD_QUEUE_REQ_MAP_S) + +#define TX_MODQ_WEIGHT3_S 24 +#define TX_MODQ_WEIGHT3_V(x) ((x) << TX_MODQ_WEIGHT3_S) + +#define TX_MODQ_WEIGHT2_S 16 +#define TX_MODQ_WEIGHT2_V(x) ((x) << TX_MODQ_WEIGHT2_S) + +#define TX_MODQ_WEIGHT1_S 8 +#define TX_MODQ_WEIGHT1_V(x) ((x) << TX_MODQ_WEIGHT1_S) + +#define TX_MODQ_WEIGHT0_S 0 +#define TX_MODQ_WEIGHT0_V(x) ((x) << TX_MODQ_WEIGHT0_S) + +#define TP_TX_SCHED_HDR_A 0x23 +#define TP_TX_SCHED_FIFO_A 0x24 +#define TP_TX_SCHED_PCMD_A 0x25 #define NUM_MPS_CLS_SRAM_L_INSTANCES 336 #define NUM_MPS_T5_CLS_SRAM_L_INSTANCES 512 @@ -1968,46 +2058,8 @@ #define EDC_STRIDE_T5 (EDC_T51_BASE_ADDR - EDC_T50_BASE_ADDR) #define EDC_REG_T5(reg, idx) (reg + EDC_STRIDE_T5 * idx) -#define A_PL_VF_REV 0x4 -#define A_PL_VF_WHOAMI 0x0 -#define A_PL_VF_REVISION 0x8 - -#define S_CHIPID 4 -#define M_CHIPID 0xfU -#define V_CHIPID(x) ((x) << S_CHIPID) -#define G_CHIPID(x) (((x) >> S_CHIPID) & M_CHIPID) - -/* TP_VLAN_PRI_MAP controls which subset of fields will be present in the - * Compressed Filter Tuple for LE filters. Each bit set in TP_VLAN_PRI_MAP - * selects for a particular field being present. These fields, when present - * in the Compressed Filter Tuple, have the following widths in bits. - */ -#define W_FT_FCOE 1 -#define W_FT_PORT 3 -#define W_FT_VNIC_ID 17 -#define W_FT_VLAN 17 -#define W_FT_TOS 8 -#define W_FT_PROTOCOL 8 -#define W_FT_ETHERTYPE 16 -#define W_FT_MACMATCH 9 -#define W_FT_MPSHITTYPE 3 -#define W_FT_FRAGMENTATION 1 - -/* Some of the Compressed Filter Tuple fields have internal structure. These - * bit shifts/masks describe those structures. All shifts are relative to the - * base position of the fields within the Compressed Filter Tuple - */ -#define S_FT_VLAN_VLD 16 -#define V_FT_VLAN_VLD(x) ((x) << S_FT_VLAN_VLD) -#define F_FT_VLAN_VLD V_FT_VLAN_VLD(1U) - -#define S_FT_VNID_ID_VF 0 -#define V_FT_VNID_ID_VF(x) ((x) << S_FT_VNID_ID_VF) - -#define S_FT_VNID_ID_PF 7 -#define V_FT_VNID_ID_PF(x) ((x) << S_FT_VNID_ID_PF) - -#define S_FT_VNID_ID_VLD 16 -#define V_FT_VNID_ID_VLD(x) ((x) << S_FT_VNID_ID_VLD) +#define PL_VF_REV_A 0x4 +#define PL_VF_WHOAMI_A 0x0 +#define PL_VF_REVISION_A 0x8 #endif /* __T4_REGS_H */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h index ecf7459..a404844 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h @@ -82,4 +82,37 @@ #define WINDOW_SHIFT_X 10 #define PCIEOFST_SHIFT_X 10 +/* TP_VLAN_PRI_MAP controls which subset of fields will be present in the + * Compressed Filter Tuple for LE filters. Each bit set in TP_VLAN_PRI_MAP + * selects for a particular field being present. These fields, when present + * in the Compressed Filter Tuple, have the following widths in bits. + */ +#define FT_FCOE_W 1 +#define FT_PORT_W 3 +#define FT_VNIC_ID_W 17 +#define FT_VLAN_W 17 +#define FT_TOS_W 8 +#define FT_PROTOCOL_W 8 +#define FT_ETHERTYPE_W 16 +#define FT_MACMATCH_W 9 +#define FT_MPSHITTYPE_W 3 +#define FT_FRAGMENTATION_W 1 + +/* Some of the Compressed Filter Tuple fields have internal structure. These + * bit shifts/masks describe those structures. All shifts are relative to the + * base position of the fields within the Compressed Filter Tuple + */ +#define FT_VLAN_VLD_S 16 +#define FT_VLAN_VLD_V(x) ((x) << FT_VLAN_VLD_S) +#define FT_VLAN_VLD_F FT_VLAN_VLD_V(1U) + +#define FT_VNID_ID_VF_S 0 +#define FT_VNID_ID_VF_V(x) ((x) << FT_VNID_ID_VF_S) + +#define FT_VNID_ID_PF_S 7 +#define FT_VNID_ID_PF_V(x) ((x) << FT_VNID_ID_PF_S) + +#define FT_VNID_ID_VLD_S 16 +#define FT_VNID_ID_VLD_V(x) ((x) << FT_VNID_ID_VLD_S) + #endif /* __T4_VALUES_H__ */ diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index aa9f9cd..7bfbacd 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -1673,7 +1673,7 @@ static void cxgb4vf_get_regs(struct net_device *dev, reg_block_dump(adapter, regbuf, T4VF_PL_BASE_ADDR + T4VF_MOD_MAP_PL_FIRST, T4VF_PL_BASE_ADDR + (is_t4(adapter->params.chip) - ? A_PL_VF_WHOAMI : A_PL_VF_REVISION)); + ? PL_VF_WHOAMI_A : PL_VF_REVISION_A)); reg_block_dump(adapter, regbuf, T4VF_CIM_BASE_ADDR + T4VF_MOD_MAP_CIM_FIRST, T4VF_CIM_BASE_ADDR + T4VF_MOD_MAP_CIM_LAST); diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c index 5e83c18..fcc6108 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c @@ -616,8 +616,8 @@ int t4vf_get_sge_params(struct adapter *adapter) * the driver can just use it. */ whoami = t4_read_reg(adapter, - T4VF_PL_BASE_ADDR + A_PL_VF_WHOAMI); - pf = SOURCEPF_GET(whoami); + T4VF_PL_BASE_ADDR + PL_VF_WHOAMI_A); + pf = SOURCEPF_G(whoami); s_hps = (HOSTPAGESIZEPF0_S + (HOSTPAGESIZEPF1_S - HOSTPAGESIZEPF0_S) * pf); @@ -1591,7 +1591,7 @@ int t4vf_prep_adapter(struct adapter *adapter) break; case CHELSIO_T5: - chipid = G_REV(t4_read_reg(adapter, A_PL_VF_REV)); + chipid = REV_G(t4_read_reg(adapter, PL_VF_REV_A)); adapter->params.chip |= CHELSIO_CHIP_CODE(CHELSIO_T5, chipid); break; } diff --git a/drivers/scsi/csiostor/csio_hw.c b/drivers/scsi/csiostor/csio_hw.c index c641931..6602835 100644 --- a/drivers/scsi/csiostor/csio_hw.c +++ b/drivers/scsi/csiostor/csio_hw.c @@ -421,17 +421,15 @@ csio_hw_sf1_read(struct csio_hw *hw, uint32_t byte_cnt, int32_t cont, if (!byte_cnt || byte_cnt > 4) return -EINVAL; - if (csio_rd_reg32(hw, SF_OP) & SF_BUSY) + if (csio_rd_reg32(hw, SF_OP_A) & SF_BUSY_F) return -EBUSY; - cont = cont ? SF_CONT : 0; - lock = lock ? SF_LOCK : 0; - - csio_wr_reg32(hw, lock | cont | BYTECNT(byte_cnt - 1), SF_OP); - ret = csio_hw_wait_op_done_val(hw, SF_OP, SF_BUSY, 0, SF_ATTEMPTS, - 10, NULL); + csio_wr_reg32(hw, SF_LOCK_V(lock) | SF_CONT_V(cont) | + BYTECNT_V(byte_cnt - 1), SF_OP_A); + ret = csio_hw_wait_op_done_val(hw, SF_OP_A, SF_BUSY_F, 0, SF_ATTEMPTS, + 10, NULL); if (!ret) - *valp = csio_rd_reg32(hw, SF_DATA); + *valp = csio_rd_reg32(hw, SF_DATA_A); return ret; } @@ -453,16 +451,14 @@ csio_hw_sf1_write(struct csio_hw *hw, uint32_t byte_cnt, uint32_t cont, { if (!byte_cnt || byte_cnt > 4) return -EINVAL; - if (csio_rd_reg32(hw, SF_OP) & SF_BUSY) + if (csio_rd_reg32(hw, SF_OP_A) & SF_BUSY_F) return -EBUSY; - cont = cont ? SF_CONT : 0; - lock = lock ? SF_LOCK : 0; - - csio_wr_reg32(hw, val, SF_DATA); - csio_wr_reg32(hw, cont | BYTECNT(byte_cnt - 1) | OP_WR | lock, SF_OP); + csio_wr_reg32(hw, val, SF_DATA_A); + csio_wr_reg32(hw, SF_CONT_V(cont) | BYTECNT_V(byte_cnt - 1) | + OP_V(1) | SF_LOCK_V(lock), SF_OP_A); - return csio_hw_wait_op_done_val(hw, SF_OP, SF_BUSY, 0, SF_ATTEMPTS, + return csio_hw_wait_op_done_val(hw, SF_OP_A, SF_BUSY_F, 0, SF_ATTEMPTS, 10, NULL); } @@ -533,7 +529,7 @@ csio_hw_read_flash(struct csio_hw *hw, uint32_t addr, uint32_t nwords, for ( ; nwords; nwords--, data++) { ret = csio_hw_sf1_read(hw, 4, nwords > 1, nwords == 1, data); if (nwords == 1) - csio_wr_reg32(hw, 0, SF_OP); /* unlock SF */ + csio_wr_reg32(hw, 0, SF_OP_A); /* unlock SF */ if (ret) return ret; if (byte_oriented) @@ -586,7 +582,7 @@ csio_hw_write_flash(struct csio_hw *hw, uint32_t addr, if (ret) goto unlock; - csio_wr_reg32(hw, 0, SF_OP); /* unlock SF */ + csio_wr_reg32(hw, 0, SF_OP_A); /* unlock SF */ /* Read the page to verify the write succeeded */ ret = csio_hw_read_flash(hw, addr & ~0xff, ARRAY_SIZE(buf), buf, 1); @@ -603,7 +599,7 @@ csio_hw_write_flash(struct csio_hw *hw, uint32_t addr, return 0; unlock: - csio_wr_reg32(hw, 0, SF_OP); /* unlock SF */ + csio_wr_reg32(hw, 0, SF_OP_A); /* unlock SF */ return ret; } @@ -641,7 +637,7 @@ out: if (ret) csio_err(hw, "erase of flash sector %d failed, error %d\n", start, ret); - csio_wr_reg32(hw, 0, SF_OP); /* unlock SF */ + csio_wr_reg32(hw, 0, SF_OP_A); /* unlock SF */ return 0; } @@ -833,7 +829,7 @@ csio_hw_get_flash_params(struct csio_hw *hw) ret = csio_hw_sf1_write(hw, 1, 1, 0, SF_RD_ID); if (!ret) ret = csio_hw_sf1_read(hw, 3, 0, 1, &info); - csio_wr_reg32(hw, 0, SF_OP); /* unlock SF */ + csio_wr_reg32(hw, 0, SF_OP_A); /* unlock SF */ if (ret != 0) return ret; @@ -861,17 +857,17 @@ csio_hw_dev_ready(struct csio_hw *hw) uint32_t reg; int cnt = 6; - while (((reg = csio_rd_reg32(hw, PL_WHOAMI)) == 0xFFFFFFFF) && - (--cnt != 0)) + while (((reg = csio_rd_reg32(hw, PL_WHOAMI_A)) == 0xFFFFFFFF) && + (--cnt != 0)) mdelay(100); - if ((cnt == 0) && (((int32_t)(SOURCEPF_GET(reg)) < 0) || - (SOURCEPF_GET(reg) >= CSIO_MAX_PFN))) { + if ((cnt == 0) && (((int32_t)(SOURCEPF_G(reg)) < 0) || + (SOURCEPF_G(reg) >= CSIO_MAX_PFN))) { csio_err(hw, "PL_WHOAMI returned 0x%x, cnt:%d\n", reg, cnt); return -EIO; } - hw->pfn = SOURCEPF_GET(reg); + hw->pfn = SOURCEPF_G(reg); return 0; } @@ -1078,7 +1074,7 @@ csio_do_reset(struct csio_hw *hw, bool fw_rst) if (!fw_rst) { /* PIO reset */ - csio_wr_reg32(hw, PIORSTMODE | PIORST, PL_RST); + csio_wr_reg32(hw, PIORSTMODE_F | PIORST_F, PL_RST_A); mdelay(2000); return 0; } @@ -1090,7 +1086,7 @@ csio_do_reset(struct csio_hw *hw, bool fw_rst) } csio_mb_reset(hw, mbp, CSIO_MB_DEFAULT_TMO, - PIORSTMODE | PIORST, 0, NULL); + PIORSTMODE_F | PIORST_F, 0, NULL); if (csio_mb_issue(hw, mbp)) { csio_err(hw, "Issue of RESET command failed.n"); @@ -1166,7 +1162,7 @@ csio_hw_fw_halt(struct csio_hw *hw, uint32_t mbox, int32_t force) } csio_mb_reset(hw, mbp, CSIO_MB_DEFAULT_TMO, - PIORSTMODE | PIORST, FW_RESET_CMD_HALT_F, + PIORSTMODE_F | PIORST_F, FW_RESET_CMD_HALT_F, NULL); if (csio_mb_issue(hw, mbp)) { @@ -1251,7 +1247,7 @@ csio_hw_fw_restart(struct csio_hw *hw, uint32_t mbox, int32_t reset) return 0; } - csio_wr_reg32(hw, PIORSTMODE | PIORST, PL_RST); + csio_wr_reg32(hw, PIORSTMODE_F | PIORST_F, PL_RST_A); msleep(2000); } else { int ms; @@ -2040,7 +2036,7 @@ csio_hw_configure(struct csio_hw *hw) } /* HW version */ - hw->chip_ver = (char)csio_rd_reg32(hw, PL_REV); + hw->chip_ver = (char)csio_rd_reg32(hw, PL_REV_A); /* Needed for FW download */ rv = csio_hw_get_flash_params(hw); @@ -2218,7 +2214,7 @@ out: return; } -#define PF_INTR_MASK (PFSW | PFCIM) +#define PF_INTR_MASK (PFSW_F | PFCIM_F) /* * csio_hw_intr_enable - Enable HW interrupts @@ -2230,8 +2226,8 @@ static void csio_hw_intr_enable(struct csio_hw *hw) { uint16_t vec = (uint16_t)csio_get_mb_intr_idx(csio_hw_to_mbm(hw)); - uint32_t pf = SOURCEPF_GET(csio_rd_reg32(hw, PL_WHOAMI)); - uint32_t pl = csio_rd_reg32(hw, PL_INT_ENABLE); + uint32_t pf = SOURCEPF_G(csio_rd_reg32(hw, PL_WHOAMI_A)); + uint32_t pl = csio_rd_reg32(hw, PL_INT_ENABLE_A); /* * Set aivec for MSI/MSIX. PCIE_PF_CFG.INTXType is set up @@ -2244,7 +2240,7 @@ csio_hw_intr_enable(struct csio_hw *hw) csio_set_reg_field(hw, MYPF_REG(PCIE_PF_CFG_A), AIVEC_V(AIVEC_M), 0); - csio_wr_reg32(hw, PF_INTR_MASK, MYPF_REG(PL_PF_INT_ENABLE)); + csio_wr_reg32(hw, PF_INTR_MASK, MYPF_REG(PL_PF_INT_ENABLE_A)); /* Turn on MB interrupts - this will internally flush PIO as well */ csio_mb_intr_enable(hw); @@ -2254,8 +2250,8 @@ csio_hw_intr_enable(struct csio_hw *hw) /* * Disable the Serial FLASH interrupt, if enabled! */ - pl &= (~SF); - csio_wr_reg32(hw, pl, PL_INT_ENABLE); + pl &= (~SF_F); + csio_wr_reg32(hw, pl, PL_INT_ENABLE_A); csio_wr_reg32(hw, ERR_CPL_EXCEED_IQE_SIZE_F | EGRESS_SIZE_ERR_F | ERR_INVALID_CIDX_INC_F | @@ -2266,7 +2262,7 @@ csio_hw_intr_enable(struct csio_hw *hw) ERR_BAD_DB_PIDX0_F | ERR_ING_CTXT_PRIO_F | ERR_EGR_CTXT_PRIO_F | INGRESS_SIZE_ERR_F, SGE_INT_ENABLE3_A); - csio_set_reg_field(hw, PL_INT_MAP0, 0, 1 << pf); + csio_set_reg_field(hw, PL_INT_MAP0_A, 0, 1 << pf); } hw->flags |= CSIO_HWF_HW_INTR_ENABLED; @@ -2282,16 +2278,16 @@ csio_hw_intr_enable(struct csio_hw *hw) void csio_hw_intr_disable(struct csio_hw *hw) { - uint32_t pf = SOURCEPF_GET(csio_rd_reg32(hw, PL_WHOAMI)); + uint32_t pf = SOURCEPF_G(csio_rd_reg32(hw, PL_WHOAMI_A)); if (!(hw->flags & CSIO_HWF_HW_INTR_ENABLED)) return; hw->flags &= ~CSIO_HWF_HW_INTR_ENABLED; - csio_wr_reg32(hw, 0, MYPF_REG(PL_PF_INT_ENABLE)); + csio_wr_reg32(hw, 0, MYPF_REG(PL_PF_INT_ENABLE_A)); if (csio_is_hw_master(hw)) - csio_set_reg_field(hw, PL_INT_MAP0, 1 << pf, 0); + csio_set_reg_field(hw, PL_INT_MAP0_A, 1 << pf, 0); /* Turn off MB interrupts */ csio_mb_intr_disable(hw); @@ -2595,7 +2591,7 @@ csio_hws_removing(struct csio_hw *hw, enum csio_hw_ev evt) * register directly. */ csio_err(hw, "Resetting HW and waiting 2 seconds...\n"); - csio_wr_reg32(hw, PIORSTMODE | PIORST, PL_RST); + csio_wr_reg32(hw, PIORSTMODE_F | PIORST_F, PL_RST_A); mdelay(2000); break; @@ -2814,7 +2810,7 @@ static void csio_ulprx_intr_handler(struct csio_hw *hw) { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, ULP_RX_INT_CAUSE, ulprx_intr_info)) + if (csio_handle_intr_status(hw, ULP_RX_INT_CAUSE_A, ulprx_intr_info)) csio_hw_fatal_err(hw); } @@ -2889,16 +2885,16 @@ static void csio_pmrx_intr_handler(struct csio_hw *hw) static void csio_cplsw_intr_handler(struct csio_hw *hw) { static struct intr_info cplsw_intr_info[] = { - { CIM_OP_MAP_PERR, "CPLSW CIM op_map parity error", -1, 1 }, - { CIM_OVFL_ERROR, "CPLSW CIM overflow", -1, 1 }, - { TP_FRAMING_ERROR, "CPLSW TP framing error", -1, 1 }, - { SGE_FRAMING_ERROR, "CPLSW SGE framing error", -1, 1 }, - { CIM_FRAMING_ERROR, "CPLSW CIM framing error", -1, 1 }, - { ZERO_SWITCH_ERROR, "CPLSW no-switch error", -1, 1 }, + { CIM_OP_MAP_PERR_F, "CPLSW CIM op_map parity error", -1, 1 }, + { CIM_OVFL_ERROR_F, "CPLSW CIM overflow", -1, 1 }, + { TP_FRAMING_ERROR_F, "CPLSW TP framing error", -1, 1 }, + { SGE_FRAMING_ERROR_F, "CPLSW SGE framing error", -1, 1 }, + { CIM_FRAMING_ERROR_F, "CPLSW CIM framing error", -1, 1 }, + { ZERO_SWITCH_ERROR_F, "CPLSW no-switch error", -1, 1 }, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, CPL_INTR_CAUSE, cplsw_intr_info)) + if (csio_handle_intr_status(hw, CPL_INTR_CAUSE_A, cplsw_intr_info)) csio_hw_fatal_err(hw); } @@ -2908,15 +2904,15 @@ static void csio_cplsw_intr_handler(struct csio_hw *hw) static void csio_le_intr_handler(struct csio_hw *hw) { static struct intr_info le_intr_info[] = { - { LIPMISS, "LE LIP miss", -1, 0 }, - { LIP0, "LE 0 LIP error", -1, 0 }, - { PARITYERR, "LE parity error", -1, 1 }, - { UNKNOWNCMD, "LE unknown command", -1, 1 }, - { REQQPARERR, "LE request queue parity error", -1, 1 }, + { LIPMISS_F, "LE LIP miss", -1, 0 }, + { LIP0_F, "LE 0 LIP error", -1, 0 }, + { PARITYERR_F, "LE parity error", -1, 1 }, + { UNKNOWNCMD_F, "LE unknown command", -1, 1 }, + { REQQPARERR_F, "LE request queue parity error", -1, 1 }, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, LE_DB_INT_CAUSE, le_intr_info)) + if (csio_handle_intr_status(hw, LE_DB_INT_CAUSE_A, le_intr_info)) csio_hw_fatal_err(hw); } @@ -3054,13 +3050,13 @@ static void csio_ma_intr_handler(struct csio_hw *hw) static void csio_smb_intr_handler(struct csio_hw *hw) { static struct intr_info smb_intr_info[] = { - { MSTTXFIFOPARINT, "SMB master Tx FIFO parity error", -1, 1 }, - { MSTRXFIFOPARINT, "SMB master Rx FIFO parity error", -1, 1 }, - { SLVFIFOPARINT, "SMB slave FIFO parity error", -1, 1 }, + { MSTTXFIFOPARINT_F, "SMB master Tx FIFO parity error", -1, 1 }, + { MSTRXFIFOPARINT_F, "SMB master Rx FIFO parity error", -1, 1 }, + { SLVFIFOPARINT_F, "SMB slave FIFO parity error", -1, 1 }, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, SMB_INT_CAUSE, smb_intr_info)) + if (csio_handle_intr_status(hw, SMB_INT_CAUSE_A, smb_intr_info)) csio_hw_fatal_err(hw); } @@ -3070,14 +3066,14 @@ static void csio_smb_intr_handler(struct csio_hw *hw) static void csio_ncsi_intr_handler(struct csio_hw *hw) { static struct intr_info ncsi_intr_info[] = { - { CIM_DM_PRTY_ERR, "NC-SI CIM parity error", -1, 1 }, - { MPS_DM_PRTY_ERR, "NC-SI MPS parity error", -1, 1 }, - { TXFIFO_PRTY_ERR, "NC-SI Tx FIFO parity error", -1, 1 }, - { RXFIFO_PRTY_ERR, "NC-SI Rx FIFO parity error", -1, 1 }, + { CIM_DM_PRTY_ERR_F, "NC-SI CIM parity error", -1, 1 }, + { MPS_DM_PRTY_ERR_F, "NC-SI MPS parity error", -1, 1 }, + { TXFIFO_PRTY_ERR_F, "NC-SI Tx FIFO parity error", -1, 1 }, + { RXFIFO_PRTY_ERR_F, "NC-SI Rx FIFO parity error", -1, 1 }, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, NCSI_INT_CAUSE, ncsi_intr_info)) + if (csio_handle_intr_status(hw, NCSI_INT_CAUSE_A, ncsi_intr_info)) csio_hw_fatal_err(hw); } @@ -3088,13 +3084,13 @@ static void csio_xgmac_intr_handler(struct csio_hw *hw, int port) { uint32_t v = csio_rd_reg32(hw, CSIO_MAC_INT_CAUSE_REG(hw, port)); - v &= TXFIFO_PRTY_ERR | RXFIFO_PRTY_ERR; + v &= TXFIFO_PRTY_ERR_F | RXFIFO_PRTY_ERR_F; if (!v) return; - if (v & TXFIFO_PRTY_ERR) + if (v & TXFIFO_PRTY_ERR_F) csio_fatal(hw, "XGMAC %d Tx FIFO parity error\n", port); - if (v & RXFIFO_PRTY_ERR) + if (v & RXFIFO_PRTY_ERR_F) csio_fatal(hw, "XGMAC %d Rx FIFO parity error\n", port); csio_wr_reg32(hw, v, CSIO_MAC_INT_CAUSE_REG(hw, port)); csio_hw_fatal_err(hw); @@ -3106,12 +3102,12 @@ static void csio_xgmac_intr_handler(struct csio_hw *hw, int port) static void csio_pl_intr_handler(struct csio_hw *hw) { static struct intr_info pl_intr_info[] = { - { FATALPERR, "T4 fatal parity error", -1, 1 }, - { PERRVFID, "PL VFID_MAP parity error", -1, 1 }, + { FATALPERR_F, "T4 fatal parity error", -1, 1 }, + { PERRVFID_F, "PL VFID_MAP parity error", -1, 1 }, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, PL_PL_INT_CAUSE, pl_intr_info)) + if (csio_handle_intr_status(hw, PL_PL_INT_CAUSE_A, pl_intr_info)) csio_hw_fatal_err(hw); } @@ -3126,7 +3122,7 @@ static void csio_pl_intr_handler(struct csio_hw *hw) int csio_hw_slow_intr_handler(struct csio_hw *hw) { - uint32_t cause = csio_rd_reg32(hw, PL_INT_CAUSE); + uint32_t cause = csio_rd_reg32(hw, PL_INT_CAUSE_A); if (!(cause & CSIO_GLBL_INTR_MASK)) { CSIO_INC_STATS(hw, n_plint_unexp); @@ -3137,75 +3133,75 @@ csio_hw_slow_intr_handler(struct csio_hw *hw) CSIO_INC_STATS(hw, n_plint_cnt); - if (cause & CIM) + if (cause & CIM_F) csio_cim_intr_handler(hw); - if (cause & MPS) + if (cause & MPS_F) csio_mps_intr_handler(hw); - if (cause & NCSI) + if (cause & NCSI_F) csio_ncsi_intr_handler(hw); - if (cause & PL) + if (cause & PL_F) csio_pl_intr_handler(hw); - if (cause & SMB) + if (cause & SMB_F) csio_smb_intr_handler(hw); - if (cause & XGMAC0) + if (cause & XGMAC0_F) csio_xgmac_intr_handler(hw, 0); - if (cause & XGMAC1) + if (cause & XGMAC1_F) csio_xgmac_intr_handler(hw, 1); - if (cause & XGMAC_KR0) + if (cause & XGMAC_KR0_F) csio_xgmac_intr_handler(hw, 2); - if (cause & XGMAC_KR1) + if (cause & XGMAC_KR1_F) csio_xgmac_intr_handler(hw, 3); - if (cause & PCIE) + if (cause & PCIE_F) hw->chip_ops->chip_pcie_intr_handler(hw); - if (cause & MC) + if (cause & MC_F) csio_mem_intr_handler(hw, MEM_MC); - if (cause & EDC0) + if (cause & EDC0_F) csio_mem_intr_handler(hw, MEM_EDC0); - if (cause & EDC1) + if (cause & EDC1_F) csio_mem_intr_handler(hw, MEM_EDC1); - if (cause & LE) + if (cause & LE_F) csio_le_intr_handler(hw); - if (cause & TP) + if (cause & TP_F) csio_tp_intr_handler(hw); - if (cause & MA) + if (cause & MA_F) csio_ma_intr_handler(hw); - if (cause & PM_TX) + if (cause & PM_TX_F) csio_pmtx_intr_handler(hw); - if (cause & PM_RX) + if (cause & PM_RX_F) csio_pmrx_intr_handler(hw); - if (cause & ULP_RX) + if (cause & ULP_RX_F) csio_ulprx_intr_handler(hw); - if (cause & CPL_SWITCH) + if (cause & CPL_SWITCH_F) csio_cplsw_intr_handler(hw); - if (cause & SGE) + if (cause & SGE_F) csio_sge_intr_handler(hw); - if (cause & ULP_TX) + if (cause & ULP_TX_F) csio_ulptx_intr_handler(hw); /* Clear the interrupts just processed for which we are the master. */ - csio_wr_reg32(hw, cause & CSIO_GLBL_INTR_MASK, PL_INT_CAUSE); - csio_rd_reg32(hw, PL_INT_CAUSE); /* flush */ + csio_wr_reg32(hw, cause & CSIO_GLBL_INTR_MASK, PL_INT_CAUSE_A); + csio_rd_reg32(hw, PL_INT_CAUSE_A); /* flush */ return 1; } diff --git a/drivers/scsi/csiostor/csio_hw.h b/drivers/scsi/csiostor/csio_hw.h index 68248da..bd97204 100644 --- a/drivers/scsi/csiostor/csio_hw.h +++ b/drivers/scsi/csiostor/csio_hw.h @@ -117,10 +117,10 @@ extern int csio_msi; #define CSIO_ASIC_DEVID_PROTO_MASK 0xFF00 #define CSIO_ASIC_DEVID_TYPE_MASK 0x00FF -#define CSIO_GLBL_INTR_MASK (CIM | MPS | PL | PCIE | MC | EDC0 | \ - EDC1 | LE | TP | MA | PM_TX | PM_RX | \ - ULP_RX | CPL_SWITCH | SGE | \ - ULP_TX | SF) +#define CSIO_GLBL_INTR_MASK (CIM_F | MPS_F | PL_F | PCIE_F | MC_F | \ + EDC0_F | EDC1_F | LE_F | TP_F | MA_F | \ + PM_TX_F | PM_RX_F | ULP_RX_F | \ + CPL_SWITCH_F | SGE_F | ULP_TX_F | SF_F) /* * Hard parameters used to initialize the card in the absence of a diff --git a/drivers/scsi/csiostor/csio_hw_chip.h b/drivers/scsi/csiostor/csio_hw_chip.h index 0198662..70c0bdd 100644 --- a/drivers/scsi/csiostor/csio_hw_chip.h +++ b/drivers/scsi/csiostor/csio_hw_chip.h @@ -77,8 +77,8 @@ static inline int csio_is_t5(uint16_t chip) (csio_is_t4(hw->chip_id) ? (LP_INT_THRESH_M) : (LP_INT_THRESH_T5_M)) #define CSIO_MAC_INT_CAUSE_REG(hw, port) \ - (csio_is_t4(hw->chip_id) ? (PORT_REG(port, XGMAC_PORT_INT_CAUSE)) : \ - (T5_PORT_REG(port, MAC_PORT_INT_CAUSE))) + (csio_is_t4(hw->chip_id) ? (PORT_REG(port, XGMAC_PORT_INT_CAUSE_A)) : \ + (T5_PORT_REG(port, MAC_PORT_INT_CAUSE_A))) #define FW_VERSION_MAJOR(hw) (csio_is_t4(hw->chip_id) ? 1 : 0) #define FW_VERSION_MINOR(hw) (csio_is_t4(hw->chip_id) ? 2 : 0) diff --git a/drivers/scsi/csiostor/csio_mb.c b/drivers/scsi/csiostor/csio_mb.c index cb53985..1132c41 100644 --- a/drivers/scsi/csiostor/csio_mb.c +++ b/drivers/scsi/csiostor/csio_mb.c @@ -1464,10 +1464,10 @@ csio_mb_isr_handler(struct csio_hw *hw) __be64 hdr; struct fw_cmd_hdr *fw_hdr; - pl_cause = csio_rd_reg32(hw, MYPF_REG(PL_PF_INT_CAUSE)); + pl_cause = csio_rd_reg32(hw, MYPF_REG(PL_PF_INT_CAUSE_A)); cim_cause = csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_CAUSE_A)); - if (!(pl_cause & PFCIM) || !(cim_cause & MBMSGRDYINT_F)) { + if (!(pl_cause & PFCIM_F) || !(cim_cause & MBMSGRDYINT_F)) { CSIO_INC_STATS(hw, n_mbint_unexp); return -EINVAL; } @@ -1479,7 +1479,7 @@ csio_mb_isr_handler(struct csio_hw *hw) * first followed by PL-Cause next. */ csio_wr_reg32(hw, MBMSGRDYINT_F, MYPF_REG(CIM_PF_HOST_INT_CAUSE_A)); - csio_wr_reg32(hw, PFCIM, MYPF_REG(PL_PF_INT_CAUSE)); + csio_wr_reg32(hw, PFCIM_F, MYPF_REG(PL_PF_INT_CAUSE_A)); ctl = csio_rd_reg32(hw, ctl_reg); diff --git a/drivers/scsi/csiostor/csio_wr.c b/drivers/scsi/csiostor/csio_wr.c index e22503b..b47ea33 100644 --- a/drivers/scsi/csiostor/csio_wr.c +++ b/drivers/scsi/csiostor/csio_wr.c @@ -1343,7 +1343,7 @@ csio_wr_fixup_host_params(struct csio_hw *hw) SGE_FL_BUFFER_SIZE3_A); } - csio_wr_reg32(hw, HPZ0(PAGE_SHIFT - 12), ULP_RX_TDDP_PSZ); + csio_wr_reg32(hw, HPZ0_V(PAGE_SHIFT - 12), ULP_RX_TDDP_PSZ_A); /* default value of rx_dma_offset of the NIC driver */ csio_set_reg_field(hw, SGE_CONTROL_A, -- cgit v0.10.2 From 149118d89355fb0e1a898f47977f8ae9be8e14e7 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 6 Jan 2015 01:04:21 +0100 Subject: netlink: Warn on unordered or illegal nla_nest_cancel() or nlmsg_cancel() Calling nla_nest_cancel() in a different order as the nesting was built up can lead to negative offsets being calculated which results in skb_trim() being called with an underflowed unsigned int. Warn if mark < skb->data as it's definitely a bug. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/include/net/netlink.h b/include/net/netlink.h index 6415835..d5869b9 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -520,8 +520,10 @@ static inline void *nlmsg_get_pos(struct sk_buff *skb) */ static inline void nlmsg_trim(struct sk_buff *skb, const void *mark) { - if (mark) + if (mark) { + WARN_ON((unsigned char *) mark < skb->data); skb_trim(skb, (unsigned char *) mark - skb->data); + } } /** -- cgit v0.10.2 From 224d019c4fbba242041e9b25a926ba873b7da1e2 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Mon, 5 Jan 2015 13:56:14 -0800 Subject: ip: Move checksum convert defines to inet Move convert_csum from udp_sock to inet_sock. This allows the possibility that we can use convert checksum for different types of sockets and also allows convert checksum to be enabled from inet layer (what we'll want to do when enabling IP_CHECKSUM cmsg). Signed-off-by: Tom Herbert Signed-off-by: David S. Miller diff --git a/include/linux/udp.h b/include/linux/udp.h index ee32775..247cfdc 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -49,11 +49,7 @@ struct udp_sock { unsigned int corkflag; /* Cork is required */ __u8 encap_type; /* Is this an Encapsulation socket? */ unsigned char no_check6_tx:1,/* Send zero UDP6 checksums on TX? */ - no_check6_rx:1,/* Allow zero UDP6 checksums on RX? */ - convert_csum:1;/* On receive, convert checksum - * unnecessary to checksum complete - * if possible. - */ + no_check6_rx:1;/* Allow zero UDP6 checksums on RX? */ /* * Following member retains the information to create a UDP header * when the socket is uncorked. @@ -102,16 +98,6 @@ static inline bool udp_get_no_check6_rx(struct sock *sk) return udp_sk(sk)->no_check6_rx; } -static inline void udp_set_convert_csum(struct sock *sk, bool val) -{ - udp_sk(sk)->convert_csum = val; -} - -static inline bool udp_get_convert_csum(struct sock *sk) -{ - return udp_sk(sk)->convert_csum; -} - #define udp_portaddr_for_each_entry(__sk, node, list) \ hlist_nulls_for_each_entry(__sk, node, list, __sk_common.skc_portaddr_node) diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index a829b77..360b110 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -184,6 +184,7 @@ struct inet_sock { mc_all:1, nodefrag:1; __u8 rcv_tos; + __u8 convert_csum; int uc_index; int mc_index; __be32 mc_addr; @@ -250,4 +251,20 @@ static inline __u8 inet_sk_flowi_flags(const struct sock *sk) return flags; } +static inline void inet_inc_convert_csum(struct sock *sk) +{ + inet_sk(sk)->convert_csum++; +} + +static inline void inet_dec_convert_csum(struct sock *sk) +{ + if (inet_sk(sk)->convert_csum > 0) + inet_sk(sk)->convert_csum--; +} + +static inline bool inet_get_convert_csum(struct sock *sk) +{ + return !!inet_sk(sk)->convert_csum; +} + #endif /* _INET_SOCK_H */ diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c index b986298..2197c36 100644 --- a/net/ipv4/fou.c +++ b/net/ipv4/fou.c @@ -490,7 +490,7 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, sk->sk_user_data = fou; fou->sock = sock; - udp_set_convert_csum(sk, true); + inet_inc_convert_csum(sk); sk->sk_allocation = GFP_ATOMIC; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 13b4dcf..53358d8 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1806,7 +1806,7 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, if (sk != NULL) { int ret; - if (udp_sk(sk)->convert_csum && uh->check && !IS_UDPLITE(sk)) + if (inet_get_convert_csum(sk) && uh->check && !IS_UDPLITE(sk)) skb_checksum_try_convert(skb, IPPROTO_UDP, uh->check, inet_compute_pseudo); diff --git a/net/ipv4/udp_tunnel.c b/net/ipv4/udp_tunnel.c index 1671263..9996e63 100644 --- a/net/ipv4/udp_tunnel.c +++ b/net/ipv4/udp_tunnel.c @@ -63,7 +63,7 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock, inet_sk(sk)->mc_loop = 0; /* Enable CHECKSUM_UNNECESSARY to CHECKSUM_COMPLETE conversion */ - udp_set_convert_csum(sk, true); + inet_inc_convert_csum(sk); rcu_assign_sk_user_data(sk, cfg->sk_user_data); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 189dc4a..e41f017 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -909,7 +909,7 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, goto csum_error; } - if (udp_sk(sk)->convert_csum && uh->check && !IS_UDPLITE(sk)) + if (inet_get_convert_csum(sk) && uh->check && !IS_UDPLITE(sk)) skb_checksum_try_convert(skb, IPPROTO_UDP, uh->check, ip6_compute_pseudo); -- cgit v0.10.2 From c44d13d6f341ca59f3d6646f2337d4d3c8a814a6 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Mon, 5 Jan 2015 13:56:15 -0800 Subject: ip: IP cmsg cleanup Move the IP_CMSG_* constants from ip_sockglue.c to inet_sock.h so that they can be referenced in other source files. Restructure ip_cmsg_recv to not go through flags using shift, check for flags by 'and'. This eliminates both the shift and a conditional per flag check. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 360b110..605ca42 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -16,7 +16,7 @@ #ifndef _INET_SOCK_H #define _INET_SOCK_H - +#include #include #include #include @@ -195,6 +195,15 @@ struct inet_sock { #define IPCORK_OPT 1 /* ip-options has been held in ipcork.opt */ #define IPCORK_ALLFRAG 2 /* always fragment (for ipv6 for now) */ +/* cmsg flags for inet */ +#define IP_CMSG_PKTINFO BIT(0) +#define IP_CMSG_TTL BIT(1) +#define IP_CMSG_TOS BIT(2) +#define IP_CMSG_RECVOPTS BIT(3) +#define IP_CMSG_RETOPTS BIT(4) +#define IP_CMSG_PASSSEC BIT(5) +#define IP_CMSG_ORIGDSTADDR BIT(6) + static inline struct inet_sock *inet_sk(const struct sock *sk) { return (struct inet_sock *)sk; diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 8a89c73..80f7856 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -45,14 +45,6 @@ #include #include -#define IP_CMSG_PKTINFO 1 -#define IP_CMSG_TTL 2 -#define IP_CMSG_TOS 4 -#define IP_CMSG_RECVOPTS 8 -#define IP_CMSG_RETOPTS 16 -#define IP_CMSG_PASSSEC 32 -#define IP_CMSG_ORIGDSTADDR 64 - /* * SOL_IP control messages. */ @@ -150,37 +142,55 @@ void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb) unsigned int flags = inet->cmsg_flags; /* Ordered by supposed usage frequency */ - if (flags & 1) + if (flags & IP_CMSG_PKTINFO) { ip_cmsg_recv_pktinfo(msg, skb); - if ((flags >>= 1) == 0) - return; - if (flags & 1) + flags &= ~IP_CMSG_PKTINFO; + if (!flags) + return; + } + + if (flags & IP_CMSG_TTL) { ip_cmsg_recv_ttl(msg, skb); - if ((flags >>= 1) == 0) - return; - if (flags & 1) + flags &= ~IP_CMSG_TTL; + if (!flags) + return; + } + + if (flags & IP_CMSG_TOS) { ip_cmsg_recv_tos(msg, skb); - if ((flags >>= 1) == 0) - return; - if (flags & 1) + flags &= ~IP_CMSG_TOS; + if (!flags) + return; + } + + if (flags & IP_CMSG_RECVOPTS) { ip_cmsg_recv_opts(msg, skb); - if ((flags >>= 1) == 0) - return; - if (flags & 1) + flags &= ~IP_CMSG_RECVOPTS; + if (!flags) + return; + } + + if (flags & IP_CMSG_RETOPTS) { ip_cmsg_recv_retopts(msg, skb); - if ((flags >>= 1) == 0) - return; - if (flags & 1) + flags &= ~IP_CMSG_RETOPTS; + if (!flags) + return; + } + + if (flags & IP_CMSG_PASSSEC) { ip_cmsg_recv_security(msg, skb); - if ((flags >>= 1) == 0) - return; - if (flags & 1) + flags &= ~IP_CMSG_PASSSEC; + if (!flags) + return; + } + + if (flags & IP_CMSG_ORIGDSTADDR) ip_cmsg_recv_dstaddr(msg, skb); } -- cgit v0.10.2 From 5961de9f199bef6ff437d7d85fe69b6a1964739b Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Mon, 5 Jan 2015 13:56:16 -0800 Subject: ip: Add offset parameter to ip_cmsg_recv Add ip_cmsg_recv_offset function which takes an offset argument that indicates the starting offset in skb where data is being received from. This will be useful in the case of UDP and provided checksum to user space. ip_cmsg_recv is an inline call to ip_cmsg_recv_offset with offset of zero. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller diff --git a/include/net/ip.h b/include/net/ip.h index 0bb6207..0e5a0ba 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -537,7 +537,7 @@ int ip_options_rcv_srr(struct sk_buff *skb); */ void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb); -void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb); +void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, int offset); int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc, bool allow_ipv6); int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval, @@ -557,6 +557,11 @@ void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port, void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 dport, u32 info); +static inline void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb) +{ + ip_cmsg_recv_offset(msg, skb, 0); +} + bool icmp_global_allow(void); extern int sysctl_icmp_msgs_per_sec; extern int sysctl_icmp_msgs_burst; diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 80f7856..513d506 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -136,7 +136,8 @@ static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb) put_cmsg(msg, SOL_IP, IP_ORIGDSTADDR, sizeof(sin), &sin); } -void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb) +void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, + int offset) { struct inet_sock *inet = inet_sk(skb->sk); unsigned int flags = inet->cmsg_flags; @@ -194,7 +195,7 @@ void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb) ip_cmsg_recv_dstaddr(msg, skb); } -EXPORT_SYMBOL(ip_cmsg_recv); +EXPORT_SYMBOL(ip_cmsg_recv_offset); int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc, bool allow_ipv6) -- cgit v0.10.2 From ad6f939ab193750cc94a265f58e007fb598c97b7 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Mon, 5 Jan 2015 13:56:17 -0800 Subject: ip: Add offset parameter to ip_cmsg_recv Add ip_cmsg_recv_offset function which takes an offset argument that indicates the starting offset in skb where data is being received from. This will be useful in the case of UDP and provided checksum to user space. ip_cmsg_recv is an inline call to ip_cmsg_recv_offset with offset of zero. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 605ca42..eb16c7b 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -203,6 +203,7 @@ struct inet_sock { #define IP_CMSG_RETOPTS BIT(4) #define IP_CMSG_PASSSEC BIT(5) #define IP_CMSG_ORIGDSTADDR BIT(6) +#define IP_CMSG_CHECKSUM BIT(7) static inline struct inet_sock *inet_sk(const struct sock *sk) { diff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h index c33a65e..589ced0 100644 --- a/include/uapi/linux/in.h +++ b/include/uapi/linux/in.h @@ -109,6 +109,7 @@ struct in_addr { #define IP_MINTTL 21 #define IP_NODEFRAG 22 +#define IP_CHECKSUM 23 /* IP_MTU_DISCOVER values */ #define IP_PMTUDISC_DONT 0 /* Never send DF frames */ diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 513d506..a317797 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -37,6 +37,7 @@ #include #include #include +#include #if IS_ENABLED(CONFIG_IPV6) #include #endif @@ -96,6 +97,20 @@ static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb) put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data); } +static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb, + int offset) +{ + __wsum csum = skb->csum; + + if (skb->ip_summed != CHECKSUM_COMPLETE) + return; + + if (offset != 0) + csum = csum_sub(csum, csum_partial(skb->data, offset, 0)); + + put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum); +} + static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb) { char *secdata; @@ -191,9 +206,16 @@ void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, return; } - if (flags & IP_CMSG_ORIGDSTADDR) + if (flags & IP_CMSG_ORIGDSTADDR) { ip_cmsg_recv_dstaddr(msg, skb); + flags &= ~IP_CMSG_ORIGDSTADDR; + if (!flags) + return; + } + + if (flags & IP_CMSG_CHECKSUM) + ip_cmsg_recv_checksum(msg, skb, offset); } EXPORT_SYMBOL(ip_cmsg_recv_offset); @@ -533,6 +555,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, case IP_MULTICAST_ALL: case IP_MULTICAST_LOOP: case IP_RECVORIGDSTADDR: + case IP_CHECKSUM: if (optlen >= sizeof(int)) { if (get_user(val, (int __user *) optval)) return -EFAULT; @@ -630,6 +653,19 @@ static int do_ip_setsockopt(struct sock *sk, int level, else inet->cmsg_flags &= ~IP_CMSG_ORIGDSTADDR; break; + case IP_CHECKSUM: + if (val) { + if (!(inet->cmsg_flags & IP_CMSG_CHECKSUM)) { + inet_inc_convert_csum(sk); + inet->cmsg_flags |= IP_CMSG_CHECKSUM; + } + } else { + if (inet->cmsg_flags & IP_CMSG_CHECKSUM) { + inet_dec_convert_csum(sk); + inet->cmsg_flags &= ~IP_CMSG_CHECKSUM; + } + } + break; case IP_TOS: /* This sets both TOS and Precedence */ if (sk->sk_type == SOCK_STREAM) { val &= ~INET_ECN_MASK; @@ -1233,6 +1269,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname, case IP_RECVORIGDSTADDR: val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0; break; + case IP_CHECKSUM: + val = (inet->cmsg_flags & IP_CMSG_CHECKSUM) != 0; + break; case IP_TOS: val = inet->tos; break; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 53358d8..97ef1f8b 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1329,7 +1329,7 @@ try_again: *addr_len = sizeof(*sin); } if (inet->cmsg_flags) - ip_cmsg_recv(msg, skb); + ip_cmsg_recv_offset(msg, skb, sizeof(struct udphdr)); err = copied; if (flags & MSG_TRUNC) -- cgit v0.10.2 From 6cb69742daa1770fe1ce54cf45f8951376518176 Mon Sep 17 00:00:00 2001 From: Hubert Sokolowski Date: Mon, 5 Jan 2015 17:29:21 +0000 Subject: net: Do not call ndo_dflt_fdb_dump if ndo_fdb_dump is defined Add checking whether the call to ndo_dflt_fdb_dump is needed. It is not expected to call ndo_dflt_fdb_dump unconditionally by some drivers (i.e. qlcnic or macvlan) that defines own ndo_fdb_dump. Other drivers define own ndo_fdb_dump and don't want ndo_dflt_fdb_dump to be called at all. At the same time it is desirable to call the default dump function on a bridge device. Fix attributes that are passed to dev->netdev_ops->ndo_fdb_dump. Add extra checking in br_fdb_dump to avoid duplicate entries as now filter_dev can be NULL. Following tests for filtering have been performed before the change and after the patch was applied to make sure they are the same and it doesn't break the filtering algorithm. [root@localhost ~]# cd /root/iproute2-3.18.0/bridge [root@localhost bridge]# modprobe dummy [root@localhost bridge]# ./bridge fdb add f1:f2:f3:f4:f5:f6 dev dummy0 [root@localhost bridge]# brctl addbr br0 [root@localhost bridge]# brctl addif br0 dummy0 [root@localhost bridge]# ip link set dev br0 address 02:00:00:12:01:04 [root@localhost bridge]# # show all [root@localhost bridge]# ./bridge fdb show 33:33:00:00:00:01 dev p2p1 self permanent 01:00:5e:00:00:01 dev p2p1 self permanent 33:33:ff:ac:ce:32 dev p2p1 self permanent 33:33:00:00:02:02 dev p2p1 self permanent 01:00:5e:00:00:fb dev p2p1 self permanent 33:33:00:00:00:01 dev p7p1 self permanent 01:00:5e:00:00:01 dev p7p1 self permanent 33:33:ff:79:50:53 dev p7p1 self permanent 33:33:00:00:02:02 dev p7p1 self permanent 01:00:5e:00:00:fb dev p7p1 self permanent f2:46:50:85:6d:d9 dev dummy0 master br0 permanent f2:46:50:85:6d:d9 dev dummy0 vlan 1 master br0 permanent 33:33:00:00:00:01 dev dummy0 self permanent f1:f2:f3:f4:f5:f6 dev dummy0 self permanent 33:33:00:00:00:01 dev br0 self permanent 02:00:00:12:01:04 dev br0 vlan 1 master br0 permanent 02:00:00:12:01:04 dev br0 master br0 permanent [root@localhost bridge]# # filter by bridge [root@localhost bridge]# ./bridge fdb show br br0 f2:46:50:85:6d:d9 dev dummy0 master br0 permanent f2:46:50:85:6d:d9 dev dummy0 vlan 1 master br0 permanent 33:33:00:00:00:01 dev dummy0 self permanent f1:f2:f3:f4:f5:f6 dev dummy0 self permanent 33:33:00:00:00:01 dev br0 self permanent 02:00:00:12:01:04 dev br0 vlan 1 master br0 permanent 02:00:00:12:01:04 dev br0 master br0 permanent [root@localhost bridge]# # filter by port [root@localhost bridge]# ./bridge fdb show brport dummy0 f2:46:50:85:6d:d9 master br0 permanent f2:46:50:85:6d:d9 vlan 1 master br0 permanent 33:33:00:00:00:01 self permanent f1:f2:f3:f4:f5:f6 self permanent [root@localhost bridge]# # filter by port + bridge [root@localhost bridge]# ./bridge fdb show br br0 brport dummy0 f2:46:50:85:6d:d9 master br0 permanent f2:46:50:85:6d:d9 vlan 1 master br0 permanent 33:33:00:00:00:01 self permanent f1:f2:f3:f4:f5:f6 self permanent [root@localhost bridge]# Signed-off-by: Hubert Sokolowski Signed-off-by: David S. Miller diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index cc36e59..e6e0372 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -686,6 +686,9 @@ int br_fdb_dump(struct sk_buff *skb, if (!(dev->priv_flags & IFF_EBRIDGE)) goto out; + if (!filter_dev) + idx = ndo_dflt_fdb_dump(skb, cb, dev, NULL, idx); + for (i = 0; i < BR_HASH_SIZE; i++) { struct net_bridge_fdb_entry *f; @@ -697,7 +700,7 @@ int br_fdb_dump(struct sk_buff *skb, (!f->dst || f->dst->dev != filter_dev)) { if (filter_dev != dev) goto skip; - /* !f->dst is a speacial case for bridge + /* !f->dst is a special case for bridge * It means the MAC belongs to the bridge * Therefore need a little more filtering * we only want to dump the !f->dst case @@ -705,6 +708,8 @@ int br_fdb_dump(struct sk_buff *skb, if (f->dst) goto skip; } + if (!filter_dev && f->dst) + goto skip; if (fdb_fill_info(skb, br, f, NETLINK_CB(cb->skb).portid, diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 9cf6fe9..da983d4 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2698,10 +2698,11 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb) idx); } - idx = ndo_dflt_fdb_dump(skb, cb, dev, NULL, idx); if (dev->netdev_ops->ndo_fdb_dump) - idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, bdev, dev, + idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, NULL, idx); + else + idx = ndo_dflt_fdb_dump(skb, cb, dev, NULL, idx); cops = NULL; } -- cgit v0.10.2 From 0409c9a5a7546043d32e8c46df0dcaf65f97f659 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 5 Jan 2015 23:57:43 +0100 Subject: net: fib6: fib6_commit_metrics: fix potential NULL pointer dereference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When IPv6 host routes with metrics attached are being added, we fetch the metrics store from the dst via COW through dst_metrics_write_ptr(), added through commit e5fd387ad5b3. One remaining problem here is that we actually call into inet_getpeer() and may end up allocating/creating a new peer from the kmemcache, which may fail. Example trace from perf probe (inet_getpeer:41) where create is 1: ip 6877 [002] 4221.391591: probe:inet_getpeer: (ffffffff8165e293) 85e294 inet_getpeer.part.7 (<- kmem_cache_alloc()) 85e578 inet_getpeer 8eb333 ipv6_cow_metrics 8f10ff fib6_commit_metrics Therefore, a check for NULL on the return of dst_metrics_write_ptr() is necessary here. Joint work with Florian Westphal. Fixes: e5fd387ad5b3 ("ipv6: do not overwrite inetpeer metrics prematurely") Cc: Michal Kubeček Signed-off-by: Florian Westphal Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index b2d1838..db4984e 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -633,18 +633,17 @@ static bool rt6_qualify_for_ecmp(struct rt6_info *rt) static int fib6_commit_metrics(struct dst_entry *dst, struct nlattr *mx, int mx_len) { + bool dst_host = dst->flags & DST_HOST; struct nlattr *nla; int remaining; u32 *mp; - if (dst->flags & DST_HOST) { - mp = dst_metrics_write_ptr(dst); - } else { - mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_ATOMIC); - if (!mp) - return -ENOMEM; + mp = dst_host ? dst_metrics_write_ptr(dst) : + kzalloc(sizeof(u32) * RTAX_MAX, GFP_ATOMIC); + if (unlikely(!mp)) + return -ENOMEM; + if (!dst_host) dst_init_metrics(dst, mp, 0); - } nla_for_each_attr(nla, mx, mx_len, remaining) { int type = nla_type(nla); -- cgit v0.10.2 From e715b6d3a5ef55834778d49224e60e8ccb5bf45f Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 5 Jan 2015 23:57:44 +0100 Subject: net: fib6: convert cfg metric to u32 outside of table write lock Do the nla validation earlier, outside the write lock. This is needed by followup patch which needs to be able to call request_module (which can sleep) if needed. Joint work with Daniel Borkmann. Signed-off-by: Daniel Borkmann Signed-off-by: Florian Westphal Signed-off-by: David S. Miller diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 8eea35d..20e80fa 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -74,6 +74,11 @@ struct fib6_node { #define FIB6_SUBTREE(fn) ((fn)->subtree) #endif +struct mx6_config { + const u32 *mx; + DECLARE_BITMAP(mx_valid, RTAX_MAX); +}; + /* * routing information * @@ -291,9 +296,8 @@ struct fib6_node *fib6_locate(struct fib6_node *root, void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg), void *arg); -int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info, - struct nlattr *mx, int mx_len); - +int fib6_add(struct fib6_node *root, struct rt6_info *rt, + struct nl_info *info, struct mx6_config *mxc); int fib6_del(struct rt6_info *rt, struct nl_info *info); void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info); diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index db4984e..03c520a 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -630,31 +630,35 @@ static bool rt6_qualify_for_ecmp(struct rt6_info *rt) RTF_GATEWAY; } -static int fib6_commit_metrics(struct dst_entry *dst, - struct nlattr *mx, int mx_len) +static void fib6_copy_metrics(u32 *mp, const struct mx6_config *mxc) { - bool dst_host = dst->flags & DST_HOST; - struct nlattr *nla; - int remaining; - u32 *mp; + int i; - mp = dst_host ? dst_metrics_write_ptr(dst) : - kzalloc(sizeof(u32) * RTAX_MAX, GFP_ATOMIC); - if (unlikely(!mp)) - return -ENOMEM; - if (!dst_host) - dst_init_metrics(dst, mp, 0); + for (i = 0; i < RTAX_MAX; i++) { + if (test_bit(i, mxc->mx_valid)) + mp[i] = mxc->mx[i]; + } +} + +static int fib6_commit_metrics(struct dst_entry *dst, struct mx6_config *mxc) +{ + if (!mxc->mx) + return 0; - nla_for_each_attr(nla, mx, mx_len, remaining) { - int type = nla_type(nla); + if (dst->flags & DST_HOST) { + u32 *mp = dst_metrics_write_ptr(dst); - if (type) { - if (type > RTAX_MAX) - return -EINVAL; + if (unlikely(!mp)) + return -ENOMEM; - mp[type - 1] = nla_get_u32(nla); - } + fib6_copy_metrics(mp, mxc); + } else { + dst_init_metrics(dst, mxc->mx, false); + + /* We've stolen mx now. */ + mxc->mx = NULL; } + return 0; } @@ -663,7 +667,7 @@ static int fib6_commit_metrics(struct dst_entry *dst, */ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, - struct nl_info *info, struct nlattr *mx, int mx_len) + struct nl_info *info, struct mx6_config *mxc) { struct rt6_info *iter = NULL; struct rt6_info **ins; @@ -772,11 +776,10 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, pr_warn("NLM_F_CREATE should be set when creating new route\n"); add: - if (mx) { - err = fib6_commit_metrics(&rt->dst, mx, mx_len); - if (err) - return err; - } + err = fib6_commit_metrics(&rt->dst, mxc); + if (err) + return err; + rt->dst.rt6_next = iter; *ins = rt; rt->rt6i_node = fn; @@ -796,11 +799,11 @@ add: pr_warn("NLM_F_REPLACE set, but no existing node found!\n"); return -ENOENT; } - if (mx) { - err = fib6_commit_metrics(&rt->dst, mx, mx_len); - if (err) - return err; - } + + err = fib6_commit_metrics(&rt->dst, mxc); + if (err) + return err; + *ins = rt; rt->rt6i_node = fn; rt->dst.rt6_next = iter->dst.rt6_next; @@ -837,8 +840,8 @@ void fib6_force_start_gc(struct net *net) * with source addr info in sub-trees */ -int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info, - struct nlattr *mx, int mx_len) +int fib6_add(struct fib6_node *root, struct rt6_info *rt, + struct nl_info *info, struct mx6_config *mxc) { struct fib6_node *fn, *pn = NULL; int err = -ENOMEM; @@ -933,7 +936,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info, } #endif - err = fib6_add_rt2node(fn, rt, info, mx, mx_len); + err = fib6_add_rt2node(fn, rt, info, mxc); if (!err) { fib6_start_gc(info->nl_net, rt); if (!(rt->rt6i_flags & RTF_CACHE)) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index c910831..454771d 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -853,14 +853,14 @@ EXPORT_SYMBOL(rt6_lookup); */ static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, - struct nlattr *mx, int mx_len) + struct mx6_config *mxc) { int err; struct fib6_table *table; table = rt->rt6i_table; write_lock_bh(&table->tb6_lock); - err = fib6_add(&table->tb6_root, rt, info, mx, mx_len); + err = fib6_add(&table->tb6_root, rt, info, mxc); write_unlock_bh(&table->tb6_lock); return err; @@ -868,10 +868,10 @@ static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, int ip6_ins_rt(struct rt6_info *rt) { - struct nl_info info = { - .nl_net = dev_net(rt->dst.dev), - }; - return __ip6_ins_rt(rt, &info, NULL, 0); + struct nl_info info = { .nl_net = dev_net(rt->dst.dev), }; + struct mx6_config mxc = { .mx = NULL, }; + + return __ip6_ins_rt(rt, &info, &mxc); } static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, @@ -1470,9 +1470,39 @@ out: return entries > rt_max_size; } -/* - * - */ +static int ip6_convert_metrics(struct mx6_config *mxc, + const struct fib6_config *cfg) +{ + struct nlattr *nla; + int remaining; + u32 *mp; + + if (cfg->fc_mx == NULL) + return 0; + + mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); + if (unlikely(!mp)) + return -ENOMEM; + + nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { + int type = nla_type(nla); + + if (type) { + if (unlikely(type > RTAX_MAX)) + goto err; + + mp[type - 1] = nla_get_u32(nla); + __set_bit(type - 1, mxc->mx_valid); + } + } + + mxc->mx = mp; + + return 0; + err: + kfree(mp); + return -EINVAL; +} int ip6_route_add(struct fib6_config *cfg) { @@ -1482,6 +1512,7 @@ int ip6_route_add(struct fib6_config *cfg) struct net_device *dev = NULL; struct inet6_dev *idev = NULL; struct fib6_table *table; + struct mx6_config mxc = { .mx = NULL, }; int addr_type; if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) @@ -1677,8 +1708,14 @@ install_route: cfg->fc_nlinfo.nl_net = dev_net(dev); - return __ip6_ins_rt(rt, &cfg->fc_nlinfo, cfg->fc_mx, cfg->fc_mx_len); + err = ip6_convert_metrics(&mxc, cfg); + if (err) + goto out; + + err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc); + kfree(mxc.mx); + return err; out: if (dev) dev_put(dev); -- cgit v0.10.2 From 29ba4fffd396f4beefe34d24d7bcd86cb5c3e492 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 5 Jan 2015 23:57:45 +0100 Subject: net: tcp: refactor reinitialization of congestion control We can just move this to an extra function and make the code a bit more readable, no functional change. Joint work with Florian Westphal. Signed-off-by: Florian Westphal Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 27ead0d..38f2f8a 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -107,6 +107,18 @@ void tcp_init_congestion_control(struct sock *sk) icsk->icsk_ca_ops->init(sk); } +static void tcp_reinit_congestion_control(struct sock *sk, + const struct tcp_congestion_ops *ca) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + + tcp_cleanup_congestion_control(sk); + icsk->icsk_ca_ops = ca; + + if (sk->sk_state != TCP_CLOSE && icsk->icsk_ca_ops->init) + icsk->icsk_ca_ops->init(sk); +} + /* Manage refcounts on socket close. */ void tcp_cleanup_congestion_control(struct sock *sk) { @@ -262,21 +274,13 @@ int tcp_set_congestion_control(struct sock *sk, const char *name) #endif if (!ca) err = -ENOENT; - else if (!((ca->flags & TCP_CONG_NON_RESTRICTED) || ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))) err = -EPERM; - else if (!try_module_get(ca->owner)) err = -EBUSY; - - else { - tcp_cleanup_congestion_control(sk); - icsk->icsk_ca_ops = ca; - - if (sk->sk_state != TCP_CLOSE && icsk->icsk_ca_ops->init) - icsk->icsk_ca_ops->init(sk); - } + else + tcp_reinit_congestion_control(sk, ca); out: rcu_read_unlock(); return err; -- cgit v0.10.2 From c5c6a8ab45ec0f18733afb4aaade0d4a139d80b3 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 5 Jan 2015 23:57:46 +0100 Subject: net: tcp: add key management to congestion control This patch adds necessary infrastructure to the congestion control framework for later per route congestion control support. For a per route congestion control possibility, our aim is to store a unique u32 key identifier into dst metrics, which can then be mapped into a tcp_congestion_ops struct. We argue that having a RTAX key entry is the most simple, generic and easy way to manage, and also keeps the memory footprint of dst entries lower on 64 bit than with storing a pointer directly, for example. Having a unique key id also allows for decoupling actual TCP congestion control module management from the FIB layer, i.e. we don't have to care about expensive module refcounting inside the FIB at this point. We first thought of using an IDR store for the realization, which takes over dynamic assignment of unused key space and also performs the key to pointer mapping in RCU. While doing so, we stumbled upon the issue that due to the nature of dynamic key distribution, it just so happens, arguably in very rare occasions, that excessive module loads and unloads can lead to a possible reuse of previously used key space. Thus, previously stale keys in the dst metric are now being reassigned to a different congestion control algorithm, which might lead to unexpected behaviour. One way to resolve this would have been to walk FIBs on the actually rare occasion of a module unload and reset the metric keys for each FIB in each netns, but that's just very costly. Therefore, we argue a better solution is to reuse the unique congestion control algorithm name member and map that into u32 key space through jhash. For that, we split the flags attribute (as it currently uses 2 bits only anyway) into two u32 attributes, flags and key, so that we can keep the cacheline boundary of 2 cachelines on x86_64 and cache the precalculated key at registration time for the fast path. On average we might expect 2 - 4 modules being loaded worst case perhaps 15, so a key collision possibility is extremely low, and guaranteed collision-free on LE/BE for all in-tree modules. Overall this results in much simpler code, and all without the overhead of an IDR. Due to the deterministic nature, modules can now be unloaded, the congestion control algorithm for a specific but unloaded key will fall back to the default one, and on module reload time it will switch back to the expected algorithm transparently. Joint work with Florian Westphal. Signed-off-by: Florian Westphal Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index 848e85c..5976bde 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -98,7 +98,8 @@ struct inet_connection_sock { const struct tcp_congestion_ops *icsk_ca_ops; const struct inet_connection_sock_af_ops *icsk_af_ops; unsigned int (*icsk_sync_mss)(struct sock *sk, u32 pmtu); - __u8 icsk_ca_state; + __u8 icsk_ca_state:7, + icsk_ca_dst_locked:1; __u8 icsk_retransmits; __u8 icsk_pending; __u8 icsk_backoff; diff --git a/include/net/tcp.h b/include/net/tcp.h index f50f29faf..135b70c 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -787,6 +787,8 @@ enum tcp_ca_ack_event_flags { #define TCP_CA_MAX 128 #define TCP_CA_BUF_MAX (TCP_CA_NAME_MAX*TCP_CA_MAX) +#define TCP_CA_UNSPEC 0 + /* Algorithm can be set on socket without CAP_NET_ADMIN privileges */ #define TCP_CONG_NON_RESTRICTED 0x1 /* Requires ECN/ECT set on all packets */ @@ -794,7 +796,8 @@ enum tcp_ca_ack_event_flags { struct tcp_congestion_ops { struct list_head list; - unsigned long flags; + u32 key; + u32 flags; /* initialize private data (optional) */ void (*init)(struct sock *sk); @@ -841,6 +844,10 @@ u32 tcp_reno_ssthresh(struct sock *sk); void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked); extern struct tcp_congestion_ops tcp_reno; +struct tcp_congestion_ops *tcp_ca_find_key(u32 key); +u32 tcp_ca_get_key_by_name(const char *name); +char *tcp_ca_get_name_by_key(u32 key, char *buffer); + static inline bool tcp_ca_needs_ecn(const struct sock *sk) { const struct inet_connection_sock *icsk = inet_csk(sk); diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 38f2f8a..63c29db 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -13,6 +13,7 @@ #include #include #include +#include #include static DEFINE_SPINLOCK(tcp_cong_list_lock); @@ -31,6 +32,34 @@ static struct tcp_congestion_ops *tcp_ca_find(const char *name) return NULL; } +/* Must be called with rcu lock held */ +static const struct tcp_congestion_ops *__tcp_ca_find_autoload(const char *name) +{ + const struct tcp_congestion_ops *ca = tcp_ca_find(name); +#ifdef CONFIG_MODULES + if (!ca && capable(CAP_NET_ADMIN)) { + rcu_read_unlock(); + request_module("tcp_%s", name); + rcu_read_lock(); + ca = tcp_ca_find(name); + } +#endif + return ca; +} + +/* Simple linear search, not much in here. */ +struct tcp_congestion_ops *tcp_ca_find_key(u32 key) +{ + struct tcp_congestion_ops *e; + + list_for_each_entry_rcu(e, &tcp_cong_list, list) { + if (e->key == key) + return e; + } + + return NULL; +} + /* * Attach new congestion control algorithm to the list * of available options. @@ -45,9 +74,12 @@ int tcp_register_congestion_control(struct tcp_congestion_ops *ca) return -EINVAL; } + ca->key = jhash(ca->name, sizeof(ca->name), strlen(ca->name)); + spin_lock(&tcp_cong_list_lock); - if (tcp_ca_find(ca->name)) { - pr_notice("%s already registered\n", ca->name); + if (ca->key == TCP_CA_UNSPEC || tcp_ca_find_key(ca->key)) { + pr_notice("%s already registered or non-unique key\n", + ca->name); ret = -EEXIST; } else { list_add_tail_rcu(&ca->list, &tcp_cong_list); @@ -70,9 +102,50 @@ void tcp_unregister_congestion_control(struct tcp_congestion_ops *ca) spin_lock(&tcp_cong_list_lock); list_del_rcu(&ca->list); spin_unlock(&tcp_cong_list_lock); + + /* Wait for outstanding readers to complete before the + * module gets removed entirely. + * + * A try_module_get() should fail by now as our module is + * in "going" state since no refs are held anymore and + * module_exit() handler being called. + */ + synchronize_rcu(); } EXPORT_SYMBOL_GPL(tcp_unregister_congestion_control); +u32 tcp_ca_get_key_by_name(const char *name) +{ + const struct tcp_congestion_ops *ca; + u32 key; + + might_sleep(); + + rcu_read_lock(); + ca = __tcp_ca_find_autoload(name); + key = ca ? ca->key : TCP_CA_UNSPEC; + rcu_read_unlock(); + + return key; +} +EXPORT_SYMBOL_GPL(tcp_ca_get_key_by_name); + +char *tcp_ca_get_name_by_key(u32 key, char *buffer) +{ + const struct tcp_congestion_ops *ca; + char *ret = NULL; + + rcu_read_lock(); + ca = tcp_ca_find_key(key); + if (ca) + ret = strncpy(buffer, ca->name, + TCP_CA_NAME_MAX); + rcu_read_unlock(); + + return ret; +} +EXPORT_SYMBOL_GPL(tcp_ca_get_name_by_key); + /* Assign choice of congestion control. */ void tcp_assign_congestion_control(struct sock *sk) { @@ -253,25 +326,17 @@ out: int tcp_set_congestion_control(struct sock *sk, const char *name) { struct inet_connection_sock *icsk = inet_csk(sk); - struct tcp_congestion_ops *ca; + const struct tcp_congestion_ops *ca; int err = 0; - rcu_read_lock(); - ca = tcp_ca_find(name); + if (icsk->icsk_ca_dst_locked) + return -EPERM; - /* no change asking for existing value */ + rcu_read_lock(); + ca = __tcp_ca_find_autoload(name); + /* No change asking for existing value */ if (ca == icsk->icsk_ca_ops) goto out; - -#ifdef CONFIG_MODULES - /* not found attempt to autoload module */ - if (!ca && capable(CAP_NET_ADMIN)) { - rcu_read_unlock(); - request_module("tcp_%s", name); - rcu_read_lock(); - ca = tcp_ca_find(name); - } -#endif if (!ca) err = -ENOENT; else if (!((ca->flags & TCP_CONG_NON_RESTRICTED) || -- cgit v0.10.2 From ea697639992d96da98016b8934e68a73876a2264 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 5 Jan 2015 23:57:47 +0100 Subject: net: tcp: add RTAX_CC_ALGO fib handling This patch adds the minimum necessary for the RTAX_CC_ALGO congestion control metric to be set up and dumped back to user space. While the internal representation of RTAX_CC_ALGO is handled as a u32 key, we avoided to expose this implementation detail to user space, thus instead, we chose the netlink attribute that is being exchanged between user space to be the actual congestion control algorithm name, similarly as in the setsockopt(2) API in order to allow for maximum flexibility, even for 3rd party modules. It is a bit unfortunate that RTAX_QUICKACK used up a whole RTAX slot as it should have been stored in RTAX_FEATURES instead, we first thought about reusing it for the congestion control key, but it brings more complications and/or confusion than worth it. Joint work with Florian Westphal. Signed-off-by: Florian Westphal Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller diff --git a/include/net/tcp.h b/include/net/tcp.h index 135b70c..95bb237 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -846,7 +846,14 @@ extern struct tcp_congestion_ops tcp_reno; struct tcp_congestion_ops *tcp_ca_find_key(u32 key); u32 tcp_ca_get_key_by_name(const char *name); +#ifdef CONFIG_INET char *tcp_ca_get_name_by_key(u32 key, char *buffer); +#else +static inline char *tcp_ca_get_name_by_key(u32 key, char *buffer) +{ + return NULL; +} +#endif static inline bool tcp_ca_needs_ecn(const struct sock *sk) { diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 9c9b8b4..d81f22d 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -389,6 +389,8 @@ enum { #define RTAX_INITRWND RTAX_INITRWND RTAX_QUICKACK, #define RTAX_QUICKACK RTAX_QUICKACK + RTAX_CC_ALGO, +#define RTAX_CC_ALGO RTAX_CC_ALGO __RTAX_MAX }; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index da983d4..6a6cdad 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -669,9 +670,19 @@ int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics) for (i = 0; i < RTAX_MAX; i++) { if (metrics[i]) { + if (i == RTAX_CC_ALGO - 1) { + char tmp[TCP_CA_NAME_MAX], *name; + + name = tcp_ca_get_name_by_key(metrics[i], tmp); + if (!name) + continue; + if (nla_put_string(skb, i + 1, name)) + goto nla_put_failure; + } else { + if (nla_put_u32(skb, i + 1, metrics[i])) + goto nla_put_failure; + } valid++; - if (nla_put_u32(skb, i+1, metrics[i])) - goto nla_put_failure; } } diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c index d332aef..df48034 100644 --- a/net/decnet/dn_fib.c +++ b/net/decnet/dn_fib.c @@ -298,7 +298,8 @@ struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct nlattr *att int type = nla_type(attr); if (type) { - if (type > RTAX_MAX || nla_len(attr) < 4) + if (type > RTAX_MAX || type == RTAX_CC_ALGO || + nla_len(attr) < 4) goto err_inval; fi->fib_metrics[type-1] = nla_get_u32(attr); diff --git a/net/decnet/dn_table.c b/net/decnet/dn_table.c index 86e3807..3f19fcb 100644 --- a/net/decnet/dn_table.c +++ b/net/decnet/dn_table.c @@ -29,6 +29,7 @@ #include /* RTF_xxx */ #include #include +#include #include #include #include @@ -273,7 +274,8 @@ static inline size_t dn_fib_nlmsg_size(struct dn_fib_info *fi) size_t payload = NLMSG_ALIGN(sizeof(struct rtmsg)) + nla_total_size(4) /* RTA_TABLE */ + nla_total_size(2) /* RTA_DST */ - + nla_total_size(4); /* RTA_PRIORITY */ + + nla_total_size(4) /* RTA_PRIORITY */ + + nla_total_size(TCP_CA_NAME_MAX); /* RTAX_CC_ALGO */ /* space for nested metrics */ payload += nla_total_size((RTAX_MAX * nla_total_size(4))); diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index f99f41b..d2b7b55 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -360,7 +360,8 @@ static inline size_t fib_nlmsg_size(struct fib_info *fi) + nla_total_size(4) /* RTA_TABLE */ + nla_total_size(4) /* RTA_DST */ + nla_total_size(4) /* RTA_PRIORITY */ - + nla_total_size(4); /* RTA_PREFSRC */ + + nla_total_size(4) /* RTA_PREFSRC */ + + nla_total_size(TCP_CA_NAME_MAX); /* RTAX_CC_ALGO */ /* space for nested metrics */ payload += nla_total_size((RTAX_MAX * nla_total_size(4))); @@ -859,7 +860,16 @@ struct fib_info *fib_create_info(struct fib_config *cfg) if (type > RTAX_MAX) goto err_inval; - val = nla_get_u32(nla); + if (type == RTAX_CC_ALGO) { + char tmp[TCP_CA_NAME_MAX]; + + nla_strlcpy(tmp, nla, sizeof(tmp)); + val = tcp_ca_get_key_by_name(tmp); + if (val == TCP_CA_UNSPEC) + goto err_inval; + } else { + val = nla_get_u32(nla); + } if (type == RTAX_ADVMSS && val > 65535 - 40) val = 65535 - 40; if (type == RTAX_MTU && val > 65535 - 15) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 454771d..34dcbb5 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1488,10 +1488,22 @@ static int ip6_convert_metrics(struct mx6_config *mxc, int type = nla_type(nla); if (type) { + u32 val; + if (unlikely(type > RTAX_MAX)) goto err; + if (type == RTAX_CC_ALGO) { + char tmp[TCP_CA_NAME_MAX]; + + nla_strlcpy(tmp, nla, sizeof(tmp)); + val = tcp_ca_get_key_by_name(tmp); + if (val == TCP_CA_UNSPEC) + goto err; + } else { + val = nla_get_u32(nla); + } - mp[type - 1] = nla_get_u32(nla); + mp[type - 1] = val; __set_bit(type - 1, mxc->mx_valid); } } @@ -2571,7 +2583,8 @@ static inline size_t rt6_nlmsg_size(void) + nla_total_size(4) /* RTA_OIF */ + nla_total_size(4) /* RTA_PRIORITY */ + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ - + nla_total_size(sizeof(struct rta_cacheinfo)); + + nla_total_size(sizeof(struct rta_cacheinfo)) + + nla_total_size(TCP_CA_NAME_MAX); /* RTAX_CC_ALGO */ } static int rt6_fill_node(struct net *net, -- cgit v0.10.2 From 81164413ad096bafe8ad1068f3f095a7dd081d8b Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 5 Jan 2015 23:57:48 +0100 Subject: net: tcp: add per route congestion control This work adds the possibility to define a per route/destination congestion control algorithm. Generally, this opens up the possibility for a machine with different links to enforce specific congestion control algorithms with optimal strategies for each of them based on their network characteristics, even transparently for a single application listening on all links. For our specific use case, this additionally facilitates deployment of DCTCP, for example, applications can easily serve internal traffic/dsts in DCTCP and external one with CUBIC. Other scenarios would also allow for utilizing e.g. long living, low priority background flows for certain destinations/routes while still being able for normal traffic to utilize the default congestion control algorithm. We also thought about a per netns setting (where different defaults are possible), but given its actually a link specific property, we argue that a per route/destination setting is the most natural and flexible. The administrator can utilize this through ip-route(8) by appending "congctl [lock] ", where denotes the name of a congestion control algorithm and the optional lock parameter allows to enforce the given algorithm so that applications in user space would not be allowed to overwrite that algorithm for that destination. The dst metric lookups are being done when a dst entry is already available in order to avoid a costly lookup and still before the algorithms are being initialized, thus overhead is very low when the feature is not being used. While the client side would need to drop the current reference on the module, on server side this can actually even be avoided as we just got a flat-copied socket clone. Joint work with Florian Westphal. Suggested-by: Hannes Frederic Sowa Signed-off-by: Florian Westphal Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller diff --git a/include/net/tcp.h b/include/net/tcp.h index 95bb237..b8fdc6b 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -448,6 +448,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb); struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, struct sk_buff *skb); +void tcp_ca_openreq_child(struct sock *sk, const struct dst_entry *dst); struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, struct request_sock *req, struct dst_entry *dst); @@ -636,6 +637,11 @@ static inline u32 tcp_rto_min_us(struct sock *sk) return jiffies_to_usecs(tcp_rto_min(sk)); } +static inline bool tcp_ca_dst_locked(const struct dst_entry *dst) +{ + return dst_metric_locked(dst, RTAX_CC_ALGO); +} + /* Compute the actual receive window we are currently advertising. * Rcv_nxt can be after the window if our peer push more data * than the offered window. diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index a3f72d7..ad3e65b 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1340,6 +1340,8 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, } sk_setup_caps(newsk, dst); + tcp_ca_openreq_child(newsk, dst); + tcp_sync_mss(newsk, dst_mtu(dst)); newtp->advmss = dst_metric_advmss(dst); if (tcp_sk(sk)->rx_opt.user_mss && diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 63d2680..bc9216d 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -399,6 +399,32 @@ static void tcp_ecn_openreq_child(struct tcp_sock *tp, tp->ecn_flags = inet_rsk(req)->ecn_ok ? TCP_ECN_OK : 0; } +void tcp_ca_openreq_child(struct sock *sk, const struct dst_entry *dst) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + u32 ca_key = dst_metric(dst, RTAX_CC_ALGO); + bool ca_got_dst = false; + + if (ca_key != TCP_CA_UNSPEC) { + const struct tcp_congestion_ops *ca; + + rcu_read_lock(); + ca = tcp_ca_find_key(ca_key); + if (likely(ca && try_module_get(ca->owner))) { + icsk->icsk_ca_dst_locked = tcp_ca_dst_locked(dst); + icsk->icsk_ca_ops = ca; + ca_got_dst = true; + } + rcu_read_unlock(); + } + + if (!ca_got_dst && !try_module_get(icsk->icsk_ca_ops->owner)) + tcp_assign_congestion_control(sk); + + tcp_set_ca_state(sk, TCP_CA_Open); +} +EXPORT_SYMBOL_GPL(tcp_ca_openreq_child); + /* This is not only more efficient than what we used to do, it eliminates * a lot of code duplication between IPv4/IPv6 SYN recv processing. -DaveM * @@ -451,10 +477,6 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, newtp->snd_cwnd = TCP_INIT_CWND; newtp->snd_cwnd_cnt = 0; - if (!try_module_get(newicsk->icsk_ca_ops->owner)) - tcp_assign_congestion_control(newsk); - - tcp_set_ca_state(newsk, TCP_CA_Open); tcp_init_xmit_timers(newsk); __skb_queue_head_init(&newtp->out_of_order_queue); newtp->write_seq = newtp->pushed_seq = treq->snt_isn + 1; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 7f18262..dc30cb5 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2939,6 +2939,25 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, } EXPORT_SYMBOL(tcp_make_synack); +static void tcp_ca_dst_init(struct sock *sk, const struct dst_entry *dst) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + const struct tcp_congestion_ops *ca; + u32 ca_key = dst_metric(dst, RTAX_CC_ALGO); + + if (ca_key == TCP_CA_UNSPEC) + return; + + rcu_read_lock(); + ca = tcp_ca_find_key(ca_key); + if (likely(ca && try_module_get(ca->owner))) { + module_put(icsk->icsk_ca_ops->owner); + icsk->icsk_ca_dst_locked = tcp_ca_dst_locked(dst); + icsk->icsk_ca_ops = ca; + } + rcu_read_unlock(); +} + /* Do all connect socket setups that can be done AF independent. */ static void tcp_connect_init(struct sock *sk) { @@ -2964,6 +2983,8 @@ static void tcp_connect_init(struct sock *sk) tcp_mtup_init(sk); tcp_sync_mss(sk, dst_mtu(dst)); + tcp_ca_dst_init(sk, dst); + if (!tp->window_clamp) tp->window_clamp = dst_metric(dst, RTAX_WINDOW); tp->advmss = dst_metric_advmss(dst); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 9c0b54e..5d46832 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1199,6 +1199,8 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen + newnp->opt->opt_flen); + tcp_ca_openreq_child(newsk, dst); + tcp_sync_mss(newsk, dst_mtu(dst)); newtp->advmss = dst_metric_advmss(dst); if (tcp_sk(sk)->rx_opt.user_mss && -- cgit v0.10.2 From d97a552210320d3bec8ee22b8ccdb1d6d189482a Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 5 Jan 2015 16:32:12 -0500 Subject: HID: wacom: use WACOM_*_FIELD macros in wacom_usage_mapping() We introduced nice macros in wacom_wac.c to check whether a field is a pen or a touch one. wacom_usage_mapping() still uses it's own tests, which are not in sync with the wacom_wac tests (.application is not checked). That means that some legitimate fields might be filtered out from the usage mapping, and thus will not be used properly while receiving the events. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 6542029..f01ab3a 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -173,10 +173,8 @@ static void wacom_usage_mapping(struct hid_device *hdev, { struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_features *features = &wacom->wacom_wac.features; - bool finger = (field->logical == HID_DG_FINGER) || - (field->physical == HID_DG_FINGER); - bool pen = (field->logical == HID_DG_STYLUS) || - (field->physical == HID_DG_STYLUS); + bool finger = WACOM_FINGER_FIELD(field); + bool pen = WACOM_PEN_FIELD(field); /* * Requiring Stylus Usage will ignore boot mouse diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index ac7447c..596a6fb5 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -15,7 +15,6 @@ #include "wacom_wac.h" #include "wacom.h" #include -#include /* resolution for penabled devices */ #define WACOM_PL_RES 20 @@ -1514,13 +1513,6 @@ static void wacom_wac_finger_report(struct hid_device *hdev, wacom_wac->shared->touch_down = wacom_wac_finger_count_touches(hdev); } -#define WACOM_PEN_FIELD(f) (((f)->logical == HID_DG_STYLUS) || \ - ((f)->physical == HID_DG_STYLUS) || \ - ((f)->application == HID_DG_PEN)) -#define WACOM_FINGER_FIELD(f) (((f)->logical == HID_DG_FINGER) || \ - ((f)->physical == HID_DG_FINGER) || \ - ((f)->application == HID_DG_TOUCHSCREEN)) - void wacom_wac_usage_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index bfad815..7436f2b 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -10,6 +10,7 @@ #define WACOM_WAC_H #include +#include /* maximum packet length for USB devices */ #define WACOM_PKGLEN_MAX 68 @@ -71,6 +72,13 @@ #define WACOM_QUIRK_MONITOR 0x0008 #define WACOM_QUIRK_BATTERY 0x0010 +#define WACOM_PEN_FIELD(f) (((f)->logical == HID_DG_STYLUS) || \ + ((f)->physical == HID_DG_STYLUS) || \ + ((f)->application == HID_DG_PEN)) +#define WACOM_FINGER_FIELD(f) (((f)->logical == HID_DG_FINGER) || \ + ((f)->physical == HID_DG_FINGER) || \ + ((f)->application == HID_DG_TOUCHSCREEN)) + enum { PENPARTNER = 0, GRAPHIRE, -- cgit v0.10.2 From 61e9e7e40a93cfb4a70180beefbbb5bd0c860aeb Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 5 Jan 2015 16:32:13 -0500 Subject: HID: wacom: add support of the Pen of the Bamboo Pad Bamboo Pads are using the generic processing but their report descriptors differ from the ISDv* line. The pen fields are marked with the .physical as Digitizer_Pen, which makes also sense. Add this field to the checks and enable for free Bamboo Pads. Reported-by: Josep Sanchez Ferreres Signed-off-by: Benjamin Tissoires Reviewed-by: Jason Gerecke Signed-off-by: Jiri Kosina diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 7436f2b..7afd929 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -74,6 +74,7 @@ #define WACOM_PEN_FIELD(f) (((f)->logical == HID_DG_STYLUS) || \ ((f)->physical == HID_DG_STYLUS) || \ + ((f)->physical == HID_DG_PEN) || \ ((f)->application == HID_DG_PEN)) #define WACOM_FINGER_FIELD(f) (((f)->logical == HID_DG_FINGER) || \ ((f)->physical == HID_DG_FINGER) || \ -- cgit v0.10.2 From 6ba8bbe8e283674bd9737bcb31a9c55ba4f8fbee Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 25 Dec 2014 13:25:19 +0100 Subject: ARM: dts: sun6i: ippo-q8h-v5: Fix serial0 alias The Ippo q8h has its serial console connected to the r-uart. Adjust the serial0 alias to match. This fixes the kernel serial console no longer working since 3.19-rc1, because 8250_dw.c now honors dt aliases, causing the serial console to be ttyS5 rather then being ttyS0, as it was in 3.18 and before. Note that adjusting bootargs instead is not an acceptable fix, because console=ttyS0,115200 is used by a lot of bootscripts, etc. and this should continue to work. Signed-off-by: Hans de Goede Signed-off-by: Maxime Ripard diff --git a/arch/arm/boot/dts/sun8i-a23-ippo-q8h-v5.dts b/arch/arm/boot/dts/sun8i-a23-ippo-q8h-v5.dts index 7f2117c..32ad808 100644 --- a/arch/arm/boot/dts/sun8i-a23-ippo-q8h-v5.dts +++ b/arch/arm/boot/dts/sun8i-a23-ippo-q8h-v5.dts @@ -55,6 +55,10 @@ model = "Ippo Q8H Dual Core Tablet (v5)"; compatible = "ippo,q8h-v5", "allwinner,sun8i-a23"; + aliases { + serial0 = &r_uart; + }; + chosen { bootargs = "earlyprintk console=ttyS0,115200"; }; -- cgit v0.10.2 From 6d40530e4789b3e2f14d61ca57ab02bd5395d5c9 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 6 Jan 2015 19:01:26 +0900 Subject: spi: sh-msiof: fix MDR1_FLD_MASK value Since the FLD bit field is bit[3:2], the MDR1_FLD_MASK value should be 0x0000000c. Signed-off-by: Yoshihiro Shimoda Acked-by: Geert Uytterhoeven Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 239be7c..87253ea 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -82,7 +82,7 @@ struct sh_msiof_spi_priv { #define MDR1_SYNCMD_LR 0x30000000 /* L/R mode */ #define MDR1_SYNCAC_SHIFT 25 /* Sync Polarity (1 = Active-low) */ #define MDR1_BITLSB_SHIFT 24 /* MSB/LSB First (1 = LSB first) */ -#define MDR1_FLD_MASK 0x000000c0 /* Frame Sync Signal Interval (0-3) */ +#define MDR1_FLD_MASK 0x0000000c /* Frame Sync Signal Interval (0-3) */ #define MDR1_FLD_SHIFT 2 #define MDR1_XXSTP 0x00000001 /* Transmission/Reception Stop on FIFO */ /* TMDR1 */ -- cgit v0.10.2 From d75bb06b61cb69ee6223d791d3bb230e68623b20 Mon Sep 17 00:00:00 2001 From: Gautam Kumar Shukla Date: Tue, 23 Dec 2014 16:55:19 +0100 Subject: cfg80211: add extensible feature flag attribute With the wiphy::features flag being used up this patch adds a new field wiphy::ext_features. Considering extensibility this new field is declared as a byte array. This extensible flag is exposed to user-space by NL80211_ATTR_EXT_FEATURES. Cc: Avinash Patil Signed-off-by: Gautam (Gautam Kumar) Shukla Signed-off-by: Arend van Spriel Signed-off-by: Johannes Berg diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index bd672ea..f38645f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3016,6 +3016,8 @@ struct wiphy_vendor_command { * @regulatory_flags: wiphy regulatory flags, see * &enum ieee80211_regulatory_flags * @features: features advertised to nl80211, see &enum nl80211_feature_flags. + * @ext_features: extended features advertised to nl80211, see + * &enum nl80211_ext_feature_index. * @bss_priv_size: each BSS struct has private data allocated with it, * this variable determines its size * @max_scan_ssids: maximum number of SSIDs the device can scan for in @@ -3125,6 +3127,7 @@ struct wiphy { u16 max_acl_mac_addrs; u32 flags, regulatory_flags, features; + u8 ext_features[DIV_ROUND_UP(NUM_NL80211_EXT_FEATURES, 8)]; u32 ap_sme_capa; @@ -5052,6 +5055,42 @@ void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev, */ void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy); +/** + * wiphy_ext_feature_set - set the extended feature flag + * + * @wiphy: the wiphy to modify. + * @ftidx: extended feature bit index. + * + * The extended features are flagged in multiple bytes (see + * &struct wiphy.@ext_features) + */ +static inline void wiphy_ext_feature_set(struct wiphy *wiphy, + enum nl80211_ext_feature_index ftidx) +{ + u8 *ft_byte; + + ft_byte = &wiphy->ext_features[ftidx / 8]; + *ft_byte |= BIT(ftidx % 8); +} + +/** + * wiphy_ext_feature_isset - check the extended feature flag + * + * @wiphy: the wiphy to modify. + * @ftidx: extended feature bit index. + * + * The extended features are flagged in multiple bytes (see + * &struct wiphy.@ext_features) + */ +static inline bool +wiphy_ext_feature_isset(struct wiphy *wiphy, + enum nl80211_ext_feature_index ftidx) +{ + u8 ft_byte; + + ft_byte = wiphy->ext_features[ftidx / 8]; + return (ft_byte & BIT(ftidx % 8)) != 0; +} /* ethtool helper */ void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info); diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 54f3911..f95d354 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1713,6 +1713,13 @@ enum nl80211_commands { * obtained from it is coming from the device's wiphy and not the global * cfg80211 regdomain. * + * @NL80211_ATTR_EXT_FEATURES: extended feature flags contained in a byte + * array. The feature flags are identified by their bit index (see &enum + * nl80211_ext_feature_index). The bit index is ordered starting at the + * least-significant bit of the first byte in the array, ie. bit index 0 + * is located at bit 0 of byte 0. bit index 25 would be located at bit 1 + * of byte 3 (u8 array). + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2072,6 +2079,8 @@ enum nl80211_attrs { NL80211_ATTR_WIPHY_SELF_MANAGED_REG, + NL80211_ATTR_EXT_FEATURES, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -4224,6 +4233,19 @@ enum nl80211_feature_flags { }; /** + * enum nl80211_ext_feature_index - bit index of extended features. + * + * @NUM_NL80211_EXT_FEATURES: number of extended features. + * @MAX_NL80211_EXT_FEATURES: highest extended feature index. + */ +enum nl80211_ext_feature_index { + + /* add new features before the definition below */ + NUM_NL80211_EXT_FEATURES, + MAX_NL80211_EXT_FEATURES = NUM_NL80211_EXT_FEATURES - 1 +}; + +/** * enum nl80211_probe_resp_offload_support_attr - optional supported * protocols for probe-response offloading by the driver/FW. * To be used with the %NL80211_ATTR_PROBE_RESP_OFFLOAD attribute. diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7029201..689e1a8 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1706,6 +1706,11 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG)) goto nla_put_failure; + if (nla_put(msg, NL80211_ATTR_EXT_FEATURES, + sizeof(rdev->wiphy.ext_features), + rdev->wiphy.ext_features)) + goto nla_put_failure; + /* done */ state->split_start = 0; break; -- cgit v0.10.2 From 71b836eca7f380fbd4c025f8c4371f9a071bc909 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 23 Dec 2014 17:17:38 +0100 Subject: nl80211: define multicast group names in header Put the group names into the userspace API header file so that userspace clients can use symbolic names from there instead of hardcoding the actual names. This doesn't really change much, but seems somewhat cleaner. Signed-off-by: Johannes Berg diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index f95d354..7ba9404 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -29,6 +29,13 @@ #define NL80211_GENL_NAME "nl80211" +#define NL80211_MULTICAST_GROUP_CONFIG "config" +#define NL80211_MULTICAST_GROUP_SCAN "scan" +#define NL80211_MULTICAST_GROUP_REG "regulatory" +#define NL80211_MULTICAST_GROUP_MLME "mlme" +#define NL80211_MULTICAST_GROUP_VENDOR "vendor" +#define NL80211_MULTICAST_GROUP_TESTMODE "testmode" + /** * DOC: Station handling * diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 689e1a8..049f505 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -59,13 +59,13 @@ enum nl80211_multicast_groups { }; static const struct genl_multicast_group nl80211_mcgrps[] = { - [NL80211_MCGRP_CONFIG] = { .name = "config", }, - [NL80211_MCGRP_SCAN] = { .name = "scan", }, - [NL80211_MCGRP_REGULATORY] = { .name = "regulatory", }, - [NL80211_MCGRP_MLME] = { .name = "mlme", }, - [NL80211_MCGRP_VENDOR] = { .name = "vendor", }, + [NL80211_MCGRP_CONFIG] = { .name = NL80211_MULTICAST_GROUP_CONFIG }, + [NL80211_MCGRP_SCAN] = { .name = NL80211_MULTICAST_GROUP_SCAN }, + [NL80211_MCGRP_REGULATORY] = { .name = NL80211_MULTICAST_GROUP_REG }, + [NL80211_MCGRP_MLME] = { .name = NL80211_MULTICAST_GROUP_MLME }, + [NL80211_MCGRP_VENDOR] = { .name = NL80211_MULTICAST_GROUP_VENDOR }, #ifdef CONFIG_NL80211_TESTMODE - [NL80211_MCGRP_TESTMODE] = { .name = "testmode", } + [NL80211_MCGRP_TESTMODE] = { .name = NL80211_MULTICAST_GROUP_TESTMODE } #endif }; -- cgit v0.10.2 From 1540035da71ea0feecdf24eeb98471ffa08da0d3 Mon Sep 17 00:00:00 2001 From: Suman Tripathi Date: Tue, 6 Jan 2015 15:32:15 +0530 Subject: ahci_xgene: Implement the xgene_ahci_poll_reg_val to support PMP. This patch implements the function xgene_ahci_poll_reg_val to poll PxCI for multiple IDENTIFY DEVICE commands to finish before restarting the DMA engine. Signed-off-by: Suman Tripathi Signed-off-by: Tejun Heo diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c index cbcd208..afa9c03 100644 --- a/drivers/ata/ahci_xgene.c +++ b/drivers/ata/ahci_xgene.c @@ -105,17 +105,69 @@ static int xgene_ahci_init_memram(struct xgene_ahci_context *ctx) } /** + * xgene_ahci_poll_reg_val- Poll a register on a specific value. + * @ap : ATA port of interest. + * @reg : Register of interest. + * @val : Value to be attained. + * @interval : waiting interval for polling. + * @timeout : timeout for achieving the value. + */ +static int xgene_ahci_poll_reg_val(struct ata_port *ap, + void __iomem *reg, unsigned + int val, unsigned long interval, + unsigned long timeout) +{ + unsigned long deadline; + unsigned int tmp; + + tmp = ioread32(reg); + deadline = ata_deadline(jiffies, timeout); + + while (tmp != val && time_before(jiffies, deadline)) { + ata_msleep(ap, interval); + tmp = ioread32(reg); + } + + return tmp; +} + +/** * xgene_ahci_restart_engine - Restart the dma engine. * @ap : ATA port of interest * - * Restarts the dma engine inside the controller. + * Waits for completion of multiple commands and restarts + * the DMA engine inside the controller. */ static int xgene_ahci_restart_engine(struct ata_port *ap) { struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 fbs; + + /* + * In case of PMP multiple IDENTIFY DEVICE commands can be + * issued inside PxCI. So need to poll PxCI for the + * completion of outstanding IDENTIFY DEVICE commands before + * we restart the DMA engine. + */ + if (xgene_ahci_poll_reg_val(ap, port_mmio + + PORT_CMD_ISSUE, 0x0, 1, 100)) + return -EBUSY; ahci_stop_engine(ap); ahci_start_fis_rx(ap); + + /* + * Enable the PxFBS.FBS_EN bit as it + * gets cleared due to stopping the engine. + */ + if (pp->fbs_supported) { + fbs = readl(port_mmio + PORT_FBS); + writel(fbs | PORT_FBS_EN, port_mmio + PORT_FBS); + fbs = readl(port_mmio + PORT_FBS); + } + hpriv->start_engine(ap); return 0; @@ -374,7 +426,7 @@ static struct ata_port_operations xgene_ahci_ops = { }; static const struct ata_port_info xgene_ahci_port_info = { - .flags = AHCI_FLAG_COMMON, + .flags = AHCI_FLAG_COMMON | ATA_FLAG_PMP, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &xgene_ahci_ops, -- cgit v0.10.2 From a3a84bc7c885eee954f1971c43e36a3587fbf565 Mon Sep 17 00:00:00 2001 From: Suman Tripathi Date: Tue, 6 Jan 2015 15:32:16 +0530 Subject: ahci_xgene: Implement the workaround to support PMP enumeration and discovery. Due to H/W errata, the controller is unable to save the PMP field fetched from command header before sending the H2D FIS. When the device returns the PMP port field in the D2H FIS, there is a mismatch and results in command completion failure. The workaround is to write the pmp value to PxFBS.DEV field before issuing any command to PMP. Signed-off-by: Suman Tripathi Signed-off-by: Tejun Heo diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c index afa9c03..7f68875 100644 --- a/drivers/ata/ahci_xgene.c +++ b/drivers/ata/ahci_xgene.c @@ -85,6 +85,7 @@ struct xgene_ahci_context { struct ahci_host_priv *hpriv; struct device *dev; u8 last_cmd[MAX_AHCI_CHN_PERCTR]; /* tracking the last command issued*/ + u32 class[MAX_AHCI_CHN_PERCTR]; /* tracking the class of device */ void __iomem *csr_core; /* Core CSR address of IP */ void __iomem *csr_diag; /* Diag CSR address of IP */ void __iomem *csr_axi; /* AXI CSR address of IP */ @@ -177,11 +178,17 @@ static int xgene_ahci_restart_engine(struct ata_port *ap) * xgene_ahci_qc_issue - Issue commands to the device * @qc: Command to issue * - * Due to Hardware errata for IDENTIFY DEVICE command and PACKET - * command of ATAPI protocol set, the controller cannot clear the BSY bit - * after receiving the PIO setup FIS. This results in the DMA state machine - * going into the CMFatalErrorUpdate state and locks up. By restarting the - * DMA engine, it removes the controller out of lock up state. + * Due to Hardware errata for IDENTIFY DEVICE command, the controller cannot + * clear the BSY bit after receiving the PIO setup FIS. This results in the dma + * state machine goes into the CMFatalErrorUpdate state and locks up. By + * restarting the dma engine, it removes the controller out of lock up state. + * + * Due to H/W errata, the controller is unable to save the PMP + * field fetched from command header before sending the H2D FIS. + * When the device returns the PMP port field in the D2H FIS, there is + * a mismatch and results in command completion failure. The + * workaround is to write the pmp value to PxFBS.DEV field before issuing + * any command to PMP. */ static unsigned int xgene_ahci_qc_issue(struct ata_queued_cmd *qc) { @@ -189,6 +196,19 @@ static unsigned int xgene_ahci_qc_issue(struct ata_queued_cmd *qc) struct ahci_host_priv *hpriv = ap->host->private_data; struct xgene_ahci_context *ctx = hpriv->plat_data; int rc = 0; + u32 port_fbs; + void *port_mmio = ahci_port_base(ap); + + /* + * Write the pmp value to PxFBS.DEV + * for case of Port Mulitplier. + */ + if (ctx->class[ap->port_no] == ATA_DEV_PMP) { + port_fbs = readl(port_mmio + PORT_FBS); + port_fbs &= ~PORT_FBS_DEV_MASK; + port_fbs |= qc->dev->link->pmp << PORT_FBS_DEV_OFFSET; + writel(port_fbs, port_mmio + PORT_FBS); + } if (unlikely((ctx->last_cmd[ap->port_no] == ATA_CMD_ID_ATA) || (ctx->last_cmd[ap->port_no] == ATA_CMD_PACKET))) @@ -417,12 +437,115 @@ static void xgene_ahci_host_stop(struct ata_host *host) ahci_platform_disable_resources(hpriv); } +/** + * xgene_ahci_pmp_softreset - Issue the softreset to the drives connected + * to Port Multiplier. + * @link: link to reset + * @class: Return value to indicate class of device + * @deadline: deadline jiffies for the operation + * + * Due to H/W errata, the controller is unable to save the PMP + * field fetched from command header before sending the H2D FIS. + * When the device returns the PMP port field in the D2H FIS, there is + * a mismatch and results in command completion failure. The workaround + * is to write the pmp value to PxFBS.DEV field before issuing any command + * to PMP. + */ +static int xgene_ahci_pmp_softreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + int pmp = sata_srst_pmp(link); + struct ata_port *ap = link->ap; + u32 rc; + void *port_mmio = ahci_port_base(ap); + u32 port_fbs; + + /* + * Set PxFBS.DEV field with pmp + * value. + */ + port_fbs = readl(port_mmio + PORT_FBS); + port_fbs &= ~PORT_FBS_DEV_MASK; + port_fbs |= pmp << PORT_FBS_DEV_OFFSET; + writel(port_fbs, port_mmio + PORT_FBS); + + rc = ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready); + + return rc; +} + +/** + * xgene_ahci_softreset - Issue the softreset to the drive. + * @link: link to reset + * @class: Return value to indicate class of device + * @deadline: deadline jiffies for the operation + * + * Due to H/W errata, the controller is unable to save the PMP + * field fetched from command header before sending the H2D FIS. + * When the device returns the PMP port field in the D2H FIS, there is + * a mismatch and results in command completion failure. The workaround + * is to write the pmp value to PxFBS.DEV field before issuing any command + * to PMP. Here is the algorithm to detect PMP : + * + * 1. Save the PxFBS value + * 2. Program PxFBS.DEV with pmp value send by framework. Framework sends + * 0xF for both PMP/NON-PMP initially + * 3. Issue softreset + * 4. If signature class is PMP goto 6 + * 5. restore the original PxFBS and goto 3 + * 6. return + */ +static int xgene_ahci_softreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + int pmp = sata_srst_pmp(link); + struct ata_port *ap = link->ap; + struct ahci_host_priv *hpriv = ap->host->private_data; + struct xgene_ahci_context *ctx = hpriv->plat_data; + void *port_mmio = ahci_port_base(ap); + u32 port_fbs; + u32 port_fbs_save; + u32 retry = 1; + u32 rc; + + port_fbs_save = readl(port_mmio + PORT_FBS); + + /* + * Set PxFBS.DEV field with pmp + * value. + */ + port_fbs = readl(port_mmio + PORT_FBS); + port_fbs &= ~PORT_FBS_DEV_MASK; + port_fbs |= pmp << PORT_FBS_DEV_OFFSET; + writel(port_fbs, port_mmio + PORT_FBS); + +softreset_retry: + rc = ahci_do_softreset(link, class, pmp, + deadline, ahci_check_ready); + + ctx->class[ap->port_no] = *class; + if (*class != ATA_DEV_PMP) { + /* + * Retry for normal drives without + * setting PxFBS.DEV field with pmp value. + */ + if (retry--) { + writel(port_fbs_save, port_mmio + PORT_FBS); + goto softreset_retry; + } + } + + return rc; +} + static struct ata_port_operations xgene_ahci_ops = { .inherits = &ahci_ops, .host_stop = xgene_ahci_host_stop, .hardreset = xgene_ahci_hardreset, .read_id = xgene_ahci_read_id, .qc_issue = xgene_ahci_qc_issue, + .softreset = xgene_ahci_softreset, + .pmp_softreset = xgene_ahci_pmp_softreset }; static const struct ata_port_info xgene_ahci_port_info = { -- cgit v0.10.2 From fa44b988d2da58334ecbcf3e00f9acccbdeace6c Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 1 Jan 2015 13:43:17 +0200 Subject: mac80211: skip disabled channels in VHT check The patch "40a11ca mac80211: check if channels allow 80 MHz for VHT probe requests" considered disabled channels as VHT enabled, and mistakenly sent out probe-requests with the VHT IE. Signed-off-by: Arik Nemtsov Signed-off-by: Johannes Berg diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 0f9bf47..ad8cb4f 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1470,10 +1470,12 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local, /* Check if any channel in this sband supports at least 80 MHz */ for (i = 0; i < sband->n_channels; i++) { - if (!(sband->channels[i].flags & IEEE80211_CHAN_NO_80MHZ)) { - have_80mhz = true; - break; - } + if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED | + IEEE80211_CHAN_NO_80MHZ)) + continue; + + have_80mhz = true; + break; } if (sband->vht_cap.vht_supported && have_80mhz) { -- cgit v0.10.2 From 8b324fa6915bf4ad7d28e58a5a004f16e470e58d Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 1 Jan 2015 00:13:39 -0800 Subject: Bluetooth: btmrvl: fix race issue while stopping main thread btmrvl_remove_card() calls kthread_stop() to stop the main thread, but kthread_should_stop() is checked when all the activities are done in the main thread before sleeping. We will have kthread_should_stop() check as soon as main thread is woken up. This fixes a crash issue caused due to an invalid memory access while unnecessarily processing interrupts after card removal. Signed-off-by: Amitkumar Karwar Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index 30939c9..e3f5711 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -616,6 +616,11 @@ static int btmrvl_service_main_thread(void *data) BT_DBG("main_thread woke up"); + if (kthread_should_stop()) { + BT_DBG("main_thread: break from main thread"); + break; + } + spin_lock_irqsave(&priv->driver_lock, flags); if (adapter->int_count) { adapter->int_count = 0; -- cgit v0.10.2 From 9b89fdfee47de2d5d904a36cb85304e1ba80934d Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 1 Jan 2015 00:13:40 -0800 Subject: Bluetooth: btmrvl: error path handling in setup handler If module init command fails, FW might not be in good state. We will return from setup handler and skip downloading further commands. Signed-off-by: Amitkumar Karwar Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index e3f5711..c435b58 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -538,8 +538,11 @@ static int btmrvl_check_device_tree(struct btmrvl_private *priv) static int btmrvl_setup(struct hci_dev *hdev) { struct btmrvl_private *priv = hci_get_drvdata(hdev); + int ret; - btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); + ret = btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); + if (ret) + return ret; priv->btmrvl_dev.gpio_gap = 0xffff; -- cgit v0.10.2 From 7b4b8740c6fc153fbb24b3c1ac391cf8908ad113 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 1 Jan 2015 00:13:41 -0800 Subject: Bluetooth: btmrvl: add surprise_removed flag This flag will be set in unload path to make sure that we skip sending further commands, ignore interrupts and stop main thread when unload starts. Signed-off-by: Amitkumar Karwar Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h index 330f8f8..5513204 100644 --- a/drivers/bluetooth/btmrvl_drv.h +++ b/drivers/bluetooth/btmrvl_drv.h @@ -104,6 +104,7 @@ struct btmrvl_private { #ifdef CONFIG_DEBUG_FS void *debugfs_data; #endif + bool surprise_removed; }; #define MRVL_VENDOR_PKT 0xFE diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index c435b58..c4a542c 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -178,6 +178,11 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode, struct sk_buff *skb; struct hci_command_hdr *hdr; + if (priv->surprise_removed) { + BT_ERR("Card is removed"); + return -EFAULT; + } + skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_ATOMIC); if (skb == NULL) { BT_ERR("No free skb"); @@ -600,7 +605,7 @@ static int btmrvl_service_main_thread(void *data) add_wait_queue(&thread->wait_q, &wait); set_current_state(TASK_INTERRUPTIBLE); - if (kthread_should_stop()) { + if (kthread_should_stop() || priv->surprise_removed) { BT_DBG("main_thread: break from main thread"); break; } @@ -619,7 +624,7 @@ static int btmrvl_service_main_thread(void *data) BT_DBG("main_thread woke up"); - if (kthread_should_stop()) { + if (kthread_should_stop() || priv->surprise_removed) { BT_DBG("main_thread: break from main thread"); break; } diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 0057c0b..80ec200 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -798,6 +798,9 @@ static void btmrvl_sdio_interrupt(struct sdio_func *func) priv = card->priv; + if (priv->surprise_removed) + return; + if (card->reg->int_read_to_clear) ret = btmrvl_sdio_read_to_clear(card, &ireg); else @@ -1466,6 +1469,7 @@ static void btmrvl_sdio_remove(struct sdio_func *func) btmrvl_sdio_disable_host_int(card); } BT_DBG("unregester dev"); + card->priv->surprise_removed = true; btmrvl_sdio_unregister_dev(card); btmrvl_remove_card(card->priv); } -- cgit v0.10.2 From 1c6098eb2b95a2af8b8ebaaf61d808b0071b639f Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 1 Jan 2015 00:13:42 -0800 Subject: bluetooth: btmrvl: increase the priority of firmware download message When driver is loaded, it is important to know if FW was already active or it is freshly downloaded. This patch increases the priority of the message. Signed-off-by: Amitkumar Karwar Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 80ec200..01d6da5 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -573,7 +573,7 @@ static int btmrvl_sdio_download_fw_w_helper(struct btmrvl_sdio_card *card) offset += txlen; } while (true); - BT_DBG("FW download over, size %d bytes", offset); + BT_INFO("FW download over, size %d bytes", offset); ret = 0; -- cgit v0.10.2 From 6810e4a394f9d781050107529b8d1465c00b7b13 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 6 Jan 2015 10:26:10 -0500 Subject: percpu_ref: remove unnecessary ACCESS_ONCE() in percpu_ref_tryget_live() __ref_is_percpu() needs the implied ACCESS_ONCE() in lockless_dereference() on @ref->percpu_count_ptr because the value is tested for !__PERCPU_REF_ATOMIC, which may be set asynchronously, and then used as a pointer. If the compiler generates a separate fetch when using it as a pointer, __PERCPU_REF_ATOMIC may be set in between contaminating the pointer value. percpu_ref_tryget_live() also uses ACCESS_ONCE() to test __PERCPU_REF_DEAD; however, there's no reason for this. I just copied ACCESS_ONCE() usage blindly from __ref_is_percpu(). All it does is confusing people trying to understand what's going on. This patch removes the unnecessary ACCESS_ONCE() usage from percpu_ref_tryget_live() and adds a comment explaining why __ref_is_percpu() needs it. Signed-off-by: Tejun Heo Cc: Kent Overstreet diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h index b433764..6a7a670 100644 --- a/include/linux/percpu-refcount.h +++ b/include/linux/percpu-refcount.h @@ -128,8 +128,22 @@ static inline void percpu_ref_kill(struct percpu_ref *ref) static inline bool __ref_is_percpu(struct percpu_ref *ref, unsigned long __percpu **percpu_countp) { - /* paired with smp_store_release() in percpu_ref_reinit() */ - unsigned long percpu_ptr = lockless_dereference(ref->percpu_count_ptr); + unsigned long percpu_ptr; + + /* + * The value of @ref->percpu_count_ptr is tested for + * !__PERCPU_REF_ATOMIC, which may be set asynchronously, and then + * used as a pointer. If the compiler generates a separate fetch + * when using it as a pointer, __PERCPU_REF_ATOMIC may be set in + * between contaminating the pointer value, meaning that + * ACCESS_ONCE() is required when fetching it. + * + * Also, we need a data dependency barrier to be paired with + * smp_store_release() in __percpu_ref_switch_to_percpu(). + * + * Use lockless deref which contains both. + */ + percpu_ptr = lockless_dereference(ref->percpu_count_ptr); /* * Theoretically, the following could test just ATOMIC; however, @@ -233,7 +247,7 @@ static inline bool percpu_ref_tryget_live(struct percpu_ref *ref) if (__ref_is_percpu(ref, &percpu_count)) { this_cpu_inc(*percpu_count); ret = true; - } else if (!(ACCESS_ONCE(ref->percpu_count_ptr) & __PERCPU_REF_DEAD)) { + } else if (!(ref->percpu_count_ptr & __PERCPU_REF_DEAD)) { ret = atomic_long_inc_not_zero(&ref->count); } -- cgit v0.10.2 From 4c907baf36d8339f393bb576d0bab29194d0e6ad Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 6 Jan 2015 10:26:10 -0500 Subject: percpu_ref: implement percpu_ref_is_dying() Implement percpu_ref_is_dying() which tests whether the ref is dying or dead. This is useful to determine the current state when a percpu_ref is used as a cyclic on/off switch via kill and reinit. Signed-off-by: Tejun Heo Cc: Kent Overstreet diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h index 6a7a670..12c9b48 100644 --- a/include/linux/percpu-refcount.h +++ b/include/linux/percpu-refcount.h @@ -295,6 +295,20 @@ static inline void percpu_ref_put(struct percpu_ref *ref) } /** + * percpu_ref_is_dying - test whether a percpu refcount is dying or dead + * @ref: percpu_ref to test + * + * Returns %true if @ref is dying or dead. + * + * This function is safe to call as long as @ref is between init and exit + * and the caller is responsible for synchronizing against state changes. + */ +static inline bool percpu_ref_is_dying(struct percpu_ref *ref) +{ + return ref->percpu_count_ptr & __PERCPU_REF_DEAD; +} + +/** * percpu_ref_is_zero - test whether a percpu refcount reached zero * @ref: percpu_ref to test * -- cgit v0.10.2 From 24dab7a7b3534ef40ecec20cfd7fb3ad99d9ff33 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 6 Jan 2015 12:02:46 -0500 Subject: cgroup: reorder SUBSYS(blkio) in cgroup_subsys.h The scheduled cgroup writeback support requires blkio to be initialized before memcg as memcg needs to provide certain blkcg related functionalities. Relocate blkio so that it's right above memory. Signed-off-by: Tejun Heo diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h index 98c4f9b..e4a96fb 100644 --- a/include/linux/cgroup_subsys.h +++ b/include/linux/cgroup_subsys.h @@ -15,6 +15,10 @@ SUBSYS(cpu) SUBSYS(cpuacct) #endif +#if IS_ENABLED(CONFIG_BLK_CGROUP) +SUBSYS(blkio) +#endif + #if IS_ENABLED(CONFIG_MEMCG) SUBSYS(memory) #endif @@ -31,10 +35,6 @@ SUBSYS(freezer) SUBSYS(net_cls) #endif -#if IS_ENABLED(CONFIG_BLK_CGROUP) -SUBSYS(blkio) -#endif - #if IS_ENABLED(CONFIG_CGROUP_PERF) SUBSYS(perf_event) #endif -- cgit v0.10.2 From f3ba53802eff25e3eedb60d7afe5262710e20bd5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 6 Jan 2015 12:02:46 -0500 Subject: cgroup: add dummy css_put() for !CONFIG_CGROUPS This will later be depended upon by the scheduled cgroup writeback support. Signed-off-by: Tejun Heo diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index da0dae0..b9cb94c 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -943,6 +943,8 @@ struct cgroup_subsys_state *css_tryget_online_from_dir(struct dentry *dentry, #else /* !CONFIG_CGROUPS */ +struct cgroup_subsys_state; + static inline int cgroup_init_early(void) { return 0; } static inline int cgroup_init(void) { return 0; } static inline void cgroup_fork(struct task_struct *p) {} @@ -955,6 +957,8 @@ static inline int cgroupstats_build(struct cgroupstats *stats, return -EINVAL; } +static inline void css_put(struct cgroup_subsys_state *css) {} + /* No cgroups - nothing to do */ static inline int cgroup_attach_task_all(struct task_struct *from, struct task_struct *t) -- cgit v0.10.2 From 2b5e368e4195749e50188dca4394ac8d25ff39f2 Mon Sep 17 00:00:00 2001 From: Laurentiu Palcu Date: Tue, 6 Jan 2015 15:23:40 +0200 Subject: spi/dln2: simplify return flow for dln2_spi_transfer_setup and dln2_spi_enable This fixes the following kbuild test robot warnings: >> drivers/spi/spi-dln2.c:124:1-4: WARNING: end returns can be simplified if negative or 0 value >> drivers/spi/spi-dln2.c:656:1-4: WARNING: end returns can be simplified if negative or 0 value Additionally, fix a comment after switching from CONFIG_PM_RUNTIME to CONFIG_PM. Reported-by: Julia Lawall Signed-off-by: Laurentiu Palcu Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-dln2.c b/drivers/spi/spi-dln2.c index cede063..3b7d91d 100644 --- a/drivers/spi/spi-dln2.c +++ b/drivers/spi/spi-dln2.c @@ -103,7 +103,6 @@ struct dln2_spi { */ static int dln2_spi_enable(struct dln2_spi *dln2, bool enable) { - int ret; u16 cmd; struct { u8 port; @@ -121,11 +120,7 @@ static int dln2_spi_enable(struct dln2_spi *dln2, bool enable) cmd = DLN2_SPI_DISABLE; } - ret = dln2_transfer_tx(dln2->pdev, cmd, &tx, len); - if (ret < 0) - return ret; - - return 0; + return dln2_transfer_tx(dln2->pdev, cmd, &tx, len); } /* @@ -653,11 +648,7 @@ static int dln2_spi_transfer_setup(struct dln2_spi *dln2, u32 speed, dln2->bpw = bpw; } - ret = dln2_spi_enable(dln2, true); - if (ret < 0) - return ret; - - return 0; + return dln2_spi_enable(dln2, true); } static int dln2_spi_transfer_one(struct spi_master *master, @@ -866,7 +857,7 @@ static int dln2_spi_runtime_resume(struct device *dev) return dln2_spi_enable(dln2, true); } -#endif /* CONFIG_PM_RUNTIME */ +#endif /* CONFIG_PM */ static const struct dev_pm_ops dln2_spi_pm = { SET_SYSTEM_SLEEP_PM_OPS(dln2_spi_suspend, dln2_spi_resume) -- cgit v0.10.2 From 38455d7ac2e383d747ddee0189c7d8c078b29b09 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Tue, 6 Jan 2015 14:07:34 +0100 Subject: spi: fsl-(e)spi: Support compile as module Signed-off-by: Esben Haabendal Signed-off-by: Mark Brown diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 9982998..468d13c 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -279,7 +279,7 @@ config SPI_FSL_CPM depends on FSL_SOC config SPI_FSL_SPI - bool "Freescale SPI controller and Aeroflex Gaisler GRLIB SPI controller" + tristate "Freescale SPI controller and Aeroflex Gaisler GRLIB SPI controller" depends on OF select SPI_FSL_LIB select SPI_FSL_CPM if FSL_SOC @@ -300,7 +300,7 @@ config SPI_FSL_DSPI mode. VF610 platform uses the controller. config SPI_FSL_ESPI - bool "Freescale eSPI controller" + tristate "Freescale eSPI controller" depends on FSL_SOC select SPI_FSL_LIB help diff --git a/drivers/spi/spi-fsl-cpm.c b/drivers/spi/spi-fsl-cpm.c index e85ab1c..9c46a30 100644 --- a/drivers/spi/spi-fsl-cpm.c +++ b/drivers/spi/spi-fsl-cpm.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,7 @@ void fsl_spi_cpm_reinit_txrx(struct mpc8xxx_spi *mspi) } } } +EXPORT_SYMBOL_GPL(fsl_spi_cpm_reinit_txrx); static void fsl_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi) { @@ -162,6 +164,7 @@ err_rx_dma: dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE); return -ENOMEM; } +EXPORT_SYMBOL_GPL(fsl_spi_cpm_bufs); void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi) { @@ -174,6 +177,7 @@ void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi) dma_unmap_single(dev, mspi->rx_dma, t->len, DMA_FROM_DEVICE); mspi->xfer_in_progress = NULL; } +EXPORT_SYMBOL_GPL(fsl_spi_cpm_bufs_complete); void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events) { @@ -198,6 +202,7 @@ void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events) else complete(&mspi->done); } +EXPORT_SYMBOL_GPL(fsl_spi_cpm_irq); static void *fsl_spi_alloc_dummy_rx(void) { @@ -375,6 +380,7 @@ err_pram: fsl_spi_free_dummy_rx(); return -ENOMEM; } +EXPORT_SYMBOL_GPL(fsl_spi_cpm_init); void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi) { @@ -389,3 +395,6 @@ void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi) cpm_muram_free(cpm_muram_offset(mspi->pram)); fsl_spi_free_dummy_rx(); } +EXPORT_SYMBOL_GPL(fsl_spi_cpm_free); + +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-fsl-lib.c b/drivers/spi/spi-fsl-lib.c index 9d3f9e0..cb35d2f 100644 --- a/drivers/spi/spi-fsl-lib.c +++ b/drivers/spi/spi-fsl-lib.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #ifdef CONFIG_FSL_SOC @@ -35,7 +36,8 @@ void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \ type *rx = mpc8xxx_spi->rx; \ *rx++ = (type)(data >> mpc8xxx_spi->rx_shift); \ mpc8xxx_spi->rx = rx; \ -} +} \ +EXPORT_SYMBOL_GPL(mpc8xxx_spi_rx_buf_##type); #define MPC8XXX_SPI_TX_BUF(type) \ u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi) \ @@ -47,7 +49,8 @@ u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi) \ data = *tx++ << mpc8xxx_spi->tx_shift; \ mpc8xxx_spi->tx = tx; \ return data; \ -} +} \ +EXPORT_SYMBOL_GPL(mpc8xxx_spi_tx_buf_##type); MPC8XXX_SPI_RX_BUF(u8) MPC8XXX_SPI_RX_BUF(u16) @@ -60,6 +63,7 @@ struct mpc8xxx_spi_probe_info *to_of_pinfo(struct fsl_spi_platform_data *pdata) { return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata); } +EXPORT_SYMBOL_GPL(to_of_pinfo); const char *mpc8xxx_spi_strmode(unsigned int flags) { @@ -75,6 +79,7 @@ const char *mpc8xxx_spi_strmode(unsigned int flags) } return "CPU"; } +EXPORT_SYMBOL_GPL(mpc8xxx_spi_strmode); void mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq) @@ -107,6 +112,7 @@ void mpc8xxx_spi_probe(struct device *dev, struct resource *mem, init_completion(&mpc8xxx_spi->done); } +EXPORT_SYMBOL_GPL(mpc8xxx_spi_probe); int mpc8xxx_spi_remove(struct device *dev) { @@ -125,6 +131,7 @@ int mpc8xxx_spi_remove(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(mpc8xxx_spi_remove); int of_mpc8xxx_spi_probe(struct platform_device *ofdev) { @@ -171,3 +178,6 @@ int of_mpc8xxx_spi_probe(struct platform_device *ofdev) return 0; } +EXPORT_SYMBOL_GPL(of_mpc8xxx_spi_probe); + +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index b4ed04e..1326a39 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -28,7 +28,7 @@ struct mpc8xxx_spi { /* rx & tx bufs from the spi_transfer */ const void *tx; void *rx; -#ifdef CONFIG_SPI_FSL_ESPI +#if IS_ENABLED(CONFIG_SPI_FSL_ESPI) int len; #endif @@ -68,7 +68,7 @@ struct mpc8xxx_spi { unsigned int flags; -#ifdef CONFIG_SPI_FSL_SPI +#if IS_ENABLED(CONFIG_SPI_FSL_SPI) int type; int native_chipselects; u8 max_bits_per_word; -- cgit v0.10.2 From 057a1573fd1309a6b3a039f9cd75b4e90f7f6cf4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 11:30:35 +0100 Subject: ASoC: broadwell: Drop unnecessary snd_soc_dapm_enable() calls DAPM widgets are enabled by default, there is no need to enable them unless they have previously been explicitly disabled. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/intel/broadwell.c b/sound/soc/intel/broadwell.c index 7cf95d5..9cf7d01 100644 --- a/sound/soc/intel/broadwell.c +++ b/sound/soc/intel/broadwell.c @@ -140,8 +140,6 @@ static struct snd_soc_ops broadwell_rt286_ops = { static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev); struct sst_hsw *broadwell = pdata->dsp; int ret; @@ -155,14 +153,6 @@ static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd) return ret; } - /* always connected - check HP for jack detect */ - snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); - snd_soc_dapm_enable_pin(dapm, "Speaker"); - snd_soc_dapm_enable_pin(dapm, "Mic Jack"); - snd_soc_dapm_enable_pin(dapm, "Line Jack"); - snd_soc_dapm_enable_pin(dapm, "DMIC1"); - snd_soc_dapm_enable_pin(dapm, "DMIC2"); - return 0; } -- cgit v0.10.2 From 7a81140b0ead01fcb27e6167b1015b06c36acbd0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 11:23:44 +0100 Subject: ASoC: byt-rt5640: Fix snd_soc_dapm_ignore_suspend() calls To work properly snd_soc_dapm_ignore_suspend() needs to be called on endpoint widgets. In this case those are the board level Speaker and Headphone widgets and not the CODEC output widgets that are connected to them. Signed-off-by: Lars-Peter Clausen Acked-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/sound/soc/intel/byt-rt5640.c b/sound/soc/intel/byt-rt5640.c index 0cba783..a51856e 100644 --- a/sound/soc/intel/byt-rt5640.c +++ b/sound/soc/intel/byt-rt5640.c @@ -171,13 +171,8 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) return ret; } - snd_soc_dapm_ignore_suspend(dapm, "HPOL"); - snd_soc_dapm_ignore_suspend(dapm, "HPOR"); - - snd_soc_dapm_ignore_suspend(dapm, "SPOLP"); - snd_soc_dapm_ignore_suspend(dapm, "SPOLN"); - snd_soc_dapm_ignore_suspend(dapm, "SPORP"); - snd_soc_dapm_ignore_suspend(dapm, "SPORN"); + snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone"); + snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker"); return ret; } -- cgit v0.10.2 From b93673be48cef887551d109683922bcc15f40d27 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 11:23:45 +0100 Subject: ASoC: byt-rt5640: Register microphone routes with the card DAPM context Board level DAPM elements should be registered with the card's DAPM context rather than the CODEC's DAPM context. Signed-off-by: Lars-Peter Clausen Acked-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/sound/soc/intel/byt-rt5640.c b/sound/soc/intel/byt-rt5640.c index a51856e..354eaad 100644 --- a/sound/soc/intel/byt-rt5640.c +++ b/sound/soc/intel/byt-rt5640.c @@ -132,7 +132,6 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) { int ret; struct snd_soc_codec *codec = runtime->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; struct snd_soc_card *card = runtime->card; const struct snd_soc_dapm_route *custom_map; int num_routes; @@ -161,7 +160,7 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map); } - ret = snd_soc_dapm_add_routes(dapm, custom_map, num_routes); + ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes); if (ret) return ret; -- cgit v0.10.2 From 8686f251e4823a4196bae86f22dab8cfd3b454cc Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 2 Jan 2015 13:56:09 +0100 Subject: ASoC: intel: Remove unnecessary snd_pcm_lib_preallocate_free_for_all() The ALSA core takes care that all preallocated memory is freed when the PCM itself is freed. There is no need to do this manually in the driver. Signed-off-by: Lars-Peter Clausen Acked-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-baytrail-pcm.c b/sound/soc/intel/sst-baytrail-pcm.c index 3bb6288..224c49c 100644 --- a/sound/soc/intel/sst-baytrail-pcm.c +++ b/sound/soc/intel/sst-baytrail-pcm.c @@ -320,11 +320,6 @@ static struct snd_pcm_ops sst_byt_pcm_ops = { .mmap = sst_byt_pcm_mmap, }; -static void sst_byt_pcm_free(struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct snd_pcm *pcm = rtd->pcm; @@ -403,7 +398,6 @@ static struct snd_soc_platform_driver byt_soc_platform = { .remove = sst_byt_pcm_remove, .ops = &sst_byt_pcm_ops, .pcm_new = sst_byt_pcm_new, - .pcm_free = sst_byt_pcm_free, }; static const struct snd_soc_component_driver byt_dai_component = { diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 6195252..13f156b 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -735,11 +735,6 @@ static void hsw_pcm_free_modules(struct hsw_priv_data *pdata) } } -static void hsw_pcm_free(struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct snd_pcm *pcm = rtd->pcm; @@ -936,7 +931,6 @@ static struct snd_soc_platform_driver hsw_soc_platform = { .remove = hsw_pcm_remove, .ops = &hsw_pcm_ops, .pcm_new = hsw_pcm_new, - .pcm_free = hsw_pcm_free, }; static const struct snd_soc_component_driver hsw_dai_component = { diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index a1a8d9d..7523cbe 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -643,12 +643,6 @@ static struct snd_pcm_ops sst_platform_ops = { .pointer = sst_platform_pcm_pointer, }; -static void sst_pcm_free(struct snd_pcm *pcm) -{ - dev_dbg(pcm->dev, "sst_pcm_free called\n"); - snd_pcm_lib_preallocate_free_for_all(pcm); -} - static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *dai = rtd->cpu_dai; @@ -679,7 +673,6 @@ static struct snd_soc_platform_driver sst_soc_platform_drv = { .ops = &sst_platform_ops, .compr_ops = &sst_platform_compr_ops, .pcm_new = sst_pcm_new, - .pcm_free = sst_pcm_free, }; static const struct snd_soc_component_driver sst_component = { -- cgit v0.10.2 From 059d3dc6e5e8d4a62803eb3ad01b34fa3f714675 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:10 +0100 Subject: ASoC: sam9g20_wm8731: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Acked-by: Bo Shen Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index 66b66d0..98ca634 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -64,33 +64,6 @@ static struct clk *mclk; -static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret; - - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - /* set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - return 0; -} - -static struct snd_soc_ops at91sam9g20ek_ops = { - .hw_params = at91sam9g20ek_hw_params, -}; - static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) @@ -173,7 +146,8 @@ static struct snd_soc_dai_link at91sam9g20ek_dai = { .init = at91sam9g20ek_wm8731_init, .platform_name = "at91rm9200_ssc.0", .codec_name = "wm8731.0-001b", - .ops = &at91sam9g20ek_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, }; static struct snd_soc_card snd_soc_at91sam9g20ek = { -- cgit v0.10.2 From b4508d0f95fa4aaed889549e31391641d675d4bb Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:11 +0100 Subject: ASoC: db1200: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c index a747ac0..c75995f 100644 --- a/sound/soc/au1x/db1200.c +++ b/sound/soc/au1x/db1200.c @@ -91,27 +91,12 @@ static int db1200_i2s_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret; /* WM8731 has its own 12MHz crystal */ snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, 12000000, SND_SOC_CLOCK_IN); - /* codec is bitclock and lrclk master */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_LEFT_J | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - goto out; - - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_LEFT_J | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - goto out; - - ret = 0; -out: - return ret; + return 0; } static struct snd_soc_ops db1200_i2s_wm8731_ops = { @@ -125,6 +110,8 @@ static struct snd_soc_dai_link db1200_i2s_dai = { .cpu_dai_name = "au1xpsc_i2s.1", .platform_name = "au1xpsc-pcm.1", .codec_name = "wm8731.0-001b", + .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, .ops = &db1200_i2s_wm8731_ops, }; -- cgit v0.10.2 From 4de59dbd28565ad1074d2bcf294a0a4b41c64e07 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:12 +0100 Subject: ASoC: eureka-tlv320: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c index 9ce70fc..8c9e900 100644 --- a/sound/soc/fsl/eukrea-tlv320.c +++ b/sound/soc/fsl/eukrea-tlv320.c @@ -42,25 +42,6 @@ static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int ret; - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM); - /* fsl_ssi lacks the set_fmt ops. */ - if (ret && ret != -ENOTSUPP) { - dev_err(cpu_dai->dev, - "Failed to set the cpu dai format.\n"); - return ret; - } - - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM); - if (ret) { - dev_err(cpu_dai->dev, - "Failed to set the codec format.\n"); - return ret; - } - ret = snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_OUT); if (ret) { @@ -91,6 +72,8 @@ static struct snd_soc_dai_link eukrea_tlv320_dai = { .name = "tlv320aic23", .stream_name = "TLV320AIC23", .codec_dai_name = "tlv320aic23-hifi", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, .ops = &eukrea_tlv320_snd_ops, }; -- cgit v0.10.2 From dea53bd36811d6b117a497eb91b2e4a54ef73548 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:13 +0100 Subject: ASoC: mx27vis-aci32x4: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/mx27vis-aic32x4.c b/sound/soc/fsl/mx27vis-aic32x4.c index b1ced7b..198eeb3 100644 --- a/sound/soc/fsl/mx27vis-aic32x4.c +++ b/sound/soc/fsl/mx27vis-aic32x4.c @@ -55,16 +55,6 @@ static int mx27vis_aic32x4_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int ret; - u32 dai_format; - - dai_format = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM; - - /* set codec DAI configuration */ - snd_soc_dai_set_fmt(codec_dai, dai_format); - - /* set cpu DAI configuration */ - snd_soc_dai_set_fmt(cpu_dai, dai_format); ret = snd_soc_dai_set_sysclk(codec_dai, 0, 25000000, SND_SOC_CLOCK_OUT); @@ -164,6 +154,8 @@ static struct snd_soc_dai_link mx27vis_aic32x4_dai = { .platform_name = "imx-ssi.0", .codec_name = "tlv320aic32x4.0-0018", .cpu_dai_name = "imx-ssi.0", + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, .ops = &mx27vis_aic32x4_snd_ops, }; -- cgit v0.10.2 From 734890a6c99ee81b935164963c03fc8bbf3f821b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:14 +0100 Subject: ASoC: wm1133-ev1: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c index 804749a..d072bd1 100644 --- a/sound/soc/fsl/wm1133-ev1.c +++ b/sound/soc/fsl/wm1133-ev1.c @@ -87,7 +87,6 @@ static int wm1133_ev1_hw_params(struct snd_pcm_substream *substream, snd_pcm_format_t format = params_format(params); unsigned int rate = params_rate(params); unsigned int channels = params_channels(params); - u32 dai_format; /* find the correct audio parameters */ for (i = 0; i < ARRAY_SIZE(wm8350_audio); i++) { @@ -104,15 +103,6 @@ static int wm1133_ev1_hw_params(struct snd_pcm_substream *substream, /* codec FLL input is 14.75 MHz from MCLK */ snd_soc_dai_set_pll(codec_dai, 0, 0, 14750000, wm8350_audio[i].sysclk); - dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM; - - /* set codec DAI configuration */ - snd_soc_dai_set_fmt(codec_dai, dai_format); - - /* set cpu DAI configuration */ - snd_soc_dai_set_fmt(cpu_dai, dai_format); - /* TODO: The SSI driver should figure this out for us */ switch (channels) { case 2: @@ -244,6 +234,8 @@ static struct snd_soc_dai_link wm1133_ev1_dai = { .init = wm1133_ev1_init, .ops = &wm1133_ev1_ops, .symmetric_rates = 1, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, }; static struct snd_soc_card wm1133_ev1 = { -- cgit v0.10.2 From bc6a5d649bda56c579853bfe710db88bf38da522 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:15 +0100 Subject: ASoC: mxs-sgtl5000: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 6f1916b..6e6fce6 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -36,7 +36,7 @@ static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; unsigned int rate = params_rate(params); - u32 dai_format, mclk; + u32 mclk; int ret; /* sgtl5000 does not support 512*rate when in 96000 fs */ @@ -65,26 +65,6 @@ static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream, return ret; } - /* set codec to slave mode */ - dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS; - - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, dai_format); - if (ret) { - dev_err(codec_dai->dev, "Failed to set dai format to %08x\n", - dai_format); - return ret; - } - - /* set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, dai_format); - if (ret) { - dev_err(cpu_dai->dev, "Failed to set dai format to %08x\n", - dai_format); - return ret; - } - return 0; } @@ -92,17 +72,22 @@ static struct snd_soc_ops mxs_sgtl5000_hifi_ops = { .hw_params = mxs_sgtl5000_hw_params, }; +#define MXS_SGTL5000_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \ + SND_SOC_DAIFMT_CBS_CFS) + static struct snd_soc_dai_link mxs_sgtl5000_dai[] = { { .name = "HiFi Tx", .stream_name = "HiFi Playback", .codec_dai_name = "sgtl5000", + .dai_fmt = MXS_SGTL5000_DAI_FMT, .ops = &mxs_sgtl5000_hifi_ops, .playback_only = true, }, { .name = "HiFi Rx", .stream_name = "HiFi Capture", .codec_dai_name = "sgtl5000", + .dai_fmt = MXS_SGTL5000_DAI_FMT, .ops = &mxs_sgtl5000_hifi_ops, .capture_only = true, }, -- cgit v0.10.2 From 3aa273e3f65455066aa7338845e11910ba2bae12 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:16 +0100 Subject: ASoC: ams-delta: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c index 4c6afb7..7066130 100644 --- a/sound/soc/omap/ams-delta.c +++ b/sound/soc/omap/ams-delta.c @@ -412,21 +412,7 @@ static struct tty_ldisc_ops cx81801_ops = { * over the modem port. */ -static int ams_delta_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - - /* Set cpu DAI configuration */ - return snd_soc_dai_set_fmt(rtd->cpu_dai, - SND_SOC_DAIFMT_DSP_A | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM); -} - -static struct snd_soc_ops ams_delta_ops = { - .hw_params = ams_delta_hw_params, -}; +static struct snd_soc_ops ams_delta_ops; /* Digital mute implemented using modem/CPU multiplexer. @@ -546,6 +532,8 @@ static struct snd_soc_dai_link ams_delta_dai_link = { .platform_name = "omap-mcbsp.1", .codec_name = "cx20442-codec", .ops = &ams_delta_ops, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, }; /* Audio card driver */ -- cgit v0.10.2 From a8bd0ee5587148cd1a23302ef37bf3f236fe6705 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:17 +0100 Subject: ASoC: raumfeld: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c index 0837065..552b763 100644 --- a/sound/soc/pxa/raumfeld.c +++ b/sound/soc/pxa/raumfeld.c @@ -88,7 +88,7 @@ static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - unsigned int fmt, clk = 0; + unsigned int clk = 0; int ret = 0; switch (params_rate(params)) { @@ -112,15 +112,6 @@ static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - fmt = SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS; - - /* setup the CODEC DAI */ - ret = snd_soc_dai_set_fmt(codec_dai, fmt); - if (ret < 0) - return ret; - ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, 0); if (ret < 0) return ret; @@ -130,10 +121,6 @@ static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - ret = snd_soc_dai_set_fmt(cpu_dai, fmt); - if (ret < 0) - return ret; - ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); if (ret < 0) return ret; @@ -169,9 +156,8 @@ static int raumfeld_ak4104_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int fmt, ret = 0, clk = 0; + int ret = 0, clk = 0; switch (params_rate(params)) { case 44100: @@ -194,22 +180,11 @@ static int raumfeld_ak4104_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF; - - /* setup the CODEC DAI */ - ret = snd_soc_dai_set_fmt(codec_dai, fmt | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - /* setup the CPU DAI */ ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk); if (ret < 0) return ret; - ret = snd_soc_dai_set_fmt(cpu_dai, fmt | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); if (ret < 0) return ret; @@ -233,6 +208,9 @@ static struct snd_soc_ops raumfeld_ak4104_ops = { .platform_name = "pxa-pcm-audio", \ .codec_dai_name = "cs4270-hifi", \ .codec_name = "cs4270.0-0048", \ + .dai_fmt = SND_SOC_DAIFMT_I2S | \ + SND_SOC_DAIFMT_NB_NF | \ + SND_SOC_DAIFMT_CBS_CFS, \ .ops = &raumfeld_cs4270_ops, \ } @@ -243,6 +221,9 @@ static struct snd_soc_ops raumfeld_ak4104_ops = { .cpu_dai_name = "pxa-ssp-dai.1", \ .codec_dai_name = "ak4104-hifi", \ .platform_name = "pxa-pcm-audio", \ + .dai_fmt = SND_SOC_DAIFMT_I2S | \ + SND_SOC_DAIFMT_NB_NF | \ + SND_SOC_DAIFMT_CBS_CFS, \ .ops = &raumfeld_ak4104_ops, \ .codec_name = "spi0.0", \ } -- cgit v0.10.2 From 132c30969b9346a628f597804d2343b6df493693 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:18 +0100 Subject: ASoC: zylonite: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index 23bf991..8f301c7 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -130,16 +130,6 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - return 0; } @@ -172,6 +162,8 @@ static struct snd_soc_dai_link zylonite_dai[] = { .platform_name = "pxa-pcm-audio", .cpu_dai_name = "pxa-ssp-dai.2", .codec_dai_name = "wm9713-voice", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, .ops = &zylonite_voice_ops, }, }; -- cgit v0.10.2 From 0af76f918adb8487a911bc136a6f1b12c46cd056 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:19 +0100 Subject: ASoC: goni_wm8994: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/goni_wm8994.c b/sound/soc/samsung/goni_wm8994.c index 3b527dc..fad56b9 100644 --- a/sound/soc/samsung/goni_wm8994.c +++ b/sound/soc/samsung/goni_wm8994.c @@ -136,22 +136,9 @@ static int goni_hifi_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; unsigned int pll_out = 24000000; int ret = 0; - /* set the cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - /* set the codec FLL */ ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, 0, pll_out, params_rate(params) * 256); @@ -182,12 +169,6 @@ static int goni_voice_hw_params(struct snd_pcm_substream *substream, if (params_rate(params) != 8000) return -EINVAL; - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_LEFT_J | - SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - /* set the codec FLL */ ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL2, 0, pll_out, params_rate(params) * 256); @@ -234,6 +215,8 @@ static struct snd_soc_dai_link goni_dai[] = { .codec_dai_name = "wm8994-aif1", .platform_name = "samsung-i2s.0", .codec_name = "wm8994-codec.0-001a", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, .init = goni_wm8994_init, .ops = &goni_hifi_ops, }, { @@ -242,6 +225,8 @@ static struct snd_soc_dai_link goni_dai[] = { .cpu_dai_name = "goni-voice-dai", .codec_dai_name = "wm8994-aif2", .codec_name = "wm8994-codec.0-001a", + .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_IB_IF | + SND_SOC_DAIFMT_CBM_CFM, .ops = &goni_voice_ops, }, }; -- cgit v0.10.2 From 5cc10b9b77c234e89e7c4aca56d8a3c571111955 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:20 +0100 Subject: ASoC: h1940_uda1380: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c index f2d7980..59b0442 100644 --- a/sound/soc/samsung/h1940_uda1380.c +++ b/sound/soc/samsung/h1940_uda1380.c @@ -76,7 +76,6 @@ static int h1940_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; int div; int ret; unsigned int rate = params_rate(params); @@ -95,18 +94,6 @@ static int h1940_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - /* set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - /* select clock source */ ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_PCLK, rate, SND_SOC_CLOCK_OUT); @@ -207,6 +194,8 @@ static struct snd_soc_dai_link h1940_uda1380_dai[] = { .init = h1940_uda1380_init, .platform_name = "s3c24xx-iis", .codec_name = "uda1380-codec.0-001a", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, .ops = &h1940_ops, }, }; -- cgit v0.10.2 From 72303cafcb5cc9fd1227d0edd458f62bef1a397e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:21 +0100 Subject: ASoC: jive_wm8750: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/jive_wm8750.c b/sound/soc/samsung/jive_wm8750.c index b5f6abd..6c3b359 100644 --- a/sound/soc/samsung/jive_wm8750.c +++ b/sound/soc/samsung/jive_wm8750.c @@ -61,20 +61,6 @@ static int jive_hw_params(struct snd_pcm_substream *substream, s3c_i2sv2_iis_calc_rate(&div, NULL, params_rate(params), s3c_i2sv2_get_clock(cpu_dai)); - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - /* set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - /* set the codec system clock for DAC and ADC */ ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk, SND_SOC_CLOCK_IN); @@ -121,6 +107,8 @@ static struct snd_soc_dai_link jive_dai = { .platform_name = "s3c2412-i2s", .codec_name = "wm8750.0-001a", .init = jive_wm8750_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, .ops = &jive_ops, }; -- cgit v0.10.2 From e16514d931635640906ab2810c98f6a9047cb87a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:22 +0100 Subject: ASoC: neo1973_wm8753: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c index 9b4a09f..65602b9 100644 --- a/sound/soc/samsung/neo1973_wm8753.c +++ b/sound/soc/samsung/neo1973_wm8753.c @@ -70,20 +70,6 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream, break; } - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, - SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - /* set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, - SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - /* set the codec system clock for DAC and ADC */ ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out, SND_SOC_CLOCK_IN); @@ -151,13 +137,6 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream, pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */ - /* todo: gg check mode (DSP_B) against CSR datasheet */ - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - /* set the codec system clock for DAC and ADC */ ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, 12288000, SND_SOC_CLOCK_IN); @@ -300,6 +279,8 @@ static struct snd_soc_dai_link neo1973_dai[] = { .cpu_dai_name = "s3c24xx-iis", .codec_dai_name = "wm8753-hifi", .codec_name = "wm8753.0-001a", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, .init = neo1973_wm8753_init, .ops = &neo1973_hifi_ops, }, @@ -309,6 +290,8 @@ static struct snd_soc_dai_link neo1973_dai[] = { .cpu_dai_name = "bt-sco-pcm", .codec_dai_name = "wm8753-voice", .codec_name = "wm8753.0-001a", + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, .ops = &neo1973_voice_ops, }, }; -- cgit v0.10.2 From 5f0acedddf533c086edf29eefdc743dc1e756b5d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:23 +0100 Subject: ASoC: rx1950_uda1380: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c index 37688eb..873f2cb 100644 --- a/sound/soc/samsung/rx1950_uda1380.c +++ b/sound/soc/samsung/rx1950_uda1380.c @@ -89,6 +89,8 @@ static struct snd_soc_dai_link rx1950_uda1380_dai[] = { .init = rx1950_uda1380_init, .platform_name = "s3c24xx-iis", .codec_name = "uda1380-codec.0-001a", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, .ops = &rx1950_ops, }, }; @@ -154,7 +156,6 @@ static int rx1950_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; int div; int ret; unsigned int rate = params_rate(params); @@ -181,18 +182,6 @@ static int rx1950_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - /* set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - /* select clock source */ ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source, rate, SND_SOC_CLOCK_OUT); -- cgit v0.10.2 From d27e51dade482acdf67021c12d692041a35bea77 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:24 +0100 Subject: ASoC: s3c24xx_simtec: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/s3c24xx_simtec.c b/sound/soc/samsung/s3c24xx_simtec.c index 2c015f6..dcc008d 100644 --- a/sound/soc/samsung/s3c24xx_simtec.c +++ b/sound/soc/samsung/s3c24xx_simtec.c @@ -169,24 +169,6 @@ static int simtec_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int ret; - /* Set the CODEC as the bus clock master, I2S */ - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM); - if (ret) { - pr_err("%s: failed set cpu dai format\n", __func__); - return ret; - } - - /* Set the CODEC as the bus clock master */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM); - if (ret) { - pr_err("%s: failed set codec dai format\n", __func__); - return ret; - } - ret = snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN); if (ret) { @@ -320,6 +302,8 @@ int simtec_audio_core_probe(struct platform_device *pdev, int ret; card->dai_link->ops = &simtec_snd_ops; + card->dai_link->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; pdata = pdev->dev.platform_data; if (!pdata) { -- cgit v0.10.2 From 517b9a2a90676aeeefc7e2b8508fe4260e02c432 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:25 +0100 Subject: ASoC: s3c24xx_uda134x: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/s3c24xx_uda134x.c b/sound/soc/samsung/s3c24xx_uda134x.c index 9c6f7db..50849e1 100644 --- a/sound/soc/samsung/s3c24xx_uda134x.c +++ b/sound/soc/samsung/s3c24xx_uda134x.c @@ -173,16 +173,6 @@ static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk, SND_SOC_CLOCK_IN); if (ret < 0) @@ -223,6 +213,8 @@ static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = { .codec_name = "uda134x-codec", .codec_dai_name = "uda134x-hifi", .cpu_dai_name = "s3c24xx-iis", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, .ops = &s3c24xx_uda134x_ops, .platform_name = "s3c24xx-iis", }; -- cgit v0.10.2 From 10756c2770d7f4758e36d2a9e37ff2637001d610 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:26 +0100 Subject: ASoC: smartq_wm8987: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c index 9b0ffac..8291d2a 100644 --- a/sound/soc/samsung/smartq_wm8987.c +++ b/sound/soc/samsung/smartq_wm8987.c @@ -56,20 +56,6 @@ static int smartq_hifi_hw_params(struct snd_pcm_substream *substream, break; } - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - /* set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - /* Use PCLK for I2S signal generation */ ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0, 0, SND_SOC_CLOCK_IN); @@ -199,6 +185,8 @@ static struct snd_soc_dai_link smartq_dai[] = { .platform_name = "samsung-i2s.0", .codec_name = "wm8750.0-0x1a", .init = smartq_wm8987_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, .ops = &smartq_hifi_ops, }, }; -- cgit v0.10.2 From 7a57ca2362a55c34b96d90539dd8b7cb6c1c09a1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:27 +0100 Subject: ASoC: smdk_wm8580: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/smdk_wm8580.c b/sound/soc/samsung/smdk_wm8580.c index b1a519f..17a2f71 100644 --- a/sound/soc/samsung/smdk_wm8580.c +++ b/sound/soc/samsung/smdk_wm8580.c @@ -32,7 +32,6 @@ static int smdk_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai; unsigned int pll_out; int bfs, rfs, ret; @@ -77,20 +76,6 @@ static int smdk_hw_params(struct snd_pcm_substream *substream, } pll_out = params_rate(params) * rfs; - /* Set the Codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S - | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - /* Set the AP DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S - | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - /* Set WM8580 to drive MCLK from its PLLA */ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK, WM8580_CLKSRC_PLLA); @@ -168,6 +153,9 @@ enum { SEC_PLAYBACK, }; +#define SMDK_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \ + SND_SOC_DAIFMT_CBM_CFM) + static struct snd_soc_dai_link smdk_dai[] = { [PRI_PLAYBACK] = { /* Primary Playback i/f */ .name = "WM8580 PAIF RX", @@ -176,6 +164,7 @@ static struct snd_soc_dai_link smdk_dai[] = { .codec_dai_name = "wm8580-hifi-playback", .platform_name = "samsung-i2s.0", .codec_name = "wm8580.0-001b", + .dai_fmt = SMDK_DAI_FMT, .ops = &smdk_ops, }, [PRI_CAPTURE] = { /* Primary Capture i/f */ @@ -185,6 +174,7 @@ static struct snd_soc_dai_link smdk_dai[] = { .codec_dai_name = "wm8580-hifi-capture", .platform_name = "samsung-i2s.0", .codec_name = "wm8580.0-001b", + .dai_fmt = SMDK_DAI_FMT, .init = smdk_wm8580_init_paiftx, .ops = &smdk_ops, }, @@ -195,6 +185,7 @@ static struct snd_soc_dai_link smdk_dai[] = { .codec_dai_name = "wm8580-hifi-playback", .platform_name = "samsung-i2s-sec", .codec_name = "wm8580.0-001b", + .dai_fmt = SMDK_DAI_FMT, .ops = &smdk_ops, }, }; -- cgit v0.10.2 From 6a5794f2c653e78127123b3cd266b3c4d4c50606 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:28 +0100 Subject: ASoC: smdk_wm8570pcm: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/smdk_wm8580pcm.c b/sound/soc/samsung/smdk_wm8580pcm.c index 05c609c..6deec52 100644 --- a/sound/soc/samsung/smdk_wm8580pcm.c +++ b/sound/soc/samsung/smdk_wm8580pcm.c @@ -62,20 +62,6 @@ static int smdk_wm8580_pcm_hw_params(struct snd_pcm_substream *substream, rfs = mclk_freq / params_rate(params) / 2; - /* Set the codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B - | SND_SOC_DAIFMT_IB_NF - | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - /* Set the cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_B - | SND_SOC_DAIFMT_IB_NF - | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - if (mclk_freq == xtal_freq) { ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_MCLK, mclk_freq, SND_SOC_CLOCK_IN); @@ -121,6 +107,9 @@ static struct snd_soc_ops smdk_wm8580_pcm_ops = { .hw_params = smdk_wm8580_pcm_hw_params, }; +#define SMDK_DAI_FMT (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF | \ + SND_SOC_DAIFMT_CBS_CFS) + static struct snd_soc_dai_link smdk_dai[] = { { .name = "WM8580 PAIF PCM RX", @@ -129,6 +118,7 @@ static struct snd_soc_dai_link smdk_dai[] = { .codec_dai_name = "wm8580-hifi-playback", .platform_name = "samsung-audio", .codec_name = "wm8580.0-001b", + .dai_fmt = SMDK_DAI_FMT, .ops = &smdk_wm8580_pcm_ops, }, { .name = "WM8580 PAIF PCM TX", @@ -137,6 +127,7 @@ static struct snd_soc_dai_link smdk_dai[] = { .codec_dai_name = "wm8580-hifi-capture", .platform_name = "samsung-pcm.0", .codec_name = "wm8580.0-001b", + .dai_fmt = SMDK_DAI_FMT, .ops = &smdk_wm8580_pcm_ops, }, }; -- cgit v0.10.2 From fce20bf879dd5ce309c593c91bbb7b5e6ffd22e7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:29 +0100 Subject: ASoC: smdk_wm8994pcm: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/smdk_wm8994pcm.c b/sound/soc/samsung/smdk_wm8994pcm.c index c470e8e..b1c89ec 100644 --- a/sound/soc/samsung/smdk_wm8994pcm.c +++ b/sound/soc/samsung/smdk_wm8994pcm.c @@ -68,20 +68,6 @@ static int smdk_wm8994_pcm_hw_params(struct snd_pcm_substream *substream, mclk_freq = params_rate(params) * rfs; - /* Set the codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B - | SND_SOC_DAIFMT_IB_NF - | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - /* Set the cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_B - | SND_SOC_DAIFMT_IB_NF - | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1, mclk_freq, SND_SOC_CLOCK_IN); if (ret < 0) @@ -118,6 +104,8 @@ static struct snd_soc_dai_link smdk_dai[] = { .codec_dai_name = "wm8994-aif1", .platform_name = "samsung-pcm.0", .codec_name = "wm8994-codec", + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBS_CFS, .ops = &smdk_wm8994_pcm_ops, }, }; -- cgit v0.10.2 From 9cb2a25ce68aec15d5d940f5d3040bd66ca688c7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Jan 2015 17:16:30 +0100 Subject: ASoC: migor: Use static DAI format setup Set the dai_fmt field in the dai_link struct instead of manually calling snd_soc_dai_fmt(). This makes the code cleaner and shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/sh/migor.c b/sound/soc/sh/migor.c index c58c252..82f5823 100644 --- a/sound/soc/sh/migor.c +++ b/sound/soc/sh/migor.c @@ -63,16 +63,6 @@ static int migor_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_NB_IF | - SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_fmt(rtd->cpu_dai, SND_SOC_DAIFMT_NB_IF | - SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - codec_freq = rate * 512; /* * This propagates the parent frequency change to children and @@ -144,6 +134,8 @@ static struct snd_soc_dai_link migor_dai = { .codec_dai_name = "wm8978-hifi", .platform_name = "siu-pcm-audio", .codec_name = "wm8978.0-001a", + .dai_fmt = SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_CBS_CFS, .ops = &migor_dai_ops, }; -- cgit v0.10.2 From 22ee76daddb87f88d2336d1b4737ef27c4f307ac Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Wed, 31 Dec 2014 11:39:14 +0800 Subject: ASoC: wm8960: Fix capture sample rate from 11250 to 11025 wm8960 codec can't support sample rate 11250, it must be 11025. Signed-off-by: Zidan Wang Acked-by: Charles Keepax Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 031a1ae..a96eb49 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -556,7 +556,7 @@ static struct { { 22050, 2 }, { 24000, 2 }, { 16000, 3 }, - { 11250, 4 }, + { 11025, 4 }, { 12000, 4 }, { 8000, 5 }, }; -- cgit v0.10.2 From 29104c748d52d579b291ca029f4714bca7765241 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Sat, 3 Jan 2015 17:04:44 +0800 Subject: spi: sirf: drop redundant sirf,marco-spi compatible string "sirf,marco-spi" is redundant as all SPI controllers in CSR SiRFSoC are compatible with prima2-spi. at the same time, the whole marco project was dropped and its replacement atlas7 is also compatible with prima2 in SPI. Signed-off-by: Barry Song Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index d075191..f5715c9 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -818,7 +818,6 @@ static SIMPLE_DEV_PM_OPS(spi_sirfsoc_pm_ops, spi_sirfsoc_suspend, static const struct of_device_id spi_sirfsoc_of_match[] = { { .compatible = "sirf,prima2-spi", }, - { .compatible = "sirf,marco-spi", }, {} }; MODULE_DEVICE_TABLE(of, spi_sirfsoc_of_match); -- cgit v0.10.2 From 7491831f4f034e2d76326518145da9525dd90932 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 6 Jan 2015 10:21:20 +0100 Subject: ASoC: atmel: sam9g20_wm8731: remove useless include A mach/ header is included but never used. Simply remove it. Signed-off-by: Alexandre Belloni Acked-by: Bo Shen Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index 66b66d0..5317289 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -47,7 +47,6 @@ #include #include -#include #include "../codecs/wm8731.h" #include "atmel-pcm.h" -- cgit v0.10.2 From fcf6c5ea7abd42cfc2dde0ff0451b209951de9b4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 15 Dec 2014 13:08:48 +0000 Subject: ASoC: dapm: Don't use async I/O The only user of the async I/O support in ASoC is SPI which was using it to avoid needless context thrashing and minimise controller runtime PM bounces. The SPI framework has now been enhanced so that even normal spi_sync() calls won't suffer these effects so we don't need to handle this in ASoC and in fact it can be more efficient not to since we don't need to set up and tear down the buffers needed to manage asynchronous I/O. The async completions that DAPM does are left in place so drivers can use them, they are very cheap if there is no asynchronous work queued. Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index ea49684..0be3ca5 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -517,8 +517,8 @@ static int soc_dapm_update_bits(struct snd_soc_dapm_context *dapm, { if (!dapm->component) return -EIO; - return snd_soc_component_update_bits_async(dapm->component, reg, - mask, value); + return snd_soc_component_update_bits(dapm->component, reg, + mask, value); } static int soc_dapm_test_bits(struct snd_soc_dapm_context *dapm, -- cgit v0.10.2 From fce091ee0a7f25108c7ab3eb06b9e436b7aeac11 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 19 Dec 2014 14:55:21 +0100 Subject: ASoC: samsung: i2s: Remove unused gpios field from struct i2s The 'gpios' field in 'struct i2s' is now unused, this change seems to be missing in commit 0429ffeff460c4302bd1520e6 ("ASoC: samsung: Remove obsolete GPIO based DT pinmuxing"). Signed-off-by: Sylwester Nawrocki Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index b5a80c5..86491c9 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -95,7 +95,6 @@ struct i2s_dai { u32 suspend_i2smod; u32 suspend_i2scon; u32 suspend_i2spsr; - unsigned long gpios[7]; /* i2s gpio line numbers */ const struct samsung_i2s_variant_regs *variant_regs; }; -- cgit v0.10.2 From 678109ea0908a643e0338e20714aae9e07ffe02a Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Tue, 6 Jan 2015 17:31:46 +0530 Subject: cxgb4: Add PCI device ID for new T5 adapter Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h b/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h index 9e4f95a..ddfb5b8 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h @@ -153,6 +153,7 @@ CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN CH_PCI_ID_TABLE_FENTRY(0x5086), /* Custom 2x T580-CR */ CH_PCI_ID_TABLE_FENTRY(0x5087), /* Custom T580-CR */ CH_PCI_ID_TABLE_FENTRY(0x5088), /* Custom T570-CR */ + CH_PCI_ID_TABLE_FENTRY(0x5089), /* Custom T520-CR */ CH_PCI_DEVICE_ID_TABLE_DEFINE_END; #endif /* CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN */ -- cgit v0.10.2 From 7c8f1e78612f3155bfd10c020b38bcb4ca175223 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Tue, 6 Jan 2015 14:26:13 +0100 Subject: arm_arch_timer: include clocksource.h directly This driver makes use of the clocksource code. Previously it had only included the proper header indirectly, but that chain was inadvertently broken by 74d23cc "time: move the timecounter/cyclecounter code into its own file." This patch fixes the issue by including clocksource.h directly. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 6a79fc4..8e67e72 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include -- cgit v0.10.2 From 83297aaa8fde85154c91a59e522c3d042c1084ec Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sun, 21 Dec 2014 12:43:44 +0100 Subject: brcmfmac: Fix incorrect casting of 64 bit physical address. The physical addresses being used by pcie and msgbuf were using a cast to long, which incorrectly caused it to limit the address to 32bit. Now explicit u64 is used where needed. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 456944a..3ff6acf 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -583,7 +583,7 @@ brcmf_msgbuf_flowring_create_worker(struct brcmf_msgbuf *msgbuf, u32 flowid; void *dma_buf; u32 dma_sz; - long long address; + u64 address; int err; flowid = work->flowid; @@ -620,7 +620,7 @@ brcmf_msgbuf_flowring_create_worker(struct brcmf_msgbuf *msgbuf, BRCMF_NROF_H2D_COMMON_MSGRINGS); memcpy(create->sa, work->sa, ETH_ALEN); memcpy(create->da, work->da, ETH_ALEN); - address = (long long)(long)msgbuf->flowring_dma_handle[flowid]; + address = (u64)msgbuf->flowring_dma_handle[flowid]; create->flow_ring_addr.high_addr = cpu_to_le32(address >> 32); create->flow_ring_addr.low_addr = cpu_to_le32(address & 0xffffffff); create->max_items = cpu_to_le16(BRCMF_H2D_TXFLOWRING_MAX_ITEM); @@ -698,7 +698,7 @@ static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid) dma_addr_t physaddr; u32 pktid; struct msgbuf_tx_msghdr *tx_msghdr; - long long address; + u64 address; commonring = msgbuf->flowrings[flowid]; if (!brcmf_commonring_write_available(commonring)) @@ -742,7 +742,7 @@ static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid) tx_msghdr->seg_cnt = 1; memcpy(tx_msghdr->txhdr, skb->data, ETH_HLEN); tx_msghdr->data_len = cpu_to_le16(skb->len - ETH_HLEN); - address = (long long)(long)physaddr; + address = (u64)physaddr; tx_msghdr->data_buf_addr.high_addr = cpu_to_le32(address >> 32); tx_msghdr->data_buf_addr.low_addr = cpu_to_le32(address & 0xffffffff); @@ -885,7 +885,7 @@ static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count) u32 pktlen; dma_addr_t physaddr; struct msgbuf_rx_bufpost *rx_bufpost; - long long address; + u64 address; u32 pktid; u32 i; @@ -921,7 +921,7 @@ static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count) } if (msgbuf->rx_metadata_offset) { - address = (long long)(long)physaddr; + address = (u64)physaddr; rx_bufpost->metadata_buf_len = cpu_to_le16(msgbuf->rx_metadata_offset); rx_bufpost->metadata_buf_addr.high_addr = @@ -936,7 +936,7 @@ static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count) rx_bufpost->msg.msgtype = MSGBUF_TYPE_RXBUF_POST; rx_bufpost->msg.request_id = cpu_to_le32(pktid); - address = (long long)(long)physaddr; + address = (u64)physaddr; rx_bufpost->data_buf_len = cpu_to_le16((u16)pktlen); rx_bufpost->data_buf_addr.high_addr = cpu_to_le32(address >> 32); @@ -992,7 +992,7 @@ brcmf_msgbuf_rxbuf_ctrl_post(struct brcmf_msgbuf *msgbuf, bool event_buf, u32 pktlen; dma_addr_t physaddr; struct msgbuf_rx_ioctl_resp_or_event *rx_bufpost; - long long address; + u64 address; u32 pktid; u32 i; @@ -1035,7 +1035,7 @@ brcmf_msgbuf_rxbuf_ctrl_post(struct brcmf_msgbuf *msgbuf, bool event_buf, MSGBUF_TYPE_IOCTLRESP_BUF_POST; rx_bufpost->msg.request_id = cpu_to_le32(pktid); - address = (long long)(long)physaddr; + address = (u64)physaddr; rx_bufpost->host_buf_len = cpu_to_le16((u16)pktlen); rx_bufpost->host_buf_addr.high_addr = cpu_to_le32(address >> 32); @@ -1348,7 +1348,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) { struct brcmf_bus_msgbuf *if_msgbuf; struct brcmf_msgbuf *msgbuf; - long long address; + u64 address; u32 count; if_msgbuf = drvr->bus_if->msgbuf; @@ -1379,7 +1379,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) GFP_KERNEL); if (!msgbuf->ioctbuf) goto fail; - address = (long long)(long)msgbuf->ioctbuf_handle; + address = (u64)msgbuf->ioctbuf_handle; msgbuf->ioctbuf_phys_hi = address >> 32; msgbuf->ioctbuf_phys_lo = address & 0xffffffff; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 905991f..e91fa9a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -959,14 +959,14 @@ brcmf_pcie_init_dmabuffer_for_device(struct brcmf_pciedev_info *devinfo, dma_addr_t *dma_handle) { void *ring; - long long address; + u64 address; ring = dma_alloc_coherent(&devinfo->pdev->dev, size, dma_handle, GFP_KERNEL); if (!ring) return NULL; - address = (long long)(long)*dma_handle; + address = (u64)*dma_handle; brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr, address & 0xffffffff); brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr + 4, address >> 32); @@ -1166,7 +1166,7 @@ brcmf_pcie_release_scratchbuffers(struct brcmf_pciedev_info *devinfo) static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) { - long long address; + u64 address; u32 addr; devinfo->shared.scratch = dma_alloc_coherent(&devinfo->pdev->dev, @@ -1180,7 +1180,7 @@ static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) addr = devinfo->shared.tcm_base_address + BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET; - address = (long long)(long)devinfo->shared.scratch_dmahandle; + address = (u64)devinfo->shared.scratch_dmahandle; brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); addr = devinfo->shared.tcm_base_address + @@ -1198,7 +1198,7 @@ static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) addr = devinfo->shared.tcm_base_address + BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET; - address = (long long)(long)devinfo->shared.ringupd_dmahandle; + address = (u64)devinfo->shared.ringupd_dmahandle; brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); addr = devinfo->shared.tcm_base_address + -- cgit v0.10.2 From f714e58e19a567cc2659ea4645748ba06e3849a7 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 21 Dec 2014 12:43:48 +0100 Subject: brcmfmac: remove unused/duplicate defines in chip.c The source file chip.c contained some duplicate defines and some unused ones. Removing them. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index ddae0b5..519b79e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -101,14 +101,7 @@ /* ARM Cortex M3 core, ID 0x82a */ #define BCM4329_CORE_ARM_BASE 0x18002000 #define BCM4329_RAMSIZE 0x48000 - /* bcm43143 */ -/* SDIO device core */ -#define BCM43143_CORE_BUS_BASE 0x18002000 -/* internal memory core */ -#define BCM43143_CORE_SOCRAM_BASE 0x18004000 -/* ARM Cortex M3 core, ID 0x82a */ -#define BCM43143_CORE_ARM_BASE 0x18003000 #define BCM43143_RAMSIZE 0x70000 #define CORE_SB(base, field) \ @@ -164,13 +157,6 @@ struct brcmf_core_priv { struct brcmf_chip_priv *chip; }; -/* ARM CR4 core specific control flag bits */ -#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020 - -/* D11 core specific control flag bits */ -#define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004 -#define D11_BCMA_IOCTL_PHYRESET 0x0008 - struct brcmf_chip_priv { struct brcmf_chip pub; const struct brcmf_buscore_ops *ops; -- cgit v0.10.2 From 118eb304d0554fbeab8567410bbece5bae533c23 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sun, 21 Dec 2014 12:43:49 +0100 Subject: brcmfmac: Fix WEP configuration for AP mode. When a device is configured for AP mode and it is configured for WEP then the keys are plumbed first, followed by AP configuration. During configuration a down command is given to the firmware which will clear the configured keys. This patch reprograms the WEP keys after AP has been brought up. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 3aecc5f..1172551 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -452,16 +452,16 @@ static void convert_key_from_CPU(struct brcmf_wsec_key *key, } static int -send_key_to_dongle(struct net_device *ndev, struct brcmf_wsec_key *key) +send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key) { int err; struct brcmf_wsec_key_le key_le; convert_key_from_CPU(key, &key_le); - brcmf_netdev_wait_pend8021x(ndev); + brcmf_netdev_wait_pend8021x(ifp); - err = brcmf_fil_bsscfg_data_set(netdev_priv(ndev), "wsec_key", &key_le, + err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le, sizeof(key_le)); if (err) @@ -1670,7 +1670,7 @@ brcmf_set_sharedkey(struct net_device *ndev, brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n", key.len, key.index, key.algo); brcmf_dbg(CONN, "key \"%s\"\n", key.data); - err = send_key_to_dongle(ndev, &key); + err = send_key_to_dongle(netdev_priv(ndev), &key); if (err) return err; @@ -2052,7 +2052,7 @@ brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev, /* check for key index change */ if (key.len == 0) { /* key delete */ - err = send_key_to_dongle(ndev, &key); + err = send_key_to_dongle(ifp, &key); if (err) brcmf_err("key delete error (%d)\n", err); } else { @@ -2108,7 +2108,7 @@ brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev, brcmf_err("Invalid cipher (0x%x)\n", params->cipher); return -EINVAL; } - err = send_key_to_dongle(ndev, &key); + err = send_key_to_dongle(ifp, &key); if (err) brcmf_err("wsec_key error (%d)\n", err); } @@ -2121,7 +2121,7 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, struct key_params *params) { struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_wsec_key key; + struct brcmf_wsec_key *key; s32 val; s32 wsec; s32 err = 0; @@ -2132,54 +2132,62 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, if (!check_vif_up(ifp->vif)) return -EIO; + if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) { + /* we ignore this key index in this case */ + brcmf_err("invalid key index (%d)\n", key_idx); + return -EINVAL; + } + if (mac_addr && (params->cipher != WLAN_CIPHER_SUITE_WEP40) && (params->cipher != WLAN_CIPHER_SUITE_WEP104)) { brcmf_dbg(TRACE, "Exit"); return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params); } - memset(&key, 0, sizeof(key)); - key.len = (u32) params->key_len; - key.index = (u32) key_idx; + key = &ifp->vif->profile.key[key_idx]; + memset(key, 0, sizeof(*key)); - if (key.len > sizeof(key.data)) { - brcmf_err("Too long key length (%u)\n", key.len); + if (params->key_len > sizeof(key->data)) { + brcmf_err("Too long key length (%u)\n", params->key_len); err = -EINVAL; goto done; } - memcpy(key.data, params->key, key.len); + key->len = params->key_len; + key->index = key_idx; - key.flags = BRCMF_PRIMARY_KEY; + memcpy(key->data, params->key, key->len); + + key->flags = BRCMF_PRIMARY_KEY; switch (params->cipher) { case WLAN_CIPHER_SUITE_WEP40: - key.algo = CRYPTO_ALGO_WEP1; + key->algo = CRYPTO_ALGO_WEP1; val = WEP_ENABLED; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); break; case WLAN_CIPHER_SUITE_WEP104: - key.algo = CRYPTO_ALGO_WEP128; + key->algo = CRYPTO_ALGO_WEP128; val = WEP_ENABLED; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); break; case WLAN_CIPHER_SUITE_TKIP: if (!brcmf_is_apmode(ifp->vif)) { brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); - memcpy(keybuf, &key.data[24], sizeof(keybuf)); - memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); - memcpy(&key.data[16], keybuf, sizeof(keybuf)); + memcpy(keybuf, &key->data[24], sizeof(keybuf)); + memcpy(&key->data[24], &key->data[16], sizeof(keybuf)); + memcpy(&key->data[16], keybuf, sizeof(keybuf)); } - key.algo = CRYPTO_ALGO_TKIP; + key->algo = CRYPTO_ALGO_TKIP; val = TKIP_ENABLED; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); break; case WLAN_CIPHER_SUITE_AES_CMAC: - key.algo = CRYPTO_ALGO_AES_CCM; + key->algo = CRYPTO_ALGO_AES_CCM; val = AES_ENABLED; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); break; case WLAN_CIPHER_SUITE_CCMP: - key.algo = CRYPTO_ALGO_AES_CCM; + key->algo = CRYPTO_ALGO_AES_CCM; val = AES_ENABLED; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n"); break; @@ -2189,7 +2197,7 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, goto done; } - err = send_key_to_dongle(ndev, &key); + err = send_key_to_dongle(ifp, key); if (err) goto done; @@ -2222,7 +2230,7 @@ brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, if (!check_vif_up(ifp->vif)) return -EIO; - if (key_idx >= DOT11_MAX_DEFAULT_KEYS) { + if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) { /* we ignore this key index in this case */ brcmf_err("invalid key index (%d)\n", key_idx); return -EINVAL; @@ -2237,7 +2245,7 @@ brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(CONN, "key index (%d)\n", key_idx); /* Set the new key/index */ - err = send_key_to_dongle(ndev, &key); + err = send_key_to_dongle(ifp, &key); brcmf_dbg(TRACE, "Exit\n"); return err; @@ -2305,6 +2313,39 @@ brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, return -EOPNOTSUPP; } +static void +brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp) +{ + s32 err; + u8 key_idx; + struct brcmf_wsec_key *key; + s32 wsec; + + for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) { + key = &ifp->vif->profile.key[key_idx]; + if ((key->algo == CRYPTO_ALGO_WEP1) || + (key->algo == CRYPTO_ALGO_WEP128)) + break; + } + if (key_idx == BRCMF_MAX_DEFAULT_KEYS) + return; + + err = send_key_to_dongle(ifp, key); + if (err) { + brcmf_err("Setting WEP key failed (%d)\n", err); + return; + } + err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); + if (err) { + brcmf_err("get wsec error (%d)\n", err); + return; + } + wsec |= WEP_ENABLED; + err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec); + if (err) + brcmf_err("set wsec error (%d)\n", err); +} + static s32 brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, const u8 *mac, struct station_info *sinfo) @@ -4057,6 +4098,10 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_err("BRCMF_C_UP error (%d)\n", err); goto exit; } + /* On DOWN the firmware removes the WEP keys, reconfigure + * them if they were set. + */ + brcmf_cfg80211_reconfigure_wep(ifp); memset(&join_params, 0, sizeof(join_params)); /* join parameters starts with ssid */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h index 9e98b8d..a5242af 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h @@ -75,6 +75,8 @@ #define BRCMF_VNDR_IE_P2PAF_SHIFT 12 +#define BRCMF_MAX_DEFAULT_KEYS 4 + /** * enum brcmf_scan_status - scan engine status @@ -125,11 +127,13 @@ struct brcmf_cfg80211_security { * @ssid: ssid of associated/associating ap. * @bssid: bssid of joined/joining ibss. * @sec: security information. + * @key: key information */ struct brcmf_cfg80211_profile { struct brcmf_ssid ssid; u8 bssid[ETH_ALEN]; struct brcmf_cfg80211_security sec; + struct brcmf_wsec_key key[BRCMF_MAX_DEFAULT_KEYS]; }; /** diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index effe6d7..e2a9e33 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -1093,9 +1093,8 @@ static int brcmf_get_pend_8021x_cnt(struct brcmf_if *ifp) return atomic_read(&ifp->pend_8021x_cnt); } -int brcmf_netdev_wait_pend8021x(struct net_device *ndev) +int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp) { - struct brcmf_if *ifp = netdev_priv(ndev); int err; err = wait_event_timeout(ifp->pend_8021x_wait, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h index 23f74b1..f2f7d3d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h @@ -29,8 +29,6 @@ /* For supporting multiple interfaces */ #define BRCMF_MAX_IFS 16 -#define DOT11_MAX_DEFAULT_KEYS 4 - /* Small, medium and maximum buffer size for dcmd */ #define BRCMF_DCMD_SMLEN 256 @@ -167,7 +165,7 @@ struct brcmf_skb_reorder_data { u8 *reorder; }; -int brcmf_netdev_wait_pend8021x(struct net_device *ndev); +int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp); /* Return pointer to interface name */ char *brcmf_ifname(struct brcmf_pub *drvr, int idx); -- cgit v0.10.2 From d2e2472cd197e32f2c90beb016066b521ce68049 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sun, 21 Dec 2014 12:43:50 +0100 Subject: brcmfmac: Change error log in standard log for rxbufpost. When there is no room in the ring for rxbufpost an error is logged, however this happens quite frequently and can be considered normal and is certainly recoverable. This patch changes the erorr into a normal msgbuf log. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 3ff6acf..ee147f5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -894,7 +894,7 @@ static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count) count, &alloced); if (!ret_ptr) { - brcmf_err("Failed to reserve space in commonring\n"); + brcmf_dbg(MSGBUF, "Failed to reserve space in commonring\n"); return 0; } -- cgit v0.10.2 From 63db1a499cee154826d63a7d0eb096fa666ab9fb Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 21 Dec 2014 12:43:51 +0100 Subject: brcmfmac: follow user-space regulatory domain selection When user-space uses a valid ISO-3166-1 country code for its regulatory domain selection, the driver will try to configure the firmware to use the same. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 1172551..2471dd5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -3982,7 +3982,6 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n", settings->ssid, settings->ssid_len, settings->auth_type, settings->inactivity_timeout); - dev_role = ifp->vif->wdev.iftype; mbss = ifp->vif->mbss; @@ -5921,6 +5920,29 @@ int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, vif_event_equals(event, action), timeout); } +static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *req) +{ + struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct brcmf_fil_country_le ccreq; + int i; + + brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator, + req->alpha2[0], req->alpha2[1]); + + /* ignore non-ISO3166 country codes */ + for (i = 0; i < sizeof(req->alpha2); i++) + if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') { + brcmf_err("not a ISO3166 code\n"); + return; + } + memset(&ccreq, 0, sizeof(ccreq)); + ccreq.rev = cpu_to_le32(-1); + memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2)); + brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq)); +} + static void brcmf_free_wiphy(struct wiphy *wiphy) { kfree(wiphy->iface_combinations); @@ -5997,6 +6019,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, goto priv_out; brcmf_dbg(INFO, "Registering custom regulatory\n"); + wiphy->reg_notifier = brcmf_cfg80211_reg_notifier; wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index 50891c0..619669b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -112,6 +112,7 @@ #define BRCMF_WOWL_MAXPATTERNS 8 #define BRCMF_WOWL_MAXPATTERNSIZE 128 +#define BRCMF_COUNTRY_BUF_SZ 4 /* join preference types for join_pref iovar */ enum brcmf_join_pref_types { @@ -525,4 +526,17 @@ struct brcmf_mbss_ssid_le { unsigned char SSID[32]; }; +/** + * struct brcmf_fil_country_le - country configuration structure. + * + * @country_abbrev: null-terminated country code used in the country IE. + * @rev: revision specifier for ccode. on set, -1 indicates unspecified. + * @ccode: null-terminated built-in country code. + */ +struct brcmf_fil_country_le { + char country_abbrev[BRCMF_COUNTRY_BUF_SZ]; + __le32 rev; + char ccode[BRCMF_COUNTRY_BUF_SZ]; +}; + #endif /* FWIL_TYPES_H_ */ -- cgit v0.10.2 From 6b89dcb35bfc787ce21401417f7170997d8490ed Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sun, 21 Dec 2014 12:43:52 +0100 Subject: brcmfmac: signal completion of 802.1x. Use cfg80211 change_station to signal the completion of 802.1x to firmware. This allows FW to take appropriate actions. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 2471dd5..c896fe2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -38,6 +38,7 @@ #include "proto.h" #include "vendor.h" #include "bus.h" +#include "common.h" #define BRCMF_SCAN_IE_LEN_MAX 2048 #define BRCMF_PNO_VERSION 2 @@ -4241,6 +4242,34 @@ brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev, return err; } +static int +brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev, + const u8 *mac, struct station_parameters *params) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err; + + brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac, + params->sta_flags_mask, params->sta_flags_set); + + /* Ignore all 00 MAC */ + if (is_zero_ether_addr(mac)) + return 0; + + if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) + return 0; + + if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE, + (void *)mac, ETH_ALEN); + else + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE, + (void *)mac, ETH_ALEN); + if (err < 0) + brcmf_err("Setting SCB (de-)authorize failed, %d\n", err); + + return err; +} static void brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy, @@ -4515,6 +4544,7 @@ static struct cfg80211_ops wl_cfg80211_ops = { .stop_ap = brcmf_cfg80211_stop_ap, .change_beacon = brcmf_cfg80211_change_beacon, .del_station = brcmf_cfg80211_del_station, + .change_station = brcmf_cfg80211_change_station, .sched_scan_start = brcmf_cfg80211_sched_scan_start, .sched_scan_stop = brcmf_cfg80211_sched_scan_stop, .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.c b/drivers/net/wireless/brcm80211/brcmfmac/common.c index 1861a13..ddf05af 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/common.c @@ -25,6 +25,9 @@ #include "fwil.h" #include "fwil_types.h" #include "tracepoint.h" +#include "common.h" + +const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; #define BRCMF_DEFAULT_BCN_TIMEOUT 3 #define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.h b/drivers/net/wireless/brcm80211/brcmfmac/common.h new file mode 100644 index 0000000..0d39d80 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/common.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef BRCMFMAC_COMMON_H +#define BRCMFMAC_COMMON_H + +extern const u8 ALLFFMAC[ETH_ALEN]; + +#endif /* BRCMFMAC_COMMON_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index 44f3a84..910fbb5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -25,6 +25,7 @@ #include "proto.h" #include "flowring.h" #include "msgbuf.h" +#include "common.h" #define BRCMF_FLOWRING_HIGH 1024 @@ -34,9 +35,6 @@ #define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] + fifo + ifidx * 16) #define BRCMF_FLOWRING_HASH_STA(fifo, ifidx) (fifo + ifidx * 16) -static const u8 ALLZEROMAC[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; -static const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - static const u8 brcmf_flowring_prio2fifo[] = { 1, 0, @@ -137,7 +135,7 @@ u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN], hash = flow->hash; for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { if ((hash[hash_idx].ifidx == BRCMF_FLOWRING_INVALID_IFIDX) && - (memcmp(hash[hash_idx].mac, ALLZEROMAC, ETH_ALEN) == 0)) { + (is_zero_ether_addr(hash[hash_idx].mac))) { found = true; break; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h index a30be68..3ede91d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h @@ -60,6 +60,8 @@ #define BRCMF_C_GET_CURR_RATESET 114 #define BRCMF_C_GET_AP 117 #define BRCMF_C_SET_AP 118 +#define BRCMF_C_SET_SCB_AUTHORIZE 121 +#define BRCMF_C_SET_SCB_DEAUTHORIZE 122 #define BRCMF_C_GET_RSSI 127 #define BRCMF_C_GET_WSEC 133 #define BRCMF_C_SET_WSEC 134 -- cgit v0.10.2 From 98027769828f772c7ce69b6e58d37b78ebe8ab28 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 21 Dec 2014 12:43:53 +0100 Subject: brcmfmac: enable 802.11d support in firmware When the driver gets beacon info containing a country IE from user-space upon .start_ap() callback, it will enable regulatory 802.11d support. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index c896fe2..1783c03 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -3966,6 +3966,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(ndev); const struct brcmf_tlv *ssid_ie; + const struct brcmf_tlv *country_ie; struct brcmf_ssid_le ssid_le; s32 err = -EPERM; const struct brcmf_tlv *rsn_ie; @@ -3975,6 +3976,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_fil_bss_enable_le bss_enable; u16 chanspec; bool mbss; + int is_11d; brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n", settings->chandef.chan->hw_value, @@ -3986,6 +3988,13 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, dev_role = ifp->vif->wdev.iftype; mbss = ifp->vif->mbss; + /* store current 11d setting */ + brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d); + country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail, + settings->beacon.tail_len, + WLAN_EID_COUNTRY); + is_11d = country_ie ? 1 : 0; + memset(&ssid_le, 0, sizeof(ssid_le)); if (settings->ssid == NULL || settings->ssid_len == 0) { ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; @@ -4051,6 +4060,14 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, goto exit; } + if (is_11d != ifp->vif->is_11d) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY, + is_11d); + if (err < 0) { + brcmf_err("Regulatory Set Error, %d\n", err); + goto exit; + } + } if (settings->beacon_interval) { err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, settings->beacon_interval); @@ -4083,6 +4100,10 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_err("SET INFRA error %d\n", err); goto exit; } + } else if (WARN_ON(is_11d != ifp->vif->is_11d)) { + /* Multiple-BSS should use same 11d configuration */ + err = -EINVAL; + goto exit; } if (dev_role == NL80211_IFTYPE_AP) { if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss)) @@ -4178,6 +4199,11 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) brcmf_err("setting INFRA mode failed %d\n", err); if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) brcmf_fil_iovar_int_set(ifp, "mbss", 0); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY, + ifp->vif->is_11d); + if (err < 0) + brcmf_err("restoring REGULATORY setting failed %d\n", + err); /* Bring device back up so it can be used again */ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); if (err < 0) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h index a5242af..d9e6d01 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h @@ -200,6 +200,7 @@ struct brcmf_cfg80211_vif { struct list_head list; u16 mgmt_rx_reg; bool mbss; + int is_11d; }; /* association inform */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h index 3ede91d..37345e7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h @@ -43,6 +43,8 @@ #define BRCMF_C_SET_RADIO 38 #define BRCMF_C_GET_PHYTYPE 39 #define BRCMF_C_SET_KEY 45 +#define BRCMF_C_GET_REGULATORY 46 +#define BRCMF_C_SET_REGULATORY 47 #define BRCMF_C_SET_PASSIVE_SCAN 49 #define BRCMF_C_SCAN 50 #define BRCMF_C_SCAN_RESULTS 51 -- cgit v0.10.2 From c0c3163a7213107478804ad92ee3da69f12fe346 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 Dec 2014 22:14:32 +0100 Subject: ath5k: drop owner assignment from platform_drivers This platform_driver does not need to set an owner, it will be populated by the driver core. Signed-off-by: Wolfram Sang Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c index 8f387cf..2ca88b5 100644 --- a/drivers/net/wireless/ath/ath5k/ahb.c +++ b/drivers/net/wireless/ath/ath5k/ahb.c @@ -227,7 +227,6 @@ static struct platform_driver ath_ahb_driver = { .remove = ath_ahb_remove, .driver = { .name = "ar231x-wmac", - .owner = THIS_MODULE, }, }; -- cgit v0.10.2 From 72df63100d54bc801700de2c6f525d0fd8bfd799 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Tue, 23 Dec 2014 19:14:05 +0530 Subject: mwifiex: report tdls peers in debugfs This patch add provision to show TDLS peer table in debugfs file. Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c index 2713f7ac..b506032 100644 --- a/drivers/net/wireless/mwifiex/debugfs.c +++ b/drivers/net/wireless/mwifiex/debugfs.c @@ -493,6 +493,15 @@ mwifiex_debug_read(struct file *file, char __user *ubuf, } } + if (info.tdls_peer_num) { + p += sprintf(p, "TDLS peer table:\n"); + for (i = 0; i < info.tdls_peer_num; i++) { + p += sprintf(p, "peer = %pM", + info.tdls_list[i].peer_addr); + p += sprintf(p, "\n"); + } + } + ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, (unsigned long) p - page); diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h index 2269acf..21b5130 100644 --- a/drivers/net/wireless/mwifiex/decl.h +++ b/drivers/net/wireless/mwifiex/decl.h @@ -41,6 +41,7 @@ #define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED 2 #define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED 16 +#define MWIFIEX_MAX_TDLS_PEER_SUPPORTED 8 #define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE 64 #define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE 64 diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h index 0847f3e..dcf4bdb 100644 --- a/drivers/net/wireless/mwifiex/ioctl.h +++ b/drivers/net/wireless/mwifiex/ioctl.h @@ -180,7 +180,11 @@ struct mwifiex_ds_tx_ba_stream_tbl { u8 amsdu; }; -#define DBG_CMD_NUM 5 +#define DBG_CMD_NUM 5 + +struct tdls_peer_info { + u8 peer_addr[ETH_ALEN]; +}; struct mwifiex_debug_info { u32 int_counter; @@ -193,6 +197,9 @@ struct mwifiex_debug_info { u32 rx_tbl_num; struct mwifiex_ds_rx_reorder_tbl rx_tbl [MWIFIEX_MAX_RX_BASTREAM_SUPPORTED]; + u32 tdls_peer_num; + struct tdls_peer_info tdls_list + [MWIFIEX_MAX_TDLS_PEER_SUPPORTED]; u16 ps_mode; u32 ps_state; u8 is_deep_sleep; diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index e66993c..b3e23f6 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -1324,6 +1324,8 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, u8 *buf, int len); int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action); int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac); +int mwifiex_get_tdls_list(struct mwifiex_private *priv, + struct tdls_peer_info *buf); void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv); bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv); u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band, diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index 22884b4..efa81d8 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -1123,6 +1123,36 @@ int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac) return TDLS_NOT_SETUP; } +int mwifiex_get_tdls_list(struct mwifiex_private *priv, + struct tdls_peer_info *buf) +{ + struct mwifiex_sta_node *sta_ptr; + struct tdls_peer_info *peer = buf; + int count = 0; + unsigned long flags; + + if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) + return 0; + + /* make sure we are in station mode and connected */ + if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) + return 0; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + list_for_each_entry(sta_ptr, &priv->sta_list, list) { + if (sta_ptr->tdls_status == TDLS_SETUP_COMPLETE) { + ether_addr_copy(peer->peer_addr, sta_ptr->mac_addr); + peer++; + count++; + if (count >= MWIFIEX_MAX_TDLS_PEER_SUPPORTED) + break; + } + } + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + + return count; +} + void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv) { struct mwifiex_sta_node *sta_ptr; diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c index b1768fb..3bd9179 100644 --- a/drivers/net/wireless/mwifiex/util.c +++ b/drivers/net/wireless/mwifiex/util.c @@ -97,6 +97,8 @@ int mwifiex_get_debug_info(struct mwifiex_private *priv, info->rx_tbl); info->tx_tbl_num = mwifiex_get_tx_ba_stream_tbl(priv, info->tx_tbl); + info->tdls_peer_num = mwifiex_get_tdls_list(priv, + info->tdls_list); info->ps_mode = adapter->ps_mode; info->ps_state = adapter->ps_state; info->is_deep_sleep = adapter->is_deep_sleep; -- cgit v0.10.2 From d35b6392281f3368c8d31be7ce4356ecf6580605 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Tue, 23 Dec 2014 19:14:06 +0530 Subject: mwifiex: add bcn_rcv_cnt and bcn_miss_cnt in getlog debugfs This patch add receive beacon count and miss beacon count statistics in debugfs getlog item. Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c index b506032..3d2fb9a 100644 --- a/drivers/net/wireless/mwifiex/debugfs.c +++ b/drivers/net/wireless/mwifiex/debugfs.c @@ -297,6 +297,8 @@ mwifiex_fw_dump_read(struct file *file, char __user *ubuf, * - Number of FCS errors * - Number of Tx frames * - WEP ICV error counts + * - Number of received beacons + * - Number of missed beacons */ static ssize_t mwifiex_getlog_read(struct file *file, char __user *ubuf, @@ -333,7 +335,9 @@ mwifiex_getlog_read(struct file *file, char __user *ubuf, "wepicverrcnt-1 %u\n" "wepicverrcnt-2 %u\n" "wepicverrcnt-3 %u\n" - "wepicverrcnt-4 %u\n", + "wepicverrcnt-4 %u\n" + "bcn_rcv_cnt %u\n" + "bcn_miss_cnt %u\n", stats.mcast_tx_frame, stats.failed, stats.retry, @@ -349,7 +353,9 @@ mwifiex_getlog_read(struct file *file, char __user *ubuf, stats.wep_icv_error[0], stats.wep_icv_error[1], stats.wep_icv_error[2], - stats.wep_icv_error[3]); + stats.wep_icv_error[3], + stats.bcn_rcv_cnt, + stats.bcn_miss_cnt); ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index fb5936e..a1b8d53 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -1076,6 +1076,8 @@ struct host_cmd_ds_802_11_get_log { __le32 tx_frame; __le32 reserved; __le32 wep_icv_err_cnt[4]; + __le32 bcn_rcv_cnt; + __le32 bcn_miss_cnt; }; /* Enumeration for rate format */ diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h index dcf4bdb..d2b05c3 100644 --- a/drivers/net/wireless/mwifiex/ioctl.h +++ b/drivers/net/wireless/mwifiex/ioctl.h @@ -137,6 +137,8 @@ struct mwifiex_ds_get_stats { u32 fcs_error; u32 tx_frame; u32 wep_icv_error[4]; + u32 bcn_rcv_cnt; + u32 bcn_miss_cnt; }; #define MWIFIEX_MAX_VER_STR_LEN 128 diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index b65e101..65d10a3 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -248,6 +248,8 @@ static int mwifiex_ret_get_log(struct mwifiex_private *priv, le32_to_cpu(get_log->wep_icv_err_cnt[2]); stats->wep_icv_error[3] = le32_to_cpu(get_log->wep_icv_err_cnt[3]); + stats->bcn_rcv_cnt = le32_to_cpu(get_log->bcn_rcv_cnt); + stats->bcn_miss_cnt = le32_to_cpu(get_log->bcn_miss_cnt); } return 0; -- cgit v0.10.2 From cbf6e05527a7654ac1c4f4787dfd7a182fcc0c73 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Tue, 23 Dec 2014 19:14:07 +0530 Subject: mwifiex: add rx histogram statistics support This patch add a new debugfs item histogram used for reporting rx data packet statitics(rx rate, snr, noise floor, signal strenth) to userspace. Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 4a66a655..f8b1ce0 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2386,6 +2386,10 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) + kfree(priv->hist_data); + return 0; } EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf); diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/mwifiex/cfp.c index b8242eb..f494fc7 100644 --- a/drivers/net/wireless/mwifiex/cfp.c +++ b/drivers/net/wireless/mwifiex/cfp.c @@ -509,3 +509,21 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates) return k; } + +u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv, + u8 rx_rate, u8 rate_info) +{ + u8 rate_index = 0; + + /* HT40 */ + if ((rate_info & BIT(0)) && (rate_info & BIT(1))) + rate_index = MWIFIEX_RATE_INDEX_MCS0 + + MWIFIEX_BW20_MCS_NUM + rx_rate; + else if (rate_info & BIT(0)) /* HT20 */ + rate_index = MWIFIEX_RATE_INDEX_MCS0 + rx_rate; + else + rate_index = (rx_rate > MWIFIEX_RATE_INDEX_OFDM0) ? + rx_rate - 1 : rx_rate; + + return rate_index; +} diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c index 3d2fb9a..fe1eb74 100644 --- a/drivers/net/wireless/mwifiex/debugfs.c +++ b/drivers/net/wireless/mwifiex/debugfs.c @@ -366,6 +366,103 @@ free_and_exit: return ret; } +/* Sysfs histogram file read handler. + * + * This function is called when the 'histogram' file is opened for reading + * It prints the following histogram information - + * - Number of histogram samples + * - Receive packet number of each rx_rate + * - Receive packet number of each snr + * - Receive packet number of each nosie_flr + * - Receive packet number of each signal streath + */ +static ssize_t +mwifiex_histogram_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *)file->private_data; + ssize_t ret; + struct mwifiex_histogram_data *phist_data; + int i, value; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *)page; + + if (!p) + return -ENOMEM; + + if (!priv || !priv->hist_data) + return -EFAULT; + phist_data = priv->hist_data; + + p += sprintf(p, "\n" + "total samples = %d\n", + atomic_read(&phist_data->num_samples)); + + p += sprintf(p, "rx rates (in Mbps): 0=1M 1=2M"); + p += sprintf(p, "2=5.5M 3=11M 4=6M 5=9M 6=12M\n"); + p += sprintf(p, "7=18M 8=24M 9=36M 10=48M 11=54M"); + p += sprintf(p, "12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n"); + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { + p += sprintf(p, "44-53=MCS0-9(VHT:BW20)"); + p += sprintf(p, "54-63=MCS0-9(VHT:BW40)"); + p += sprintf(p, "64-73=MCS0-9(VHT:BW80)\n\n"); + } else { + p += sprintf(p, "\n"); + } + + for (i = 0; i < MWIFIEX_MAX_RX_RATES; i++) { + value = atomic_read(&phist_data->rx_rate[i]); + if (value) + p += sprintf(p, "rx_rate[%02d] = %d\n", i, value); + } + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { + for (i = MWIFIEX_MAX_RX_RATES; i < MWIFIEX_MAX_AC_RX_RATES; + i++) { + value = atomic_read(&phist_data->rx_rate[i]); + if (value) + p += sprintf(p, "rx_rate[%02d] = %d\n", + i, value); + } + } + + for (i = 0; i < MWIFIEX_MAX_SNR; i++) { + value = atomic_read(&phist_data->snr[i]); + if (value) + p += sprintf(p, "snr[%02ddB] = %d\n", i, value); + } + for (i = 0; i < MWIFIEX_MAX_NOISE_FLR; i++) { + value = atomic_read(&phist_data->noise_flr[i]); + if (value) + p += sprintf(p, "noise_flr[-%02ddBm] = %d\n", + (int)(i-128), value); + } + for (i = 0; i < MWIFIEX_MAX_SIG_STRENGTH; i++) { + value = atomic_read(&phist_data->sig_str[i]); + if (value) + p += sprintf(p, "sig_strength[-%02ddBm] = %d\n", + i, value); + } + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page, + (unsigned long)p - page); + + return ret; +} + +static ssize_t +mwifiex_histogram_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = (void *)file->private_data; + + if (priv && priv->hist_data) + mwifiex_hist_data_reset(priv); + return 0; +} + static struct mwifiex_debug_info info; /* @@ -832,6 +929,7 @@ MWIFIEX_DFS_FILE_READ_OPS(fw_dump); MWIFIEX_DFS_FILE_OPS(regrdwr); MWIFIEX_DFS_FILE_OPS(rdeeprom); MWIFIEX_DFS_FILE_OPS(hscfg); +MWIFIEX_DFS_FILE_OPS(histogram); /* * This function creates the debug FS directory structure and the files. @@ -855,6 +953,7 @@ mwifiex_dev_debugfs_init(struct mwifiex_private *priv) MWIFIEX_DFS_ADD_FILE(rdeeprom); MWIFIEX_DFS_ADD_FILE(fw_dump); MWIFIEX_DFS_ADD_FILE(hscfg); + MWIFIEX_DFS_ADD_FILE(histogram); } /* diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h index 21b5130..cdca8ed 100644 --- a/drivers/net/wireless/mwifiex/decl.h +++ b/drivers/net/wireless/mwifiex/decl.h @@ -93,6 +93,14 @@ #define MWIFIEX_TDLS_MAX_FAIL_COUNT 4 #define MWIFIEX_AUTO_TDLS_IDLE_TIME 10 +/* 54M rates, index from 0 to 11 */ +#define MWIFIEX_RATE_INDEX_MCS0 12 +/* 12-27=MCS0-15(BW20) */ +#define MWIFIEX_BW20_MCS_NUM 15 + +/* Rate index for OFDM 0 */ +#define MWIFIEX_RATE_INDEX_OFDM0 4 + enum mwifiex_bss_type { MWIFIEX_BSS_TYPE_STA = 0, MWIFIEX_BSS_TYPE_UAP = 1, @@ -205,4 +213,20 @@ struct mwifiex_chan_stats { u16 cca_scan_dur; u16 cca_busy_dur; } __packed; + +#define MWIFIEX_HIST_MAX_SAMPLES 1048576 +#define MWIFIEX_MAX_RX_RATES 44 +#define MWIFIEX_MAX_AC_RX_RATES 74 +#define MWIFIEX_MAX_SNR 256 +#define MWIFIEX_MAX_NOISE_FLR 256 +#define MWIFIEX_MAX_SIG_STRENGTH 256 + +struct mwifiex_histogram_data { + atomic_t rx_rate[MWIFIEX_MAX_AC_RX_RATES]; + atomic_t snr[MWIFIEX_MAX_SNR]; + atomic_t noise_flr[MWIFIEX_MAX_NOISE_FLR]; + atomic_t sig_str[MWIFIEX_MAX_SIG_STRENGTH]; + atomic_t num_samples; +}; + #endif /* !_MWIFIEX_DECL_H_ */ diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index d4d2223..0c44c5ee 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -847,6 +847,7 @@ static const struct net_device_ops mwifiex_netdev_ops = { * - Nick name : Set to null * - Number of Tx timeout : Set to 0 * - Device address : Set to current address + * - Rx histogram statistc : Set to 0 * * In addition, the CFG80211 work queue is also created. */ @@ -867,6 +868,13 @@ void mwifiex_init_priv_params(struct mwifiex_private *priv, priv->rsn_idx = MWIFIEX_AUTO_IDX_MASK; priv->num_tx_timeout = 0; memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + priv->hist_data = kmalloc(sizeof(*priv->hist_data), GFP_KERNEL); + if (priv->hist_data) + mwifiex_hist_data_reset(priv); + } } /* diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index b3e23f6..6f7e060 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -582,6 +582,8 @@ struct mwifiex_private { struct idr ack_status_frames; /* spin lock for ack status */ spinlock_t ack_status_lock; + /** rx histogram data */ + struct mwifiex_histogram_data *hist_data; }; enum mwifiex_ba_status { @@ -1350,6 +1352,14 @@ struct sk_buff * mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv, struct sk_buff *skb, u8 flag, u64 *cookie); +void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr, + s8 nflr); +void mwifiex_hist_data_reset(struct mwifiex_private *priv); +void mwifiex_hist_data_add(struct mwifiex_private *priv, + u8 rx_rate, s8 snr, s8 nflr); +u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv, + u8 rx_rate, u8 ht_info); + #ifdef CONFIG_DEBUG_FS void mwifiex_debugfs_init(void); void mwifiex_debugfs_remove(void); diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index b8c171d..fbec95b 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -90,6 +90,10 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code) priv->is_data_rate_auto = true; priv->data_rate = 0; + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && priv->hist_data) + mwifiex_hist_data_reset(priv); + if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { priv->adhoc_state = ADHOC_IDLE; priv->adhoc_is_link_sensed = false; diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c index c2ad3b6..b8729c9 100644 --- a/drivers/net/wireless/mwifiex/sta_rx.c +++ b/drivers/net/wireless/mwifiex/sta_rx.c @@ -90,6 +90,7 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv, struct ethhdr *eth; u16 rx_pkt_off, rx_pkt_len; u8 *offset; + u8 adj_rx_rate = 0; local_rx_pd = (struct rxpd *) (skb->data); @@ -155,6 +156,14 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv, priv->rxpd_htinfo = local_rx_pd->ht_info; + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + adj_rx_rate = mwifiex_adjust_data_rate(priv, priv->rxpd_rate, + priv->rxpd_htinfo); + mwifiex_hist_data_add(priv, adj_rx_rate, local_rx_pd->snr, + local_rx_pd->nf); + } + ret = mwifiex_recv_packet(priv, skb); if (ret == -1) dev_err(priv->adapter->dev, "recv packet failed\n"); diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c index c54a537..f31086c 100644 --- a/drivers/net/wireless/mwifiex/uap_event.c +++ b/drivers/net/wireless/mwifiex/uap_event.c @@ -132,6 +132,8 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) dev_dbg(adapter->dev, "AP EVENT: event id: %#x\n", eventcause); memcpy(priv->netdev->dev_addr, adapter->event_body + 2, ETH_ALEN); + if (priv->hist_data) + mwifiex_hist_data_reset(priv); break; case EVENT_UAP_MIC_COUNTERMEASURES: /* For future development */ diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c index 3bd9179..6a83c04 100644 --- a/drivers/net/wireless/mwifiex/util.c +++ b/drivers/net/wireless/mwifiex/util.c @@ -406,3 +406,44 @@ void mwifiex_del_all_sta_list(struct mwifiex_private *priv) spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); return; } + +/* This function adds histogram data to histogram array*/ +void mwifiex_hist_data_add(struct mwifiex_private *priv, + u8 rx_rate, s8 snr, s8 nflr) +{ + struct mwifiex_histogram_data *phist_data = priv->hist_data; + + if (atomic_read(&phist_data->num_samples) > MWIFIEX_HIST_MAX_SAMPLES) + mwifiex_hist_data_reset(priv); + mwifiex_hist_data_set(priv, rx_rate, snr, nflr); +} + +/* function to add histogram record */ +void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr, + s8 nflr) +{ + struct mwifiex_histogram_data *phist_data = priv->hist_data; + + atomic_inc(&phist_data->num_samples); + atomic_inc(&phist_data->rx_rate[rx_rate]); + atomic_inc(&phist_data->snr[snr]); + atomic_inc(&phist_data->noise_flr[128 + nflr]); + atomic_inc(&phist_data->sig_str[nflr - snr]); +} + +/* function to reset histogram data during init/reset */ +void mwifiex_hist_data_reset(struct mwifiex_private *priv) +{ + int ix; + struct mwifiex_histogram_data *phist_data = priv->hist_data; + + atomic_set(&phist_data->num_samples, 0); + for (ix = 0; ix < MWIFIEX_MAX_AC_RX_RATES; ix++) + atomic_set(&phist_data->rx_rate[ix], 0); + for (ix = 0; ix < MWIFIEX_MAX_SNR; ix++) + atomic_set(&phist_data->snr[ix], 0); + for (ix = 0; ix < MWIFIEX_MAX_NOISE_FLR; ix++) + atomic_set(&phist_data->noise_flr[ix], 0); + for (ix = 0; ix < MWIFIEX_MAX_SIG_STRENGTH; ix++) + atomic_set(&phist_data->sig_str[ix], 0); +} -- cgit v0.10.2 From 617543033ed77a4d144555202b4d8e43fcd6ba1b Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Tue, 23 Dec 2014 19:14:08 +0530 Subject: mwifiex: move pm_wakeup_card_complete definition to usb.c Move mwifiex_pm_wakeup_card_complete handler to source file. Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index 1b56495..8ae7a85 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -1010,6 +1010,13 @@ static void mwifiex_usb_submit_rem_rx_urbs(struct mwifiex_adapter *adapter) } } +/* This function is called after the card has woken up. */ +static inline int +mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) +{ + return 0; +} + static struct mwifiex_if_ops usb_ops = { .register_dev = mwifiex_register_dev, .unregister_dev = mwifiex_unregister_dev, diff --git a/drivers/net/wireless/mwifiex/usb.h b/drivers/net/wireless/mwifiex/usb.h index a7cbba1..0ad1beb 100644 --- a/drivers/net/wireless/mwifiex/usb.h +++ b/drivers/net/wireless/mwifiex/usb.h @@ -96,11 +96,4 @@ struct fw_data { u8 data[1]; }; -/* This function is called after the card has woken up. */ -static inline int -mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) -{ - return 0; -} - #endif /*_MWIFIEX_USB_H */ -- cgit v0.10.2 From bb5097fec91efd3253d2b769ba2ccd9f8d67a5d6 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Tue, 23 Dec 2014 19:14:09 +0530 Subject: mwifiex: move debug_data dump function to common utililty file mwifiex_debug_info also need be save to file when firmware dump happens. Move its dump implementation function to commmon utilility file, thus it can be reused in firmware dump function. Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c index fe1eb74..1fb329d 100644 --- a/drivers/net/wireless/mwifiex/debugfs.c +++ b/drivers/net/wireless/mwifiex/debugfs.c @@ -39,111 +39,6 @@ static char *bss_modes[] = { "P2P_DEVICE", }; -/* size/addr for mwifiex_debug_info */ -#define item_size(n) (FIELD_SIZEOF(struct mwifiex_debug_info, n)) -#define item_addr(n) (offsetof(struct mwifiex_debug_info, n)) - -/* size/addr for struct mwifiex_adapter */ -#define adapter_item_size(n) (FIELD_SIZEOF(struct mwifiex_adapter, n)) -#define adapter_item_addr(n) (offsetof(struct mwifiex_adapter, n)) - -struct mwifiex_debug_data { - char name[32]; /* variable/array name */ - u32 size; /* size of the variable/array */ - size_t addr; /* address of the variable/array */ - int num; /* number of variables in an array */ -}; - -static struct mwifiex_debug_data items[] = { - {"int_counter", item_size(int_counter), - item_addr(int_counter), 1}, - {"wmm_ac_vo", item_size(packets_out[WMM_AC_VO]), - item_addr(packets_out[WMM_AC_VO]), 1}, - {"wmm_ac_vi", item_size(packets_out[WMM_AC_VI]), - item_addr(packets_out[WMM_AC_VI]), 1}, - {"wmm_ac_be", item_size(packets_out[WMM_AC_BE]), - item_addr(packets_out[WMM_AC_BE]), 1}, - {"wmm_ac_bk", item_size(packets_out[WMM_AC_BK]), - item_addr(packets_out[WMM_AC_BK]), 1}, - {"tx_buf_size", item_size(tx_buf_size), - item_addr(tx_buf_size), 1}, - {"curr_tx_buf_size", item_size(curr_tx_buf_size), - item_addr(curr_tx_buf_size), 1}, - {"ps_mode", item_size(ps_mode), - item_addr(ps_mode), 1}, - {"ps_state", item_size(ps_state), - item_addr(ps_state), 1}, - {"is_deep_sleep", item_size(is_deep_sleep), - item_addr(is_deep_sleep), 1}, - {"wakeup_dev_req", item_size(pm_wakeup_card_req), - item_addr(pm_wakeup_card_req), 1}, - {"wakeup_tries", item_size(pm_wakeup_fw_try), - item_addr(pm_wakeup_fw_try), 1}, - {"hs_configured", item_size(is_hs_configured), - item_addr(is_hs_configured), 1}, - {"hs_activated", item_size(hs_activated), - item_addr(hs_activated), 1}, - {"num_tx_timeout", item_size(num_tx_timeout), - item_addr(num_tx_timeout), 1}, - {"is_cmd_timedout", item_size(is_cmd_timedout), - item_addr(is_cmd_timedout), 1}, - {"timeout_cmd_id", item_size(timeout_cmd_id), - item_addr(timeout_cmd_id), 1}, - {"timeout_cmd_act", item_size(timeout_cmd_act), - item_addr(timeout_cmd_act), 1}, - {"last_cmd_id", item_size(last_cmd_id), - item_addr(last_cmd_id), DBG_CMD_NUM}, - {"last_cmd_act", item_size(last_cmd_act), - item_addr(last_cmd_act), DBG_CMD_NUM}, - {"last_cmd_index", item_size(last_cmd_index), - item_addr(last_cmd_index), 1}, - {"last_cmd_resp_id", item_size(last_cmd_resp_id), - item_addr(last_cmd_resp_id), DBG_CMD_NUM}, - {"last_cmd_resp_index", item_size(last_cmd_resp_index), - item_addr(last_cmd_resp_index), 1}, - {"last_event", item_size(last_event), - item_addr(last_event), DBG_CMD_NUM}, - {"last_event_index", item_size(last_event_index), - item_addr(last_event_index), 1}, - {"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure), - item_addr(num_cmd_host_to_card_failure), 1}, - {"num_cmd_sleep_cfm_fail", - item_size(num_cmd_sleep_cfm_host_to_card_failure), - item_addr(num_cmd_sleep_cfm_host_to_card_failure), 1}, - {"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure), - item_addr(num_tx_host_to_card_failure), 1}, - {"num_evt_deauth", item_size(num_event_deauth), - item_addr(num_event_deauth), 1}, - {"num_evt_disassoc", item_size(num_event_disassoc), - item_addr(num_event_disassoc), 1}, - {"num_evt_link_lost", item_size(num_event_link_lost), - item_addr(num_event_link_lost), 1}, - {"num_cmd_deauth", item_size(num_cmd_deauth), - item_addr(num_cmd_deauth), 1}, - {"num_cmd_assoc_ok", item_size(num_cmd_assoc_success), - item_addr(num_cmd_assoc_success), 1}, - {"num_cmd_assoc_fail", item_size(num_cmd_assoc_failure), - item_addr(num_cmd_assoc_failure), 1}, - {"cmd_sent", item_size(cmd_sent), - item_addr(cmd_sent), 1}, - {"data_sent", item_size(data_sent), - item_addr(data_sent), 1}, - {"cmd_resp_received", item_size(cmd_resp_received), - item_addr(cmd_resp_received), 1}, - {"event_received", item_size(event_received), - item_addr(event_received), 1}, - - /* variables defined in struct mwifiex_adapter */ - {"cmd_pending", adapter_item_size(cmd_pending), - adapter_item_addr(cmd_pending), 1}, - {"tx_pending", adapter_item_size(tx_pending), - adapter_item_addr(tx_pending), 1}, - {"rx_pending", adapter_item_size(rx_pending), - adapter_item_addr(rx_pending), 1}, -}; - -static int num_of_items = ARRAY_SIZE(items); - /* * Proc info file read handler. * @@ -518,13 +413,9 @@ mwifiex_debug_read(struct file *file, char __user *ubuf, { struct mwifiex_private *priv = (struct mwifiex_private *) file->private_data; - struct mwifiex_debug_data *d = &items[0]; unsigned long page = get_zeroed_page(GFP_KERNEL); char *p = (char *) page; ssize_t ret; - size_t size, addr; - long val; - int i, j; if (!p) return -ENOMEM; @@ -533,77 +424,7 @@ mwifiex_debug_read(struct file *file, char __user *ubuf, if (ret) goto free_and_exit; - for (i = 0; i < num_of_items; i++) { - p += sprintf(p, "%s=", d[i].name); - - size = d[i].size / d[i].num; - - if (i < (num_of_items - 3)) - addr = d[i].addr + (size_t) &info; - else /* The last 3 items are struct mwifiex_adapter variables */ - addr = d[i].addr + (size_t) priv->adapter; - - for (j = 0; j < d[i].num; j++) { - switch (size) { - case 1: - val = *((u8 *) addr); - break; - case 2: - val = *((u16 *) addr); - break; - case 4: - val = *((u32 *) addr); - break; - case 8: - val = *((long long *) addr); - break; - default: - val = -1; - break; - } - - p += sprintf(p, "%#lx ", val); - addr += size; - } - - p += sprintf(p, "\n"); - } - - if (info.tx_tbl_num) { - p += sprintf(p, "Tx BA stream table:\n"); - for (i = 0; i < info.tx_tbl_num; i++) - p += sprintf(p, "tid = %d, ra = %pM\n", - info.tx_tbl[i].tid, info.tx_tbl[i].ra); - } - - if (info.rx_tbl_num) { - p += sprintf(p, "Rx reorder table:\n"); - for (i = 0; i < info.rx_tbl_num; i++) { - p += sprintf(p, "tid = %d, ta = %pM, " - "start_win = %d, " - "win_size = %d, buffer: ", - info.rx_tbl[i].tid, - info.rx_tbl[i].ta, - info.rx_tbl[i].start_win, - info.rx_tbl[i].win_size); - - for (j = 0; j < info.rx_tbl[i].win_size; j++) - p += sprintf(p, "%c ", - info.rx_tbl[i].buffer[j] ? - '1' : '0'); - - p += sprintf(p, "\n"); - } - } - - if (info.tdls_peer_num) { - p += sprintf(p, "TDLS peer table:\n"); - for (i = 0; i < info.tdls_peer_num; i++) { - p += sprintf(p, "peer = %pM", - info.tdls_list[i].peer_addr); - p += sprintf(p, "\n"); - } - } + p += mwifiex_debug_info_to_buffer(priv, p, &info); ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, (unsigned long) p - page); diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c index 6a83c04..70731979 100644 --- a/drivers/net/wireless/mwifiex/util.c +++ b/drivers/net/wireless/mwifiex/util.c @@ -25,6 +25,96 @@ #include "wmm.h" #include "11n.h" +static struct mwifiex_debug_data items[] = { + {"int_counter", item_size(int_counter), + item_addr(int_counter), 1}, + {"wmm_ac_vo", item_size(packets_out[WMM_AC_VO]), + item_addr(packets_out[WMM_AC_VO]), 1}, + {"wmm_ac_vi", item_size(packets_out[WMM_AC_VI]), + item_addr(packets_out[WMM_AC_VI]), 1}, + {"wmm_ac_be", item_size(packets_out[WMM_AC_BE]), + item_addr(packets_out[WMM_AC_BE]), 1}, + {"wmm_ac_bk", item_size(packets_out[WMM_AC_BK]), + item_addr(packets_out[WMM_AC_BK]), 1}, + {"tx_buf_size", item_size(tx_buf_size), + item_addr(tx_buf_size), 1}, + {"curr_tx_buf_size", item_size(curr_tx_buf_size), + item_addr(curr_tx_buf_size), 1}, + {"ps_mode", item_size(ps_mode), + item_addr(ps_mode), 1}, + {"ps_state", item_size(ps_state), + item_addr(ps_state), 1}, + {"is_deep_sleep", item_size(is_deep_sleep), + item_addr(is_deep_sleep), 1}, + {"wakeup_dev_req", item_size(pm_wakeup_card_req), + item_addr(pm_wakeup_card_req), 1}, + {"wakeup_tries", item_size(pm_wakeup_fw_try), + item_addr(pm_wakeup_fw_try), 1}, + {"hs_configured", item_size(is_hs_configured), + item_addr(is_hs_configured), 1}, + {"hs_activated", item_size(hs_activated), + item_addr(hs_activated), 1}, + {"num_tx_timeout", item_size(num_tx_timeout), + item_addr(num_tx_timeout), 1}, + {"is_cmd_timedout", item_size(is_cmd_timedout), + item_addr(is_cmd_timedout), 1}, + {"timeout_cmd_id", item_size(timeout_cmd_id), + item_addr(timeout_cmd_id), 1}, + {"timeout_cmd_act", item_size(timeout_cmd_act), + item_addr(timeout_cmd_act), 1}, + {"last_cmd_id", item_size(last_cmd_id), + item_addr(last_cmd_id), DBG_CMD_NUM}, + {"last_cmd_act", item_size(last_cmd_act), + item_addr(last_cmd_act), DBG_CMD_NUM}, + {"last_cmd_index", item_size(last_cmd_index), + item_addr(last_cmd_index), 1}, + {"last_cmd_resp_id", item_size(last_cmd_resp_id), + item_addr(last_cmd_resp_id), DBG_CMD_NUM}, + {"last_cmd_resp_index", item_size(last_cmd_resp_index), + item_addr(last_cmd_resp_index), 1}, + {"last_event", item_size(last_event), + item_addr(last_event), DBG_CMD_NUM}, + {"last_event_index", item_size(last_event_index), + item_addr(last_event_index), 1}, + {"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure), + item_addr(num_cmd_host_to_card_failure), 1}, + {"num_cmd_sleep_cfm_fail", + item_size(num_cmd_sleep_cfm_host_to_card_failure), + item_addr(num_cmd_sleep_cfm_host_to_card_failure), 1}, + {"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure), + item_addr(num_tx_host_to_card_failure), 1}, + {"num_evt_deauth", item_size(num_event_deauth), + item_addr(num_event_deauth), 1}, + {"num_evt_disassoc", item_size(num_event_disassoc), + item_addr(num_event_disassoc), 1}, + {"num_evt_link_lost", item_size(num_event_link_lost), + item_addr(num_event_link_lost), 1}, + {"num_cmd_deauth", item_size(num_cmd_deauth), + item_addr(num_cmd_deauth), 1}, + {"num_cmd_assoc_ok", item_size(num_cmd_assoc_success), + item_addr(num_cmd_assoc_success), 1}, + {"num_cmd_assoc_fail", item_size(num_cmd_assoc_failure), + item_addr(num_cmd_assoc_failure), 1}, + {"cmd_sent", item_size(cmd_sent), + item_addr(cmd_sent), 1}, + {"data_sent", item_size(data_sent), + item_addr(data_sent), 1}, + {"cmd_resp_received", item_size(cmd_resp_received), + item_addr(cmd_resp_received), 1}, + {"event_received", item_size(event_received), + item_addr(event_received), 1}, + + /* variables defined in struct mwifiex_adapter */ + {"cmd_pending", adapter_item_size(cmd_pending), + adapter_item_addr(cmd_pending), 1}, + {"tx_pending", adapter_item_size(tx_pending), + adapter_item_addr(tx_pending), 1}, + {"rx_pending", adapter_item_size(rx_pending), + adapter_item_addr(rx_pending), 1}, +}; + +static int num_of_items = ARRAY_SIZE(items); + /* * Firmware initialization complete callback handler. * @@ -143,6 +233,93 @@ int mwifiex_get_debug_info(struct mwifiex_private *priv, return 0; } +int mwifiex_debug_info_to_buffer(struct mwifiex_private *priv, char *buf, + struct mwifiex_debug_info *info) +{ + char *p = buf; + struct mwifiex_debug_data *d = &items[0]; + size_t size, addr; + long val; + int i, j; + + if (!info) + return 0; + + for (i = 0; i < num_of_items; i++) { + p += sprintf(p, "%s=", d[i].name); + + size = d[i].size / d[i].num; + + if (i < (num_of_items - 3)) + addr = d[i].addr + (size_t)info; + else /* The last 3 items are struct mwifiex_adapter variables */ + addr = d[i].addr + (size_t)priv->adapter; + + for (j = 0; j < d[i].num; j++) { + switch (size) { + case 1: + val = *((u8 *)addr); + break; + case 2: + val = *((u16 *)addr); + break; + case 4: + val = *((u32 *)addr); + break; + case 8: + val = *((long long *)addr); + break; + default: + val = -1; + break; + } + + p += sprintf(p, "%#lx ", val); + addr += size; + } + + p += sprintf(p, "\n"); + } + + if (info->tx_tbl_num) { + p += sprintf(p, "Tx BA stream table:\n"); + for (i = 0; i < info->tx_tbl_num; i++) + p += sprintf(p, "tid = %d, ra = %pM\n", + info->tx_tbl[i].tid, info->tx_tbl[i].ra); + } + + if (info->rx_tbl_num) { + p += sprintf(p, "Rx reorder table:\n"); + for (i = 0; i < info->rx_tbl_num; i++) { + p += sprintf(p, "tid = %d, ta = %pM, ", + info->rx_tbl[i].tid, + info->rx_tbl[i].ta); + p += sprintf(p, "start_win = %d, ", + info->rx_tbl[i].start_win); + p += sprintf(p, "win_size = %d, buffer: ", + info->rx_tbl[i].win_size); + + for (j = 0; j < info->rx_tbl[i].win_size; j++) + p += sprintf(p, "%c ", + info->rx_tbl[i].buffer[j] ? + '1' : '0'); + + p += sprintf(p, "\n"); + } + } + + if (info->tdls_peer_num) { + p += sprintf(p, "TDLS peer table:\n"); + for (i = 0; i < info->tdls_peer_num; i++) { + p += sprintf(p, "peer = %pM", + info->tdls_list[i].peer_addr); + p += sprintf(p, "\n"); + } + } + + return p - buf; +} + static int mwifiex_parse_mgmt_packet(struct mwifiex_private *priv, u8 *payload, u16 len, struct rxpd *rx_pd) diff --git a/drivers/net/wireless/mwifiex/util.h b/drivers/net/wireless/mwifiex/util.h index 40296cb..b541d66 100644 --- a/drivers/net/wireless/mwifiex/util.h +++ b/drivers/net/wireless/mwifiex/util.h @@ -20,6 +20,8 @@ #ifndef _MWIFIEX_UTIL_H_ #define _MWIFIEX_UTIL_H_ +struct mwifiex_private; + struct mwifiex_dma_mapping { dma_addr_t addr; size_t len; @@ -33,6 +35,21 @@ struct mwifiex_cb { }; }; +/* size/addr for mwifiex_debug_info */ +#define item_size(n) (FIELD_SIZEOF(struct mwifiex_debug_info, n)) +#define item_addr(n) (offsetof(struct mwifiex_debug_info, n)) + +/* size/addr for struct mwifiex_adapter */ +#define adapter_item_size(n) (FIELD_SIZEOF(struct mwifiex_adapter, n)) +#define adapter_item_addr(n) (offsetof(struct mwifiex_adapter, n)) + +struct mwifiex_debug_data { + char name[32]; /* variable/array name */ + u32 size; /* size of the variable/array */ + size_t addr; /* address of the variable/array */ + int num; /* number of variables in an array */ +}; + static inline struct mwifiex_rxinfo *MWIFIEX_SKB_RXCB(struct sk_buff *skb) { struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; @@ -73,4 +90,7 @@ static inline dma_addr_t MWIFIEX_SKB_DMA_ADDR(struct sk_buff *skb) return mapping.addr; } +int mwifiex_debug_info_to_buffer(struct mwifiex_private *priv, char *buf, + struct mwifiex_debug_info *info); + #endif /* !_MWIFIEX_UTIL_H_ */ -- cgit v0.10.2 From 11cd07a9694cbd4d06361d6f46b735a47daa9a77 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Tue, 23 Dec 2014 19:14:10 +0530 Subject: mwifiex: save driver information to file when firmware dump This patch adds support to dump driver information to a file when firmware dump happens. This information can be used to root casue FW crash. Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/ethtool.c b/drivers/net/wireless/mwifiex/ethtool.c index 04e56b5..65d8d6d 100644 --- a/drivers/net/wireless/mwifiex/ethtool.c +++ b/drivers/net/wireless/mwifiex/ethtool.c @@ -76,7 +76,9 @@ mwifiex_get_dump_flag(struct net_device *dev, struct ethtool_dump *dump) dump->flag = adapter->curr_mem_idx; dump->version = 1; - if (adapter->curr_mem_idx != MWIFIEX_FW_DUMP_IDX) { + if (adapter->curr_mem_idx == MWIFIEX_DRV_INFO_IDX) { + dump->len = adapter->drv_info_size; + } else if (adapter->curr_mem_idx != MWIFIEX_FW_DUMP_IDX) { entry = &adapter->mem_type_mapping_tbl[adapter->curr_mem_idx]; dump->len = entry->mem_size; } else { @@ -98,6 +100,13 @@ mwifiex_get_dump_data(struct net_device *dev, struct ethtool_dump *dump, if (!adapter->if_ops.fw_dump) return -ENOTSUPP; + if (adapter->curr_mem_idx == MWIFIEX_DRV_INFO_IDX) { + if (!adapter->drv_info_dump) + return -EFAULT; + memcpy(p, adapter->drv_info_dump, adapter->drv_info_size); + return 0; + } + if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) { dev_err(adapter->dev, "firmware dump in progress!!\n"); return -EBUSY; @@ -125,6 +134,11 @@ static int mwifiex_set_dump(struct net_device *dev, struct ethtool_dump *val) if (!adapter->if_ops.fw_dump) return -ENOTSUPP; + if (val->flag == MWIFIEX_DRV_INFO_IDX) { + adapter->curr_mem_idx = MWIFIEX_DRV_INFO_IDX; + return 0; + } + if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) { dev_err(adapter->dev, "firmware dump in progress!!\n"); return -EBUSY; diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 520ad4a..a3dd601 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -411,6 +411,11 @@ mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) entry->mem_size = 0; } + if (adapter->drv_info_dump) { + vfree(adapter->drv_info_dump); + adapter->drv_info_size = 0; + } + if (adapter->sleep_cfm) dev_kfree_skb_any(adapter->sleep_cfm); } diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 0c44c5ee..4edaaf4 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -801,6 +801,108 @@ mwifiex_tx_timeout(struct net_device *dev) } } +void mwifiex_dump_drv_info(struct mwifiex_adapter *adapter) +{ + void *p; + char drv_version[64]; + struct usb_card_rec *cardp; + struct sdio_mmc_card *sdio_card; + struct mwifiex_private *priv; + int i, idx; + struct netdev_queue *txq; + struct mwifiex_debug_info *debug_info; + + if (adapter->drv_info_dump) { + vfree(adapter->drv_info_dump); + adapter->drv_info_size = 0; + } + + dev_info(adapter->dev, "=== DRIVER INFO DUMP START===\n"); + + adapter->drv_info_dump = vzalloc(MWIFIEX_DRV_INFO_SIZE_MAX); + + if (!adapter->drv_info_dump) + return; + + p = (char *)(adapter->drv_info_dump); + p += sprintf(p, "driver_name = " "\"mwifiex\"\n"); + + mwifiex_drv_get_driver_version(adapter, drv_version, + sizeof(drv_version) - 1); + p += sprintf(p, "driver_version = %s\n", drv_version); + + if (adapter->iface_type == MWIFIEX_USB) { + cardp = (struct usb_card_rec *)adapter->card; + p += sprintf(p, "tx_cmd_urb_pending = %d\n", + atomic_read(&cardp->tx_cmd_urb_pending)); + p += sprintf(p, "tx_data_urb_pending = %d\n", + atomic_read(&cardp->tx_data_urb_pending)); + p += sprintf(p, "rx_cmd_urb_pending = %d\n", + atomic_read(&cardp->rx_cmd_urb_pending)); + p += sprintf(p, "rx_data_urb_pending = %d\n", + atomic_read(&cardp->rx_data_urb_pending)); + } + + p += sprintf(p, "tx_pending = %d\n", + atomic_read(&adapter->tx_pending)); + p += sprintf(p, "rx_pending = %d\n", + atomic_read(&adapter->rx_pending)); + + if (adapter->iface_type == MWIFIEX_SDIO) { + sdio_card = (struct sdio_mmc_card *)adapter->card; + p += sprintf(p, "\nmp_rd_bitmap=0x%x curr_rd_port=0x%x\n", + sdio_card->mp_rd_bitmap, sdio_card->curr_rd_port); + p += sprintf(p, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n", + sdio_card->mp_wr_bitmap, sdio_card->curr_wr_port); + } + + for (i = 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i] || !adapter->priv[i]->netdev) + continue; + priv = adapter->priv[i]; + p += sprintf(p, "\n[interface : \"%s\"]\n", + priv->netdev->name); + p += sprintf(p, "wmm_tx_pending[0] = %d\n", + atomic_read(&priv->wmm_tx_pending[0])); + p += sprintf(p, "wmm_tx_pending[1] = %d\n", + atomic_read(&priv->wmm_tx_pending[1])); + p += sprintf(p, "wmm_tx_pending[2] = %d\n", + atomic_read(&priv->wmm_tx_pending[2])); + p += sprintf(p, "wmm_tx_pending[3] = %d\n", + atomic_read(&priv->wmm_tx_pending[3])); + p += sprintf(p, "media_state=\"%s\"\n", !priv->media_connected ? + "Disconnected" : "Connected"); + p += sprintf(p, "carrier %s\n", (netif_carrier_ok(priv->netdev) + ? "on" : "off")); + for (idx = 0; idx < priv->netdev->num_tx_queues; idx++) { + txq = netdev_get_tx_queue(priv->netdev, idx); + p += sprintf(p, "tx queue %d:%s ", idx, + netif_tx_queue_stopped(txq) ? + "stopped" : "started"); + } + p += sprintf(p, "\n%s: num_tx_timeout = %d\n", + priv->netdev->name, priv->num_tx_timeout); + } + + p += sprintf(p, "\n=== MORE DEBUG INFORMATION\n"); + debug_info = kzalloc(sizeof(*debug_info), GFP_KERNEL); + if (debug_info) { + for (i = 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i] || !adapter->priv[i]->netdev) + continue; + priv = adapter->priv[i]; + mwifiex_get_debug_info(priv, debug_info); + p += mwifiex_debug_info_to_buffer(priv, p, debug_info); + break; + } + kfree(debug_info); + } + + adapter->drv_info_size = p - adapter->drv_info_dump; + dev_info(adapter->dev, "=== DRIVER INFO DUMP END===\n"); +} +EXPORT_SYMBOL_GPL(mwifiex_dump_drv_info); + /* * CFG802.11 network device handler for statistics retrieval. */ diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 6f7e060..2f085de 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -41,6 +41,8 @@ #include "util.h" #include "fw.h" #include "pcie.h" +#include "usb.h" +#include "sdio.h" extern const char driver_version[]; @@ -136,6 +138,8 @@ enum { /* Threshold for tx_timeout_cnt before we trigger a card reset */ #define TX_TIMEOUT_THRESHOLD 6 +#define MWIFIEX_DRV_INFO_SIZE_MAX 0x40000 + struct mwifiex_dbg { u32 num_cmd_host_to_card_failure; u32 num_cmd_sleep_cfm_host_to_card_failure; @@ -413,6 +417,7 @@ struct mwifiex_roc_cfg { }; #define MWIFIEX_FW_DUMP_IDX 0xff +#define MWIFIEX_DRV_INFO_IDX 20 #define FW_DUMP_MAX_NAME_LEN 8 #define FW_DUMP_HOST_READY 0xEE #define FW_DUMP_DONE 0xFF @@ -867,6 +872,8 @@ struct mwifiex_adapter { struct memory_type_mapping *mem_type_mapping_tbl; u8 num_mem_types; u8 curr_mem_idx; + void *drv_info_dump; + u32 drv_info_size; bool scan_chan_gap_enabled; struct sk_buff_head rx_data_q; struct mwifiex_chan_stats *chan_stats; @@ -1360,6 +1367,8 @@ void mwifiex_hist_data_add(struct mwifiex_private *priv, u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv, u8 rx_rate, u8 ht_info); +void mwifiex_dump_drv_info(struct mwifiex_adapter *adapter); + #ifdef CONFIG_DEBUG_FS void mwifiex_debugfs_init(void); void mwifiex_debugfs_remove(void); diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 933dae1..6bdfe67 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -2023,6 +2023,8 @@ static void mwifiex_sdio_fw_dump_work(struct work_struct *work) u32 memory_size; static char *env[] = { "DRIVER=mwifiex_sdio", "EVENT=fw_dump", NULL }; + mwifiex_dump_drv_info(adapter); + if (!card->supports_fw_dump) return; -- cgit v0.10.2 From 809c6ea8abe9f18b74253e6c8c7b23dd73f74b7a Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Tue, 23 Dec 2014 19:14:11 +0530 Subject: mwifiex: save sdio register values before firmware dump This patch saves sdio registers value before firmware dump, this can be used to find out reason for FW dump. Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 4edaaf4..6125d1c 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -884,6 +884,12 @@ void mwifiex_dump_drv_info(struct mwifiex_adapter *adapter) priv->netdev->name, priv->num_tx_timeout); } + if (adapter->iface_type == MWIFIEX_SDIO) { + p += sprintf(p, "\n=== SDIO register DUMP===\n"); + if (adapter->if_ops.reg_dump) + p += adapter->if_ops.reg_dump(adapter, p); + } + p += sprintf(p, "\n=== MORE DEBUG INFORMATION\n"); debug_info = kzalloc(sizeof(*debug_info), GFP_KERNEL); if (debug_info) { diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 2f085de..dd55bc1 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -724,6 +724,7 @@ struct mwifiex_if_ops { int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); void (*card_reset) (struct mwifiex_adapter *); void (*fw_dump)(struct mwifiex_adapter *); + int (*reg_dump)(struct mwifiex_adapter *, char *); int (*clean_pcie_ring) (struct mwifiex_adapter *adapter); void (*iface_work)(struct work_struct *work); void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter); diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 6bdfe67..274a1b2 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -2168,6 +2168,99 @@ static void mwifiex_sdio_fw_dump(struct mwifiex_adapter *adapter) schedule_work(&adapter->iface_work); } +/* Function to dump SDIO function registers and SDIO scratch registers in case + * of FW crash + */ +static int +mwifiex_sdio_reg_dump(struct mwifiex_adapter *adapter, char *drv_buf) +{ + char *p = drv_buf; + struct sdio_mmc_card *cardp = adapter->card; + int ret = 0; + u8 count, func, data, index = 0, size = 0; + u8 reg, reg_start, reg_end; + char buf[256], *ptr; + + if (!p) + return 0; + + dev_info(adapter->dev, "SDIO register DUMP START\n"); + + mwifiex_pm_wakeup_card(adapter); + + sdio_claim_host(cardp->func); + + for (count = 0; count < 5; count++) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + + switch (count) { + case 0: + /* Read the registers of SDIO function0 */ + func = count; + reg_start = 0; + reg_end = 9; + break; + case 1: + /* Read the registers of SDIO function1 */ + func = count; + reg_start = cardp->reg->func1_dump_reg_start; + reg_end = cardp->reg->func1_dump_reg_end; + break; + case 2: + index = 0; + func = 1; + reg_start = cardp->reg->func1_spec_reg_table[index++]; + size = cardp->reg->func1_spec_reg_num; + reg_end = cardp->reg->func1_spec_reg_table[size-1]; + break; + default: + /* Read the scratch registers of SDIO function1 */ + if (count == 4) + mdelay(100); + func = 1; + reg_start = cardp->reg->func1_scratch_reg; + reg_end = reg_start + MWIFIEX_SDIO_SCRATCH_SIZE; + } + + if (count != 2) + ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", + func, reg_start, reg_end); + else + ptr += sprintf(ptr, "SDIO Func%d: ", func); + + for (reg = reg_start; reg <= reg_end;) { + if (func == 0) + data = sdio_f0_readb(cardp->func, reg, &ret); + else + data = sdio_readb(cardp->func, reg, &ret); + + if (count == 2) + ptr += sprintf(ptr, "(%#x) ", reg); + if (!ret) { + ptr += sprintf(ptr, "%02x ", data); + } else { + ptr += sprintf(ptr, "ERR"); + break; + } + + if (count == 2 && reg < reg_end) + reg = cardp->reg->func1_spec_reg_table[index++]; + else + reg++; + } + + dev_info(adapter->dev, "%s\n", buf); + p += sprintf(p, "%s\n", buf); + } + + sdio_release_host(cardp->func); + + dev_info(adapter->dev, "SDIO register DUMP END\n"); + + return p - drv_buf; +} + static struct mwifiex_if_ops sdio_ops = { .init_if = mwifiex_init_sdio, .cleanup_if = mwifiex_cleanup_sdio, @@ -2190,6 +2283,7 @@ static struct mwifiex_if_ops sdio_ops = { .card_reset = mwifiex_sdio_card_reset, .iface_work = mwifiex_sdio_work, .fw_dump = mwifiex_sdio_fw_dump, + .reg_dump = mwifiex_sdio_reg_dump, }; /* diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h index 54c0715..895eea0 100644 --- a/drivers/net/wireless/mwifiex/sdio.h +++ b/drivers/net/wireless/mwifiex/sdio.h @@ -44,6 +44,9 @@ #define MWIFIEX_SDIO_BYTE_MODE_MASK 0x80000000 +#define MWIFIEX_MAX_FUNC2_REG_NUM 13 +#define MWIFIEX_SDIO_SCRATCH_SIZE 10 + #define SDIO_MPA_ADDR_BASE 0x1000 #define CTRL_PORT 0 #define CTRL_PORT_MASK 0x0001 @@ -219,6 +222,11 @@ struct mwifiex_sdio_card_reg { u8 fw_dump_ctrl; u8 fw_dump_start; u8 fw_dump_end; + u8 func1_dump_reg_start; + u8 func1_dump_reg_end; + u8 func1_scratch_reg; + u8 func1_spec_reg_num; + u8 func1_spec_reg_table[MWIFIEX_MAX_FUNC2_REG_NUM]; }; struct sdio_mmc_card { @@ -291,6 +299,11 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = { .rd_len_p0_l = 0x08, .rd_len_p0_u = 0x09, .card_misc_cfg_reg = 0x6c, + .func1_dump_reg_start = 0x0, + .func1_dump_reg_end = 0x9, + .func1_scratch_reg = 0x60, + .func1_spec_reg_num = 5, + .func1_spec_reg_table = {0x28, 0x30, 0x34, 0x38, 0x3c}, }; static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = { @@ -335,6 +348,12 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = { .fw_dump_ctrl = 0xe2, .fw_dump_start = 0xe3, .fw_dump_end = 0xea, + .func1_dump_reg_start = 0x0, + .func1_dump_reg_end = 0xb, + .func1_scratch_reg = 0xc0, + .func1_spec_reg_num = 8, + .func1_spec_reg_table = {0x4C, 0x50, 0x54, 0x55, 0x58, + 0x59, 0x5c, 0x5d}, }; static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8887 = { @@ -376,6 +395,13 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8887 = { .cmd_cfg_1 = 0xc5, .cmd_cfg_2 = 0xc6, .cmd_cfg_3 = 0xc7, + .func1_dump_reg_start = 0x10, + .func1_dump_reg_end = 0x17, + .func1_scratch_reg = 0x90, + .func1_spec_reg_num = 13, + .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, + 0x61, 0x62, 0x64, 0x65, 0x66, + 0x68, 0x69, 0x6a}, }; static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = { -- cgit v0.10.2 From 0b4155d1cc8e7562f490124c081e710c36cb2cf8 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Tue, 23 Dec 2014 19:14:12 +0530 Subject: mwifiex: module parameter for deep sleep configuration This patch adds module parameter for auto deepsleep configuration. By default, module_param is 0 and auto deep sleep is enabled. If mwifiex driver is loaded with disable_auto_ds=1, deep sleep configuration would not be downloaded to FW and FW would not enter deepsleep upon initializing. Signed-off-by: Avinash Patil Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 1c2ca29..f7b920d 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -26,6 +26,10 @@ #include "11n.h" #include "11ac.h" +static bool disable_auto_ds; +module_param(disable_auto_ds, bool, 0); +MODULE_PARM_DESC(disable_auto_ds, + "deepsleep enabled=0(default), deepsleep disabled=1"); /* * This function prepares command to set/get RSSI information. * @@ -2031,7 +2035,8 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) if (ret) return -1; - if (first_sta && priv->adapter->iface_type != MWIFIEX_USB && + if (!disable_auto_ds && + first_sta && priv->adapter->iface_type != MWIFIEX_USB && priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { /* Enable auto deep sleep */ auto_ds.auto_ds = DEEP_SLEEP_ON; -- cgit v0.10.2 From 84596ccbf102644c846e050caf4714447f083206 Mon Sep 17 00:00:00 2001 From: Xie XiuQi Date: Tue, 11 Nov 2014 12:03:26 +0800 Subject: documentation: Update sysfs path for rcu_cpu_stall_timeout Commit 6bfc09e2327d ("rcu: Provide RCU CPU stall warnings for tiny RCU") moved the rcu_cpu_stall_timeout module parameter from rcutree.c to rcupdate.c, but failed to update Documentation/RCU/stallwarn.txt. This commit therefore repairs this omission. commit 96224daa16d6 ("documentation: Update sysfs path for rcu_cpu_stall_suppress") updated the path for rcu_cpu_stall_suppress, but failed to update for rcu_cpu_stall_timeout. Signed-off-by: Xie XiuQi Signed-off-by: Paul E. McKenney diff --git a/Documentation/RCU/stallwarn.txt b/Documentation/RCU/stallwarn.txt index ed186a9..4f8e339 100644 --- a/Documentation/RCU/stallwarn.txt +++ b/Documentation/RCU/stallwarn.txt @@ -15,7 +15,7 @@ CONFIG_RCU_CPU_STALL_TIMEOUT 21 seconds. This configuration parameter may be changed at runtime via the - /sys/module/rcutree/parameters/rcu_cpu_stall_timeout, however + /sys/module/rcupdate/parameters/rcu_cpu_stall_timeout, however this parameter is checked only at the beginning of a cycle. So if you are 10 seconds into a 40-second stall, setting this sysfs parameter to (say) five will shorten the timeout for the -- cgit v0.10.2 From 536fa402221f09633e7c5801b327055ab716a363 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 5 Sep 2014 11:14:48 -0700 Subject: compiler: Allow 1- and 2-byte smp_load_acquire() and smp_store_release() CPUs without single-byte and double-byte loads and stores place some "interesting" requirements on concurrent code. For example (adapted from Peter Hurley's test code), suppose we have the following structure: struct foo { spinlock_t lock1; spinlock_t lock2; char a; /* Protected by lock1. */ char b; /* Protected by lock2. */ }; struct foo *foop; Of course, it is common (and good) practice to place data protected by different locks in separate cache lines. However, if the locks are rarely acquired (for example, only in rare error cases), and there are a great many instances of the data structure, then memory footprint can trump false-sharing concerns, so that it can be better to place them in the same cache cache line as above. But if the CPU does not support single-byte loads and stores, a store to foop->a will do a non-atomic read-modify-write operation on foop->b, which will come as a nasty surprise to someone holding foop->lock2. So we now require CPUs to support single-byte and double-byte loads and stores. Therefore, this commit adjusts the definition of __native_word() to allow these sizes to be used by smp_load_acquire() and smp_store_release(). Signed-off-by: Paul E. McKenney Cc: Peter Zijlstra diff --git a/include/linux/compiler.h b/include/linux/compiler.h index a1c81f8..49811cd 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -385,7 +385,7 @@ static __always_inline void __assign_once_size(volatile void *p, void *res, int /* Is this type a native word size -- useful for atomic operations */ #ifndef __native_word -# define __native_word(t) (sizeof(t) == sizeof(int) || sizeof(t) == sizeof(long)) +# define __native_word(t) (sizeof(t) == sizeof(char) || sizeof(t) == sizeof(short) || sizeof(t) == sizeof(int) || sizeof(t) == sizeof(long)) #endif /* Compile time object size, -1 for unknown */ -- cgit v0.10.2 From ac59853c06993a442e8060bc19040b2ca3025aec Mon Sep 17 00:00:00 2001 From: Pranith Kumar Date: Thu, 13 Nov 2014 14:24:14 -0500 Subject: rcupdate: Replace smp_read_barrier_depends() with lockless_dereference() Recently lockless_dereference() was added which can be used in place of hard-coding smp_read_barrier_depends(). The following PATCH makes the change. Signed-off-by: Pranith Kumar Signed-off-by: Paul E. McKenney diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index ed4f593..386ba28 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -582,11 +582,11 @@ static inline void rcu_preempt_sleep_check(void) }) #define __rcu_dereference_check(p, c, space) \ ({ \ - typeof(*p) *_________p1 = (typeof(*p) *__force)ACCESS_ONCE(p); \ + /* Dependency order vs. p above. */ \ + typeof(*p) *________p1 = (typeof(*p) *__force)lockless_dereference(p); \ rcu_lockdep_assert(c, "suspicious rcu_dereference_check() usage"); \ rcu_dereference_sparse(p, space); \ - smp_read_barrier_depends(); /* Dependency order vs. p above. */ \ - ((typeof(*p) __force __kernel *)(_________p1)); \ + ((typeof(*p) __force __kernel *)(________p1)); \ }) #define __rcu_dereference_protected(p, c, space) \ ({ \ @@ -603,10 +603,10 @@ static inline void rcu_preempt_sleep_check(void) }) #define __rcu_dereference_index_check(p, c) \ ({ \ - typeof(p) _________p1 = ACCESS_ONCE(p); \ + /* Dependency order vs. p above. */ \ + typeof(p) _________p1 = lockless_dereference(p); \ rcu_lockdep_assert(c, \ "suspicious rcu_dereference_index_check() usage"); \ - smp_read_barrier_depends(); /* Dependency order vs. p above. */ \ (_________p1); \ }) -- cgit v0.10.2 From 5f6130fa52ee0df0b1da4518c5bbef42bcfe7d83 Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Tue, 9 Dec 2014 17:53:34 +0800 Subject: tiny_rcu: Directly force QS when call_rcu_[bh|sched]() on idle_task For RCU in UP, context-switch = QS = GP, thus we can force a context-switch when any call_rcu_[bh|sched]() is happened on idle_task. After doing so, rcu_idle/irq_enter/exit() are useless, so we can simply make these functions empty. More important, this change does not change the functionality logically. Note: raise_softirq(RCU_SOFTIRQ)/rcu_sched_qs() in rcu_idle_enter() and outmost rcu_irq_exit() will have to wake up the ksoftirqd (due to in_interrupt() == 0). Before this patch After this patch: call_rcu_sched() in idle; call_rcu_sched() in idle set resched do other stuffs; do other stuffs outmost rcu_irq_exit() outmost rcu_irq_exit() (empty function) (or rcu_idle_enter()) (or rcu_idle_enter(), also empty function) start to resched. (see above) rcu_sched_qs() rcu_sched_qs() QS,and GP and advance cb QS,and GP and advance cb wake up the ksoftirqd wake up the ksoftirqd set resched resched to ksoftirqd (or other) resched to ksoftirqd (or other) These two code patches are almost the same. Size changed after patched: size kernel/rcu/tiny-old.o kernel/rcu/tiny-patched.o text data bss dec hex filename 3449 206 8 3663 e4f kernel/rcu/tiny-old.o 2406 144 8 2558 9fe kernel/rcu/tiny-patched.o Signed-off-by: Lai Jiangshan Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/rcu.h b/kernel/rcu/rcu.h index 07bb02e..80adef7 100644 --- a/kernel/rcu/rcu.h +++ b/kernel/rcu/rcu.h @@ -137,4 +137,10 @@ int rcu_jiffies_till_stall_check(void); void rcu_early_boot_tests(void); +/* + * This function really isn't for public consumption, but RCU is special in + * that context switches can allow the state machine to make progress. + */ +extern void resched_cpu(int cpu); + #endif /* __LINUX_RCU_H */ diff --git a/kernel/rcu/tiny.c b/kernel/rcu/tiny.c index 805b6d5..85287ec 100644 --- a/kernel/rcu/tiny.c +++ b/kernel/rcu/tiny.c @@ -47,54 +47,14 @@ static void __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu), struct rcu_ctrlblk *rcp); -static long long rcu_dynticks_nesting = DYNTICK_TASK_EXIT_IDLE; - #include "tiny_plugin.h" -/* Common code for rcu_idle_enter() and rcu_irq_exit(), see kernel/rcu/tree.c. */ -static void rcu_idle_enter_common(long long newval) -{ - if (newval) { - RCU_TRACE(trace_rcu_dyntick(TPS("--="), - rcu_dynticks_nesting, newval)); - rcu_dynticks_nesting = newval; - return; - } - RCU_TRACE(trace_rcu_dyntick(TPS("Start"), - rcu_dynticks_nesting, newval)); - if (IS_ENABLED(CONFIG_RCU_TRACE) && !is_idle_task(current)) { - struct task_struct *idle __maybe_unused = idle_task(smp_processor_id()); - - RCU_TRACE(trace_rcu_dyntick(TPS("Entry error: not idle task"), - rcu_dynticks_nesting, newval)); - ftrace_dump(DUMP_ALL); - WARN_ONCE(1, "Current pid: %d comm: %s / Idle pid: %d comm: %s", - current->pid, current->comm, - idle->pid, idle->comm); /* must be idle task! */ - } - rcu_sched_qs(); /* implies rcu_bh_inc() */ - barrier(); - rcu_dynticks_nesting = newval; -} - /* * Enter idle, which is an extended quiescent state if we have fully - * entered that mode (i.e., if the new value of dynticks_nesting is zero). + * entered that mode. */ void rcu_idle_enter(void) { - unsigned long flags; - long long newval; - - local_irq_save(flags); - WARN_ON_ONCE((rcu_dynticks_nesting & DYNTICK_TASK_NEST_MASK) == 0); - if ((rcu_dynticks_nesting & DYNTICK_TASK_NEST_MASK) == - DYNTICK_TASK_NEST_VALUE) - newval = 0; - else - newval = rcu_dynticks_nesting - DYNTICK_TASK_NEST_VALUE; - rcu_idle_enter_common(newval); - local_irq_restore(flags); } EXPORT_SYMBOL_GPL(rcu_idle_enter); @@ -103,55 +63,14 @@ EXPORT_SYMBOL_GPL(rcu_idle_enter); */ void rcu_irq_exit(void) { - unsigned long flags; - long long newval; - - local_irq_save(flags); - newval = rcu_dynticks_nesting - 1; - WARN_ON_ONCE(newval < 0); - rcu_idle_enter_common(newval); - local_irq_restore(flags); } EXPORT_SYMBOL_GPL(rcu_irq_exit); -/* Common code for rcu_idle_exit() and rcu_irq_enter(), see kernel/rcu/tree.c. */ -static void rcu_idle_exit_common(long long oldval) -{ - if (oldval) { - RCU_TRACE(trace_rcu_dyntick(TPS("++="), - oldval, rcu_dynticks_nesting)); - return; - } - RCU_TRACE(trace_rcu_dyntick(TPS("End"), oldval, rcu_dynticks_nesting)); - if (IS_ENABLED(CONFIG_RCU_TRACE) && !is_idle_task(current)) { - struct task_struct *idle __maybe_unused = idle_task(smp_processor_id()); - - RCU_TRACE(trace_rcu_dyntick(TPS("Exit error: not idle task"), - oldval, rcu_dynticks_nesting)); - ftrace_dump(DUMP_ALL); - WARN_ONCE(1, "Current pid: %d comm: %s / Idle pid: %d comm: %s", - current->pid, current->comm, - idle->pid, idle->comm); /* must be idle task! */ - } -} - /* * Exit idle, so that we are no longer in an extended quiescent state. */ void rcu_idle_exit(void) { - unsigned long flags; - long long oldval; - - local_irq_save(flags); - oldval = rcu_dynticks_nesting; - WARN_ON_ONCE(rcu_dynticks_nesting < 0); - if (rcu_dynticks_nesting & DYNTICK_TASK_NEST_MASK) - rcu_dynticks_nesting += DYNTICK_TASK_NEST_VALUE; - else - rcu_dynticks_nesting = DYNTICK_TASK_EXIT_IDLE; - rcu_idle_exit_common(oldval); - local_irq_restore(flags); } EXPORT_SYMBOL_GPL(rcu_idle_exit); @@ -160,15 +79,6 @@ EXPORT_SYMBOL_GPL(rcu_idle_exit); */ void rcu_irq_enter(void) { - unsigned long flags; - long long oldval; - - local_irq_save(flags); - oldval = rcu_dynticks_nesting; - rcu_dynticks_nesting++; - WARN_ON_ONCE(rcu_dynticks_nesting == 0); - rcu_idle_exit_common(oldval); - local_irq_restore(flags); } EXPORT_SYMBOL_GPL(rcu_irq_enter); @@ -179,7 +89,7 @@ EXPORT_SYMBOL_GPL(rcu_irq_enter); */ bool notrace __rcu_is_watching(void) { - return rcu_dynticks_nesting; + return true; } EXPORT_SYMBOL(__rcu_is_watching); @@ -347,6 +257,11 @@ static void __call_rcu(struct rcu_head *head, rcp->curtail = &head->next; RCU_TRACE(rcp->qlen++); local_irq_restore(flags); + + if (unlikely(is_idle_task(current))) { + /* force scheduling for rcu_sched_qs() */ + resched_cpu(0); + } } /* diff --git a/kernel/rcu/tiny_plugin.h b/kernel/rcu/tiny_plugin.h index 858c565..b5fc5e5 100644 --- a/kernel/rcu/tiny_plugin.h +++ b/kernel/rcu/tiny_plugin.h @@ -147,7 +147,7 @@ static void check_cpu_stall(struct rcu_ctrlblk *rcp) js = ACCESS_ONCE(rcp->jiffies_stall); if (*rcp->curtail && ULONG_CMP_GE(j, js)) { pr_err("INFO: %s stall on CPU (%lu ticks this GP) idle=%llx (t=%lu jiffies q=%ld)\n", - rcp->name, rcp->ticks_this_gp, rcu_dynticks_nesting, + rcp->name, rcp->ticks_this_gp, DYNTICK_TASK_EXIT_IDLE, jiffies - rcp->gp_start, rcp->qlen); dump_stack(); } diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 9c25b99..203b50d 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -935,12 +935,6 @@ static int dyntick_save_progress_counter(struct rcu_data *rdp, } /* - * This function really isn't for public consumption, but RCU is special in - * that context switches can allow the state machine to make progress. - */ -extern void resched_cpu(int cpu); - -/* * Return true if the specified CPU has passed through a quiescent * state by virtue of being in or having passed through an dynticks * idle state since the last call to dyntick_save_progress_counter() -- cgit v0.10.2 From f520c98e3e5212d8c282a86d9b7697dd70326192 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Fri, 12 Dec 2014 09:36:14 +0800 Subject: rculist: Fix sparse warning This fixes the following sparse warnings: make C=1 CF=-D__CHECK_ENDIAN__ net/ipv6/addrconf.o net/ipv6/addrconf.c:3495:9: error: incompatible types in comparison expression (different address spaces) net/ipv6/addrconf.c:3495:9: error: incompatible types in comparison expression (different address spaces) net/ipv6/addrconf.c:3495:9: error: incompatible types in comparison expression (different address spaces) net/ipv6/addrconf.c:3495:9: error: incompatible types in comparison expression (different address spaces) To silence these spare complaints, an RCU annotation should be added to "next" pointer of hlist_node structure through hlist_next_rcu() macro when iterating over a hlist with hlist_for_each_entry_continue_rcu_bh(). By the way, this commit also resolves the same error appearing in hlist_for_each_entry_continue_rcu(). Signed-off-by: Ying Xue Signed-off-by: Paul E. McKenney diff --git a/include/linux/rculist.h b/include/linux/rculist.h index 529bc94..a18b16f 100644 --- a/include/linux/rculist.h +++ b/include/linux/rculist.h @@ -524,11 +524,11 @@ static inline void hlist_add_behind_rcu(struct hlist_node *n, * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_continue_rcu(pos, member) \ - for (pos = hlist_entry_safe(rcu_dereference((pos)->member.next),\ - typeof(*(pos)), member); \ + for (pos = hlist_entry_safe(rcu_dereference_raw(hlist_next_rcu( \ + &(pos)->member)), typeof(*(pos)), member); \ pos; \ - pos = hlist_entry_safe(rcu_dereference((pos)->member.next),\ - typeof(*(pos)), member)) + pos = hlist_entry_safe(rcu_dereference_raw(hlist_next_rcu( \ + &(pos)->member)), typeof(*(pos)), member)) /** * hlist_for_each_entry_continue_rcu_bh - iterate over a hlist continuing after current point @@ -536,11 +536,11 @@ static inline void hlist_add_behind_rcu(struct hlist_node *n, * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_continue_rcu_bh(pos, member) \ - for (pos = hlist_entry_safe(rcu_dereference_bh((pos)->member.next),\ - typeof(*(pos)), member); \ + for (pos = hlist_entry_safe(rcu_dereference_bh(hlist_next_rcu( \ + &(pos)->member)), typeof(*(pos)), member); \ pos; \ - pos = hlist_entry_safe(rcu_dereference_bh((pos)->member.next),\ - typeof(*(pos)), member)) + pos = hlist_entry_safe(rcu_dereference_bh(hlist_next_rcu( \ + &(pos)->member)), typeof(*(pos)), member)) /** * hlist_for_each_entry_from_rcu - iterate over a hlist continuing from current point -- cgit v0.10.2 From 87af9e7ff9d909e70a006ca0974466e2a1d8db0a Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Fri, 12 Dec 2014 10:11:44 +0100 Subject: hotplugcpu: Avoid deadlocks by waking active_writer Commit b2c4623dcd07 ("rcu: More on deadlock between CPU hotplug and expedited grace periods") introduced another problem that can easily be reproduced by starting/stopping cpus in a loop. E.g.: for i in `seq 5000`; do echo 1 > /sys/devices/system/cpu/cpu1/online echo 0 > /sys/devices/system/cpu/cpu1/online done Will result in: INFO: task /cpu_start_stop:1 blocked for more than 120 seconds. Call Trace: ([<00000000006a028e>] __schedule+0x406/0x91c) [<0000000000130f60>] cpu_hotplug_begin+0xd0/0xd4 [<0000000000130ff6>] _cpu_up+0x3e/0x1c4 [<0000000000131232>] cpu_up+0xb6/0xd4 [<00000000004a5720>] device_online+0x80/0xc0 [<00000000004a57f0>] online_store+0x90/0xb0 ... And a deadlock. Problem is that if the last ref in put_online_cpus() can't get the cpu_hotplug.lock the puts_pending count is incremented, but a sleeping active_writer might never be woken up, therefore never exiting the loop in cpu_hotplug_begin(). This fix removes puts_pending and turns refcount into an atomic variable. We also introduce a wait queue for the active_writer, to avoid possible races and use-after-free. There is no need to take the lock in put_online_cpus() anymore. Can't reproduce it with this fix. Signed-off-by: David Hildenbrand Signed-off-by: Paul E. McKenney diff --git a/kernel/cpu.c b/kernel/cpu.c index 5d22023..1972b16 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -58,22 +58,23 @@ static int cpu_hotplug_disabled; static struct { struct task_struct *active_writer; - struct mutex lock; /* Synchronizes accesses to refcount, */ + /* wait queue to wake up the active_writer */ + wait_queue_head_t wq; + /* verifies that no writer will get active while readers are active */ + struct mutex lock; /* * Also blocks the new readers during * an ongoing cpu hotplug operation. */ - int refcount; - /* And allows lockless put_online_cpus(). */ - atomic_t puts_pending; + atomic_t refcount; #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif } cpu_hotplug = { .active_writer = NULL, + .wq = __WAIT_QUEUE_HEAD_INITIALIZER(cpu_hotplug.wq), .lock = __MUTEX_INITIALIZER(cpu_hotplug.lock), - .refcount = 0, #ifdef CONFIG_DEBUG_LOCK_ALLOC .dep_map = {.name = "cpu_hotplug.lock" }, #endif @@ -86,15 +87,6 @@ static struct { #define cpuhp_lock_acquire() lock_map_acquire(&cpu_hotplug.dep_map) #define cpuhp_lock_release() lock_map_release(&cpu_hotplug.dep_map) -static void apply_puts_pending(int max) -{ - int delta; - - if (atomic_read(&cpu_hotplug.puts_pending) >= max) { - delta = atomic_xchg(&cpu_hotplug.puts_pending, 0); - cpu_hotplug.refcount -= delta; - } -} void get_online_cpus(void) { @@ -103,8 +95,7 @@ void get_online_cpus(void) return; cpuhp_lock_acquire_read(); mutex_lock(&cpu_hotplug.lock); - apply_puts_pending(65536); - cpu_hotplug.refcount++; + atomic_inc(&cpu_hotplug.refcount); mutex_unlock(&cpu_hotplug.lock); } EXPORT_SYMBOL_GPL(get_online_cpus); @@ -116,8 +107,7 @@ bool try_get_online_cpus(void) if (!mutex_trylock(&cpu_hotplug.lock)) return false; cpuhp_lock_acquire_tryread(); - apply_puts_pending(65536); - cpu_hotplug.refcount++; + atomic_inc(&cpu_hotplug.refcount); mutex_unlock(&cpu_hotplug.lock); return true; } @@ -125,20 +115,18 @@ EXPORT_SYMBOL_GPL(try_get_online_cpus); void put_online_cpus(void) { + int refcount; + if (cpu_hotplug.active_writer == current) return; - if (!mutex_trylock(&cpu_hotplug.lock)) { - atomic_inc(&cpu_hotplug.puts_pending); - cpuhp_lock_release(); - return; - } - if (WARN_ON(!cpu_hotplug.refcount)) - cpu_hotplug.refcount++; /* try to fix things up */ + refcount = atomic_dec_return(&cpu_hotplug.refcount); + if (WARN_ON(refcount < 0)) /* try to fix things up */ + atomic_inc(&cpu_hotplug.refcount); + + if (refcount <= 0 && waitqueue_active(&cpu_hotplug.wq)) + wake_up(&cpu_hotplug.wq); - if (!--cpu_hotplug.refcount && unlikely(cpu_hotplug.active_writer)) - wake_up_process(cpu_hotplug.active_writer); - mutex_unlock(&cpu_hotplug.lock); cpuhp_lock_release(); } @@ -168,18 +156,20 @@ EXPORT_SYMBOL_GPL(put_online_cpus); */ void cpu_hotplug_begin(void) { - cpu_hotplug.active_writer = current; + DEFINE_WAIT(wait); + cpu_hotplug.active_writer = current; cpuhp_lock_acquire(); + for (;;) { mutex_lock(&cpu_hotplug.lock); - apply_puts_pending(1); - if (likely(!cpu_hotplug.refcount)) - break; - __set_current_state(TASK_UNINTERRUPTIBLE); + prepare_to_wait(&cpu_hotplug.wq, &wait, TASK_UNINTERRUPTIBLE); + if (likely(!atomic_read(&cpu_hotplug.refcount))) + break; mutex_unlock(&cpu_hotplug.lock); schedule(); } + finish_wait(&cpu_hotplug.wq, &wait); } void cpu_hotplug_done(void) -- cgit v0.10.2 From 41050a009640f9f330a5b916563ca7faf853a98c Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 18 Dec 2014 12:31:27 -0800 Subject: rcu: Fix rcu_barrier() race that could result in too-short wait The rcu_barrier() no-callbacks check for no-CBs CPUs has race conditions. It checks a given CPU's lists of callbacks, and if all three no-CBs lists are empty, ignores that CPU. However, these three lists could potentially be empty even when callbacks are present if the check executed just as the callbacks were being moved from one list to another. It turns out that recent versions of rcutorture can spot this race. This commit plugs this hole by consolidating the per-list counts of no-CBs callbacks into a single count, which is incremented before the corresponding callback is posted and after it is invoked. Then rcu_barrier() checks this single count to reliably determine whether the corresponding CPU has no-CBs callbacks. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 203b50d..5b9f3b9 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -3344,6 +3344,7 @@ static void _rcu_barrier(struct rcu_state *rsp) } else { _rcu_barrier_trace(rsp, "OnlineNoCB", cpu, rsp->n_barrier_done); + smp_mb__before_atomic(); atomic_inc(&rsp->barrier_cpu_count); __call_rcu(&rdp->barrier_head, rcu_barrier_callback, rsp, cpu, 0); diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 8e7b184..cb59086 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -340,14 +340,10 @@ struct rcu_data { #ifdef CONFIG_RCU_NOCB_CPU struct rcu_head *nocb_head; /* CBs waiting for kthread. */ struct rcu_head **nocb_tail; - atomic_long_t nocb_q_count; /* # CBs waiting for kthread */ - atomic_long_t nocb_q_count_lazy; /* (approximate). */ + atomic_long_t nocb_q_count; /* # CBs waiting for nocb */ + atomic_long_t nocb_q_count_lazy; /* invocation (all stages). */ struct rcu_head *nocb_follower_head; /* CBs ready to invoke. */ struct rcu_head **nocb_follower_tail; - atomic_long_t nocb_follower_count; /* # CBs ready to invoke. */ - atomic_long_t nocb_follower_count_lazy; /* (approximate). */ - int nocb_p_count; /* # CBs being invoked by kthread */ - int nocb_p_count_lazy; /* (approximate). */ wait_queue_head_t nocb_wq; /* For nocb kthreads to sleep on. */ struct task_struct *nocb_kthread; int nocb_defer_wakeup; /* Defer wakeup of nocb_kthread. */ @@ -356,8 +352,6 @@ struct rcu_data { struct rcu_head *nocb_gp_head ____cacheline_internodealigned_in_smp; /* CBs waiting for GP. */ struct rcu_head **nocb_gp_tail; - long nocb_gp_count; - long nocb_gp_count_lazy; bool nocb_leader_sleep; /* Is the nocb leader thread asleep? */ struct rcu_data *nocb_next_follower; /* Next follower in wakeup chain. */ @@ -622,24 +616,15 @@ static void rcu_dynticks_task_exit(void); #endif /* #ifndef RCU_TREE_NONCORE */ #ifdef CONFIG_RCU_TRACE -#ifdef CONFIG_RCU_NOCB_CPU -/* Sum up queue lengths for tracing. */ +/* Read out queue lengths for tracing. */ static inline void rcu_nocb_q_lengths(struct rcu_data *rdp, long *ql, long *qll) { - *ql = atomic_long_read(&rdp->nocb_q_count) + - rdp->nocb_p_count + - atomic_long_read(&rdp->nocb_follower_count) + - rdp->nocb_p_count + rdp->nocb_gp_count; - *qll = atomic_long_read(&rdp->nocb_q_count_lazy) + - rdp->nocb_p_count_lazy + - atomic_long_read(&rdp->nocb_follower_count_lazy) + - rdp->nocb_p_count_lazy + rdp->nocb_gp_count_lazy; -} +#ifdef CONFIG_RCU_NOCB_CPU + *ql = atomic_long_read(&rdp->nocb_q_count); + *qll = atomic_long_read(&rdp->nocb_q_count_lazy); #else /* #ifdef CONFIG_RCU_NOCB_CPU */ -static inline void rcu_nocb_q_lengths(struct rcu_data *rdp, long *ql, long *qll) -{ *ql = 0; *qll = 0; -} #endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */ +} #endif /* #ifdef CONFIG_RCU_TRACE */ diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 3ec85cb..e5c43b7 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -2056,9 +2056,26 @@ static void wake_nocb_leader(struct rcu_data *rdp, bool force) static bool rcu_nocb_cpu_needs_barrier(struct rcu_state *rsp, int cpu) { struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu); + unsigned long ret; +#ifdef CONFIG_PROVE_RCU struct rcu_head *rhp; +#endif /* #ifdef CONFIG_PROVE_RCU */ + + /* + * Check count of all no-CBs callbacks awaiting invocation. + * There needs to be a barrier before this function is called, + * but associated with a prior determination that no more + * callbacks would be posted. In the worst case, the first + * barrier in _rcu_barrier() suffices (but the caller cannot + * necessarily rely on this, not a substitute for the caller + * getting the concurrency design right!). There must also be + * a barrier between the following load an posting of a callback + * (if a callback is in fact needed). This is associated with an + * atomic_inc() in the caller. + */ + ret = atomic_long_read(&rdp->nocb_q_count); - /* No-CBs CPUs might have callbacks on any of three lists. */ +#ifdef CONFIG_PROVE_RCU rhp = ACCESS_ONCE(rdp->nocb_head); if (!rhp) rhp = ACCESS_ONCE(rdp->nocb_gp_head); @@ -2072,8 +2089,9 @@ static bool rcu_nocb_cpu_needs_barrier(struct rcu_state *rsp, int cpu) cpu, rhp->func); WARN_ON_ONCE(1); } +#endif /* #ifdef CONFIG_PROVE_RCU */ - return !!rhp; + return !!ret; } /* @@ -2095,9 +2113,10 @@ static void __call_rcu_nocb_enqueue(struct rcu_data *rdp, struct task_struct *t; /* Enqueue the callback on the nocb list and update counts. */ + atomic_long_add(rhcount, &rdp->nocb_q_count); + /* rcu_barrier() relies on ->nocb_q_count add before xchg. */ old_rhpp = xchg(&rdp->nocb_tail, rhtp); ACCESS_ONCE(*old_rhpp) = rhp; - atomic_long_add(rhcount, &rdp->nocb_q_count); atomic_long_add(rhcount_lazy, &rdp->nocb_q_count_lazy); smp_mb__after_atomic(); /* Store *old_rhpp before _wake test. */ @@ -2288,9 +2307,6 @@ wait_again: /* Move callbacks to wait-for-GP list, which is empty. */ ACCESS_ONCE(rdp->nocb_head) = NULL; rdp->nocb_gp_tail = xchg(&rdp->nocb_tail, &rdp->nocb_head); - rdp->nocb_gp_count = atomic_long_xchg(&rdp->nocb_q_count, 0); - rdp->nocb_gp_count_lazy = - atomic_long_xchg(&rdp->nocb_q_count_lazy, 0); gotcbs = true; } @@ -2338,9 +2354,6 @@ wait_again: /* Append callbacks to follower's "done" list. */ tail = xchg(&rdp->nocb_follower_tail, rdp->nocb_gp_tail); *tail = rdp->nocb_gp_head; - atomic_long_add(rdp->nocb_gp_count, &rdp->nocb_follower_count); - atomic_long_add(rdp->nocb_gp_count_lazy, - &rdp->nocb_follower_count_lazy); smp_mb__after_atomic(); /* Store *tail before wakeup. */ if (rdp != my_rdp && tail == &rdp->nocb_follower_head) { /* @@ -2415,13 +2428,11 @@ static int rcu_nocb_kthread(void *arg) trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, "WokeNonEmpty"); ACCESS_ONCE(rdp->nocb_follower_head) = NULL; tail = xchg(&rdp->nocb_follower_tail, &rdp->nocb_follower_head); - c = atomic_long_xchg(&rdp->nocb_follower_count, 0); - cl = atomic_long_xchg(&rdp->nocb_follower_count_lazy, 0); - rdp->nocb_p_count += c; - rdp->nocb_p_count_lazy += cl; /* Each pass through the following loop invokes a callback. */ - trace_rcu_batch_start(rdp->rsp->name, cl, c, -1); + trace_rcu_batch_start(rdp->rsp->name, + atomic_long_read(&rdp->nocb_q_count_lazy), + atomic_long_read(&rdp->nocb_q_count), -1); c = cl = 0; while (list) { next = list->next; @@ -2443,9 +2454,9 @@ static int rcu_nocb_kthread(void *arg) list = next; } trace_rcu_batch_end(rdp->rsp->name, c, !!list, 0, 0, 1); - ACCESS_ONCE(rdp->nocb_p_count) = rdp->nocb_p_count - c; - ACCESS_ONCE(rdp->nocb_p_count_lazy) = - rdp->nocb_p_count_lazy - cl; + smp_mb__before_atomic(); /* _add after CB invocation. */ + atomic_long_add(-c, &rdp->nocb_q_count); + atomic_long_add(-cl, &rdp->nocb_q_count_lazy); rdp->n_nocbs_invoked += c; } return 0; -- cgit v0.10.2 From 5a43b88e98eaca88fa12abcacd0000adc879dd2c Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Wed, 24 Dec 2014 17:55:28 +0800 Subject: rcu: Remove "select IRQ_WORK" from config TREE_RCU The 48a7639ce80c ("rcu: Make callers awaken grace-period kthread") removed the irq_work_queue(), so the TREE_RCU doesn't need irq work any more. This commit therefore updates RCU's Kconfig and Signed-off-by: Lai Jiangshan Signed-off-by: Paul E. McKenney diff --git a/init/Kconfig b/init/Kconfig index 9afb971..39b4313 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -470,7 +470,6 @@ choice config TREE_RCU bool "Tree-based hierarchical RCU" depends on !PREEMPT && SMP - select IRQ_WORK help This option selects the RCU implementation that is designed for very large SMP system with hundreds or @@ -480,7 +479,6 @@ config TREE_RCU config PREEMPT_RCU bool "Preemptible tree-based hierarchical RCU" depends on PREEMPT - select IRQ_WORK help This option selects the RCU implementation that is designed for very large SMP systems with hundreds or diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index cb59086..41860b9 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -27,7 +27,6 @@ #include #include #include -#include /* * Define shape of hierarchy based on NR_CPUS, CONFIG_RCU_FANOUT, and -- cgit v0.10.2 From b08ea27d95bcaee6d9cf4edd64f373006661424a Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 29 Oct 2014 15:39:39 -0700 Subject: rcu: Protect rcu_boost() lockless accesses with ACCESS_ONCE() This commit prevents random compiler optimizations by applying ACCESS_ONCE() to lockless accesses. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 3ec85cb..d59913e 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -1127,7 +1127,8 @@ static int rcu_boost(struct rcu_node *rnp) struct task_struct *t; struct list_head *tb; - if (rnp->exp_tasks == NULL && rnp->boost_tasks == NULL) + if (ACCESS_ONCE(rnp->exp_tasks) == NULL && + ACCESS_ONCE(rnp->boost_tasks) == NULL) return 0; /* Nothing left to boost. */ raw_spin_lock_irqsave(&rnp->lock, flags); -- cgit v0.10.2 From 74e871ac6cb17f67cbefb569d98a8d05de666e07 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 30 Oct 2014 21:08:53 -0700 Subject: rcu: Rename "empty" to "empty_norm" in preparation for boost rework This commit undertakes a simple variable renaming to make way for some rework of RCU priority boosting. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index d59913e..3d22f0b 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -313,8 +313,8 @@ static struct list_head *rcu_next_node_entry(struct task_struct *t, */ void rcu_read_unlock_special(struct task_struct *t) { - int empty; int empty_exp; + int empty_norm; int empty_exp_now; unsigned long flags; struct list_head *np; @@ -367,7 +367,7 @@ void rcu_read_unlock_special(struct task_struct *t) break; raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ } - empty = !rcu_preempt_blocked_readers_cgp(rnp); + empty_norm = !rcu_preempt_blocked_readers_cgp(rnp); empty_exp = !rcu_preempted_readers_exp(rnp); smp_mb(); /* ensure expedited fastpath sees end of RCU c-s. */ np = rcu_next_node_entry(t, rnp); @@ -393,7 +393,7 @@ void rcu_read_unlock_special(struct task_struct *t) * so we must take a snapshot of the expedited state. */ empty_exp_now = !rcu_preempted_readers_exp(rnp); - if (!empty && !rcu_preempt_blocked_readers_cgp(rnp)) { + if (!empty_norm && !rcu_preempt_blocked_readers_cgp(rnp)) { trace_rcu_quiescent_state_report(TPS("preempt_rcu"), rnp->gpnum, 0, rnp->qsmask, -- cgit v0.10.2 From 8af3a5e78cfb63abe8813743946b7bd5a8a3134c Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 31 Oct 2014 11:22:37 -0700 Subject: rcu: Abstract rcu_cleanup_dead_rnp() from rcu_cleanup_dead_cpu() This commit abstracts rcu_cleanup_dead_rnp() from rcu_cleanup_dead_cpu() in preparation for the rework of RCU priority boosting. This new function will be invoked from rcu_read_unlock_special() in the reworked scheme, which is why rcu_cleanup_dead_rnp() assumes that the leaf rcu_node structure's ->qsmaskinit field has already been updated. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 4c106fc..75c6b33 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -2227,6 +2227,46 @@ static void rcu_cleanup_dying_cpu(struct rcu_state *rsp) } /* + * All CPUs for the specified rcu_node structure have gone offline, + * and all tasks that were preempted within an RCU read-side critical + * section while running on one of those CPUs have since exited their RCU + * read-side critical section. Some other CPU is reporting this fact with + * the specified rcu_node structure's ->lock held and interrupts disabled. + * This function therefore goes up the tree of rcu_node structures, + * clearing the corresponding bits in the ->qsmaskinit fields. Note that + * the leaf rcu_node structure's ->qsmaskinit field has already been + * updated + * + * This function does check that the specified rcu_node structure has + * all CPUs offline and no blocked tasks, so it is OK to invoke it + * prematurely. That said, invoking it after the fact will cost you + * a needless lock acquisition. So once it has done its work, don't + * invoke it again. + */ +static void rcu_cleanup_dead_rnp(struct rcu_node *rnp_leaf) +{ + long mask; + struct rcu_node *rnp = rnp_leaf; + + if (rnp->qsmaskinit || rcu_preempt_has_tasks(rnp)) + return; + for (;;) { + mask = rnp->grpmask; + rnp = rnp->parent; + if (!rnp) + break; + raw_spin_lock(&rnp->lock); /* irqs already disabled. */ + smp_mb__after_unlock_lock(); /* GP memory ordering. */ + rnp->qsmaskinit &= ~mask; + if (rnp->qsmaskinit) { + raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ + return; + } + raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ + } +} + +/* * The CPU has been completely removed, and some other CPU is reporting * this fact from process context. Do the remainder of the cleanup, * including orphaning the outgoing CPU's RCU callbacks, and also @@ -2236,7 +2276,6 @@ static void rcu_cleanup_dying_cpu(struct rcu_state *rsp) static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp) { unsigned long flags; - unsigned long mask; int need_report = 0; struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu); struct rcu_node *rnp = rdp->mynode; /* Outgoing CPU's rdp & rnp. */ @@ -2252,24 +2291,14 @@ static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp) rcu_send_cbs_to_orphanage(cpu, rsp, rnp, rdp); rcu_adopt_orphan_cbs(rsp, flags); - /* Remove the outgoing CPU from the masks in the rcu_node hierarchy. */ - mask = rdp->grpmask; /* rnp->grplo is constant. */ - do { - raw_spin_lock(&rnp->lock); /* irqs already disabled. */ - smp_mb__after_unlock_lock(); - rnp->qsmaskinit &= ~mask; - if (rnp->qsmaskinit != 0) { - if (rnp != rdp->mynode) - raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ - break; - } - if (rnp == rdp->mynode) - need_report = rcu_preempt_offline_tasks(rsp, rnp, rdp); - else - raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ - mask = rnp->grpmask; - rnp = rnp->parent; - } while (rnp != NULL); + /* Remove outgoing CPU from mask in the leaf rcu_node structure. */ + raw_spin_lock(&rnp->lock); /* irqs already disabled. */ + smp_mb__after_unlock_lock(); /* Enforce GP memory-order guarantee. */ + rnp->qsmaskinit &= ~rdp->grpmask; + if (rnp->qsmaskinit == 0) { + need_report = rcu_preempt_offline_tasks(rsp, rnp, rdp); + rcu_cleanup_dead_rnp(rnp); + } /* * We still hold the leaf rcu_node structure lock here, and diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 8e7b184..9315477 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -552,6 +552,7 @@ static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp); #ifdef CONFIG_HOTPLUG_CPU static void rcu_report_unblock_qs_rnp(struct rcu_node *rnp, unsigned long flags); +static bool rcu_preempt_has_tasks(struct rcu_node *rnp); #endif /* #ifdef CONFIG_HOTPLUG_CPU */ static void rcu_print_detail_task_stall(struct rcu_state *rsp); static int rcu_print_task_stall(struct rcu_node *rnp); diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 3d22f0b..d044b9c 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -307,6 +307,15 @@ static struct list_head *rcu_next_node_entry(struct task_struct *t, } /* + * Return true if the specified rcu_node structure has tasks that were + * preempted within an RCU read-side critical section. + */ +static bool rcu_preempt_has_tasks(struct rcu_node *rnp) +{ + return !list_empty(&rnp->blkd_tasks); +} + +/* * Handle special cases during rcu_read_unlock(), such as needing to * notify RCU core processing or task having blocked during the RCU * read-side critical section. @@ -967,6 +976,14 @@ static void rcu_report_unblock_qs_rnp(struct rcu_node *rnp, unsigned long flags) raw_spin_unlock_irqrestore(&rnp->lock, flags); } +/* + * Because there is no preemptible RCU, there can be no readers blocked. + */ +static bool rcu_preempt_has_tasks(struct rcu_node *rnp) +{ + return false; +} + #endif /* #ifdef CONFIG_HOTPLUG_CPU */ /* -- cgit v0.10.2 From b6a932d1d9840727eee619d455bdeeedaa205be9 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 31 Oct 2014 12:05:04 -0700 Subject: rcu: Make rcu_read_unlock_special() propagate ->qsmaskinit bit clearing This commit causes rcu_read_unlock_special() to propagate ->qsmaskinit bit clearing up the rcu_node tree once a given rcu_node structure's blkd_tasks list becomes empty. This is the final commit in preparation for the rework of RCU priority boosting: It enables preempted tasks to remain queued on their rcu_node structure even after all of that rcu_node structure's CPUs have gone offline. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 75c6b33..6625a1b 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -2329,6 +2329,10 @@ static void rcu_cleanup_dying_cpu(struct rcu_state *rsp) { } +static void __maybe_unused rcu_cleanup_dead_rnp(struct rcu_node *rnp_leaf) +{ +} + static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp) { } diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index d044b9c..8a2b841 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -322,9 +322,10 @@ static bool rcu_preempt_has_tasks(struct rcu_node *rnp) */ void rcu_read_unlock_special(struct task_struct *t) { - int empty_exp; - int empty_norm; - int empty_exp_now; + bool empty; + bool empty_exp; + bool empty_norm; + bool empty_exp_now; unsigned long flags; struct list_head *np; #ifdef CONFIG_RCU_BOOST @@ -376,6 +377,7 @@ void rcu_read_unlock_special(struct task_struct *t) break; raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ } + empty = !rcu_preempt_has_tasks(rnp); empty_norm = !rcu_preempt_blocked_readers_cgp(rnp); empty_exp = !rcu_preempted_readers_exp(rnp); smp_mb(); /* ensure expedited fastpath sees end of RCU c-s. */ @@ -396,6 +398,14 @@ void rcu_read_unlock_special(struct task_struct *t) #endif /* #ifdef CONFIG_RCU_BOOST */ /* + * If this was the last task on the list, go see if we + * need to propagate ->qsmaskinit bit clearing up the + * rcu_node tree. + */ + if (!empty && !rcu_preempt_has_tasks(rnp)) + rcu_cleanup_dead_rnp(rnp); + + /* * If this was the last task on the current list, and if * we aren't waiting on any CPUs, report the quiescent state. * Note that rcu_report_unblock_qs_rnp() releases rnp->lock, -- cgit v0.10.2 From d19fb8d1f3f66cc342d30aa48f090c70afb753ed Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 31 Oct 2014 12:56:16 -0700 Subject: rcu: Don't migrate blocked tasks even if all corresponding CPUs offline When the last CPU associated with a given leaf rcu_node structure goes offline, something must be done about the tasks queued on that rcu_node structure. Each of these tasks has been preempted on one of the leaf rcu_node structure's CPUs while in an RCU read-side critical section that it have not yet exited. Handling these tasks is the job of rcu_preempt_offline_tasks(), which migrates them from the leaf rcu_node structure to the root rcu_node structure. Unfortunately, this migration has to be done one task at a time because each tasks allegiance must be shifted from the original leaf rcu_node to the root, so that future attempts to deal with these tasks will acquire the root rcu_node structure's ->lock rather than that of the leaf. Worse yet, this migration must be done with interrupts disabled, which is not so good for realtime response, especially given that there is no bound on the number of tasks on a given rcu_node structure's list. (OK, OK, there is a bound, it is just that it is unreasonably large, especially on 64-bit systems.) This was not considered a problem back when rcu_preempt_offline_tasks() was first written because realtime systems were assumed not to do CPU-hotplug operations while real-time applications were running. This assumption has proved of dubious validity given that people are starting to run multiple realtime applications on a single SMP system and that it is common practice to offline then online a CPU before starting its real-time application in order to clear extraneous processing off of that CPU. So we now need CPU hotplug operations to avoid undue latencies. This commit therefore avoids migrating these tasks, instead letting them be dequeued one by one from the original leaf rcu_node structure by rcu_read_unlock_special(). This means that the clearing of bits from the upper-level rcu_node structures must be deferred until the last such task has been dequeued, because otherwise subsequent grace periods won't wait on them. This commit has the beneficial side effect of simplifying the CPU-hotplug code for TREE_PREEMPT_RCU, especially in CONFIG_RCU_BOOST builds. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 6625a1b..84f16cf 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -2276,7 +2276,6 @@ static void rcu_cleanup_dead_rnp(struct rcu_node *rnp_leaf) static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp) { unsigned long flags; - int need_report = 0; struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu); struct rcu_node *rnp = rdp->mynode; /* Outgoing CPU's rdp & rnp. */ @@ -2295,25 +2294,10 @@ static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp) raw_spin_lock(&rnp->lock); /* irqs already disabled. */ smp_mb__after_unlock_lock(); /* Enforce GP memory-order guarantee. */ rnp->qsmaskinit &= ~rdp->grpmask; - if (rnp->qsmaskinit == 0) { - need_report = rcu_preempt_offline_tasks(rsp, rnp, rdp); + if (rnp->qsmaskinit == 0 && !rcu_preempt_has_tasks(rnp)) rcu_cleanup_dead_rnp(rnp); - } - - /* - * We still hold the leaf rcu_node structure lock here, and - * irqs are still disabled. The reason for this subterfuge is - * because invoking rcu_report_unblock_qs_rnp() with ->orphan_lock - * held leads to deadlock. - */ raw_spin_unlock(&rsp->orphan_lock); /* irqs remain disabled. */ - rnp = rdp->mynode; - if (need_report & RCU_OFL_TASKS_NORM_GP) - rcu_report_unblock_qs_rnp(rnp, flags); - else - raw_spin_unlock_irqrestore(&rnp->lock, flags); - if (need_report & RCU_OFL_TASKS_EXP_GP) - rcu_report_exp_rnp(rsp, rnp, true); + raw_spin_unlock_irqrestore(&rnp->lock, flags); WARN_ONCE(rdp->qlen != 0 || rdp->nxtlist != NULL, "rcu_cleanup_dead_cpu: Callbacks on offline CPU %d: qlen=%lu, nxtlist=%p\n", cpu, rdp->qlen, rdp->nxtlist); diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 9315477..883ebc8 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -514,13 +514,6 @@ extern struct list_head rcu_struct_flavors; #define for_each_rcu_flavor(rsp) \ list_for_each_entry((rsp), &rcu_struct_flavors, flavors) -/* Return values for rcu_preempt_offline_tasks(). */ - -#define RCU_OFL_TASKS_NORM_GP 0x1 /* Tasks blocking normal */ - /* GP were moved to root. */ -#define RCU_OFL_TASKS_EXP_GP 0x2 /* Tasks blocking expedited */ - /* GP were moved to root. */ - /* * RCU implementation internal declarations: */ @@ -550,24 +543,13 @@ long rcu_batches_completed(void); static void rcu_preempt_note_context_switch(void); static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp); #ifdef CONFIG_HOTPLUG_CPU -static void rcu_report_unblock_qs_rnp(struct rcu_node *rnp, - unsigned long flags); static bool rcu_preempt_has_tasks(struct rcu_node *rnp); #endif /* #ifdef CONFIG_HOTPLUG_CPU */ static void rcu_print_detail_task_stall(struct rcu_state *rsp); static int rcu_print_task_stall(struct rcu_node *rnp); static void rcu_preempt_check_blocked_tasks(struct rcu_node *rnp); -#ifdef CONFIG_HOTPLUG_CPU -static int rcu_preempt_offline_tasks(struct rcu_state *rsp, - struct rcu_node *rnp, - struct rcu_data *rdp); -#endif /* #ifdef CONFIG_HOTPLUG_CPU */ static void rcu_preempt_check_callbacks(void); void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu)); -#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PREEMPT_RCU) -static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp, - bool wake); -#endif /* #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PREEMPT_RCU) */ static void __init __rcu_init_preempt(void); static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags); static void rcu_preempt_boost_start_gp(struct rcu_node *rnp); diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 8a2b841..d594da4 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -103,6 +103,8 @@ RCU_STATE_INITIALIZER(rcu_preempt, 'p', call_rcu); static struct rcu_state *rcu_state_p = &rcu_preempt_state; static int rcu_preempted_readers_exp(struct rcu_node *rnp); +static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp, + bool wake); /* * Tell them what RCU they are running. @@ -545,92 +547,6 @@ static void rcu_preempt_check_blocked_tasks(struct rcu_node *rnp) #ifdef CONFIG_HOTPLUG_CPU -/* - * Handle tasklist migration for case in which all CPUs covered by the - * specified rcu_node have gone offline. Move them up to the root - * rcu_node. The reason for not just moving them to the immediate - * parent is to remove the need for rcu_read_unlock_special() to - * make more than two attempts to acquire the target rcu_node's lock. - * Returns true if there were tasks blocking the current RCU grace - * period. - * - * Returns 1 if there was previously a task blocking the current grace - * period on the specified rcu_node structure. - * - * The caller must hold rnp->lock with irqs disabled. - */ -static int rcu_preempt_offline_tasks(struct rcu_state *rsp, - struct rcu_node *rnp, - struct rcu_data *rdp) -{ - struct list_head *lp; - struct list_head *lp_root; - int retval = 0; - struct rcu_node *rnp_root = rcu_get_root(rsp); - struct task_struct *t; - - if (rnp == rnp_root) { - WARN_ONCE(1, "Last CPU thought to be offlined?"); - return 0; /* Shouldn't happen: at least one CPU online. */ - } - - /* If we are on an internal node, complain bitterly. */ - WARN_ON_ONCE(rnp != rdp->mynode); - - /* - * Move tasks up to root rcu_node. Don't try to get fancy for - * this corner-case operation -- just put this node's tasks - * at the head of the root node's list, and update the root node's - * ->gp_tasks and ->exp_tasks pointers to those of this node's, - * if non-NULL. This might result in waiting for more tasks than - * absolutely necessary, but this is a good performance/complexity - * tradeoff. - */ - if (rcu_preempt_blocked_readers_cgp(rnp) && rnp->qsmask == 0) - retval |= RCU_OFL_TASKS_NORM_GP; - if (rcu_preempted_readers_exp(rnp)) - retval |= RCU_OFL_TASKS_EXP_GP; - lp = &rnp->blkd_tasks; - lp_root = &rnp_root->blkd_tasks; - while (!list_empty(lp)) { - t = list_entry(lp->next, typeof(*t), rcu_node_entry); - raw_spin_lock(&rnp_root->lock); /* irqs already disabled */ - smp_mb__after_unlock_lock(); - list_del(&t->rcu_node_entry); - t->rcu_blocked_node = rnp_root; - list_add(&t->rcu_node_entry, lp_root); - if (&t->rcu_node_entry == rnp->gp_tasks) - rnp_root->gp_tasks = rnp->gp_tasks; - if (&t->rcu_node_entry == rnp->exp_tasks) - rnp_root->exp_tasks = rnp->exp_tasks; -#ifdef CONFIG_RCU_BOOST - if (&t->rcu_node_entry == rnp->boost_tasks) - rnp_root->boost_tasks = rnp->boost_tasks; -#endif /* #ifdef CONFIG_RCU_BOOST */ - raw_spin_unlock(&rnp_root->lock); /* irqs still disabled */ - } - - rnp->gp_tasks = NULL; - rnp->exp_tasks = NULL; -#ifdef CONFIG_RCU_BOOST - rnp->boost_tasks = NULL; - /* - * In case root is being boosted and leaf was not. Make sure - * that we boost the tasks blocking the current grace period - * in this case. - */ - raw_spin_lock(&rnp_root->lock); /* irqs already disabled */ - smp_mb__after_unlock_lock(); - if (rnp_root->boost_tasks != NULL && - rnp_root->boost_tasks != rnp_root->gp_tasks && - rnp_root->boost_tasks != rnp_root->exp_tasks) - rnp_root->boost_tasks = rnp_root->gp_tasks; - raw_spin_unlock(&rnp_root->lock); /* irqs still disabled */ -#endif /* #ifdef CONFIG_RCU_BOOST */ - - return retval; -} - #endif /* #ifdef CONFIG_HOTPLUG_CPU */ /* @@ -979,13 +895,6 @@ static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp) #ifdef CONFIG_HOTPLUG_CPU -/* Because preemptible RCU does not exist, no quieting of tasks. */ -static void rcu_report_unblock_qs_rnp(struct rcu_node *rnp, unsigned long flags) - __releases(rnp->lock) -{ - raw_spin_unlock_irqrestore(&rnp->lock, flags); -} - /* * Because there is no preemptible RCU, there can be no readers blocked. */ @@ -1023,23 +932,6 @@ static void rcu_preempt_check_blocked_tasks(struct rcu_node *rnp) WARN_ON_ONCE(rnp->qsmask); } -#ifdef CONFIG_HOTPLUG_CPU - -/* - * Because preemptible RCU does not exist, it never needs to migrate - * tasks that were blocked within RCU read-side critical sections, and - * such non-existent tasks cannot possibly have been blocking the current - * grace period. - */ -static int rcu_preempt_offline_tasks(struct rcu_state *rsp, - struct rcu_node *rnp, - struct rcu_data *rdp) -{ - return 0; -} - -#endif /* #ifdef CONFIG_HOTPLUG_CPU */ - /* * Because preemptible RCU does not exist, it never has any callbacks * to check. @@ -1058,20 +950,6 @@ void synchronize_rcu_expedited(void) } EXPORT_SYMBOL_GPL(synchronize_rcu_expedited); -#ifdef CONFIG_HOTPLUG_CPU - -/* - * Because preemptible RCU does not exist, there is never any need to - * report on tasks preempted in RCU read-side critical sections during - * expedited RCU grace periods. - */ -static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp, - bool wake) -{ -} - -#endif /* #ifdef CONFIG_HOTPLUG_CPU */ - /* * Because preemptible RCU does not exist, rcu_barrier() is just * another name for rcu_barrier_sched(). -- cgit v0.10.2 From a8f4cbadfb5c5f7f5d9eef70821276a4e2e9289d Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 31 Oct 2014 13:48:41 -0700 Subject: rcu: Shorten irq-disable region in rcu_cleanup_dead_cpu() Now that we are not migrating callbacks, there is no need to hold the ->orphan_lock across the the ->qsmaskinit bit-clearing process. This commit therefore releases ->orphan_lock immediately after adopting the orphaned RCU callbacks. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 84f16cf..990b406 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -2289,14 +2289,14 @@ static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp) /* Orphan the dead CPU's callbacks, and adopt them if appropriate. */ rcu_send_cbs_to_orphanage(cpu, rsp, rnp, rdp); rcu_adopt_orphan_cbs(rsp, flags); + raw_spin_unlock_irqrestore(&rsp->orphan_lock, flags); /* Remove outgoing CPU from mask in the leaf rcu_node structure. */ - raw_spin_lock(&rnp->lock); /* irqs already disabled. */ + raw_spin_lock_irqsave(&rnp->lock, flags); smp_mb__after_unlock_lock(); /* Enforce GP memory-order guarantee. */ rnp->qsmaskinit &= ~rdp->grpmask; if (rnp->qsmaskinit == 0 && !rcu_preempt_has_tasks(rnp)) rcu_cleanup_dead_rnp(rnp); - raw_spin_unlock(&rsp->orphan_lock); /* irqs remain disabled. */ raw_spin_unlock_irqrestore(&rnp->lock, flags); WARN_ONCE(rdp->qlen != 0 || rdp->nxtlist != NULL, "rcu_cleanup_dead_cpu: Callbacks on offline CPU %d: qlen=%lu, nxtlist=%p\n", -- cgit v0.10.2 From 96e92021d4fad8543d598b5e4926cb34fd40c4c4 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 31 Oct 2014 14:09:23 -0700 Subject: rcu: Make use of rcu_preempt_has_tasks() Given that there is now arcu_preempt_has_tasks() function that checks to see if the ->blkd_tasks list is non-empty, this commit makes use of it. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index d594da4..bcc6d91 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -540,7 +540,7 @@ static int rcu_print_task_stall(struct rcu_node *rnp) static void rcu_preempt_check_blocked_tasks(struct rcu_node *rnp) { WARN_ON_ONCE(rcu_preempt_blocked_readers_cgp(rnp)); - if (!list_empty(&rnp->blkd_tasks)) + if (rcu_preempt_has_tasks(rnp)) rnp->gp_tasks = rnp->blkd_tasks.next; WARN_ON_ONCE(rnp->qsmask); } @@ -706,7 +706,7 @@ sync_rcu_preempt_exp_init(struct rcu_state *rsp, struct rcu_node *rnp) raw_spin_lock_irqsave(&rnp->lock, flags); smp_mb__after_unlock_lock(); - if (list_empty(&rnp->blkd_tasks)) { + if (!rcu_preempt_has_tasks(rnp)) { raw_spin_unlock_irqrestore(&rnp->lock, flags); } else { rnp->exp_tasks = rnp->blkd_tasks.next; @@ -985,7 +985,7 @@ void exit_rcu(void) static void rcu_initiate_boost_trace(struct rcu_node *rnp) { - if (list_empty(&rnp->blkd_tasks)) + if (!rcu_preempt_has_tasks(rnp)) rnp->n_balk_blkd_tasks++; else if (rnp->exp_tasks == NULL && rnp->gp_tasks == NULL) rnp->n_balk_exp_gp_tasks++; -- cgit v0.10.2 From 3e9f5c70d85060ff0db71826e2048daffec336e7 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 3 Nov 2014 18:15:17 -0800 Subject: rcu: Don't spawn rcub kthreads on root rcu_node structure Now that offlining CPUs no longer moves leaf rcu_node structures' ->blkd_tasks lists to the root, there is no way for the root rcu_node structure's ->blkd_task list to be nonempty, unless the root node is also the sole leaf node. This commit therefore refrains from creating an rcub kthread for the root rcu_node structure unless it is also the sole leaf. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index bcc6d91..3e64bb1 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -1352,12 +1352,8 @@ static void __init rcu_spawn_boost_kthreads(void) for_each_possible_cpu(cpu) per_cpu(rcu_cpu_has_work, cpu) = 0; BUG_ON(smpboot_register_percpu_thread(&rcu_cpu_thread_spec)); - rnp = rcu_get_root(rcu_state_p); - (void)rcu_spawn_one_boost_kthread(rcu_state_p, rnp); - if (NUM_RCU_NODES > 1) { - rcu_for_each_leaf_node(rcu_state_p, rnp) - (void)rcu_spawn_one_boost_kthread(rcu_state_p, rnp); - } + rcu_for_each_leaf_node(rcu_state_p, rnp) + (void)rcu_spawn_one_boost_kthread(rcu_state_p, rnp); } static void rcu_prepare_kthreads(int cpu) -- cgit v0.10.2 From 1be0085b515e786e962b9f2d03616209eb6eb9a7 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 3 Nov 2014 18:20:20 -0800 Subject: rcu: Don't initiate RCU priority boosting on root rcu_node Because there is no longer any preempted tasks on the root rcu_node, and because there is no longer ever an rcub kthread for the root rcu_node, this commit drops the code in force_qs_rnp() that attempts to awaken the non-existent root rcub kthread. This is strictly a performance enhancement, removing a root rcu_node ->lock acquisition and release along with some tests in rcu_initiate_boost(), ending with the test that notes that there is no rcub kthread. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 990b406..5a0a4c96 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -2513,12 +2513,6 @@ static void force_qs_rnp(struct rcu_state *rsp, } raw_spin_unlock_irqrestore(&rnp->lock, flags); } - rnp = rcu_get_root(rsp); - if (rnp->qsmask == 0) { - raw_spin_lock_irqsave(&rnp->lock, flags); - smp_mb__after_unlock_lock(); - rcu_initiate_boost(rnp, flags); /* releases rnp->lock. */ - } } /* -- cgit v0.10.2 From 5d0b024973027556b48a09bb36b55dc853ec7c6e Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 10 Nov 2014 08:07:08 -0800 Subject: rcu: Don't bother affinitying rcub kthreads away from offline CPUs When rcu_boost_kthread_setaffinity() sees that all CPUs for a given rcu_node structure are now offline, it affinities the corresponding RCU-boost ("rcub") kthread away from those CPUs. This is pointless because the kthread cannot run on those offline CPUs in any case. This commit therefore removes this unneeded code. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 3e64bb1..1fac682 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -1322,12 +1322,8 @@ static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu) for (cpu = rnp->grplo; cpu <= rnp->grphi; cpu++, mask >>= 1) if ((mask & 0x1) && cpu != outgoingcpu) cpumask_set_cpu(cpu, cm); - if (cpumask_weight(cm) == 0) { + if (cpumask_weight(cm) == 0) cpumask_setall(cm); - for (cpu = rnp->grplo; cpu <= rnp->grphi; cpu++) - cpumask_clear_cpu(cpu, cm); - WARN_ON_ONCE(cpumask_weight(cm) == 0); - } set_cpus_allowed_ptr(t, cm); free_cpumask_var(cm); } -- cgit v0.10.2 From 3ba4d0e09bf965297e97adf195e0ea246cfe5c74 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 12 Nov 2014 09:57:51 -0800 Subject: rcu: Note quiescent state when CPU goes offline The rcu_cleanup_dead_cpu() function (called after a CPU has gone completely offline) has not reported a quiescent state because there was probably at least one synchronize_rcu() between the time the CPU went offline and the CPU_DEAD notifier, and this would have detected the CPU's offline state via quiescent-state forcing. However, the plan is for CPUs to take themselves offline, at which point it makes sense for them to report their own quiescent state. This commit makes this change in preparation for the new CPU-hotplug setup. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 5a0a4c96..24d3c67 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -2297,7 +2297,7 @@ static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp) rnp->qsmaskinit &= ~rdp->grpmask; if (rnp->qsmaskinit == 0 && !rcu_preempt_has_tasks(rnp)) rcu_cleanup_dead_rnp(rnp); - raw_spin_unlock_irqrestore(&rnp->lock, flags); + rcu_report_qs_rnp(rdp->grpmask, rsp, rnp, flags); /* Rlses rnp->lock. */ WARN_ONCE(rdp->qlen != 0 || rdp->nxtlist != NULL, "rcu_cleanup_dead_cpu: Callbacks on offline CPU %d: qlen=%lu, nxtlist=%p\n", cpu, rdp->qlen, rdp->nxtlist); -- cgit v0.10.2 From abaf3f9d275b8d856ae5e47531e40c0bfeac012b Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Tue, 18 Nov 2014 16:30:01 +0800 Subject: rcu: Revert "Allow post-unlock reference for rt_mutex" to avoid priority-inversion The patch dfeb9765ce3c ("Allow post-unlock reference for rt_mutex") ensured rcu-boost safe even the rt_mutex has post-unlock reference. But rt_mutex allowing post-unlock reference is definitely a bug and it was fixed by the commit 27e35715df54 ("rtmutex: Plug slow unlock race"). This fix made the previous patch (dfeb9765ce3c) useless. And even worse, the priority-inversion introduced by the the previous patch still exists. rcu_read_unlock_special() { rt_mutex_unlock(&rnp->boost_mtx); /* Priority-Inversion: * the current task had been deboosted and preempted as a low * priority task immediately, it could wait long before reschedule in, * and the rcu-booster also waits on this low priority task and sleeps. * This priority-inversion makes rcu-booster can't work * as expected. */ complete(&rnp->boost_completion); } Just revert the patch to avoid it. Cc: Thomas Gleixner Cc: Steven Rostedt Cc: Peter Zijlstra Signed-off-by: Lai Jiangshan Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 883ebc8..9535647 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -172,11 +172,6 @@ struct rcu_node { /* queued on this rcu_node structure that */ /* are blocking the current grace period, */ /* there can be no such task. */ - struct completion boost_completion; - /* Used to ensure that the rt_mutex used */ - /* to carry out the boosting is fully */ - /* released with no future boostee accesses */ - /* before that rt_mutex is re-initialized. */ struct rt_mutex boost_mtx; /* Used only for the priority-boosting */ /* side effect, not as a lock. */ diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 1fac682..625e260 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -429,10 +429,8 @@ void rcu_read_unlock_special(struct task_struct *t) #ifdef CONFIG_RCU_BOOST /* Unboost if we were boosted. */ - if (drop_boost_mutex) { + if (drop_boost_mutex) rt_mutex_unlock(&rnp->boost_mtx); - complete(&rnp->boost_completion); - } #endif /* #ifdef CONFIG_RCU_BOOST */ /* @@ -1081,15 +1079,11 @@ static int rcu_boost(struct rcu_node *rnp) */ t = container_of(tb, struct task_struct, rcu_node_entry); rt_mutex_init_proxy_locked(&rnp->boost_mtx, t); - init_completion(&rnp->boost_completion); raw_spin_unlock_irqrestore(&rnp->lock, flags); /* Lock only for side effect: boosts task t's priority. */ rt_mutex_lock(&rnp->boost_mtx); rt_mutex_unlock(&rnp->boost_mtx); /* Then keep lockdep happy. */ - /* Wait for boostee to be done w/boost_mtx before reinitializing. */ - wait_for_completion(&rnp->boost_completion); - return ACCESS_ONCE(rnp->exp_tasks) != NULL || ACCESS_ONCE(rnp->boost_tasks) != NULL; } -- cgit v0.10.2 From 6cd534ef8bfe422a5a847b953d1039841509c374 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 10 Dec 2014 07:45:13 -0800 Subject: rcu: Don't scan root rcu_node structure for stalled tasks Now that blocked tasks are no longer migrated to the root rcu_node structure, there is no need to scan the root rcu_node structure for blocked tasks stalling the current grace period. This commit therefore removes this scan. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 24d3c67..4ed2c28 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -1107,15 +1107,6 @@ static void print_other_cpu_stall(struct rcu_state *rsp) raw_spin_unlock_irqrestore(&rnp->lock, flags); } - /* - * Now rat on any tasks that got kicked up to the root rcu_node - * due to CPU offlining. - */ - rnp = rcu_get_root(rsp); - raw_spin_lock_irqsave(&rnp->lock, flags); - ndetected += rcu_print_task_stall(rnp); - raw_spin_unlock_irqrestore(&rnp->lock, flags); - print_cpu_stall_info_end(); for_each_possible_cpu(cpu) totqlen += per_cpu_ptr(rsp->rda, cpu)->qlen; -- cgit v0.10.2 From ab954c167ed9ac107a8beb1d6e1745ae698a3421 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 17 Dec 2014 18:25:59 -0800 Subject: rcu: Remove redundant callback-list initialization The RCU callback lists are initialized in both rcu_boot_init_percpu_data() and rcu_init_percpu_data(). The former is intended for initializing immutable data, so this commit removes the initialization from rcu_boot_init_percpu_data() and leaves it in rcu_init_percpu_data(). This change prepares for permitting callbacks to be queued very early in boot. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 4ed2c28..febd0f7 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -3419,9 +3419,6 @@ rcu_boot_init_percpu_data(int cpu, struct rcu_state *rsp) /* Set up local state, ensuring consistent view of global state. */ raw_spin_lock_irqsave(&rnp->lock, flags); rdp->grpmask = 1UL << (cpu - rdp->mynode->grplo); - init_callback_list(rdp); - rdp->qlen_lazy = 0; - ACCESS_ONCE(rdp->qlen) = 0; rdp->dynticks = &per_cpu(rcu_dynticks, cpu); WARN_ON_ONCE(rdp->dynticks->dynticks_nesting != DYNTICK_TASK_EXIT_IDLE); WARN_ON_ONCE(atomic_read(&rdp->dynticks->dynticks) != 1); -- cgit v0.10.2 From a5c198f4f7da6cc48116ca239c59c9f44b753364 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 23 Nov 2014 20:30:06 -0800 Subject: rcu: Expand SRCU ->completed to 64 bits When rcutorture used only the low-order 32 bits of the grace-period number, it was not a problem for SRCU to use a 32-bit completed field. However, rcutorture now uses the full 64 bits on 64-bit systems, so this commit converts SRCU's ->completed field to unsigned long so as to provide 64 bits on 64-bit systems. Signed-off-by: Paul E. McKenney diff --git a/include/linux/srcu.h b/include/linux/srcu.h index a2783cb..ef923dd 100644 --- a/include/linux/srcu.h +++ b/include/linux/srcu.h @@ -45,7 +45,7 @@ struct rcu_batch { #define RCU_BATCH_INIT(name) { NULL, &(name.head) } struct srcu_struct { - unsigned completed; + unsigned long completed; struct srcu_struct_array __percpu *per_cpu_ref; spinlock_t queue_lock; /* protect ->batch_queue, ->running */ bool running; @@ -135,7 +135,7 @@ int __srcu_read_lock(struct srcu_struct *sp) __acquires(sp); void __srcu_read_unlock(struct srcu_struct *sp, int idx) __releases(sp); void synchronize_srcu(struct srcu_struct *sp); void synchronize_srcu_expedited(struct srcu_struct *sp); -long srcu_batches_completed(struct srcu_struct *sp); +unsigned long srcu_batches_completed(struct srcu_struct *sp); void srcu_barrier(struct srcu_struct *sp); #ifdef CONFIG_DEBUG_LOCK_ALLOC diff --git a/kernel/rcu/srcu.c b/kernel/rcu/srcu.c index e037f3e..445bf8f 100644 --- a/kernel/rcu/srcu.c +++ b/kernel/rcu/srcu.c @@ -546,7 +546,7 @@ EXPORT_SYMBOL_GPL(srcu_barrier); * Report the number of batches, correlated with, but not necessarily * precisely the same as, the number of grace periods that have elapsed. */ -long srcu_batches_completed(struct srcu_struct *sp) +unsigned long srcu_batches_completed(struct srcu_struct *sp) { return sp->completed; } -- cgit v0.10.2 From 9735af5c78599703be633c057af3faee26482028 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 26 Nov 2014 10:42:50 -0800 Subject: rcu: Combine DEFINE_SRCU() and DEFINE_STATIC_SRCU() The DEFINE_SRCU() and DEFINE_STATIC_SRCU() definitions are quite similar, so this commit combines them, saving a bit of code and removing redundancy. Signed-off-by: Paul E. McKenney diff --git a/include/linux/srcu.h b/include/linux/srcu.h index ef923dd..9cfd962 100644 --- a/include/linux/srcu.h +++ b/include/linux/srcu.h @@ -102,13 +102,11 @@ void process_srcu(struct work_struct *work); * define and init a srcu struct at build time. * dont't call init_srcu_struct() nor cleanup_srcu_struct() on it. */ -#define DEFINE_SRCU(name) \ +#define __DEFINE_SRCU(name, is_static) \ static DEFINE_PER_CPU(struct srcu_struct_array, name##_srcu_array);\ - struct srcu_struct name = __SRCU_STRUCT_INIT(name); - -#define DEFINE_STATIC_SRCU(name) \ - static DEFINE_PER_CPU(struct srcu_struct_array, name##_srcu_array);\ - static struct srcu_struct name = __SRCU_STRUCT_INIT(name); + is_static struct srcu_struct name = __SRCU_STRUCT_INIT(name) +#define DEFINE_SRCU(name) __DEFINE_SRCU(name, /* not static */) +#define DEFINE_STATIC_SRCU(name) __DEFINE_SRCU(name, static) /** * call_srcu() - Queue a callback for invocation after an SRCU grace period -- cgit v0.10.2 From 83fe27ea531161a655f02dc7732d14cfaa27fd5d Mon Sep 17 00:00:00 2001 From: Pranith Kumar Date: Fri, 5 Dec 2014 11:24:45 -0500 Subject: rcu: Make SRCU optional by using CONFIG_SRCU SRCU is not necessary to be compiled by default in all cases. For tinification efforts not compiling SRCU unless necessary is desirable. The current patch tries to make compiling SRCU optional by introducing a new Kconfig option CONFIG_SRCU which is selected when any of the components making use of SRCU are selected. If we do not select CONFIG_SRCU, srcu.o will not be compiled at all. text data bss dec hex filename 2007 0 0 2007 7d7 kernel/rcu/srcu.o Size of arch/powerpc/boot/zImage changes from text data bss dec hex filename 831552 64180 23944 919676 e087c arch/powerpc/boot/zImage : before 829504 64180 23952 917636 e0084 arch/powerpc/boot/zImage : after so the savings are about ~2000 bytes. Signed-off-by: Pranith Kumar CC: Paul E. McKenney CC: Josh Triplett CC: Lai Jiangshan Signed-off-by: Paul E. McKenney [ paulmck: resolve conflict due to removal of arch/ia64/kvm/Kconfig. ] diff --git a/arch/arm/kvm/Kconfig b/arch/arm/kvm/Kconfig index 466bd29..3afee5f 100644 --- a/arch/arm/kvm/Kconfig +++ b/arch/arm/kvm/Kconfig @@ -23,6 +23,7 @@ config KVM select HAVE_KVM_CPU_RELAX_INTERCEPT select KVM_MMIO select KVM_ARM_HOST + select SRCU depends on ARM_VIRT_EXT && ARM_LPAE ---help--- Support hosting virtualized guest machines. You will also diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig index 8ba85e9..b334084 100644 --- a/arch/arm64/kvm/Kconfig +++ b/arch/arm64/kvm/Kconfig @@ -26,6 +26,7 @@ config KVM select KVM_ARM_HOST select KVM_ARM_VGIC select KVM_ARM_TIMER + select SRCU ---help--- Support hosting virtualized guest machines. diff --git a/arch/mips/kvm/Kconfig b/arch/mips/kvm/Kconfig index 30e334e..2ae1282 100644 --- a/arch/mips/kvm/Kconfig +++ b/arch/mips/kvm/Kconfig @@ -20,6 +20,7 @@ config KVM select PREEMPT_NOTIFIERS select ANON_INODES select KVM_MMIO + select SRCU ---help--- Support for hosting Guest kernels. Currently supported on MIPS32 processors. diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig index f5769f1..11850f3 100644 --- a/arch/powerpc/kvm/Kconfig +++ b/arch/powerpc/kvm/Kconfig @@ -21,6 +21,7 @@ config KVM select PREEMPT_NOTIFIERS select ANON_INODES select HAVE_KVM_EVENTFD + select SRCU config KVM_BOOK3S_HANDLER bool diff --git a/arch/s390/kvm/Kconfig b/arch/s390/kvm/Kconfig index 646db9c..5fce52c 100644 --- a/arch/s390/kvm/Kconfig +++ b/arch/s390/kvm/Kconfig @@ -28,6 +28,7 @@ config KVM select HAVE_KVM_IRQCHIP select HAVE_KVM_IRQFD select HAVE_KVM_IRQ_ROUTING + select SRCU ---help--- Support hosting paravirtualized guest machines using the SIE virtualization capability on the mainframe. This should work diff --git a/arch/tile/kvm/Kconfig b/arch/tile/kvm/Kconfig index 2298cb1..1e968f7 100644 --- a/arch/tile/kvm/Kconfig +++ b/arch/tile/kvm/Kconfig @@ -21,6 +21,7 @@ config KVM depends on HAVE_KVM && MODULES select PREEMPT_NOTIFIERS select ANON_INODES + select SRCU ---help--- Support hosting paravirtualized guest machines. diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index ba397bd..6612699 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -138,6 +138,7 @@ config X86 select HAVE_ACPI_APEI_NMI if ACPI select ACPI_LEGACY_TABLES_LOOKUP if ACPI select X86_FEATURE_NAMES if PROC_FS + select SRCU config INSTRUCTION_DECODER def_bool y diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig index f9d16ff..7dc7ba5 100644 --- a/arch/x86/kvm/Kconfig +++ b/arch/x86/kvm/Kconfig @@ -40,6 +40,7 @@ config KVM select HAVE_KVM_MSI select HAVE_KVM_CPU_RELAX_INTERCEPT select KVM_VFIO + select SRCU ---help--- Support hosting fully virtualized guest machines using hardware virtualization extensions. You will need a fairly recent diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 3f44f29..91f8613 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -13,6 +13,7 @@ config COMMON_CLK bool select HAVE_CLK_PREPARE select CLKDEV_LOOKUP + select SRCU ---help--- The common clock framework is a single definition of struct clk, useful across many platforms, as well as an diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 29b2ef5..a171fef 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -2,6 +2,7 @@ menu "CPU Frequency scaling" config CPU_FREQ bool "CPU Frequency scaling" + select SRCU help CPU Frequency scaling allows you to change the clock speed of CPUs on the fly. This is a nice method to save power, because diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index faf4e70..3891f67 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -1,5 +1,6 @@ menuconfig PM_DEVFREQ bool "Generic Dynamic Voltage and Frequency Scaling (DVFS) support" + select SRCU help A device may have a list of frequencies and voltages available. devfreq, a generic DVFS framework can be registered for a device diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 5bdedf6..c355a22 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -5,6 +5,7 @@ menuconfig MD bool "Multiple devices driver support (RAID and LVM)" depends on BLOCK + select SRCU help Support multiple physical spindles through a single logical device. Required for RAID and logical volume management. diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index d6607ee..84673eb 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -197,6 +197,7 @@ config NETCONSOLE_DYNAMIC config NETPOLL def_bool NETCONSOLE + select SRCU config NET_POLL_CONTROLLER def_bool NETPOLL diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig index a66768e..80e9c18 100644 --- a/fs/btrfs/Kconfig +++ b/fs/btrfs/Kconfig @@ -8,6 +8,7 @@ config BTRFS_FS select LZO_DECOMPRESS select RAID6_PQ select XOR_BLOCKS + select SRCU help Btrfs is a general purpose copy-on-write filesystem with extents, diff --git a/fs/notify/Kconfig b/fs/notify/Kconfig index 22c629e..2a24249 100644 --- a/fs/notify/Kconfig +++ b/fs/notify/Kconfig @@ -1,5 +1,6 @@ config FSNOTIFY def_bool n + select SRCU source "fs/notify/dnotify/Kconfig" source "fs/notify/inotify/Kconfig" diff --git a/fs/quota/Kconfig b/fs/quota/Kconfig index c51df1d..4a09975 100644 --- a/fs/quota/Kconfig +++ b/fs/quota/Kconfig @@ -5,6 +5,7 @@ config QUOTA bool "Quota support" select QUOTACTL + select SRCU help If you say Y here, you will be able to set per user limits for disk usage (also called disk quotas). Currently, it works for the diff --git a/init/Kconfig b/init/Kconfig index 9afb971..f085969 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -501,9 +501,17 @@ config TINY_RCU endchoice +config SRCU + bool + help + This option selects the sleepable version of RCU. This version + permits arbitrary sleeping or blocking within RCU read-side critical + sections. + config TASKS_RCU bool "Task_based RCU implementation using voluntary context switch" default n + select SRCU help This option enables a task-based RCU implementation that uses only voluntary context switch (not preemption!), idle, and @@ -1595,6 +1603,7 @@ config PERF_EVENTS depends on HAVE_PERF_EVENTS select ANON_INODES select IRQ_WORK + select SRCU help Enable kernel support for various performance events provided by software and hardware. diff --git a/kernel/notifier.c b/kernel/notifier.c index 4803da6..ae9fc7c 100644 --- a/kernel/notifier.c +++ b/kernel/notifier.c @@ -402,6 +402,7 @@ int raw_notifier_call_chain(struct raw_notifier_head *nh, } EXPORT_SYMBOL_GPL(raw_notifier_call_chain); +#ifdef CONFIG_SRCU /* * SRCU notifier chain routines. Registration and unregistration * use a mutex, and call_chain is synchronized by SRCU (no locks). @@ -528,6 +529,8 @@ void srcu_init_notifier_head(struct srcu_notifier_head *nh) } EXPORT_SYMBOL_GPL(srcu_init_notifier_head); +#endif /* CONFIG_SRCU */ + static ATOMIC_NOTIFIER_HEAD(die_chain); int notrace notify_die(enum die_val val, const char *str, diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 48b28d3..7e01f78 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -251,6 +251,7 @@ config APM_EMULATION config PM_OPP bool + select SRCU ---help--- SOCs have a standard set of tuples consisting of frequency and voltage pairs that the device will support per voltage domain. This diff --git a/kernel/rcu/Makefile b/kernel/rcu/Makefile index e6fae50..50a8084 100644 --- a/kernel/rcu/Makefile +++ b/kernel/rcu/Makefile @@ -1,4 +1,5 @@ -obj-y += update.o srcu.o +obj-y += update.o +obj-$(CONFIG_SRCU) += srcu.o obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o obj-$(CONFIG_TREE_RCU) += tree.o obj-$(CONFIG_PREEMPT_RCU) += tree.o diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 5f2ce61..7a9c93e 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1215,6 +1215,7 @@ config RCU_TORTURE_TEST tristate "torture tests for RCU" depends on DEBUG_KERNEL select TORTURE_TEST + select SRCU default n help This option provides a kernel module that runs torture tests diff --git a/mm/Kconfig b/mm/Kconfig index 1d1ae6b..4395b12 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -325,6 +325,7 @@ config VIRT_TO_BUS config MMU_NOTIFIER bool + select SRCU config KSM bool "Enable KSM for page merging" diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig index 8eb779b9d..604e718 100644 --- a/security/tomoyo/Kconfig +++ b/security/tomoyo/Kconfig @@ -5,6 +5,7 @@ config SECURITY_TOMOYO select SECURITYFS select SECURITY_PATH select SECURITY_NETWORK + select SRCU default n help This selects TOMOYO Linux, pathname-based access control. -- cgit v0.10.2 From 68158fe2b2aa0f0c1cf96b40020c7d180915479b Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 8 Dec 2014 07:41:31 -0800 Subject: rcu: Set default to RCU_CPU_STALL_INFO=y The RCU_CPU_STALL_INFO code has been in for quite some time, and has proven reliable. This commit therefore enables it by default. Signed-off-by: Paul E. McKenney diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 5f2ce61..b47d9e4 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1257,7 +1257,7 @@ config RCU_CPU_STALL_TIMEOUT config RCU_CPU_STALL_INFO bool "Print additional diagnostics on RCU CPU stall" depends on (TREE_RCU || PREEMPT_RCU) && DEBUG_KERNEL - default n + default y help For each stalled CPU that is aware of the current RCU grace period, print out additional per-CPU diagnostic information -- cgit v0.10.2 From e9408e4f278875aa5862560b4b425b27dfcb643f Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 8 Dec 2014 09:13:52 -0800 Subject: rcutorture: Add checks for stall ending before dump start The current rcutorture scripting checks for actual stalls (via the "Call Trace:" check), but fails to spot the case where a stall ends just as it is being detected. This commit therefore adds a check for this case. Signed-off-by: Paul E. McKenney diff --git a/tools/testing/selftests/rcutorture/bin/parse-console.sh b/tools/testing/selftests/rcutorture/bin/parse-console.sh index f962ba4..d8f35cf 100755 --- a/tools/testing/selftests/rcutorture/bin/parse-console.sh +++ b/tools/testing/selftests/rcutorture/bin/parse-console.sh @@ -36,7 +36,7 @@ if grep -Pq '\x00' < $file then print_warning Console output contains nul bytes, old qemu still running? fi -egrep 'Badness|WARNING:|Warn|BUG|===========|Call Trace:|Oops:' < $file | grep -v 'ODEBUG: ' | grep -v 'Warning: unable to open an initial console' > $T +egrep 'Badness|WARNING:|Warn|BUG|===========|Call Trace:|Oops:|Stall ended before state dump start' < $file | grep -v 'ODEBUG: ' | grep -v 'Warning: unable to open an initial console' > $T if test -s $T then print_warning Assertion failure in $file $title -- cgit v0.10.2 From fc908ed33e7c1428f799abb12399f906da03b397 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 8 Dec 2014 09:57:48 -0800 Subject: rcu: Make RCU_CPU_STALL_INFO include number of fqs attempts One way that an RCU CPU stall warning can happen is if the grace-period kthread is not allowed to execute. One proxy for this kthread's forward progress is the number of force-quiescent-state (fqs) scans. This commit therefore adds the number of fqs scans to the RCU CPU stall warning printouts when CONFIG_RCU_CPU_STALL_INFO=y. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 4c106fc..654b15b 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -1043,6 +1043,7 @@ static void record_gp_stall_check_time(struct rcu_state *rsp) j1 = rcu_jiffies_till_stall_check(); ACCESS_ONCE(rsp->jiffies_stall) = j + j1; rsp->jiffies_resched = j + j1 / 2; + rsp->n_force_qs_gpstart = ACCESS_ONCE(rsp->n_force_qs); } /* diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 8e7b184..e300848 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -492,6 +492,8 @@ struct rcu_state { /* for CPU stalls. */ unsigned long jiffies_resched; /* Time at which to resched */ /* a reluctant CPU. */ + unsigned long n_force_qs_gpstart; /* Snapshot of n_force_qs at */ + /* GP start. */ unsigned long gp_max; /* Maximum GP duration in */ /* jiffies. */ const char *name; /* Name of structure. */ diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 3ec85cb..769384d7 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -1898,11 +1898,12 @@ static void print_cpu_stall_info(struct rcu_state *rsp, int cpu) ticks_value = rsp->gpnum - rdp->gpnum; } print_cpu_stall_fast_no_hz(fast_no_hz, cpu); - pr_err("\t%d: (%lu %s) idle=%03x/%llx/%d softirq=%u/%u %s\n", + pr_err("\t%d: (%lu %s) idle=%03x/%llx/%d softirq=%u/%u fqs=%ld %s\n", cpu, ticks_value, ticks_title, atomic_read(&rdtp->dynticks) & 0xfff, rdtp->dynticks_nesting, rdtp->dynticks_nmi_nesting, rdp->softirq_snap, kstat_softirqs_cpu(RCU_SOFTIRQ, cpu), + ACCESS_ONCE(rsp->n_force_qs) - rsp->n_force_qs_gpstart, fast_no_hz); } -- cgit v0.10.2 From 6ccd2ecd422644277b7d8b37222e3af3f43ea9ae Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 11 Dec 2014 10:20:59 -0800 Subject: rcu: Improve diagnostics for spurious RCU CPU stall warnings The current RCU CPU stall warning code will print "Stall ended before state dump start" any time that the stall-warning code is triggered on a CPU that has already reported a quiescent state for the current grace period and if all quiescent states have been reported for the current grace period. However, a true stall can result in these symptoms, for example, by preventing RCU's grace-period kthreads from ever running This commit therefore checks for this condition, reporting the end of the stall only if one of the grace-period counters has actually advanced. Otherwise, it reports the last time that the grace-period kthread made meaningful progress. (In normal situations, the grace-period kthread should make meaningful progress at least every jiffies_till_next_fqs jiffies.) Reported-by: Miroslav Benes Signed-off-by: Paul E. McKenney Tested-by: Miroslav Benes diff --git a/Documentation/RCU/stallwarn.txt b/Documentation/RCU/stallwarn.txt index ed186a9..55f9707 100644 --- a/Documentation/RCU/stallwarn.txt +++ b/Documentation/RCU/stallwarn.txt @@ -187,6 +187,11 @@ o For !CONFIG_PREEMPT kernels, a CPU looping anywhere in the behavior, you might need to replace some of the cond_resched() calls with calls to cond_resched_rcu_qs(). +o Anything that prevents RCU's grace-period kthreads from running. + This can result in the "All QSes seen" console-log message. + This message will include information on when the kthread last + ran and how often it should be expected to run. + o A CPU-bound real-time task in a CONFIG_PREEMPT kernel, which might happen to preempt a low-priority task in the middle of an RCU read-side critical section. This is especially damaging if diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 654b15b..a2ceb66 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -1066,11 +1066,13 @@ static void rcu_dump_cpu_stacks(struct rcu_state *rsp) } } -static void print_other_cpu_stall(struct rcu_state *rsp) +static void print_other_cpu_stall(struct rcu_state *rsp, unsigned long gpnum) { int cpu; long delta; unsigned long flags; + unsigned long gpa; + unsigned long j; int ndetected = 0; struct rcu_node *rnp = rcu_get_root(rsp); long totqlen = 0; @@ -1123,10 +1125,22 @@ static void print_other_cpu_stall(struct rcu_state *rsp) pr_cont("(detected by %d, t=%ld jiffies, g=%ld, c=%ld, q=%lu)\n", smp_processor_id(), (long)(jiffies - rsp->gp_start), (long)rsp->gpnum, (long)rsp->completed, totqlen); - if (ndetected == 0) - pr_err("INFO: Stall ended before state dump start\n"); - else + if (ndetected) { rcu_dump_cpu_stacks(rsp); + } else { + if (ACCESS_ONCE(rsp->gpnum) != gpnum || + ACCESS_ONCE(rsp->completed) == gpnum) { + pr_err("INFO: Stall ended before state dump start\n"); + } else { + j = jiffies; + gpa = ACCESS_ONCE(rsp->gp_activity); + pr_err("All QSes seen, last %s kthread activity %ld (%ld-%ld), jiffies_till_next_fqs=%ld\n", + rsp->name, j - gpa, j, gpa, + jiffies_till_next_fqs); + /* In this case, the current CPU might be at fault. */ + sched_show_task(current); + } + } /* Complain about tasks blocking the grace period. */ @@ -1226,7 +1240,7 @@ static void check_cpu_stall(struct rcu_state *rsp, struct rcu_data *rdp) ULONG_CMP_GE(j, js + RCU_STALL_RAT_DELAY)) { /* They had a few time units to dump stack, so complain. */ - print_other_cpu_stall(rsp); + print_other_cpu_stall(rsp, gpnum); } } @@ -1622,6 +1636,7 @@ static int rcu_gp_init(struct rcu_state *rsp) struct rcu_data *rdp; struct rcu_node *rnp = rcu_get_root(rsp); + ACCESS_ONCE(rsp->gp_activity) = jiffies; rcu_bind_gp_kthread(); raw_spin_lock_irq(&rnp->lock); smp_mb__after_unlock_lock(); @@ -1682,6 +1697,7 @@ static int rcu_gp_init(struct rcu_state *rsp) rnp->grphi, rnp->qsmask); raw_spin_unlock_irq(&rnp->lock); cond_resched_rcu_qs(); + ACCESS_ONCE(rsp->gp_activity) = jiffies; } mutex_unlock(&rsp->onoff_mutex); @@ -1698,6 +1714,7 @@ static int rcu_gp_fqs(struct rcu_state *rsp, int fqs_state_in) unsigned long maxj; struct rcu_node *rnp = rcu_get_root(rsp); + ACCESS_ONCE(rsp->gp_activity) = jiffies; rsp->n_force_qs++; if (fqs_state == RCU_SAVE_DYNTICK) { /* Collect dyntick-idle snapshots. */ @@ -1736,6 +1753,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) struct rcu_data *rdp; struct rcu_node *rnp = rcu_get_root(rsp); + ACCESS_ONCE(rsp->gp_activity) = jiffies; raw_spin_lock_irq(&rnp->lock); smp_mb__after_unlock_lock(); gp_duration = jiffies - rsp->gp_start; @@ -1772,6 +1790,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) nocb += rcu_future_gp_cleanup(rsp, rnp); raw_spin_unlock_irq(&rnp->lock); cond_resched_rcu_qs(); + ACCESS_ONCE(rsp->gp_activity) = jiffies; } rnp = rcu_get_root(rsp); raw_spin_lock_irq(&rnp->lock); @@ -1821,6 +1840,7 @@ static int __noreturn rcu_gp_kthread(void *arg) if (rcu_gp_init(rsp)) break; cond_resched_rcu_qs(); + ACCESS_ONCE(rsp->gp_activity) = jiffies; WARN_ON(signal_pending(current)); trace_rcu_grace_period(rsp->name, ACCESS_ONCE(rsp->gpnum), @@ -1864,9 +1884,11 @@ static int __noreturn rcu_gp_kthread(void *arg) ACCESS_ONCE(rsp->gpnum), TPS("fqsend")); cond_resched_rcu_qs(); + ACCESS_ONCE(rsp->gp_activity) = jiffies; } else { /* Deal with stray signal. */ cond_resched_rcu_qs(); + ACCESS_ONCE(rsp->gp_activity) = jiffies; WARN_ON(signal_pending(current)); trace_rcu_grace_period(rsp->name, ACCESS_ONCE(rsp->gpnum), diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index e300848..5ec81cf 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -488,6 +488,8 @@ struct rcu_state { /* due to no GP active. */ unsigned long gp_start; /* Time at which GP started, */ /* but in jiffies. */ + unsigned long gp_activity; /* Time of last GP kthread */ + /* activity in jiffies. */ unsigned long jiffies_stall; /* Time at which to check */ /* for CPU stalls. */ unsigned long jiffies_resched; /* Time at which to resched */ -- cgit v0.10.2 From e3663b1024d1f94688e5233440ad67a9bc10b94e Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 8 Dec 2014 20:26:55 -0800 Subject: rcu: Handle gpnum/completed wrap while dyntick idle Subtle race conditions can result if a CPU stays in dyntick-idle mode long enough for the ->gpnum and ->completed fields to wrap. For example, consider the following sequence of events: o CPU 1 encounters a quiescent state while waiting for grace period 5 to complete, but then enters dyntick-idle mode. o While CPU 1 is in dyntick-idle mode, the grace-period counters wrap around so that the grace period number is now 4. o Just as CPU 1 exits dyntick-idle mode, grace period 4 completes and grace period 5 begins. o The quiescent state that CPU 1 passed through during the old grace period 5 looks like it applies to the new grace period 5. Therefore, the new grace period 5 completes without CPU 1 having passed through a quiescent state. This could clearly be a fatal surprise to any long-running RCU read-side critical section that happened to be running on CPU 1 at the time. At one time, this was not a problem, given that it takes significant time for the grace-period counters to overflow even on 32-bit systems. However, with the advent of NO_HZ_FULL and SMP embedded systems, arbitrarily long idle periods are now becoming quite feasible. It is therefore time to close this race. This commit therefore avoids this race condition by having the quiescent-state forcing code detect when a CPU is falling too far behind, and setting a new rcu_data field ->gpwrap when this happens. Whenever this new ->gpwrap field is set, the CPU's ->gpnum and ->completed fields are known to be untrustworthy, and can be ignored, along with any associated quiescent states. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index a2ceb66..5987fdc 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -930,6 +930,9 @@ static int dyntick_save_progress_counter(struct rcu_data *rdp, trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, TPS("dti")); return 1; } else { + if (ULONG_CMP_LT(ACCESS_ONCE(rdp->gpnum) + ULONG_MAX / 4, + rdp->mynode->gpnum)) + ACCESS_ONCE(rdp->gpwrap) = true; return 0; } } @@ -1577,7 +1580,8 @@ static bool __note_gp_changes(struct rcu_state *rsp, struct rcu_node *rnp, bool ret; /* Handle the ends of any preceding grace periods first. */ - if (rdp->completed == rnp->completed) { + if (rdp->completed == rnp->completed && + !unlikely(ACCESS_ONCE(rdp->gpwrap))) { /* No grace period end, so just accelerate recent callbacks. */ ret = rcu_accelerate_cbs(rsp, rnp, rdp); @@ -1592,7 +1596,7 @@ static bool __note_gp_changes(struct rcu_state *rsp, struct rcu_node *rnp, trace_rcu_grace_period(rsp->name, rdp->gpnum, TPS("cpuend")); } - if (rdp->gpnum != rnp->gpnum) { + if (rdp->gpnum != rnp->gpnum || unlikely(ACCESS_ONCE(rdp->gpwrap))) { /* * If the current grace period is waiting for this CPU, * set up to detect a quiescent state, otherwise don't @@ -1603,6 +1607,7 @@ static bool __note_gp_changes(struct rcu_state *rsp, struct rcu_node *rnp, rdp->passed_quiesce = 0; rdp->qs_pending = !!(rnp->qsmask & rdp->grpmask); zero_cpu_stall_ticks(rdp); + ACCESS_ONCE(rdp->gpwrap) = false; } return ret; } @@ -1616,7 +1621,8 @@ static void note_gp_changes(struct rcu_state *rsp, struct rcu_data *rdp) local_irq_save(flags); rnp = rdp->mynode; if ((rdp->gpnum == ACCESS_ONCE(rnp->gpnum) && - rdp->completed == ACCESS_ONCE(rnp->completed)) || /* w/out lock. */ + rdp->completed == ACCESS_ONCE(rnp->completed) && + !unlikely(ACCESS_ONCE(rdp->gpwrap))) || /* w/out lock. */ !raw_spin_trylock(&rnp->lock)) { /* irqs already off, so later. */ local_irq_restore(flags); return; @@ -2066,7 +2072,7 @@ rcu_report_qs_rdp(int cpu, struct rcu_state *rsp, struct rcu_data *rdp) raw_spin_lock_irqsave(&rnp->lock, flags); smp_mb__after_unlock_lock(); if (rdp->passed_quiesce == 0 || rdp->gpnum != rnp->gpnum || - rnp->completed == rnp->gpnum) { + rnp->completed == rnp->gpnum || rdp->gpwrap) { /* * The grace period in which this quiescent state was @@ -3190,7 +3196,8 @@ static int __rcu_pending(struct rcu_state *rsp, struct rcu_data *rdp) } /* Has a new RCU grace period started? */ - if (ACCESS_ONCE(rnp->gpnum) != rdp->gpnum) { /* outside lock */ + if (ACCESS_ONCE(rnp->gpnum) != rdp->gpnum || + unlikely(ACCESS_ONCE(rdp->gpwrap))) { /* outside lock */ rdp->n_rp_gp_started++; return 1; } diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 5ec81cf..7472ff3 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -260,6 +260,7 @@ struct rcu_data { bool passed_quiesce; /* User-mode/idle loop etc. */ bool qs_pending; /* Core waits for quiesc state. */ bool beenonline; /* CPU online at least once. */ + bool gpwrap; /* Possible gpnum/completed wrap. */ struct rcu_node *mynode; /* This CPU's leaf of hierarchy */ unsigned long grpmask; /* Mask to apply to leaf qsmask. */ #ifdef CONFIG_RCU_CPU_STALL_INFO diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 769384d7..81ff8b9 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -1605,7 +1605,8 @@ static bool __maybe_unused rcu_try_advance_all_cbs(void) * completed since we last checked and there are * callbacks not yet ready to invoke. */ - if (rdp->completed != rnp->completed && + if ((rdp->completed != rnp->completed || + unlikely(ACCESS_ONCE(rdp->gpwrap))) && rdp->nxttail[RCU_DONE_TAIL] != rdp->nxttail[RCU_NEXT_TAIL]) note_gp_changes(rsp, rdp); -- cgit v0.10.2 From 8b3a38daff6f50027039d6979b9eb026907508eb Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 23 Dec 2014 19:04:23 +0100 Subject: brcmfmac: Add support for bcm43340/1 wireless chipsets This patch adds support for the bcm43340 and bcm43341 wireless chipsets. These two chipsets are identical from wireless parts perspective. As such they use the same firmware image. Cc: Samuel Ortiz Cc: Rob Herring Signed-off-by: John Stultz [arend@broadcom.com: squash to single commit, remove 43341 chipid] Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 9880dae..dffd9e4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -1005,6 +1005,8 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { BRCMF_SDIO_DEVICE(BRCM_SDIO_4329_DEVICE_ID), BRCMF_SDIO_DEVICE(BRCM_SDIO_4330_DEVICE_ID), BRCMF_SDIO_DEVICE(BRCM_SDIO_4334_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_43340_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_43341_DEVICE_ID), BRCMF_SDIO_DEVICE(BRCM_SDIO_43362_DEVICE_ID), BRCMF_SDIO_DEVICE(BRCM_SDIO_4335_4339_DEVICE_ID), BRCMF_SDIO_DEVICE(BRCM_SDIO_4354_DEVICE_ID), diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index 519b79e..04d2ca0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -481,6 +481,7 @@ static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) ci->pub.ramsize = 0x48000; break; case BRCM_CC_4334_CHIP_ID: + case BRCM_CC_43340_CHIP_ID: ci->pub.ramsize = 0x80000; break; case BRCM_CC_4335_CHIP_ID: diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 0b0d51a..551da35 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -608,6 +608,8 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { #define BCM4330_NVRAM_NAME "brcm/brcmfmac4330-sdio.txt" #define BCM4334_FIRMWARE_NAME "brcm/brcmfmac4334-sdio.bin" #define BCM4334_NVRAM_NAME "brcm/brcmfmac4334-sdio.txt" +#define BCM43340_FIRMWARE_NAME "brcm/brcmfmac43340-sdio.bin" +#define BCM43340_NVRAM_NAME "brcm/brcmfmac43340-sdio.txt" #define BCM4335_FIRMWARE_NAME "brcm/brcmfmac4335-sdio.bin" #define BCM4335_NVRAM_NAME "brcm/brcmfmac4335-sdio.txt" #define BCM43362_FIRMWARE_NAME "brcm/brcmfmac43362-sdio.bin" @@ -629,6 +631,8 @@ MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4330_NVRAM_NAME); MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4334_NVRAM_NAME); +MODULE_FIRMWARE(BCM43340_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43340_NVRAM_NAME); MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4335_NVRAM_NAME); MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); @@ -660,6 +664,7 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) }, { BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) }, { BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, + { BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43340) }, { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index 6996fcc..00215ef 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -34,6 +34,7 @@ #define BRCM_CC_4329_CHIP_ID 0x4329 #define BRCM_CC_4330_CHIP_ID 0x4330 #define BRCM_CC_4334_CHIP_ID 0x4334 +#define BRCM_CC_43340_CHIP_ID 43340 #define BRCM_CC_43362_CHIP_ID 43362 #define BRCM_CC_4335_CHIP_ID 0x4335 #define BRCM_CC_4339_CHIP_ID 0x4339 @@ -51,6 +52,8 @@ #define BRCM_SDIO_4329_DEVICE_ID BRCM_CC_4329_CHIP_ID #define BRCM_SDIO_4330_DEVICE_ID BRCM_CC_4330_CHIP_ID #define BRCM_SDIO_4334_DEVICE_ID BRCM_CC_4334_CHIP_ID +#define BRCM_SDIO_43340_DEVICE_ID BRCM_CC_43340_CHIP_ID +#define BRCM_SDIO_43341_DEVICE_ID 43341 #define BRCM_SDIO_43362_DEVICE_ID BRCM_CC_43362_CHIP_ID #define BRCM_SDIO_4335_4339_DEVICE_ID BRCM_CC_4335_CHIP_ID #define BRCM_SDIO_4354_DEVICE_ID BRCM_CC_4354_CHIP_ID diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index 0f01fe0..9968079 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -24,13 +24,15 @@ * Vendors and devices. Sort key: vendor first, device next. */ #define SDIO_VENDOR_ID_BROADCOM 0x02d0 -#define SDIO_DEVICE_ID_BROADCOM_43143 43143 +#define SDIO_DEVICE_ID_BROADCOM_43143 0xa887 #define SDIO_DEVICE_ID_BROADCOM_43241 0x4324 #define SDIO_DEVICE_ID_BROADCOM_4329 0x4329 #define SDIO_DEVICE_ID_BROADCOM_4330 0x4330 #define SDIO_DEVICE_ID_BROADCOM_4334 0x4334 +#define SDIO_DEVICE_ID_BROADCOM_43340 0xa94c +#define SDIO_DEVICE_ID_BROADCOM_43341 0xa94d #define SDIO_DEVICE_ID_BROADCOM_4335_4339 0x4335 -#define SDIO_DEVICE_ID_BROADCOM_43362 43362 +#define SDIO_DEVICE_ID_BROADCOM_43362 0xa962 #define SDIO_DEVICE_ID_BROADCOM_4354 0x4354 #define SDIO_VENDOR_ID_INTEL 0x0089 -- cgit v0.10.2 From bd7d0d103ae8c841c0e2b9fd6c5e591b2fae3bb5 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 26 Dec 2014 03:02:31 -0800 Subject: mwifiex: enable -D__CHECK_ENDIAN__ for sparse by default Enable the endian checks by default. Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/mwifiex/Makefile index 9487d72..fdfd9bf 100644 --- a/drivers/net/wireless/mwifiex/Makefile +++ b/drivers/net/wireless/mwifiex/Makefile @@ -53,3 +53,5 @@ obj-$(CONFIG_MWIFIEX_PCIE) += mwifiex_pcie.o mwifiex_usb-y += usb.o obj-$(CONFIG_MWIFIEX_USB) += mwifiex_usb.o + +ccflags-y += -D__CHECK_ENDIAN -- cgit v0.10.2 From 2ab87d5d670d6946cae03b519ab9ef76c932d1a3 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Fri, 26 Dec 2014 03:02:32 -0800 Subject: mwifiex: do not send key material cmd when delete wep key This patch fixes memory corruption reported by community developer. "Memory corruption occurs in mwifiex_ret_802_11_key_material_v1() when a short command response is received without a key length causing non initialised memory to be interpreted as the key length resulting in a memcpy() overwriting the part of the driver's private data structure beyond the key area." For v1 key material API firmwares, there is no need to send command to delete WEP key. WEP encryption/decryption is controlled by mac_control command. This patch avoids sending key material command in del_key case. Reported-by: Martin Fuzzey Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 1626868..fb9c5fc 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -902,9 +902,12 @@ static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, if (wep_key->key_length) { void *enc_key; - if (encrypt_key->key_disable) + if (encrypt_key->key_disable) { memset(&priv->wep_key[index], 0, sizeof(struct mwifiex_wep_key)); + if (wep_key->key_length) + goto done; + } if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) enc_key = encrypt_key; @@ -918,6 +921,7 @@ static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, return ret; } +done: if (priv->sec_info.wep_enabled) priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; else -- cgit v0.10.2 From 84b313b35f8158d7ff35d649d18428a7740db49e Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Fri, 26 Dec 2014 03:02:33 -0800 Subject: mwifiex: make tx packet 64 byte DMA aligned This patch adds support for DMA alignment of 64 bytes for TX packets. Signed-off-by: Xinming Hu Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index 8720a3d..9b983b5 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -101,6 +101,13 @@ mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, { struct txpd *local_tx_pd; struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); + unsigned int pad; + int headroom = (priv->adapter->iface_type == + MWIFIEX_USB) ? 0 : INTF_HEADER_LEN; + + pad = ((void *)skb->data - sizeof(*local_tx_pd) - + headroom - NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1); + skb_push(skb, pad); skb_push(skb, sizeof(*local_tx_pd)); @@ -114,10 +121,12 @@ mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, local_tx_pd->bss_num = priv->bss_num; local_tx_pd->bss_type = priv->bss_type; /* Always zero as the data is followed by struct txpd */ - local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd)); + local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd) + + pad); local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU); local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len - - sizeof(*local_tx_pd)); + sizeof(*local_tx_pd) - + pad); if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET; @@ -182,7 +191,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, ra_list_flags); return -1; } - skb_reserve(skb_aggr, headroom + sizeof(struct txpd)); + skb_reserve(skb_aggr, MWIFIEX_MIN_DATA_HEADER_LEN); tx_info_aggr = MWIFIEX_SKB_TXCB(skb_aggr); memset(tx_info_aggr, 0, sizeof(*tx_info_aggr)); diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h index cdca8ed..7aa988e 100644 --- a/drivers/net/wireless/mwifiex/decl.h +++ b/drivers/net/wireless/mwifiex/decl.h @@ -32,9 +32,12 @@ #define MWIFIEX_MAX_BSS_NUM (3) -#define MWIFIEX_MIN_DATA_HEADER_LEN 36 /* sizeof(mwifiex_txpd) - * + 4 byte alignment - */ +#define MWIFIEX_DMA_ALIGN_SZ 64 +#define MAX_TXPD_SZ 32 +#define INTF_HDR_ALIGN 4 + +#define MWIFIEX_MIN_DATA_HEADER_LEN (MWIFIEX_DMA_ALIGN_SZ + INTF_HDR_ALIGN + \ + MAX_TXPD_SZ) #define MWIFIEX_MGMT_FRAME_HEADER_SIZE 8 /* sizeof(pkt_type) * + sizeof(tx_control) */ diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c index b896d73..1debe76 100644 --- a/drivers/net/wireless/mwifiex/sta_tx.c +++ b/drivers/net/wireless/mwifiex/sta_tx.c @@ -47,8 +47,10 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, struct mwifiex_adapter *adapter = priv->adapter; struct txpd *local_tx_pd; struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); - u8 pad; + unsigned int pad; u16 pkt_type, pkt_offset; + int hroom = (priv->adapter->iface_type == MWIFIEX_USB) ? 0 : + INTF_HEADER_LEN; if (!skb->len) { dev_err(adapter->dev, "Tx: bad packet length: %d\n", skb->len); @@ -56,13 +58,12 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, return skb->data; } - pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; + BUG_ON(skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN); - /* If skb->data is not aligned; add padding */ - pad = (4 - (((void *)skb->data - NULL) & 0x3)) % 4; + pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; - BUG_ON(skb_headroom(skb) < (sizeof(*local_tx_pd) + INTF_HEADER_LEN - + pad)); + pad = ((void *)skb->data - (sizeof(*local_tx_pd) + hroom)- + NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1); skb_push(skb, sizeof(*local_tx_pd) + pad); local_tx_pd = (struct txpd *) skb->data; @@ -70,8 +71,8 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, local_tx_pd->bss_num = priv->bss_num; local_tx_pd->bss_type = priv->bss_type; local_tx_pd->tx_pkt_length = cpu_to_le16((u16)(skb->len - - (sizeof(struct txpd) - + pad))); + (sizeof(struct txpd) + + pad))); local_tx_pd->priority = (u8) skb->priority; local_tx_pd->pkt_delay_2ms = @@ -115,7 +116,7 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, local_tx_pd->tx_pkt_offset = cpu_to_le16(pkt_offset); /* make space for INTF_HEADER_LEN */ - skb_push(skb, INTF_HEADER_LEN); + skb_push(skb, hroom); if (!local_tx_pd->tx_control) /* TxCtrl set by user or default */ diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c index be3a203..38ac4d7 100644 --- a/drivers/net/wireless/mwifiex/uap_txrx.c +++ b/drivers/net/wireless/mwifiex/uap_txrx.c @@ -348,8 +348,10 @@ void *mwifiex_process_uap_txpd(struct mwifiex_private *priv, struct mwifiex_adapter *adapter = priv->adapter; struct uap_txpd *txpd; struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); - int pad, len; - u16 pkt_type; + int pad; + u16 pkt_type, pkt_offset; + int hroom = (priv->adapter->iface_type == MWIFIEX_USB) ? 0 : + INTF_HEADER_LEN; if (!skb->len) { dev_err(adapter->dev, "Tx: bad packet length: %d\n", skb->len); @@ -357,22 +359,21 @@ void *mwifiex_process_uap_txpd(struct mwifiex_private *priv, return skb->data; } - pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; - - /* If skb->data is not aligned, add padding */ - pad = (4 - (((void *)skb->data - NULL) & 0x3)) % 4; + BUG_ON(skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN); - len = sizeof(*txpd) + pad; + pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; - BUG_ON(skb_headroom(skb) < len + INTF_HEADER_LEN); + pad = ((void *)skb->data - (sizeof(*txpd) + hroom) - NULL) & + (MWIFIEX_DMA_ALIGN_SZ - 1); - skb_push(skb, len); + skb_push(skb, sizeof(*txpd) + pad); txpd = (struct uap_txpd *)skb->data; memset(txpd, 0, sizeof(*txpd)); txpd->bss_num = priv->bss_num; txpd->bss_type = priv->bss_type; - txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - len)); + txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - (sizeof(*txpd) + + pad))); txpd->priority = (u8)skb->priority; txpd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb); @@ -392,16 +393,17 @@ void *mwifiex_process_uap_txpd(struct mwifiex_private *priv, cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[txpd->priority]); /* Offset of actual data */ + pkt_offset = sizeof(*txpd) + pad; if (pkt_type == PKT_TYPE_MGMT) { /* Set the packet type and add header for management frame */ txpd->tx_pkt_type = cpu_to_le16(pkt_type); - len += MWIFIEX_MGMT_FRAME_HEADER_SIZE; + pkt_offset += MWIFIEX_MGMT_FRAME_HEADER_SIZE; } - txpd->tx_pkt_offset = cpu_to_le16(len); + txpd->tx_pkt_offset = cpu_to_le16(pkt_offset); /* make space for INTF_HEADER_LEN */ - skb_push(skb, INTF_HEADER_LEN); + skb_push(skb, hroom); if (!txpd->tx_control) /* TxCtrl set by user or default */ -- cgit v0.10.2 From 4f3dfdfb4cbf1771f2e0e17d0dcb2cd1852fd392 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 26 Dec 2014 03:02:34 -0800 Subject: mwifiex: get supported BA stream info from FW This patch adds support to get number of BA streams supported information from FW. Some newer chips(e.g. 8897 series) support 4 BA streams for TX; so driver should not disallow BA stream setup just after 2 streams have been established. Signed-off-by: Avinash Patil Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h index f275675..8e2e394 100644 --- a/drivers/net/wireless/mwifiex/11n.h +++ b/drivers/net/wireless/mwifiex/11n.h @@ -130,7 +130,9 @@ static inline u8 mwifiex_space_avail_for_new_ba_stream( { struct mwifiex_private *priv; u8 i; - u32 ba_stream_num = 0; + u32 ba_stream_num = 0, ba_stream_max; + + ba_stream_max = MWIFIEX_MAX_TX_BASTREAM_SUPPORTED; for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; @@ -139,8 +141,14 @@ static inline u8 mwifiex_space_avail_for_new_ba_stream( &priv->tx_ba_stream_tbl_ptr); } - return ((ba_stream_num < - MWIFIEX_MAX_TX_BASTREAM_SUPPORTED) ? true : false); + if (adapter->fw_api_ver == MWIFIEX_FW_V15) { + ba_stream_max = + GETSUPP_TXBASTREAMS(adapter->hw_dot_11n_dev_cap); + if (!ba_stream_max) + ba_stream_max = MWIFIEX_MAX_TX_BASTREAM_SUPPORTED; + } + + return ((ba_stream_num < ba_stream_max) ? true : false); } /* diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index a1b8d53..15ad776 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -233,6 +233,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22)) #define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30)) #define ISALLOWED_CHANWIDTH40(ht_param) (ht_param & BIT(2)) +#define GETSUPP_TXBASTREAMS(Dot11nDevCap) ((Dot11nDevCap >> 18) & 0xF) /* httxcfg bitmap * 0 reserved -- cgit v0.10.2 From 53831aa12538f8753fb77b7ab6408cce54973b30 Mon Sep 17 00:00:00 2001 From: Shrikrishna Khare Date: Tue, 6 Jan 2015 09:20:15 -0800 Subject: Driver: Vmxnet3: Make Rx ring 2 size configurable Rx ring 2 size can be configured by adjusting rx-jumbo parameter of ethtool -G. Signed-off-by: Ramya Bolla Signed-off-by: Shreyas Bhatewara Signed-off-by: Shrikrishna Khare Signed-off-by: David S. Miller diff --git a/drivers/net/vmxnet3/vmxnet3_defs.h b/drivers/net/vmxnet3/vmxnet3_defs.h index 4d84912..25b6fa4 100644 --- a/drivers/net/vmxnet3/vmxnet3_defs.h +++ b/drivers/net/vmxnet3/vmxnet3_defs.h @@ -342,6 +342,7 @@ union Vmxnet3_GenericDesc { #define VMXNET3_TX_RING_MAX_SIZE 4096 #define VMXNET3_TC_RING_MAX_SIZE 4096 #define VMXNET3_RX_RING_MAX_SIZE 4096 +#define VMXNET3_RX_RING2_MAX_SIZE 2048 #define VMXNET3_RC_RING_MAX_SIZE 8192 /* a list of reasons for queue stop */ diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index afd2953..7af1f5c 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -2505,6 +2505,9 @@ vmxnet3_adjust_rx_ring_size(struct vmxnet3_adapter *adapter) ring0_size = min_t(u32, ring0_size, VMXNET3_RX_RING_MAX_SIZE / sz * sz); ring1_size = adapter->rx_queue[0].rx_ring[1].size; + ring1_size = (ring1_size + sz - 1) / sz * sz; + ring1_size = min_t(u32, ring1_size, VMXNET3_RX_RING2_MAX_SIZE / + sz * sz); comp_size = ring0_size + ring1_size; for (i = 0; i < adapter->num_rx_queues; i++) { @@ -2585,7 +2588,7 @@ vmxnet3_open(struct net_device *netdev) err = vmxnet3_create_queues(adapter, adapter->tx_ring_size, adapter->rx_ring_size, - VMXNET3_DEF_RX_RING_SIZE); + adapter->rx_ring2_size); if (err) goto queue_err; @@ -2964,6 +2967,7 @@ vmxnet3_probe_device(struct pci_dev *pdev, adapter->tx_ring_size = VMXNET3_DEF_TX_RING_SIZE; adapter->rx_ring_size = VMXNET3_DEF_RX_RING_SIZE; + adapter->rx_ring2_size = VMXNET3_DEF_RX_RING2_SIZE; spin_lock_init(&adapter->cmd_lock); adapter->adapter_pa = dma_map_single(&adapter->pdev->dev, adapter, diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c index b7b5332..8a5a90e 100644 --- a/drivers/net/vmxnet3/vmxnet3_ethtool.c +++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c @@ -447,12 +447,12 @@ vmxnet3_get_ringparam(struct net_device *netdev, param->rx_max_pending = VMXNET3_RX_RING_MAX_SIZE; param->tx_max_pending = VMXNET3_TX_RING_MAX_SIZE; param->rx_mini_max_pending = 0; - param->rx_jumbo_max_pending = 0; + param->rx_jumbo_max_pending = VMXNET3_RX_RING2_MAX_SIZE; param->rx_pending = adapter->rx_ring_size; param->tx_pending = adapter->tx_ring_size; param->rx_mini_pending = 0; - param->rx_jumbo_pending = 0; + param->rx_jumbo_pending = adapter->rx_ring2_size; } @@ -461,7 +461,7 @@ vmxnet3_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *param) { struct vmxnet3_adapter *adapter = netdev_priv(netdev); - u32 new_tx_ring_size, new_rx_ring_size; + u32 new_tx_ring_size, new_rx_ring_size, new_rx_ring2_size; u32 sz; int err = 0; @@ -473,6 +473,10 @@ vmxnet3_set_ringparam(struct net_device *netdev, VMXNET3_RX_RING_MAX_SIZE) return -EINVAL; + if (param->rx_jumbo_pending == 0 || + param->rx_jumbo_pending > VMXNET3_RX_RING2_MAX_SIZE) + return -EINVAL; + /* if adapter not yet initialized, do nothing */ if (adapter->rx_buf_per_pkt == 0) { netdev_err(netdev, "adapter not completely initialized, " @@ -500,8 +504,15 @@ vmxnet3_set_ringparam(struct net_device *netdev, sz) != 0) return -EINVAL; - if (new_tx_ring_size == adapter->tx_queue[0].tx_ring.size && - new_rx_ring_size == adapter->rx_queue[0].rx_ring[0].size) { + /* ring2 has to be a multiple of VMXNET3_RING_SIZE_ALIGN */ + new_rx_ring2_size = (param->rx_jumbo_pending + VMXNET3_RING_SIZE_MASK) & + ~VMXNET3_RING_SIZE_MASK; + new_rx_ring2_size = min_t(u32, new_rx_ring2_size, + VMXNET3_RX_RING2_MAX_SIZE); + + if (new_tx_ring_size == adapter->tx_ring_size && + new_rx_ring_size == adapter->rx_ring_size && + new_rx_ring2_size == adapter->rx_ring2_size) { return 0; } @@ -522,7 +533,7 @@ vmxnet3_set_ringparam(struct net_device *netdev, vmxnet3_rq_destroy_all(adapter); err = vmxnet3_create_queues(adapter, new_tx_ring_size, - new_rx_ring_size, VMXNET3_DEF_RX_RING_SIZE); + new_rx_ring_size, new_rx_ring2_size); if (err) { /* failed, most likely because of OOM, try default @@ -530,11 +541,12 @@ vmxnet3_set_ringparam(struct net_device *netdev, netdev_err(netdev, "failed to apply new sizes, " "try the default ones\n"); new_rx_ring_size = VMXNET3_DEF_RX_RING_SIZE; + new_rx_ring2_size = VMXNET3_DEF_RX_RING2_SIZE; new_tx_ring_size = VMXNET3_DEF_TX_RING_SIZE; err = vmxnet3_create_queues(adapter, new_tx_ring_size, new_rx_ring_size, - VMXNET3_DEF_RX_RING_SIZE); + new_rx_ring2_size); if (err) { netdev_err(netdev, "failed to create queues " "with default sizes. Closing it\n"); @@ -549,6 +561,7 @@ vmxnet3_set_ringparam(struct net_device *netdev, } adapter->tx_ring_size = new_tx_ring_size; adapter->rx_ring_size = new_rx_ring_size; + adapter->rx_ring2_size = new_rx_ring2_size; out: clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state); diff --git a/drivers/net/vmxnet3/vmxnet3_int.h b/drivers/net/vmxnet3/vmxnet3_int.h index 5f0199f..048f020 100644 --- a/drivers/net/vmxnet3/vmxnet3_int.h +++ b/drivers/net/vmxnet3/vmxnet3_int.h @@ -69,10 +69,10 @@ /* * Version numbers */ -#define VMXNET3_DRIVER_VERSION_STRING "1.2.1.0-k" +#define VMXNET3_DRIVER_VERSION_STRING "1.3.1.0-k" /* a 32-bit int, each byte encode a verion number in VMXNET3_DRIVER_VERSION */ -#define VMXNET3_DRIVER_VERSION_NUM 0x01020100 +#define VMXNET3_DRIVER_VERSION_NUM 0x01030100 #if defined(CONFIG_PCI_MSI) /* RSS only makes sense if MSI-X is supported. */ @@ -352,6 +352,7 @@ struct vmxnet3_adapter { /* Ring sizes */ u32 tx_ring_size; u32 rx_ring_size; + u32 rx_ring2_size; struct work_struct work; @@ -384,6 +385,7 @@ struct vmxnet3_adapter { /* must be a multiple of VMXNET3_RING_SIZE_ALIGN */ #define VMXNET3_DEF_TX_RING_SIZE 512 #define VMXNET3_DEF_RX_RING_SIZE 256 +#define VMXNET3_DEF_RX_RING2_SIZE 128 #define VMXNET3_MAX_ETH_HDR_SIZE 22 #define VMXNET3_MAX_SKB_BUF_SIZE (3*1024) -- cgit v0.10.2 From 7197325b17737144e2e7cb9f68d2ebd1a0696ee1 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 31 Dec 2014 02:36:38 -0800 Subject: mwifiex: remove redundant flag MWIFIEX_HW_STATUS_FW_READY This flag is never set but checked at two places. We will get rid of this code. Signed-off-by: Cathy Luo Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 6125d1c..2568395 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -511,8 +511,7 @@ err_dnld_fw: if (adapter->if_ops.unregister_dev) adapter->if_ops.unregister_dev(adapter); - if ((adapter->hw_status == MWIFIEX_HW_STATUS_FW_READY) || - (adapter->hw_status == MWIFIEX_HW_STATUS_READY)) { + if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) { pr_debug("info: %s: shutdown mwifiex\n", __func__); adapter->init_wait_q_woken = false; @@ -1116,8 +1115,7 @@ err_init_fw: pr_debug("info: %s: unregister device\n", __func__); if (adapter->if_ops.unregister_dev) adapter->if_ops.unregister_dev(adapter); - if ((adapter->hw_status == MWIFIEX_HW_STATUS_FW_READY) || - (adapter->hw_status == MWIFIEX_HW_STATUS_READY)) { + if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) { pr_debug("info: %s: shutdown mwifiex\n", __func__); adapter->init_wait_q_woken = false; diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index dd55bc1..f927939 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -165,7 +165,6 @@ struct mwifiex_dbg { enum MWIFIEX_HARDWARE_STATUS { MWIFIEX_HW_STATUS_READY, MWIFIEX_HW_STATUS_INITIALIZING, - MWIFIEX_HW_STATUS_FW_READY, MWIFIEX_HW_STATUS_INIT_DONE, MWIFIEX_HW_STATUS_RESET, MWIFIEX_HW_STATUS_CLOSING, -- cgit v0.10.2 From 9d2e85e001c15789798a1550cce5e7cc3ffe4e9b Mon Sep 17 00:00:00 2001 From: Marc Yang Date: Wed, 31 Dec 2014 02:36:39 -0800 Subject: mwifiex: Adjust calling place of mwifiex_terminate_workqueue Workqueue needs to be flushed early when removing card, otherwise soft lockup issue may happen because main_process is triggered by interrupt while card is being removed. Signed-off-by: Marc Yang Signed-off-by: Cathy Luo Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 2568395..2d4047f 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -1166,6 +1166,8 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) adapter->surprise_removed = true; + mwifiex_terminate_workqueue(adapter); + /* Stop data */ for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; @@ -1208,8 +1210,6 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) wiphy_unregister(adapter->wiphy); wiphy_free(adapter->wiphy); - mwifiex_terminate_workqueue(adapter); - /* Unregister device */ dev_dbg(adapter->dev, "info: unregister device\n"); if (adapter->if_ops.unregister_dev) -- cgit v0.10.2 From b4f1b177be27103cd84a3692ae71bf857700e27f Mon Sep 17 00:00:00 2001 From: Marc Yang Date: Wed, 31 Dec 2014 02:36:40 -0800 Subject: mwifiex: increase delay during card reset 200ms is the requirement to allow VDD1.1 and VDD1.8 to drop to have proper reset. Signed-off-by: Cathy Luo Signed-off-by: Marc Yang Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 274a1b2..4e8aedd 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -1958,8 +1958,8 @@ static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter) pr_err("Resetting card...\n"); mmc_remove_host(target); - /* 20ms delay is based on experiment with sdhci controller */ - mdelay(20); + /* 200ms delay is based on experiment with sdhci controller */ + mdelay(200); target->rescan_entered = 0; /* rescan non-removable cards */ mmc_add_host(target); } -- cgit v0.10.2 From 4636187da60b6e33526050235c610409d9cc00e8 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 31 Dec 2014 02:36:41 -0800 Subject: mwifiex: add wakeup timer based recovery mechanism If host fails to wakeup the firmware, we will trigger card reset after 3 second timeout. Signed-off-by: Cathy Luo Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index a3dd601..524692a 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -52,6 +52,18 @@ static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv) return 0; } +static void wakeup_timer_fn(unsigned long data) +{ + struct mwifiex_adapter *adapter = (struct mwifiex_adapter *)data; + + dev_err(adapter->dev, "Firmware wakeup failed\n"); + adapter->hw_status = MWIFIEX_HW_STATUS_RESET; + mwifiex_cancel_all_pending_cmd(adapter); + + if (adapter->if_ops.card_reset) + adapter->if_ops.card_reset(adapter); +} + /* * This function initializes the private structure and sets default * values to the members. @@ -285,6 +297,9 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter) adapter->ext_scan = true; adapter->key_api_major_ver = 0; adapter->key_api_minor_ver = 0; + + setup_timer(&adapter->wakeup_timer, wakeup_timer_fn, + (unsigned long)adapter); } /* @@ -391,6 +406,7 @@ mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) return; } + del_timer(&adapter->wakeup_timer); mwifiex_cancel_all_pending_cmd(adapter); /* Free lock variables */ diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 2d4047f..119e875 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -237,6 +237,7 @@ process_start: (is_command_pending(adapter) || !mwifiex_wmm_lists_empty(adapter))) { adapter->pm_wakeup_fw_try = true; + mod_timer(&adapter->wakeup_timer, jiffies + (HZ*3)); adapter->if_ops.wakeup(adapter); continue; } @@ -244,6 +245,7 @@ process_start: if (IS_CARD_RX_RCVD(adapter)) { adapter->data_received = false; adapter->pm_wakeup_fw_try = false; + del_timer_sync(&adapter->wakeup_timer); if (adapter->ps_state == PS_STATE_SLEEP) adapter->ps_state = PS_STATE_AWAKE; } else { diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index f927939..55273ee 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -830,6 +830,7 @@ struct mwifiex_adapter { u16 gen_null_pkt; u16 pps_uapsd_mode; u32 pm_wakeup_fw_try; + struct timer_list wakeup_timer; u8 is_hs_configured; struct mwifiex_hs_config_param hs_cfg; u8 hs_activated; diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index c3a20f9..10e4a93 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -2064,6 +2064,7 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) * state until cookie is set */ adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_fw_try = false; + del_timer(&adapter->wakeup_timer); } } } diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index fbec95b..419e35f 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -312,6 +312,8 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_card_req = false; adapter->pm_wakeup_fw_try = false; + mod_timer(&adapter->wakeup_timer, + jiffies + (HZ*3)); break; } if (!mwifiex_send_null_packet @@ -326,6 +328,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_card_req = false; adapter->pm_wakeup_fw_try = false; + del_timer_sync(&adapter->wakeup_timer); break; diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index 8ae7a85..199b43f 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -990,6 +990,7 @@ static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) { /* Simulation of HS_AWAKE event */ adapter->pm_wakeup_fw_try = false; + del_timer_sync(&adapter->wakeup_timer); adapter->pm_wakeup_card_req = false; adapter->ps_state = PS_STATE_AWAKE; -- cgit v0.10.2 From fc647467c178936aad29a1e5332c05a23d6efb39 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 31 Dec 2014 02:36:42 -0800 Subject: mwifiex: wakeup pending wait queues Wakeup pending wait queues in unload path. This will help to avoid soft lockup issues in corner cases. Signed-off-by: Cathy Luo Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 524692a..b115e0f 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -408,6 +408,8 @@ mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) del_timer(&adapter->wakeup_timer); mwifiex_cancel_all_pending_cmd(adapter); + wake_up_interruptible(&adapter->cmd_wait_q.wait); + wake_up_interruptible(&adapter->hs_activate_wait_q); /* Free lock variables */ mwifiex_free_lock_list(adapter); -- cgit v0.10.2 From b420166fa87a281a5476716982655009fd11e724 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 31 Dec 2014 02:36:43 -0800 Subject: mwifiex: do not release lock during list_for_each_entry_safe() As we are releasing the lock, during next iteration we may end up getting page fault if other thread has already deleted that node. Signed-off-by: Cathy Luo Signed-off-by: Avinash Patil Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 8559720..946a2f7 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -1008,11 +1008,9 @@ mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter) list_for_each_entry_safe(cmd_node, tmp_node, &adapter->scan_pending_q, list) { list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); cmd_node->wait_q_enabled = false; mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); } spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); @@ -1070,12 +1068,8 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter) list_for_each_entry_safe(cmd_node, tmp_node, &adapter->scan_pending_q, list) { list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - scan_pending_q_flags); cmd_node->wait_q_enabled = false; mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - spin_lock_irqsave(&adapter->scan_pending_q_lock, - scan_pending_q_flags); } spin_unlock_irqrestore(&adapter->scan_pending_q_lock, scan_pending_q_flags); -- cgit v0.10.2 From ca5b20e6b4dfb1561cda8ef82dbe555aa26a036e Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 31 Dec 2014 02:36:44 -0800 Subject: mwifiex: Increase priority of firmware download message When driver is loaded, it is important to know if FW was already active or it is freshly downloaded. This patch increases the priority of these messages. Signed-off-by: Cathy Luo Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 10e4a93..a460b0e 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -1952,8 +1952,8 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, offset += txlen; } while (true); - dev_dbg(adapter->dev, "info:\nFW download over, size %d bytes\n", - offset); + dev_notice(adapter->dev, + "info: FW download over, size %d bytes\n", offset); ret = 0; diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 4e8aedd..a70f1033 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -986,8 +986,8 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, offset += txlen; } while (true); - dev_dbg(adapter->dev, "info: FW download over, size %d bytes\n", - offset); + dev_notice(adapter->dev, + "info: FW download over, size %d bytes\n", offset); ret = 0; done: diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index 199b43f..6c62995 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -930,7 +930,8 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, } while ((dnld_cmd != FW_HAS_LAST_BLOCK) && retries); cleanup: - dev_dbg(adapter->dev, "%s: %d bytes downloaded\n", __func__, tlen); + dev_notice(adapter->dev, + "info: FW download over, size %d bytes\n", tlen); kfree(recv_buff); kfree(fwdata); -- cgit v0.10.2 From 468133c54f4e5e194cb4bfd4e3bf640ae2304e97 Mon Sep 17 00:00:00 2001 From: Maithili Hinge Date: Wed, 31 Dec 2014 02:36:45 -0800 Subject: mwifiex: Move code for wowlan magic-packet and patterns to a function This patch moves code for wowlan magic-packet and patterns to a function mwifiex_set_mef_filter. Signed-off-by: Maithili Hinge Signed-off-by: Avinash Patil Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index f8b1ce0..2be2dc5 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2425,30 +2425,16 @@ mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq, } #ifdef CONFIG_PM -static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, - struct cfg80211_wowlan *wowlan) +static int mwifiex_set_mef_filter(struct mwifiex_private *priv, + struct cfg80211_wowlan *wowlan) { - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - struct mwifiex_ds_mef_cfg mef_cfg; - struct mwifiex_mef_entry *mef_entry; - int i, filt_num = 0, ret; + int i, filt_num = 0, ret = 0; bool first_pat = true; u8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1]; const u8 ipv4_mc_mac[] = {0x33, 0x33}; const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; - struct mwifiex_private *priv = - mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); - - if (!wowlan) { - dev_warn(adapter->dev, "None of the WOWLAN triggers enabled\n"); - return 0; - } - - if (!priv->media_connected) { - dev_warn(adapter->dev, - "Can not configure WOWLAN in disconnected state\n"); - return 0; - } + struct mwifiex_ds_mef_cfg mef_cfg; + struct mwifiex_mef_entry *mef_entry; mef_entry = kzalloc(sizeof(*mef_entry), GFP_KERNEL); if (!mef_entry) @@ -2463,9 +2449,9 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, for (i = 0; i < wowlan->n_patterns; i++) { memset(byte_seq, 0, sizeof(byte_seq)); if (!mwifiex_is_pattern_supported(&wowlan->patterns[i], - byte_seq, - MWIFIEX_MEF_MAX_BYTESEQ)) { - wiphy_err(wiphy, "Pattern not supported\n"); + byte_seq, + MWIFIEX_MEF_MAX_BYTESEQ)) { + dev_err(priv->adapter->dev, "Pattern not supported\n"); kfree(mef_entry); return -EOPNOTSUPP; } @@ -2489,9 +2475,9 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, mef_entry->filter[filt_num].repeat = 1; mef_entry->filter[filt_num].offset = - wowlan->patterns[i].pkt_offset; + wowlan->patterns[i].pkt_offset; memcpy(mef_entry->filter[filt_num].byte_seq, byte_seq, - sizeof(byte_seq)); + sizeof(byte_seq)); mef_entry->filter[filt_num].filt_type = TYPE_EQ; if (first_pat) @@ -2506,9 +2492,9 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST; mef_entry->filter[filt_num].repeat = 16; memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, - ETH_ALEN); + ETH_ALEN); mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = - ETH_ALEN; + ETH_ALEN; mef_entry->filter[filt_num].offset = 28; mef_entry->filter[filt_num].filt_type = TYPE_EQ; if (filt_num) @@ -2517,9 +2503,9 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, filt_num++; mef_entry->filter[filt_num].repeat = 16; memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, - ETH_ALEN); + ETH_ALEN); mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = - ETH_ALEN; + ETH_ALEN; mef_entry->filter[filt_num].offset = 56; mef_entry->filter[filt_num].filt_type = TYPE_EQ; mef_entry->filter[filt_num].filt_action = TYPE_OR; @@ -2527,16 +2513,46 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, if (!mef_cfg.criteria) mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST | - MWIFIEX_CRITERIA_UNICAST | - MWIFIEX_CRITERIA_MULTICAST; + MWIFIEX_CRITERIA_UNICAST | + MWIFIEX_CRITERIA_MULTICAST; ret = mwifiex_send_cmd(priv, HostCmd_CMD_MEF_CFG, - HostCmd_ACT_GEN_SET, 0, &mef_cfg, true); + HostCmd_ACT_GEN_SET, 0, &mef_cfg, true); kfree(mef_entry); return ret; } +static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, + struct cfg80211_wowlan *wowlan) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + int ret = 0; + struct mwifiex_private *priv = + mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); + + if (!wowlan) { + dev_warn(adapter->dev, "None of the WOWLAN triggers enabled\n"); + return 0; + } + + if (!priv->media_connected) { + dev_warn(adapter->dev, + "Can not configure WOWLAN in disconnected state\n"); + return 0; + } + + if (wowlan->n_patterns || wowlan->magic_pkt) { + ret = mwifiex_set_mef_filter(priv, wowlan); + if (ret) { + dev_err(adapter->dev, "Failed to set MEF filter\n"); + return ret; + } + } + + return ret; +} + static int mwifiex_cfg80211_resume(struct wiphy *wiphy) { return 0; -- cgit v0.10.2 From 8a40084e7bb8f0fd32a301a64fd869726516a1df Mon Sep 17 00:00:00 2001 From: Maithili Hinge Date: Wed, 31 Dec 2014 02:36:46 -0800 Subject: mwifiex: Add support for wowlan disconnect This patch adds support for wowlan disconnect. Signed-off-by: Maithili Hinge Signed-off-by: Avinash Patil Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 2be2dc5..93ab36f 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2527,6 +2527,7 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wowlan) { struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_ds_hs_cfg hs_cfg; int ret = 0; struct mwifiex_private *priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); @@ -2550,6 +2551,20 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, } } + if (wowlan->disconnect) { + memset(&hs_cfg, 0, sizeof(hs_cfg)); + hs_cfg.is_invoke_hostcmd = false; + hs_cfg.conditions = HS_CFG_COND_MAC_EVENT; + hs_cfg.gpio = HS_CFG_GPIO_DEF; + hs_cfg.gap = HS_CFG_GAP_DEF; + ret = mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, + MWIFIEX_SYNC_CMD, &hs_cfg); + if (ret) { + dev_err(adapter->dev, "Failed to set HS params\n"); + return ret; + } + } + return ret; } @@ -2890,7 +2905,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { #ifdef CONFIG_PM static const struct wiphy_wowlan_support mwifiex_wowlan_support = { - .flags = WIPHY_WOWLAN_MAGIC_PKT, + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, .n_patterns = MWIFIEX_MEF_MAX_FILTERS, .pattern_min_len = 1, .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN, -- cgit v0.10.2 From 83ac237a950e130c974d0170cce30891dcd8f250 Mon Sep 17 00:00:00 2001 From: Christoph Jaeger Date: Mon, 5 Jan 2015 22:34:09 -0500 Subject: livepatch: kconfig: use bool instead of boolean Keyword 'boolean' for type definition attributes is considered deprecated and should not be used anymore. No functional changes. Reference: http://lkml.kernel.org/r/cover.1418003065.git.cj@linux.com Reference: http://lkml.kernel.org/r/1419108071-11607-1-git-send-email-cj@linux.com Signed-off-by: Christoph Jaeger Reviewed-by: Petr Mladek Reviewed-by: Jingoo Han Acked-by: Josh Poimboeuf Signed-off-by: Jiri Kosina diff --git a/kernel/livepatch/Kconfig b/kernel/livepatch/Kconfig index 96da00f..66797aa 100644 --- a/kernel/livepatch/Kconfig +++ b/kernel/livepatch/Kconfig @@ -1,10 +1,10 @@ config ARCH_HAVE_LIVE_PATCHING - boolean + bool help Arch supports kernel live patching config LIVE_PATCHING - boolean "Kernel Live Patching" + bool "Kernel Live Patching" depends on DYNAMIC_FTRACE_WITH_REGS depends on MODULES depends on SYSFS -- cgit v0.10.2 From 6ce901eb61aa30ba8565c62049ee80c90728ef14 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 29 Dec 2014 15:21:26 +0100 Subject: HID: input: fix confusion on conflicting mappings On an PC-101/103/104 keyboard (American layout) the 'Enter' key and its neighbours look like this: +---+ +---+ +-------+ | 1 | | 2 | | 5 | +---+ +---+ +-------+ +---+ +-----------+ | 3 | | 4 | +---+ +-----------+ On a PC-102/105 keyboard (European layout) it looks like this: +---+ +---+ +-------+ | 1 | | 2 | | | +---+ +---+ +-+ 4 | +---+ +---+ | | | 3 | | 5 | | | +---+ +---+ +-----+ (Note that the number of keys is the same, but key '5' is moved down and the shape of key '4' is changed. Keys '1' to '3' are exactly the same.) The keys 1-4 report the same scan-code in HID in both layouts, even though the keysym they produce is usually different depending on the XKB-keymap used by user-space. However, key '5' (US 'backslash'/'pipe') reports 0x31 for the upper layout and 0x32 for the lower layout, as defined by the HID spec. This is highly confusing as the linux-input API uses a single keycode for both. So far, this was never a problem as there never has been a keyboard with both of those keys present at the same time. It would have to look something like this: +---+ +---+ +-------+ | 1 | | 2 | | x31 | +---+ +---+ +-------+ +---+ +---+ +-----+ | 3 | |x32| | 4 | +---+ +---+ +-----+ HID can represent such a keyboard, but the linux-input API cannot. Furthermore, any user-space mapping would be confused by this and, luckily, no-one ever produced such hardware. Now, the HID input layer fixed this mess by mapping both 0x31 and 0x32 to the same keycode (KEY_BACKSLASH==0x2b). As only one of both physical keys is present on a hardware, this works just fine. Lets introduce hardware-vendors into this: ------------------------------------------ Unfortunately, it seems way to expensive to produce a different device for American and European layouts. Therefore, hardware-vendors put both keys, (0x31 and 0x32) on the same keyboard, but only one of them is hooked up to the physical button, the other one is 'dead'. This means, they can use the same hardware, with a different button-layout and automatically produce the correct HID events for American *and* European layouts. This is unproblematic for normal keyboards, as the 'dead' key will never report any KEY-DOWN events. But RollOver keyboards send the whole matrix on each key-event, allowing n-key roll-over mode. This means, we get a 0x31 and 0x32 event on each key-press. One of them will always be 0, the other reports the real state. As we map both to the same keycode, we will get spurious key-events, even though the real key-state never changed. The easiest way would be to blacklist 'dead' keys and never handle those. We could simply read the 'country' tag of USB devices and blacklist either key according to the layout. But... hardware vendors... want the same device for all countries and thus many of them set 'country' to 0 for all devices. Meh.. So we have to deal with this properly. As we cannot know which of the keys is 'dead', we either need a heuristic and track those keys, or we simply make use of our value-tracking for HID fields. We simply ignore HID events for absolute data if the data didn't change. As HID tracks events on the HID level, we haven't done the keycode translation, yet. Therefore, the 'dead' key is tracked independently of the real key, therefore, any events on it will be ignored. This patch simply discards any HID events for absolute data if it didn't change compared to the last report. We need to ignore relative and buffered-byte reports for obvious reasons. But those cannot be affected by this bug, so we're fine. Preferably, we'd do this filtering on the HID-core level. But this might break a lot of custom drivers, if they do not follow the HID specs. Therefore, we do this late in hid-input just before we inject it into the input layer (which does the exact same filtering, but on the keycode level). If this turns out to break some devices, we might have to limit filtering to EV_KEY events. But lets try to do the Right Thing first, and properly filter any absolute data that didn't change. This patch is tagged for 'stable' as it fixes a lot of n-key RollOver hardware. We might wanna wait with backporting for a while, before we know it doesn't break anything else, though. Cc: Reported-by: Adam Goode Reported-by: Fredrik Hallenberg Tested-by: Fredrik Hallenberg Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index e0a0f06..84b6899 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1101,6 +1101,22 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; } + /* + * Ignore reports for absolute data if the data didn't change. This is + * not only an optimization but also fixes 'dead' key reports. Some + * RollOver implementations for localized keys (like BACKSLASH/PIPE; HID + * 0x31 and 0x32) report multiple keys, even though a localized keyboard + * can only have one of them physically available. The 'dead' keys + * report constant 0. As all map to the same keycode, they'd confuse + * the input layer. If we filter the 'dead' keys on the HID level, we + * skip the keycode translation and only forward real events. + */ + if (!(field->flags & (HID_MAIN_ITEM_RELATIVE | + HID_MAIN_ITEM_BUFFERED_BYTE)) && + usage->usage_index < field->maxusage && + value == field->value[usage->usage_index]) + return; + /* report the usage code as scancode if the key status has changed */ if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value) input_event(input, EV_MSC, MSC_SCAN, usage->hid); -- cgit v0.10.2 From 79bc33bd60ac3d5ab3bd1918fba95234ac9d510e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 6 Jan 2015 09:09:13 +0100 Subject: HID: fix Kconfig text Signed-off-by: Geert Uytterhoeven Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 230b6f8..303b295 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -388,7 +388,7 @@ config HID_LOGITECH_HIDPP Say Y if you want support for Logitech devices relying on the HID++ specification. Such devices are the various Logitech Touchpads (T650, T651, TK820), some mice (Zone Touch mouse), or even keyboards (Solar - Keayboard). + Keyboard). config LOGITECH_FF bool "Logitech force feedback support" -- cgit v0.10.2 From cbd366bea2b8513bc0fc1c9e8832cb0ab221d6d5 Mon Sep 17 00:00:00 2001 From: Ross Skaliotis Date: Sat, 20 Dec 2014 19:01:35 -0500 Subject: HID: apple: fix battery support for the 2009 ANSI wireless keyboard Enabled quirks necessary for correct battery capacity reporting. Cleaned up surrounding style. Signed-off-by: Ross Skaliotis Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index e0a0f06..146da43 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -306,10 +306,13 @@ static enum power_supply_property hidinput_battery_props[] = { static const struct hid_device_id hid_battery_quirks[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, - USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), - HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, + USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), + HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI), + HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, - USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI), + USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI), HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI), -- cgit v0.10.2 From 8e7b341037db1835ee6eea64663013cbfcf33575 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 6 Jan 2015 22:34:19 +0100 Subject: HID: fixup the conflicting keyboard mappings quirk The ignore check that got added in 6ce901eb61 ("HID: input: fix confusion on conflicting mappings") needs to properly check for VARIABLE reports as well (ARRAY reports should be ignored), otherwise legitimate keyboards might break. Cc: Fixes: 6ce901eb61 ("HID: input: fix confusion on conflicting mappings") Reported-by: Fredrik Hallenberg Reported-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 84b6899..a758900 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1113,6 +1113,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct */ if (!(field->flags & (HID_MAIN_ITEM_RELATIVE | HID_MAIN_ITEM_BUFFERED_BYTE)) && + (field->flags & HID_MAIN_ITEM_VARIABLE) && usage->usage_index < field->maxusage && value == field->value[usage->usage_index]) return; -- cgit v0.10.2 From 2f4383667d57d1c719070db86b14277277752841 Mon Sep 17 00:00:00 2001 From: Ed Swierk Date: Fri, 2 Jan 2015 17:27:56 -0800 Subject: ethtool: Extend ethtool plugin module eeprom API to phylib This patch extends the ethtool plugin module eeprom API to support cards whose phy support is delegated to a separate driver. The handlers for ETHTOOL_GMODULEINFO and ETHTOOL_GMODULEEEPROM call the module_info and module_eeprom functions if the phy driver provides them; otherwise the handlers call the equivalent ethtool_ops functions provided by network drivers with built-in phy support. Signed-off-by: Ed Swierk Signed-off-by: David S. Miller diff --git a/include/linux/phy.h b/include/linux/phy.h index 22af8f8..9c189a1 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -565,6 +565,15 @@ struct phy_driver { void (*write_mmd_indirect)(struct phy_device *dev, int ptrad, int devnum, int regnum, u32 val); + /* Get the size and type of the eeprom contained within a plug-in + * module */ + int (*module_info)(struct phy_device *dev, + struct ethtool_modinfo *modinfo); + + /* Get the eeprom information from the plug-in module */ + int (*module_eeprom)(struct phy_device *dev, + struct ethtool_eeprom *ee, u8 *data); + struct device_driver driver; }; #define to_phy_driver(d) container_of(d, struct phy_driver, driver) diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 550892c..91f74f3 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -1597,20 +1597,31 @@ static int ethtool_get_ts_info(struct net_device *dev, void __user *useraddr) return err; } +static int __ethtool_get_module_info(struct net_device *dev, + struct ethtool_modinfo *modinfo) +{ + const struct ethtool_ops *ops = dev->ethtool_ops; + struct phy_device *phydev = dev->phydev; + + if (phydev && phydev->drv && phydev->drv->module_info) + return phydev->drv->module_info(phydev, modinfo); + + if (ops->get_module_info) + return ops->get_module_info(dev, modinfo); + + return -EOPNOTSUPP; +} + static int ethtool_get_module_info(struct net_device *dev, void __user *useraddr) { int ret; struct ethtool_modinfo modinfo; - const struct ethtool_ops *ops = dev->ethtool_ops; - - if (!ops->get_module_info) - return -EOPNOTSUPP; if (copy_from_user(&modinfo, useraddr, sizeof(modinfo))) return -EFAULT; - ret = ops->get_module_info(dev, &modinfo); + ret = __ethtool_get_module_info(dev, &modinfo); if (ret) return ret; @@ -1620,21 +1631,33 @@ static int ethtool_get_module_info(struct net_device *dev, return 0; } +static int __ethtool_get_module_eeprom(struct net_device *dev, + struct ethtool_eeprom *ee, u8 *data) +{ + const struct ethtool_ops *ops = dev->ethtool_ops; + struct phy_device *phydev = dev->phydev; + + if (phydev && phydev->drv && phydev->drv->module_eeprom) + return phydev->drv->module_eeprom(phydev, ee, data); + + if (ops->get_module_eeprom) + return ops->get_module_eeprom(dev, ee, data); + + return -EOPNOTSUPP; +} + static int ethtool_get_module_eeprom(struct net_device *dev, void __user *useraddr) { int ret; struct ethtool_modinfo modinfo; - const struct ethtool_ops *ops = dev->ethtool_ops; - - if (!ops->get_module_info || !ops->get_module_eeprom) - return -EOPNOTSUPP; - ret = ops->get_module_info(dev, &modinfo); + ret = __ethtool_get_module_info(dev, &modinfo); if (ret) return ret; - return ethtool_get_any_eeprom(dev, useraddr, ops->get_module_eeprom, + return ethtool_get_any_eeprom(dev, useraddr, + __ethtool_get_module_eeprom, modinfo.eeprom_len); } -- cgit v0.10.2 From 1fcf77c87ad659a92e1dcfb883388cb43baeaab6 Mon Sep 17 00:00:00 2001 From: Andy Fleming Date: Sun, 4 Jan 2015 17:36:02 +0800 Subject: net/fsl: Add mEMAC MDIO support to XGMAC MDIO The Freescale mEMAC supports operating at 10/100/1000/10G, and its associated MDIO controller is likewise capable of operating both Clause 22 and Clause 45 MDIO buses. It is nearly identical to the MDIO controller on the XGMAC, so we just modify that driver. Portions of this driver developed by: Sandeep Singh Roy Zang Signed-off-by: Andy Fleming Signed-off-by: Shaohui Xie Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig index 2703083..ba84c4a 100644 --- a/drivers/net/ethernet/freescale/Kconfig +++ b/drivers/net/ethernet/freescale/Kconfig @@ -69,7 +69,8 @@ config FSL_XGMAC_MDIO select PHYLIB select OF_MDIO ---help--- - This driver supports the MDIO bus on the Fman 10G Ethernet MACs. + This driver supports the MDIO bus on the Fman 10G Ethernet MACs, and + on the FMan mEMAC (which supports both Clauses 22 and 45) config UCC_GETH tristate "Freescale QE Gigabit Ethernet" diff --git a/drivers/net/ethernet/freescale/xgmac_mdio.c b/drivers/net/ethernet/freescale/xgmac_mdio.c index a352445..e0fc3d1 100644 --- a/drivers/net/ethernet/freescale/xgmac_mdio.c +++ b/drivers/net/ethernet/freescale/xgmac_mdio.c @@ -32,6 +32,7 @@ struct tgec_mdio_controller { __be32 mdio_addr; /* MDIO address */ } __packed; +#define MDIO_STAT_ENC BIT(6) #define MDIO_STAT_CLKDIV(x) (((x>>1) & 0xff) << 8) #define MDIO_STAT_BSY (1 << 0) #define MDIO_STAT_RD_ER (1 << 1) @@ -91,20 +92,40 @@ static int xgmac_wait_until_done(struct device *dev, static int xgmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value) { struct tgec_mdio_controller __iomem *regs = bus->priv; - uint16_t dev_addr = regnum >> 16; + uint16_t dev_addr; + u32 mdio_ctl, mdio_stat; int ret; - /* Set the port and dev addr */ - out_be32(®s->mdio_ctl, - MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr)); + mdio_stat = in_be32(®s->mdio_stat); + if (regnum & MII_ADDR_C45) { + /* Clause 45 (ie 10G) */ + dev_addr = (regnum >> 16) & 0x1f; + mdio_stat |= MDIO_STAT_ENC; + } else { + /* Clause 22 (ie 1G) */ + dev_addr = regnum & 0x1f; + mdio_stat &= ~MDIO_STAT_ENC; + } - /* Set the register address */ - out_be32(®s->mdio_addr, regnum & 0xffff); + out_be32(®s->mdio_stat, mdio_stat); ret = xgmac_wait_until_free(&bus->dev, regs); if (ret) return ret; + /* Set the port and dev addr */ + mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); + out_be32(®s->mdio_ctl, mdio_ctl); + + /* Set the register address */ + if (regnum & MII_ADDR_C45) { + out_be32(®s->mdio_addr, regnum & 0xffff); + + ret = xgmac_wait_until_free(&bus->dev, regs); + if (ret) + return ret; + } + /* Write the value to the register */ out_be32(®s->mdio_data, MDIO_DATA(value)); @@ -123,21 +144,39 @@ static int xgmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 val static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum) { struct tgec_mdio_controller __iomem *regs = bus->priv; - uint16_t dev_addr = regnum >> 16; + uint16_t dev_addr; + uint32_t mdio_stat; uint32_t mdio_ctl; uint16_t value; int ret; + mdio_stat = in_be32(®s->mdio_stat); + if (regnum & MII_ADDR_C45) { + dev_addr = (regnum >> 16) & 0x1f; + mdio_stat |= MDIO_STAT_ENC; + } else { + dev_addr = regnum & 0x1f; + mdio_stat = ~MDIO_STAT_ENC; + } + + out_be32(®s->mdio_stat, mdio_stat); + + ret = xgmac_wait_until_free(&bus->dev, regs); + if (ret) + return ret; + /* Set the Port and Device Addrs */ mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); out_be32(®s->mdio_ctl, mdio_ctl); /* Set the register address */ - out_be32(®s->mdio_addr, regnum & 0xffff); + if (regnum & MII_ADDR_C45) { + out_be32(®s->mdio_addr, regnum & 0xffff); - ret = xgmac_wait_until_free(&bus->dev, regs); - if (ret) - return ret; + ret = xgmac_wait_until_free(&bus->dev, regs); + if (ret) + return ret; + } /* Initiate the read */ out_be32(®s->mdio_ctl, mdio_ctl | MDIO_CTL_READ); @@ -224,6 +263,9 @@ static struct of_device_id xgmac_mdio_match[] = { { .compatible = "fsl,fman-xmdio", }, + { + .compatible = "fsl,fman-memac-mdio", + }, {}, }; MODULE_DEVICE_TABLE(of, xgmac_mdio_match); -- cgit v0.10.2 From de7b5b3d790a2524c3f992d357983600635c441b Mon Sep 17 00:00:00 2001 From: Feng Kan Date: Tue, 6 Jan 2015 15:41:33 -0700 Subject: net: eth: xgene: change APM X-Gene SoC platform ethernet to support ACPI This adds support for APM X-Gene ethernet driver to use ACPI table to derive ethernet driver parameter. Signed-off-by: Feng Kan Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index 7ba83ff..869d97f 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -593,10 +593,12 @@ static int xgene_enet_reset(struct xgene_enet_pdata *pdata) if (!xgene_ring_mgr_init(pdata)) return -ENODEV; - clk_prepare_enable(pdata->clk); - clk_disable_unprepare(pdata->clk); - clk_prepare_enable(pdata->clk); - xgene_enet_ecc_init(pdata); + if (!efi_enabled(EFI_BOOT)) { + clk_prepare_enable(pdata->clk); + clk_disable_unprepare(pdata->clk); + clk_prepare_enable(pdata->clk); + xgene_enet_ecc_init(pdata); + } xgene_enet_config_ring_if_assoc(pdata); /* Enable auto-incr for scanning */ @@ -663,15 +665,20 @@ static int xgene_enet_phy_connect(struct net_device *ndev) struct phy_device *phy_dev; struct device *dev = &pdata->pdev->dev; - phy_np = of_parse_phandle(dev->of_node, "phy-handle", 0); - if (!phy_np) { - netdev_dbg(ndev, "No phy-handle found\n"); - return -ENODEV; + if (dev->of_node) { + phy_np = of_parse_phandle(dev->of_node, "phy-handle", 0); + if (!phy_np) { + netdev_dbg(ndev, "No phy-handle found in DT\n"); + return -ENODEV; + } + pdata->phy_dev = of_phy_find_device(phy_np); } - phy_dev = of_phy_connect(ndev, phy_np, &xgene_enet_adjust_link, - 0, pdata->phy_mode); - if (!phy_dev) { + phy_dev = pdata->phy_dev; + + if (!phy_dev || + phy_connect_direct(ndev, phy_dev, &xgene_enet_adjust_link, + pdata->phy_mode)) { netdev_err(ndev, "Could not connect to PHY\n"); return -ENODEV; } @@ -681,32 +688,71 @@ static int xgene_enet_phy_connect(struct net_device *ndev) ~SUPPORTED_100baseT_Half & ~SUPPORTED_1000baseT_Half; phy_dev->advertising = phy_dev->supported; - pdata->phy_dev = phy_dev; return 0; } -int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata) +static int xgene_mdiobus_register(struct xgene_enet_pdata *pdata, + struct mii_bus *mdio) { - struct net_device *ndev = pdata->ndev; struct device *dev = &pdata->pdev->dev; + struct net_device *ndev = pdata->ndev; + struct phy_device *phy; struct device_node *child_np; struct device_node *mdio_np = NULL; - struct mii_bus *mdio_bus; int ret; + u32 phy_id; + + if (dev->of_node) { + for_each_child_of_node(dev->of_node, child_np) { + if (of_device_is_compatible(child_np, + "apm,xgene-mdio")) { + mdio_np = child_np; + break; + } + } - for_each_child_of_node(dev->of_node, child_np) { - if (of_device_is_compatible(child_np, "apm,xgene-mdio")) { - mdio_np = child_np; - break; + if (!mdio_np) { + netdev_dbg(ndev, "No mdio node in the dts\n"); + return -ENXIO; } - } - if (!mdio_np) { - netdev_dbg(ndev, "No mdio node in the dts\n"); - return -ENXIO; + return of_mdiobus_register(mdio, mdio_np); } + /* Mask out all PHYs from auto probing. */ + mdio->phy_mask = ~0; + + /* Register the MDIO bus */ + ret = mdiobus_register(mdio); + if (ret) + return ret; + + ret = device_property_read_u32(dev, "phy-channel", &phy_id); + if (ret) + ret = device_property_read_u32(dev, "phy-addr", &phy_id); + if (ret) + return -EINVAL; + + phy = get_phy_device(mdio, phy_id, true); + if (!phy || IS_ERR(phy)) + return -EIO; + + ret = phy_device_register(phy); + if (ret) + phy_device_free(phy); + else + pdata->phy_dev = phy; + + return ret; +} + +int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata) +{ + struct net_device *ndev = pdata->ndev; + struct mii_bus *mdio_bus; + int ret; + mdio_bus = mdiobus_alloc(); if (!mdio_bus) return -ENOMEM; @@ -720,7 +766,7 @@ int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata) mdio_bus->priv = pdata; mdio_bus->parent = &ndev->dev; - ret = of_mdiobus_register(mdio_bus, mdio_np); + ret = xgene_mdiobus_register(pdata, mdio_bus); if (ret) { netdev_err(ndev, "Failed to register MDIO bus\n"); mdiobus_free(mdio_bus); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 83a5028..1e56bf3 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -24,6 +24,10 @@ #include "xgene_enet_sgmac.h" #include "xgene_enet_xgmac.h" +#define RES_ENET_CSR 0 +#define RES_RING_CSR 1 +#define RES_RING_CMD 2 + static void xgene_enet_init_bufpool(struct xgene_enet_desc_ring *buf_pool) { struct xgene_enet_raw_desc16 *raw_desc; @@ -746,6 +750,41 @@ static const struct net_device_ops xgene_ndev_ops = { .ndo_set_mac_address = xgene_enet_set_mac_address, }; +static int xgene_get_mac_address(struct device *dev, + unsigned char *addr) +{ + int ret; + + ret = device_property_read_u8_array(dev, "local-mac-address", addr, 6); + if (ret) + ret = device_property_read_u8_array(dev, "mac-address", + addr, 6); + if (ret) + return -ENODEV; + + return ETH_ALEN; +} + +static int xgene_get_phy_mode(struct device *dev) +{ + int i, ret; + char *modestr; + + ret = device_property_read_string(dev, "phy-connection-type", + (const char **)&modestr); + if (ret) + ret = device_property_read_string(dev, "phy-mode", + (const char **)&modestr); + if (ret) + return -ENODEV; + + for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) { + if (!strcasecmp(modestr, phy_modes(i))) + return i; + } + return -ENODEV; +} + static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) { struct platform_device *pdev; @@ -753,29 +792,42 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) struct device *dev; struct resource *res; void __iomem *base_addr; - const char *mac; int ret; pdev = pdata->pdev; dev = &pdev->dev; ndev = pdata->ndev; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "enet_csr"); - pdata->base_addr = devm_ioremap_resource(dev, res); + res = platform_get_resource(pdev, IORESOURCE_MEM, RES_ENET_CSR); + if (!res) { + dev_err(dev, "Resource enet_csr not defined\n"); + return -ENODEV; + } + pdata->base_addr = devm_ioremap(dev, res->start, resource_size(res)); if (IS_ERR(pdata->base_addr)) { dev_err(dev, "Unable to retrieve ENET Port CSR region\n"); return PTR_ERR(pdata->base_addr); } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ring_csr"); - pdata->ring_csr_addr = devm_ioremap_resource(dev, res); + res = platform_get_resource(pdev, IORESOURCE_MEM, RES_RING_CSR); + if (!res) { + dev_err(dev, "Resource ring_csr not defined\n"); + return -ENODEV; + } + pdata->ring_csr_addr = devm_ioremap(dev, res->start, + resource_size(res)); if (IS_ERR(pdata->ring_csr_addr)) { dev_err(dev, "Unable to retrieve ENET Ring CSR region\n"); return PTR_ERR(pdata->ring_csr_addr); } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ring_cmd"); - pdata->ring_cmd_addr = devm_ioremap_resource(dev, res); + res = platform_get_resource(pdev, IORESOURCE_MEM, RES_RING_CMD); + if (!res) { + dev_err(dev, "Resource ring_cmd not defined\n"); + return -ENODEV; + } + pdata->ring_cmd_addr = devm_ioremap(dev, res->start, + resource_size(res)); if (IS_ERR(pdata->ring_cmd_addr)) { dev_err(dev, "Unable to retrieve ENET Ring command region\n"); return PTR_ERR(pdata->ring_cmd_addr); @@ -789,14 +841,12 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) } pdata->rx_irq = ret; - mac = of_get_mac_address(dev->of_node); - if (mac) - memcpy(ndev->dev_addr, mac, ndev->addr_len); - else + if (xgene_get_mac_address(dev, ndev->dev_addr) != ETH_ALEN) eth_hw_addr_random(ndev); + memcpy(ndev->perm_addr, ndev->dev_addr, ndev->addr_len); - pdata->phy_mode = of_get_phy_mode(pdev->dev.of_node); + pdata->phy_mode = xgene_get_phy_mode(dev); if (pdata->phy_mode < 0) { dev_err(dev, "Unable to get phy-connection-type\n"); return pdata->phy_mode; @@ -809,11 +859,9 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) } pdata->clk = devm_clk_get(&pdev->dev, NULL); - ret = IS_ERR(pdata->clk); if (IS_ERR(pdata->clk)) { - dev_err(&pdev->dev, "can't get clock\n"); - ret = PTR_ERR(pdata->clk); - return ret; + /* Firmware may have set up the clock already. */ + pdata->clk = NULL; } base_addr = pdata->base_addr; @@ -924,7 +972,7 @@ static int xgene_enet_probe(struct platform_device *pdev) goto err; } - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64)); if (ret) { netdev_err(ndev, "No usable DMA configuration\n"); goto err; @@ -972,17 +1020,26 @@ static int xgene_enet_remove(struct platform_device *pdev) return 0; } -static struct of_device_id xgene_enet_match[] = { +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgene_enet_acpi_match[] = { + { "APMC0D05", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, xgene_enet_acpi_match); +#endif + +static struct of_device_id xgene_enet_of_match[] = { {.compatible = "apm,xgene-enet",}, {}, }; -MODULE_DEVICE_TABLE(of, xgene_enet_match); +MODULE_DEVICE_TABLE(of, xgene_enet_of_match); static struct platform_driver xgene_enet_driver = { .driver = { .name = "xgene-enet", - .of_match_table = xgene_enet_match, + .of_match_table = of_match_ptr(xgene_enet_of_match), + .acpi_match_table = ACPI_PTR(xgene_enet_acpi_match), }, .probe = xgene_enet_probe, .remove = xgene_enet_remove, diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h index f9958fa..c2d465c 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h @@ -22,7 +22,10 @@ #ifndef __XGENE_ENET_MAIN_H__ #define __XGENE_ENET_MAIN_H__ +#include #include +#include +#include #include #include #include -- cgit v0.10.2 From 9da7dae94fb8adab5cc5f395640e30736a66e910 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Tue, 6 Jan 2015 17:29:29 +0100 Subject: workqueue.h: remove loops of single statement macros checkpatch.pl complained about two single statement macros in do while (0) loops. The loops and the trailing semicolons are now removed, which makes checkpatch happy and the two macros consistent with the rest of the file. Signed-off-by: Valentin Rothberg Signed-off-by: Tejun Heo diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index b996e6cd..74db135 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -220,14 +220,10 @@ static inline unsigned int work_static(struct work_struct *work) { return 0; } #endif #define INIT_WORK(_work, _func) \ - do { \ - __INIT_WORK((_work), (_func), 0); \ - } while (0) + __INIT_WORK((_work), (_func), 0) #define INIT_WORK_ONSTACK(_work, _func) \ - do { \ - __INIT_WORK((_work), (_func), 1); \ - } while (0) + __INIT_WORK((_work), (_func), 1) #define __INIT_DELAYED_WORK(_work, _func, _tflags) \ do { \ -- cgit v0.10.2 From 32ac7cb262177f58c7c8846311599460e5e7e3c5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 6 Jan 2015 18:36:44 -0500 Subject: libata: clean up MAINTAINERS entries Make all libata entries start with LIBATA and collect them in one place. Driver specfic ones have the second SATA or PATA prefix. Signed-off-by: Tejun Heo diff --git a/MAINTAINERS b/MAINTAINERS index ddb9ac8..c20aebe 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -754,13 +754,6 @@ L: linux-media@vger.kernel.org S: Maintained F: drivers/media/i2c/aptina-pll.* -ARASAN COMPACT FLASH PATA CONTROLLER -M: Viresh Kumar -L: linux-ide@vger.kernel.org -S: Maintained -F: include/linux/pata_arasan_cf_data.h -F: drivers/ata/pata_arasan_cf.c - ARC FRAMEBUFFER DRIVER M: Jaya Kumar S: Maintained @@ -5693,6 +5686,40 @@ F: drivers/lguest/ F: include/linux/lguest*.h F: tools/lguest/ +LIBATA SUBSYSTEM (Serial and Parallel ATA drivers) +M: Tejun Heo +L: linux-ide@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git +S: Maintained +F: drivers/ata/ +F: include/linux/ata.h +F: include/linux/libata.h + +LIBATA PATA ARASAN COMPACT FLASH CONTROLLER +M: Viresh Kumar +L: linux-ide@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git +S: Maintained +F: include/linux/pata_arasan_cf_data.h +F: drivers/ata/pata_arasan_cf.c + +LIBATA SATA AHCI PLATFORM devices support +M: Hans de Goede +M: Tejun Heo +L: linux-ide@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git +S: Maintained +F: drivers/ata/ahci_platform.c +F: drivers/ata/libahci_platform.c +F: include/linux/ahci_platform.h + +LIBATA SATA PROMISE TX2/TX4 CONTROLLER DRIVER +M: Mikael Pettersson +L: linux-ide@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git +S: Maintained +F: drivers/ata/sata_promise.* + LIBLOCKDEP M: Sasha Levin S: Maintained @@ -7566,12 +7593,6 @@ W: http://wireless.kernel.org/en/users/Drivers/p54 S: Obsolete F: drivers/net/wireless/prism54/ -PROMISE SATA TX2/TX4 CONTROLLER LIBATA DRIVER -M: Mikael Pettersson -L: linux-ide@vger.kernel.org -S: Maintained -F: drivers/ata/sata_promise.* - PS3 NETWORK SUPPORT M: Geoff Levand L: netdev@vger.kernel.org @@ -8546,25 +8567,6 @@ S: Maintained F: drivers/misc/phantom.c F: include/uapi/linux/phantom.h -SERIAL ATA (SATA) SUBSYSTEM -M: Tejun Heo -L: linux-ide@vger.kernel.org -T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git -S: Supported -F: drivers/ata/ -F: include/linux/ata.h -F: include/linux/libata.h - -SERIAL ATA AHCI PLATFORM devices support -M: Hans de Goede -M: Tejun Heo -L: linux-ide@vger.kernel.org -T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git -S: Supported -F: drivers/ata/ahci_platform.c -F: drivers/ata/libahci_platform.c -F: include/linux/ahci_platform.h - SERVER ENGINES 10Gbps iSCSI - BladeEngine 2 DRIVER M: Jayamohan Kallickal L: linux-scsi@vger.kernel.org -- cgit v0.10.2 From c7fa056c820fef26de34d346b148943e717958a0 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 2 Dec 2014 17:29:08 +0100 Subject: ata: add MAINTAINERS entry for libata PATA drivers Add myself as the primary maintainer for libata PATA drivers. The merging process would remain unchanged with patches going through Tejun's tree. Cc: Alan Cox Cc: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz Acked-by: Kyungmin Park Signed-off-by: Tejun Heo diff --git a/MAINTAINERS b/MAINTAINERS index c20aebe..5d7b387 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5703,6 +5703,15 @@ S: Maintained F: include/linux/pata_arasan_cf_data.h F: drivers/ata/pata_arasan_cf.c +LIBATA PATA DRIVERS +M: Bartlomiej Zolnierkiewicz +M: Tejun Heo +L: linux-ide@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git +S: Maintained +F: drivers/ata/pata_*.c +F: drivers/ata/ata_generic.c + LIBATA SATA AHCI PLATFORM devices support M: Hans de Goede M: Tejun Heo -- cgit v0.10.2 From 596c4051eb6b0c7f5fc5437c2269b7191b59ff3b Mon Sep 17 00:00:00 2001 From: Yingjoe Chen Date: Wed, 10 Dec 2014 17:55:02 +0800 Subject: irqchip: mtk-sysirq: Use IS_ERR() instead of NULL pointer check Beniamino noticed a bug that an invalid DT file for the mediatek interrupt polarity extension will cause kernel oops. The reason is that the interrupt polarity support for mediatek chips merely checks for NULL pointer instead of a casted error return value in mtk_sysirq_of_init() so any other casted error value passes the NULL pointer check and causes a kernel panic when dereferenced. Use IS_ERR() and return the error value via PTR_ERR(). [ jac: took V2 over V3 for diff formatting, hand-added V3 changes, tweaked subject line. ] Reported-by: Beniamino Galvani Signed-off-by: Yingjoe Chen Link: https://lkml.kernel.org/r/1418205302-22531-1-git-send-email-yingjoe.chen@mediatek.com Signed-off-by: Jason Cooper diff --git a/drivers/irqchip/irq-mtk-sysirq.c b/drivers/irqchip/irq-mtk-sysirq.c index 7e342df..0b0d2c0 100644 --- a/drivers/irqchip/irq-mtk-sysirq.c +++ b/drivers/irqchip/irq-mtk-sysirq.c @@ -137,9 +137,9 @@ static int __init mtk_sysirq_of_init(struct device_node *node, return -ENOMEM; chip_data->intpol_base = of_io_request_and_map(node, 0, "intpol"); - if (!chip_data->intpol_base) { + if (IS_ERR(chip_data->intpol_base)) { pr_err("mtk_sysirq: unable to map sysirq register\n"); - ret = -ENOMEM; + ret = PTR_ERR(chip_data->intpol_base); goto out_free; } -- cgit v0.10.2 From 03d3d45be413196790f82d601dc0527539804830 Mon Sep 17 00:00:00 2001 From: Wang Long Date: Thu, 11 Dec 2014 11:03:36 +0000 Subject: irqchip: hip04: Initialize hip04_cpu_map to 0xffff HiP04 GIC extends to support 16 cores, so we should initialize the hip04_cpu_map to 0xffff. Signed-off-by: Wang Long Acked-by: Haojian Zhuang Link: https://lkml.kernel.org/r/1418295816-179583-1-git-send-email-long.wanglong@huawei.com Signed-off-by: Jason Cooper diff --git a/drivers/irqchip/irq-hip04.c b/drivers/irqchip/irq-hip04.c index 29b8f21..6bc2deb 100644 --- a/drivers/irqchip/irq-hip04.c +++ b/drivers/irqchip/irq-hip04.c @@ -381,7 +381,7 @@ hip04_of_init(struct device_node *node, struct device_node *parent) * It will be refined as each CPU probes its ID. */ for (i = 0; i < NR_HIP04_CPU_IF; i++) - hip04_cpu_map[i] = 0xff; + hip04_cpu_map[i] = 0xffff; /* * Find out how many interrupts are supported. -- cgit v0.10.2 From 96555c474b917963da7065f88cdab376c8af0e87 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 17 Dec 2014 14:11:09 +0000 Subject: irqchip: gic-v3-its: Fix use of max with decimal constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit arm64 defconfig spits out the following compiler warning from the ITS driver: In file included from include/linux/bitmap.h:9:0, from drivers/irqchip/irq-gic-v3-its.c:18: drivers/irqchip/irq-gic-v3-its.c: In function ‘its_create_device’: include/linux/kernel.h:716:17: warning: comparison of distinct pointer types lacks a cast (void) (&_max1 == &_max2); \ ^ drivers/irqchip/irq-gic-v3-its.c:1056:12: note: in expansion of macro ‘max’ nr_ites = max(2, roundup_pow_of_two(nvecs)); Fix the warning by specifying the decimal constant `2' explicitly as an unsigned long type. Cc: Marc Zyngier Signed-off-by: Will Deacon Acked-by: Marc Zyngier Link: https://lkml.kernel.org/r/1418825469-30529-1-git-send-email-will.deacon@arm.com Signed-off-by: Jason Cooper diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 86e4684..d8996bd 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1053,7 +1053,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, * of two entries. No, the architecture doesn't let you * express an ITT with a single entry. */ - nr_ites = max(2, roundup_pow_of_two(nvecs)); + nr_ites = max(2UL, roundup_pow_of_two(nvecs)); sz = nr_ites * its->ite_size; sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1; itt = kmalloc(sz, GFP_KERNEL); -- cgit v0.10.2 From 4b149e417463bbb6d1d9b805f729627ca2b54495 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 6 Jan 2015 14:38:08 -0600 Subject: irqchip: omap-intc: Fix legacy DMA regression commit 55601c9f2467 (arm: omap: intc: switch over to linear irq domain) introduced a regression with SDMA legacy driver because that driver strictly depends on INTC's IRQs starting at NR_IRQs. Aparently irq_domain_add_linear() won't guarantee that, since we see a 7 IRQs difference when booting with and without the commit cited above. Until arch/arm/plat-omap/dma.c is properly fixed, we must maintain OMAP2/3 using irq_domain_add_legacy(). A FIXME note was added so people know to delete that code once that legacy DMA driver is fixed up. Fixes: 55601c9f2467 (arm: omap: intc: switch over to linear irq domain) Cc: # v3.18 Tested-by: Aaro Koskinen Tested-by: Tony Lindgren Signed-off-by: Felipe Balbi Link: https://lkml.kernel.org/r/1420576688-10604-1-git-send-email-balbi@ti.com Signed-off-by: Jason Cooper diff --git a/drivers/irqchip/irq-omap-intc.c b/drivers/irqchip/irq-omap-intc.c index 28718d3..c03f140 100644 --- a/drivers/irqchip/irq-omap-intc.c +++ b/drivers/irqchip/irq-omap-intc.c @@ -263,7 +263,7 @@ static int __init omap_init_irq_of(struct device_node *node) return ret; } -static int __init omap_init_irq_legacy(u32 base) +static int __init omap_init_irq_legacy(u32 base, struct device_node *node) { int j, irq_base; @@ -277,7 +277,7 @@ static int __init omap_init_irq_legacy(u32 base) irq_base = 0; } - domain = irq_domain_add_legacy(NULL, omap_nr_irqs, irq_base, 0, + domain = irq_domain_add_legacy(node, omap_nr_irqs, irq_base, 0, &irq_domain_simple_ops, NULL); omap_irq_soft_reset(); @@ -301,10 +301,26 @@ static int __init omap_init_irq(u32 base, struct device_node *node) { int ret; - if (node) + /* + * FIXME legacy OMAP DMA driver sitting under arch/arm/plat-omap/dma.c + * depends is still not ready for linear IRQ domains; because of that + * we need to temporarily "blacklist" OMAP2 and OMAP3 devices from using + * linear IRQ Domain until that driver is finally fixed. + */ + if (of_device_is_compatible(node, "ti,omap2-intc") || + of_device_is_compatible(node, "ti,omap3-intc")) { + struct resource res; + + if (of_address_to_resource(node, 0, &res)) + return -ENOMEM; + + base = res.start; + ret = omap_init_irq_legacy(base, node); + } else if (node) { ret = omap_init_irq_of(node); - else - ret = omap_init_irq_legacy(base); + } else { + ret = omap_init_irq_legacy(base, NULL); + } if (ret == 0) omap_irq_enable_protection(); -- cgit v0.10.2 From b2d8957f288e2e51085e7c468a05d5d98f0a785f Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Mon, 29 Dec 2014 13:08:39 +0100 Subject: ALSA: sound/atmel/ac97c.c: Add device tree support This adds device tree support for the AC97 controller. It uses the soc-ac97link bindings, but actually only ac97-reset is used. Signed-off-by: Alexander Stein Acked-by: Alexandre Belloni Signed-off-by: Takashi Iwai diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index 5e6a1db..cf4cedf 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -22,6 +22,9 @@ #include #include #include +#include +#include +#include #include #include @@ -902,6 +905,40 @@ static void atmel_ac97c_reset(struct atmel_ac97c *chip) } } +#ifdef CONFIG_OF +static const struct of_device_id atmel_ac97c_dt_ids[] = { + { .compatible = "atmel,at91sam9263-ac97c", }, + { } +}; +MODULE_DEVICE_TABLE(of, atmel_ac97c_dt_ids); + +static struct ac97c_platform_data *atmel_ac97c_probe_dt(struct device *dev) +{ + struct ac97c_platform_data *pdata; + struct device_node *node = dev->of_node; + const struct of_device_id *match; + + if (!node) { + dev_err(dev, "Device does not have associated DT data\n"); + return ERR_PTR(-EINVAL); + } + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + pdata->reset_pin = of_get_named_gpio(dev->of_node, "ac97-gpios", 2); + + return pdata; +} +#else +static struct ac97c_platform_data *atmel_ac97c_probe_dt(struct device *dev) +{ + dev_err(dev, "no platform data defined\n"); + return ERR_PTR(-ENXIO); +} +#endif + static int atmel_ac97c_probe(struct platform_device *pdev) { struct snd_card *card; @@ -922,10 +959,11 @@ static int atmel_ac97c_probe(struct platform_device *pdev) return -ENXIO; } - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { - dev_dbg(&pdev->dev, "no platform data\n"); - return -ENXIO; + pdata = atmel_ac97c_probe_dt(&pdev->dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); } irq = platform_get_irq(pdev, 0); @@ -1204,6 +1242,7 @@ static struct platform_driver atmel_ac97c_driver = { .driver = { .name = "atmel_ac97c", .pm = ATMEL_AC97C_PM_OPS, + .of_match_table = of_match_ptr(atmel_ac97c_dt_ids), }, }; module_platform_driver(atmel_ac97c_driver); -- cgit v0.10.2 From c20e7789be9f64ed6000e3d985bc7203781a671c Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Wed, 24 Dec 2014 23:08:44 +0800 Subject: wil6210: use 'uint64_t' instead of 'cycles_t' to avoid warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit do_div() checks the type strictly. 'cycles_t' may be 32-bit under quite a few architectures (parisc, arm, avr32 ...). So use 'uint64_t' instead of, the related warning (with allmodconfig under parisc): CC [M] drivers/net/wireless/ath/wil6210/debugfs.o In file included from arch/parisc/include/generated/asm/div64.h:1:0, from include/linux/kernel.h:124, from include/linux/list.h:8, from include/linux/module.h:9, from drivers/net/wireless/ath/wil6210/debugfs.c:17: drivers/net/wireless/ath/wil6210/debugfs.c: In function ‘wil_vring_debugfs_show’: include/asm-generic/div64.h:43:28: warning: comparison of distinct pointer types lacks a cast (void)(((typeof((n)) *)0) == ((uint64_t *)0)); \ ^ drivers/net/wireless/ath/wil6210/debugfs.c:107:4: note: in expansion of macro ‘do_div’ do_div(idle, total); ^ In file included from include/uapi/linux/stddef.h:1:0, from include/linux/stddef.h:4, from ./include/uapi/linux/posix_types.h:4, from include/uapi/linux/types.h:13, from include/linux/types.h:5, from include/linux/list.h:4, from include/linux/module.h:9, from drivers/net/wireless/ath/wil6210/debugfs.c:17: include/asm-generic/div64.h:44:18: warning: right shift count >= width of type [-Wshift-count-overflow] if (likely(((n) >> 32) == 0)) { \ ^ include/linux/compiler.h:159:40: note: in definition of macro ‘likely’ # define likely(x) __builtin_expect(!!(x), 1) ^ drivers/net/wireless/ath/wil6210/debugfs.c:107:4: note: in expansion of macro ‘do_div’ do_div(idle, total); ^ In file included from arch/parisc/include/generated/asm/div64.h:1:0, from include/linux/kernel.h:124, from include/linux/list.h:8, from include/linux/module.h:9, from drivers/net/wireless/ath/wil6210/debugfs.c:17: include/asm-generic/div64.h:48:22: warning: passing argument 1 of ‘__div64_32’ from incompatible pointer type [-Wincompatible-pointer-types] __rem = __div64_32(&(n), __base); \ ^ drivers/net/wireless/ath/wil6210/debugfs.c:107:4: note: in expansion of macro ‘do_div’ do_div(idle, total); ^ include/asm-generic/div64.h:35:17: note: expected ‘uint64_t * {aka long long unsigned int *}’ but argument is of type ‘cycles_t * {aka long unsigned int *}’ extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor); ^ Signed-off-by: Chen Gang Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 4e6e145..cd991fa 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -101,8 +101,8 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data) char name[10]; /* performance monitoring */ cycles_t now = get_cycles(); - cycles_t idle = txdata->idle * 100; - cycles_t total = now - txdata->begin; + uint64_t idle = txdata->idle * 100; + uint64_t total = now - txdata->begin; do_div(idle, total); txdata->begin = now; -- cgit v0.10.2 From b9d305cc47407f6a615145ff69e89060e7def6c7 Mon Sep 17 00:00:00 2001 From: Fred Chou Date: Fri, 26 Dec 2014 16:19:18 +0800 Subject: rt2x00: use helper to check capability/requirement Use rt2x00_has_cap_flag macro to check rt2x00dev->cap_flags. Signed-off-by: Fred Chou Acked-by: Stanislaw Gruszka Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c index 1122dc4..48a2cad 100644 --- a/drivers/net/wireless/rt2x00/rt2x00config.c +++ b/drivers/net/wireless/rt2x00/rt2x00config.c @@ -240,7 +240,7 @@ void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, rt2x00dev->rf_channel = libconf.rf.channel; } - if (test_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags) && + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) && (ieee80211_flags & IEEE80211_CONF_CHANGE_PS)) cancel_delayed_work_sync(&rt2x00dev->autowakeup_work); @@ -257,7 +257,7 @@ void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, rt2x00link_reset_tuner(rt2x00dev, false); if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && - test_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags) && + rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) && (ieee80211_flags & IEEE80211_CONF_CHANGE_PS) && (conf->flags & IEEE80211_CONF_PS)) { beacon_diff = (long)jiffies - (long)rt2x00dev->last_beacon; diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 9967a1d..5639ed8 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -351,7 +351,7 @@ void rt2x00lib_txdone(struct queue_entry *entry, /* * Remove L2 padding which was added during */ - if (test_bit(REQUIRE_L2PAD, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_L2PAD)) rt2x00queue_remove_l2pad(entry->skb, header_length); /* @@ -460,7 +460,7 @@ void rt2x00lib_txdone(struct queue_entry *entry, * send the status report back. */ if (!(skbdesc_flags & SKBDESC_NOT_MAC80211)) { - if (test_bit(REQUIRE_TASKLET_CONTEXT, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TASKLET_CONTEXT)) ieee80211_tx_status(rt2x00dev->hw, entry->skb); else ieee80211_tx_status_ni(rt2x00dev->hw, entry->skb); @@ -1056,9 +1056,9 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) /* * Take TX headroom required for alignment into account. */ - if (test_bit(REQUIRE_L2PAD, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_L2PAD)) rt2x00dev->hw->extra_tx_headroom += RT2X00_L2PAD_SIZE; - else if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags)) + else if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DMA)) rt2x00dev->hw->extra_tx_headroom += RT2X00_ALIGN_SIZE; /* @@ -1069,7 +1069,7 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) /* * Allocate tx status FIFO for driver use. */ - if (test_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TXSTATUS_FIFO)) { /* * Allocate the txstatus fifo. In the worst case the tx * status fifo has to hold the tx status of all entries @@ -1131,7 +1131,7 @@ static void rt2x00lib_uninitialize(struct rt2x00_dev *rt2x00dev) /* * Stop rfkill polling. */ - if (test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) rt2x00rfkill_unregister(rt2x00dev); /* @@ -1173,7 +1173,7 @@ static int rt2x00lib_initialize(struct rt2x00_dev *rt2x00dev) /* * Start rfkill polling. */ - if (test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) rt2x00rfkill_register(rt2x00dev); return 0; @@ -1389,7 +1389,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) /* * Start rfkill polling. */ - if (!test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) rt2x00rfkill_register(rt2x00dev); return 0; @@ -1408,7 +1408,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) /* * Stop rfkill polling. */ - if (!test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) rt2x00rfkill_unregister(rt2x00dev); /* diff --git a/drivers/net/wireless/rt2x00/rt2x00firmware.c b/drivers/net/wireless/rt2x00/rt2x00firmware.c index fbae279..5813300 100644 --- a/drivers/net/wireless/rt2x00/rt2x00firmware.c +++ b/drivers/net/wireless/rt2x00/rt2x00firmware.c @@ -96,7 +96,7 @@ int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev) { int retval; - if (!test_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags)) + if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_FIRMWARE)) return 0; if (!rt2x00dev->fw) { diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index cb40245..300876d 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -119,7 +119,7 @@ void rt2x00mac_tx(struct ieee80211_hw *hw, * Use the ATIM queue if appropriate and present. */ if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM && - test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags)) + rt2x00_has_cap_flag(rt2x00dev, REQUIRE_ATIM_QUEUE)) qid = QID_ATIM; queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 66ff364..68b620b 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -85,7 +85,7 @@ struct sk_buff *rt2x00queue_alloc_rxskb(struct queue_entry *entry, gfp_t gfp) memset(skbdesc, 0, sizeof(*skbdesc)); skbdesc->entry = entry; - if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DMA)) { dma_addr_t skb_dma; skb_dma = dma_map_single(rt2x00dev->dev, skb->data, skb->len, @@ -198,7 +198,7 @@ static void rt2x00queue_create_tx_descriptor_seq(struct rt2x00_dev *rt2x00dev, __set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags); - if (!test_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags)) { + if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_SW_SEQNO)) { /* * rt2800 has a H/W (or F/W) bug, device incorrectly increase * seqno on retransmited data (non-QOS) frames. To workaround @@ -484,7 +484,7 @@ static void rt2x00queue_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, rt2x00crypto_create_tx_descriptor(rt2x00dev, skb, txdesc); rt2x00queue_create_tx_descriptor_seq(rt2x00dev, skb, txdesc); - if (test_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_HT_TX_DESC)) rt2x00queue_create_tx_descriptor_ht(rt2x00dev, skb, txdesc, sta, hwrate); else @@ -526,7 +526,7 @@ static int rt2x00queue_write_tx_data(struct queue_entry *entry, /* * Map the skb to DMA. */ - if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags) && + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DMA) && rt2x00queue_map_txskb(entry)) return -ENOMEM; @@ -646,7 +646,7 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, */ if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc.flags) && !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc.flags)) { - if (test_bit(REQUIRE_COPY_IV, &queue->rt2x00dev->cap_flags)) + if (rt2x00_has_cap_flag(queue->rt2x00dev, REQUIRE_COPY_IV)) rt2x00crypto_tx_copy_iv(skb, &txdesc); else rt2x00crypto_tx_remove_iv(skb, &txdesc); @@ -660,9 +660,9 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, * PCI and USB devices, while header alignment only is valid * for PCI devices. */ - if (test_bit(REQUIRE_L2PAD, &queue->rt2x00dev->cap_flags)) + if (rt2x00_has_cap_flag(queue->rt2x00dev, REQUIRE_L2PAD)) rt2x00queue_insert_l2pad(skb, txdesc.header_length); - else if (test_bit(REQUIRE_DMA, &queue->rt2x00dev->cap_flags)) + else if (rt2x00_has_cap_flag(queue->rt2x00dev, REQUIRE_DMA)) rt2x00queue_align_frame(skb); /* @@ -1178,7 +1178,7 @@ int rt2x00queue_initialize(struct rt2x00_dev *rt2x00dev) if (status) goto exit; - if (test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_ATIM_QUEUE)) { status = rt2x00queue_alloc_entries(rt2x00dev->atim); if (status) goto exit; @@ -1234,7 +1234,7 @@ int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev) struct data_queue *queue; enum data_queue_qid qid; unsigned int req_atim = - !!test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags); + rt2x00_has_cap_flag(rt2x00dev, REQUIRE_ATIM_QUEUE); /* * We need the following queues: diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 892270d..7627af6 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -274,7 +274,7 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb) * Schedule the delayed work for reading the TX status * from the device. */ - if (!test_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags) || + if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TXSTATUS_FIFO) || !kfifo_is_empty(&rt2x00dev->txstatus_fifo)) queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); } @@ -456,7 +456,7 @@ static bool rt2x00usb_flush_entry(struct queue_entry *entry, void *data) * Kill guardian urb (if required by driver). */ if ((entry->queue->qid == QID_BEACON) && - (test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags))) + (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_BEACON_GUARD))) usb_kill_urb(bcn_priv->guardian_urb); return false; @@ -655,7 +655,7 @@ static int rt2x00usb_alloc_entries(struct data_queue *queue) * then we are done. */ if (queue->qid != QID_BEACON || - !test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags)) + !rt2x00_has_cap_flag(rt2x00dev, REQUIRE_BEACON_GUARD)) return 0; for (i = 0; i < queue->limit; i++) { @@ -690,7 +690,7 @@ static void rt2x00usb_free_entries(struct data_queue *queue) * then we are done. */ if (queue->qid != QID_BEACON || - !test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags)) + !rt2x00_has_cap_flag(rt2x00dev, REQUIRE_BEACON_GUARD)) return; for (i = 0; i < queue->limit; i++) { -- cgit v0.10.2 From 983988ec0a2499a5d31fdc96a1d2d7af2b3609a6 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 26 Dec 2014 15:35:37 +0100 Subject: wireless: cw1200: Use setup_timer Convert a call to init_timer and accompanying intializations of the timer's data and function fields to a call to setup_timer. A simplified version of the semantic match that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression t,f,d; @@ -init_timer(&t); +setup_timer(&t,f,d); -t.data = d; -t.function = f; // Signed-off-by: Julia Lawall Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/cw1200/pm.c b/drivers/net/wireless/cw1200/pm.c index 6907c8f..d2202ae 100644 --- a/drivers/net/wireless/cw1200/pm.c +++ b/drivers/net/wireless/cw1200/pm.c @@ -101,9 +101,8 @@ int cw1200_pm_init(struct cw1200_pm_state *pm, { spin_lock_init(&pm->lock); - init_timer(&pm->stay_awake); - pm->stay_awake.data = (unsigned long)pm; - pm->stay_awake.function = cw1200_pm_stay_awake_tmo; + setup_timer(&pm->stay_awake, cw1200_pm_stay_awake_tmo, + (unsigned long)pm); return 0; } -- cgit v0.10.2 From dabefea6937dc0015624a43ec1b8764f5b3dcb68 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 26 Dec 2014 15:35:38 +0100 Subject: cw1200: main: Use setup_timer Convert a call to init_timer and accompanying intializations of the timer's data and function fields to a call to setup_timer. A simplified version of the semantic match that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression t,f,d; @@ -init_timer(&t); +setup_timer(&t,f,d); -t.data = d; -t.function = f; // Signed-off-by: Julia Lawall Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c index 3e78cc3..fa965ee 100644 --- a/drivers/net/wireless/cw1200/main.c +++ b/drivers/net/wireless/cw1200/main.c @@ -374,9 +374,8 @@ static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr, INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work); INIT_WORK(&priv->set_beacon_wakeup_period_work, cw1200_set_beacon_wakeup_period_work); - init_timer(&priv->mcast_timeout); - priv->mcast_timeout.data = (unsigned long)priv; - priv->mcast_timeout.function = cw1200_mcast_timeout; + setup_timer(&priv->mcast_timeout, cw1200_mcast_timeout, + (unsigned long)priv); if (cw1200_queue_stats_init(&priv->tx_queue_stats, CW1200_LINK_ID_MAX, -- cgit v0.10.2 From 0be01bf297215e36fbcbe07fc838792aa7c963e5 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 26 Dec 2014 15:35:39 +0100 Subject: cw1200: queue: Use setup_timer Convert a call to init_timer and accompanying intializations of the timer's data and function fields to a call to setup_timer. A simplified version of the semantic match that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression t,f,d; @@ -init_timer(&t); +setup_timer(&t,f,d); -t.data = d; -t.function = f; // Signed-off-by: Julia Lawall Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/cw1200/queue.c b/drivers/net/wireless/cw1200/queue.c index 9c3925f..0ba5ef9 100644 --- a/drivers/net/wireless/cw1200/queue.c +++ b/drivers/net/wireless/cw1200/queue.c @@ -179,9 +179,7 @@ int cw1200_queue_init(struct cw1200_queue *queue, INIT_LIST_HEAD(&queue->pending); INIT_LIST_HEAD(&queue->free_pool); spin_lock_init(&queue->lock); - init_timer(&queue->gc); - queue->gc.data = (unsigned long)queue; - queue->gc.function = cw1200_queue_gc; + setup_timer(&queue->gc, cw1200_queue_gc, (unsigned long)queue); queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity, GFP_KERNEL); -- cgit v0.10.2 From af68b87f721166a08f3cd3282a07e0cbce15ea80 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 26 Dec 2014 15:35:48 +0100 Subject: iwl4965: Use setup_timer Convert a call to init_timer and accompanying intializations of the timer's data and function fields to a call to setup_timer. A simplified version of the semantic match that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression t,f,d; @@ -init_timer(&t); +setup_timer(&t,f,d); -t.data = d; -t.function = f; // Signed-off-by: Julia Lawall Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 2748fde..976f65f 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -6247,13 +6247,10 @@ il4965_setup_deferred_work(struct il_priv *il) INIT_WORK(&il->txpower_work, il4965_bg_txpower_work); - init_timer(&il->stats_periodic); - il->stats_periodic.data = (unsigned long)il; - il->stats_periodic.function = il4965_bg_stats_periodic; + setup_timer(&il->stats_periodic, il4965_bg_stats_periodic, + (unsigned long)il); - init_timer(&il->watchdog); - il->watchdog.data = (unsigned long)il; - il->watchdog.function = il_bg_watchdog; + setup_timer(&il->watchdog, il_bg_watchdog, (unsigned long)il); tasklet_init(&il->irq_tasklet, (void (*)(unsigned long))il4965_irq_tasklet, -- cgit v0.10.2 From 1a94ace406ad6befab73ac62f254c39599617c90 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 26 Dec 2014 15:35:49 +0100 Subject: iwl3945: Use setup_timer Convert a call to init_timer and accompanying intializations of the timer's data and function fields to a call to setup_timer. A simplified version of the semantic match that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression t,f,d; @@ -init_timer(&t); +setup_timer(&t,f,d); -t.data = d; -t.function = f; // Signed-off-by: Julia Lawall Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c index dc1d20c..e566580 100644 --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c @@ -3429,9 +3429,7 @@ il3945_setup_deferred_work(struct il_priv *il) il3945_hw_setup_deferred_work(il); - init_timer(&il->watchdog); - il->watchdog.data = (unsigned long)il; - il->watchdog.function = il_bg_watchdog; + setup_timer(&il->watchdog, il_bg_watchdog, (unsigned long)il); tasklet_init(&il->irq_tasklet, (void (*)(unsigned long))il3945_irq_tasklet, -- cgit v0.10.2 From 99a1b74395a51b5be91a92c23da47c0032ff7507 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 26 Dec 2014 15:35:55 +0100 Subject: orinoco_usb: Use setup_timer Convert a call to init_timer and accompanying intializations of the timer's data and function fields to a call to setup_timer. A simplified version of the semantic match that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression t,f,d; @@ -init_timer(&t); +setup_timer(&t,f,d); -t.function = f; -t.data = d; // Signed-off-by: Julia Lawall Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/orinoco/orinoco_usb.c b/drivers/net/wireless/orinoco/orinoco_usb.c index 9958464..91f0544 100644 --- a/drivers/net/wireless/orinoco/orinoco_usb.c +++ b/drivers/net/wireless/orinoco/orinoco_usb.c @@ -364,9 +364,7 @@ static struct request_context *ezusb_alloc_ctx(struct ezusb_priv *upriv, atomic_set(&ctx->refcount, 1); init_completion(&ctx->done); - init_timer(&ctx->timer); - ctx->timer.function = ezusb_request_timerfn; - ctx->timer.data = (u_long) ctx; + setup_timer(&ctx->timer, ezusb_request_timerfn, (u_long)ctx); return ctx; } -- cgit v0.10.2 From c6c33e772407031bc3b7482296e400a0ea6e7b32 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 26 Dec 2014 15:35:56 +0100 Subject: mwifiex: main: Use setup_timer Convert a call to init_timer and accompanying intializations of the timer's data and function fields to a call to setup_timer. A simplified version of the semantic match that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression t,f,d; @@ -init_timer(&t); +setup_timer(&t,f,d); -t.function = f; -t.data = d; // Signed-off-by: Julia Lawall Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 119e875..d235adb 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -83,9 +83,8 @@ static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops, } mwifiex_init_lock_list(adapter); - init_timer(&adapter->cmd_timer); - adapter->cmd_timer.function = mwifiex_cmd_timeout_func; - adapter->cmd_timer.data = (unsigned long) adapter; + setup_timer(&adapter->cmd_timer, mwifiex_cmd_timeout_func, + (unsigned long)adapter); return 0; -- cgit v0.10.2 From e4a1c3f88e6530b3128a361f8d869819e922c3a1 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 26 Dec 2014 15:35:57 +0100 Subject: mwifiex: 11n_rxreorder: Use setup_timer Convert a call to init_timer and accompanying intializations of the timer's data and function fields to a call to setup_timer. A simplified version of the semantic match that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression t,f,d; @@ -init_timer(&t); +setup_timer(&t,f,d); -t.function = f; -t.data = d; // Signed-off-by: Julia Lawall Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c index d73fda3..f33dc81 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c @@ -391,10 +391,8 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, new_node->timer_context.priv = priv; new_node->timer_context.timer_is_set = false; - init_timer(&new_node->timer_context.timer); - new_node->timer_context.timer.function = mwifiex_flush_data; - new_node->timer_context.timer.data = - (unsigned long) &new_node->timer_context; + setup_timer(&new_node->timer_context.timer, mwifiex_flush_data, + (unsigned long)&new_node->timer_context); for (i = 0; i < win_size; ++i) new_node->rx_reorder_ptr[i] = NULL; -- cgit v0.10.2 From 6383539b973a22b4d066779b04de8e9937754e60 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 26 Dec 2014 15:35:58 +0100 Subject: mwifiex: tdls: Use setup_timer Convert a call to init_timer and accompanying intializations of the timer's data and function fields to a call to setup_timer. A simplified version of the semantic match that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression t,f,d; @@ -init_timer(&t); +setup_timer(&t,f,d); -t.function = f; -t.data = d; // Signed-off-by: Julia Lawall Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index efa81d8..087d847 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -1397,9 +1397,8 @@ void mwifiex_check_auto_tdls(unsigned long context) void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv) { - init_timer(&priv->auto_tdls_timer); - priv->auto_tdls_timer.function = mwifiex_check_auto_tdls; - priv->auto_tdls_timer.data = (unsigned long)priv; + setup_timer(&priv->auto_tdls_timer, mwifiex_check_auto_tdls, + (unsigned long)priv); priv->auto_tdls_timer_active = true; mod_timer(&priv->auto_tdls_timer, jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); -- cgit v0.10.2 From e4a93be6cb281fb3dbbd41b22aee02cb0e551ba8 Mon Sep 17 00:00:00 2001 From: Oscar Forner Martinez Date: Tue, 6 Jan 2015 16:54:19 -0800 Subject: isofs: Fix bug in the way to check if the year is a leap year Changed the whole algorithm for a call to mktime64 that takes care of all that details. Signed-off-by: Oscar Forner Martinez Signed-off-by: Jan Kara diff --git a/fs/isofs/util.c b/fs/isofs/util.c index 01e1ee7..005a15c 100644 --- a/fs/isofs/util.c +++ b/fs/isofs/util.c @@ -2,6 +2,7 @@ * linux/fs/isofs/util.c */ +#include #include "isofs.h" /* @@ -17,9 +18,9 @@ int iso_date(char * p, int flag) { int year, month, day, hour, minute, second, tz; - int crtime, days, i; + int crtime; - year = p[0] - 70; + year = p[0]; month = p[1]; day = p[2]; hour = p[3]; @@ -31,18 +32,7 @@ int iso_date(char * p, int flag) if (year < 0) { crtime = 0; } else { - int monlen[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; - - days = year * 365; - if (year > 2) - days += (year+1) / 4; - for (i = 1; i < month; i++) - days += monlen[i-1]; - if (((year+2) % 4) == 0 && month > 2) - days++; - days += day - 1; - crtime = ((((days * 24) + hour) * 60 + minute) * 60) - + second; + crtime = mktime64(year+1900, month, day, hour, minute, second); /* sign extend */ if (tz & 0x80) -- cgit v0.10.2 From 5e7e9e90b5867a3754159a8ce524299d930fbac8 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Tue, 6 Jan 2015 18:32:51 -0800 Subject: HID: microsoft: add support for Japanese Surface Type Cover 3 Based on code for the US Surface Type Cover 3 from commit be3b16341d5cd8cf2a64fcc7a604a8efe6599ff0 ("HID: add support for MS Surface Pro 3 Type Cover"): Signed-off-by: Alan Wu Tested-by: Karlis Dreizis Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index c3d0ac1..0cea5a0 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -704,7 +704,8 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type) hid->group = HID_GROUP_SENSOR_HUB; if (hid->vendor == USB_VENDOR_ID_MICROSOFT && - hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 && + (hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 || + hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3_JP) && hid->group == HID_GROUP_MULTITOUCH) hid->group = HID_GROUP_GENERIC; } @@ -1860,6 +1861,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP) }, { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 7460f34..95ee91b 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -653,6 +653,7 @@ #define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7 #define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 #define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07dc +#define USB_DEVICE_ID_MS_TYPE_COVER_3_JP 0x07dd #define USB_VENDOR_ID_MOJO 0x8282 #define USB_DEVICE_ID_RETRO_ADAPTER 0x3201 diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index cacda43..fbaea6e 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -276,6 +276,8 @@ static const struct hid_device_id ms_devices[] = { .driver_data = MS_DUPLICATE_USAGES }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3), .driver_data = MS_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP), + .driver_data = MS_HIDINPUT }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT), .driver_data = MS_PRESENTER }, diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index dc89be9..6591b5d 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -80,6 +80,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET }, { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS }, -- cgit v0.10.2 From ae9b56e3996dadbb59c727018f45486c06844261 Mon Sep 17 00:00:00 2001 From: Punnaiah Choudary Kalluri Date: Tue, 6 Jan 2015 23:13:47 +0530 Subject: EDAC, synps: Add EDAC support for zynq ddr ecc controller Add EDAC support for ecc errors reporting on the synopsys ddr controller. The ddr ecc controller corrects single bit errors and detects double bit errors. Selected important-ish notes from the changelog: - I have not taken care of spliting synps_edac_geterror_info function as it adds additional indentation levels and moreover the existing changes were made as part of the v2 review comments - Removed dt binding info as already there is a binding info available under memorycontroller. so, updated ecc info there. - Shortened the prefix "sysnopsys" to "synps" Signed-off-by: Punnaiah Choudary Kalluri Link: http://lkml.kernel.org/r/a728a8d4678f4dbf9de189a480297c3d@BY2FFO11FD034.protection.gbl [ Boris: massage commit message. ] Signed-off-by: Borislav Petkov diff --git a/MAINTAINERS b/MAINTAINERS index ddb9ac8..375e248 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1583,6 +1583,7 @@ N: xilinx F: drivers/clocksource/cadence_ttc_timer.c F: drivers/i2c/busses/i2c-cadence.c F: drivers/mmc/host/sdhci-of-arasan.c +F: drivers/edac/synopsys_edac.c ARM SMMU DRIVER M: Will Deacon diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 49c2652..cb59619 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -385,4 +385,11 @@ config EDAC_ALTERA_MC preloader must initialize the SDRAM before loading the kernel. +config EDAC_SYNOPSYS + tristate "Synopsys DDR Memory Controller" + depends on EDAC_MM_EDAC && ARCH_ZYNQ + help + Support for error detection and correction on the Synopsys DDR + memory controller. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index d40c69a..b255f36 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -67,3 +67,4 @@ obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o obj-$(CONFIG_EDAC_ALTERA_MC) += altera_edac.o +obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c new file mode 100644 index 0000000..1c96915 --- /dev/null +++ b/drivers/edac/synopsys_edac.c @@ -0,0 +1,535 @@ +/* + * Synopsys DDR ECC Driver + * This driver is based on ppc4xx_edac.c drivers + * + * Copyright (C) 2012 - 2014 Xilinx, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details + */ + +#include +#include +#include + +#include "edac_core.h" + +/* Number of cs_rows needed per memory controller */ +#define SYNPS_EDAC_NR_CSROWS 1 + +/* Number of channels per memory controller */ +#define SYNPS_EDAC_NR_CHANS 1 + +/* Granularity of reported error in bytes */ +#define SYNPS_EDAC_ERR_GRAIN 1 + +#define SYNPS_EDAC_MSG_SIZE 256 + +#define SYNPS_EDAC_MOD_STRING "synps_edac" +#define SYNPS_EDAC_MOD_VER "1" + +/* Synopsys DDR memory controller registers that are relevant to ECC */ +#define CTRL_OFST 0x0 +#define T_ZQ_OFST 0xA4 + +/* ECC control register */ +#define ECC_CTRL_OFST 0xC4 +/* ECC log register */ +#define CE_LOG_OFST 0xC8 +/* ECC address register */ +#define CE_ADDR_OFST 0xCC +/* ECC data[31:0] register */ +#define CE_DATA_31_0_OFST 0xD0 + +/* Uncorrectable error info registers */ +#define UE_LOG_OFST 0xDC +#define UE_ADDR_OFST 0xE0 +#define UE_DATA_31_0_OFST 0xE4 + +#define STAT_OFST 0xF0 +#define SCRUB_OFST 0xF4 + +/* Control register bit field definitions */ +#define CTRL_BW_MASK 0xC +#define CTRL_BW_SHIFT 2 + +#define DDRCTL_WDTH_16 1 +#define DDRCTL_WDTH_32 0 + +/* ZQ register bit field definitions */ +#define T_ZQ_DDRMODE_MASK 0x2 + +/* ECC control register bit field definitions */ +#define ECC_CTRL_CLR_CE_ERR 0x2 +#define ECC_CTRL_CLR_UE_ERR 0x1 + +/* ECC correctable/uncorrectable error log register definitions */ +#define LOG_VALID 0x1 +#define CE_LOG_BITPOS_MASK 0xFE +#define CE_LOG_BITPOS_SHIFT 1 + +/* ECC correctable/uncorrectable error address register definitions */ +#define ADDR_COL_MASK 0xFFF +#define ADDR_ROW_MASK 0xFFFF000 +#define ADDR_ROW_SHIFT 12 +#define ADDR_BANK_MASK 0x70000000 +#define ADDR_BANK_SHIFT 28 + +/* ECC statistic register definitions */ +#define STAT_UECNT_MASK 0xFF +#define STAT_CECNT_MASK 0xFF00 +#define STAT_CECNT_SHIFT 8 + +/* ECC scrub register definitions */ +#define SCRUB_MODE_MASK 0x7 +#define SCRUB_MODE_SECDED 0x4 + +/** + * struct ecc_error_info - ECC error log information + * @row: Row number + * @col: Column number + * @bank: Bank number + * @bitpos: Bit position + * @data: Data causing the error + */ +struct ecc_error_info { + u32 row; + u32 col; + u32 bank; + u32 bitpos; + u32 data; +}; + +/** + * struct synps_ecc_status - ECC status information to report + * @ce_cnt: Correctable error count + * @ue_cnt: Uncorrectable error count + * @ceinfo: Correctable error log information + * @ueinfo: Uncorrectable error log information + */ +struct synps_ecc_status { + u32 ce_cnt; + u32 ue_cnt; + struct ecc_error_info ceinfo; + struct ecc_error_info ueinfo; +}; + +/** + * struct synps_edac_priv - DDR memory controller private instance data + * @baseaddr: Base address of the DDR controller + * @message: Buffer for framing the event specific info + * @stat: ECC status information + * @ce_cnt: Correctable Error count + * @ue_cnt: Uncorrectable Error count + */ +struct synps_edac_priv { + void __iomem *baseaddr; + char message[SYNPS_EDAC_MSG_SIZE]; + struct synps_ecc_status stat; + u32 ce_cnt; + u32 ue_cnt; +}; + +/** + * synps_edac_geterror_info - Get the current ecc error info + * @base: Pointer to the base address of the ddr memory controller + * @p: Pointer to the synopsys ecc status structure + * + * Determines there is any ecc error or not + * + * Return: one if there is no error otherwise returns zero + */ +static int synps_edac_geterror_info(void __iomem *base, + struct synps_ecc_status *p) +{ + u32 regval, clearval = 0; + + regval = readl(base + STAT_OFST); + if (!regval) + return 1; + + p->ce_cnt = (regval & STAT_CECNT_MASK) >> STAT_CECNT_SHIFT; + p->ue_cnt = regval & STAT_UECNT_MASK; + + regval = readl(base + CE_LOG_OFST); + if (!(p->ce_cnt && (regval & LOG_VALID))) + goto ue_err; + + p->ceinfo.bitpos = (regval & CE_LOG_BITPOS_MASK) >> CE_LOG_BITPOS_SHIFT; + regval = readl(base + CE_ADDR_OFST); + p->ceinfo.row = (regval & ADDR_ROW_MASK) >> ADDR_ROW_SHIFT; + p->ceinfo.col = regval & ADDR_COL_MASK; + p->ceinfo.bank = (regval & ADDR_BANK_MASK) >> ADDR_BANK_SHIFT; + p->ceinfo.data = readl(base + CE_DATA_31_0_OFST); + edac_dbg(3, "ce bit position: %d data: %d\n", p->ceinfo.bitpos, + p->ceinfo.data); + clearval = ECC_CTRL_CLR_CE_ERR; + +ue_err: + regval = readl(base + UE_LOG_OFST); + if (!(p->ue_cnt && (regval & LOG_VALID))) + goto out; + + regval = readl(base + UE_ADDR_OFST); + p->ueinfo.row = (regval & ADDR_ROW_MASK) >> ADDR_ROW_SHIFT; + p->ueinfo.col = regval & ADDR_COL_MASK; + p->ueinfo.bank = (regval & ADDR_BANK_MASK) >> ADDR_BANK_SHIFT; + p->ueinfo.data = readl(base + UE_DATA_31_0_OFST); + clearval |= ECC_CTRL_CLR_UE_ERR; + +out: + writel(clearval, base + ECC_CTRL_OFST); + writel(0x0, base + ECC_CTRL_OFST); + + return 0; +} + +/** + * synps_edac_handle_error - Handle controller error types CE and UE + * @mci: Pointer to the edac memory controller instance + * @p: Pointer to the synopsys ecc status structure + * + * Handles the controller ECC correctable and un correctable error. + */ +static void synps_edac_handle_error(struct mem_ctl_info *mci, + struct synps_ecc_status *p) +{ + struct synps_edac_priv *priv = mci->pvt_info; + struct ecc_error_info *pinf; + + if (p->ce_cnt) { + pinf = &p->ceinfo; + snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, + "DDR ECC error type :%s Row %d Bank %d Col %d ", + "CE", pinf->row, pinf->bank, pinf->col); + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + p->ce_cnt, 0, 0, 0, 0, 0, -1, + priv->message, ""); + } + + if (p->ue_cnt) { + pinf = &p->ueinfo; + snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, + "DDR ECC error type :%s Row %d Bank %d Col %d ", + "UE", pinf->row, pinf->bank, pinf->col); + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + p->ue_cnt, 0, 0, 0, 0, 0, -1, + priv->message, ""); + } + + memset(p, 0, sizeof(*p)); +} + +/** + * synps_edac_check - Check controller for ECC errors + * @mci: Pointer to the edac memory controller instance + * + * Used to check and post ECC errors. Called by the polling thread + */ +static void synps_edac_check(struct mem_ctl_info *mci) +{ + struct synps_edac_priv *priv = mci->pvt_info; + int status; + + status = synps_edac_geterror_info(priv->baseaddr, &priv->stat); + if (status) + return; + + priv->ce_cnt += priv->stat.ce_cnt; + priv->ue_cnt += priv->stat.ue_cnt; + synps_edac_handle_error(mci, &priv->stat); + + edac_dbg(3, "Total error count ce %d ue %d\n", + priv->ce_cnt, priv->ue_cnt); +} + +/** + * synps_edac_get_dtype - Return the controller memory width + * @base: Pointer to the ddr memory controller base address + * + * Get the EDAC device type width appropriate for the current controller + * configuration. + * + * Return: a device type width enumeration. + */ +static enum dev_type synps_edac_get_dtype(const void __iomem *base) +{ + enum dev_type dt; + u32 width; + + width = readl(base + CTRL_OFST); + width = (width & CTRL_BW_MASK) >> CTRL_BW_SHIFT; + + switch (width) { + case DDRCTL_WDTH_16: + dt = DEV_X2; + break; + case DDRCTL_WDTH_32: + dt = DEV_X4; + break; + default: + dt = DEV_UNKNOWN; + } + + return dt; +} + +/** + * synps_edac_get_eccstate - Return the controller ecc enable/disable status + * @base: Pointer to the ddr memory controller base address + * + * Get the ECC enable/disable status for the controller + * + * Return: a ecc status boolean i.e true/false - enabled/disabled. + */ +static bool synps_edac_get_eccstate(void __iomem *base) +{ + enum dev_type dt; + u32 ecctype; + bool state = false; + + dt = synps_edac_get_dtype(base); + if (dt == DEV_UNKNOWN) + return state; + + ecctype = readl(base + SCRUB_OFST) & SCRUB_MODE_MASK; + if ((ecctype == SCRUB_MODE_SECDED) && (dt == DEV_X2)) + state = true; + + return state; +} + +/** + * synps_edac_get_memsize - reads the size of the attached memory device + * + * Return: the memory size in bytes + */ +static u32 synps_edac_get_memsize(void) +{ + struct sysinfo inf; + + si_meminfo(&inf); + + return inf.totalram * inf.mem_unit; +} + +/** + * synps_edac_get_mtype - Returns controller memory type + * @base: pointer to the synopsys ecc status structure + * + * Get the EDAC memory type appropriate for the current controller + * configuration. + * + * Return: a memory type enumeration. + */ +static enum mem_type synps_edac_get_mtype(const void __iomem *base) +{ + enum mem_type mt; + u32 memtype; + + memtype = readl(base + T_ZQ_OFST); + + if (memtype & T_ZQ_DDRMODE_MASK) + mt = MEM_DDR3; + else + mt = MEM_DDR2; + + return mt; +} + +/** + * synps_edac_init_csrows - Initialize the cs row data + * @mci: Pointer to the edac memory controller instance + * + * Initializes the chip select rows associated with the EDAC memory + * controller instance + * + * Return: Unconditionally 0. + */ +static int synps_edac_init_csrows(struct mem_ctl_info *mci) +{ + struct csrow_info *csi; + struct dimm_info *dimm; + struct synps_edac_priv *priv = mci->pvt_info; + u32 size; + int row, j; + + for (row = 0; row < mci->nr_csrows; row++) { + csi = mci->csrows[row]; + size = synps_edac_get_memsize(); + + for (j = 0; j < csi->nr_channels; j++) { + dimm = csi->channels[j]->dimm; + dimm->edac_mode = EDAC_FLAG_SECDED; + dimm->mtype = synps_edac_get_mtype(priv->baseaddr); + dimm->nr_pages = (size >> PAGE_SHIFT) / csi->nr_channels; + dimm->grain = SYNPS_EDAC_ERR_GRAIN; + dimm->dtype = synps_edac_get_dtype(priv->baseaddr); + } + } + + return 0; +} + +/** + * synps_edac_mc_init - Initialize driver instance + * @mci: Pointer to the edac memory controller instance + * @pdev: Pointer to the platform_device struct + * + * Performs initialization of the EDAC memory controller instance and + * related driver-private data associated with the memory controller the + * instance is bound to. + * + * Return: Always zero. + */ +static int synps_edac_mc_init(struct mem_ctl_info *mci, + struct platform_device *pdev) +{ + int status; + struct synps_edac_priv *priv; + + mci->pdev = &pdev->dev; + priv = mci->pvt_info; + platform_set_drvdata(pdev, mci); + + /* Initialize controller capabilities and configuration */ + mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR2; + mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; + mci->scrub_cap = SCRUB_HW_SRC; + mci->scrub_mode = SCRUB_NONE; + + mci->edac_cap = EDAC_FLAG_SECDED; + mci->ctl_name = "synps_ddr_controller"; + mci->dev_name = SYNPS_EDAC_MOD_STRING; + mci->mod_name = SYNPS_EDAC_MOD_VER; + mci->mod_ver = "1"; + + edac_op_state = EDAC_OPSTATE_POLL; + mci->edac_check = synps_edac_check; + mci->ctl_page_to_phys = NULL; + + status = synps_edac_init_csrows(mci); + + return status; +} + +/** + * synps_edac_mc_probe - Check controller and bind driver + * @pdev: Pointer to the platform_device struct + * + * Probes a specific controller instance for binding with the driver. + * + * Return: 0 if the controller instance was successfully bound to the + * driver; otherwise, < 0 on error. + */ +static int synps_edac_mc_probe(struct platform_device *pdev) +{ + struct mem_ctl_info *mci; + struct edac_mc_layer layers[2]; + struct synps_edac_priv *priv; + int rc; + struct resource *res; + void __iomem *baseaddr; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + baseaddr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(baseaddr)) + return PTR_ERR(baseaddr); + + if (!synps_edac_get_eccstate(baseaddr)) { + edac_printk(KERN_INFO, EDAC_MC, "ECC not enabled\n"); + return -ENXIO; + } + + layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; + layers[0].size = SYNPS_EDAC_NR_CSROWS; + layers[0].is_virt_csrow = true; + layers[1].type = EDAC_MC_LAYER_CHANNEL; + layers[1].size = SYNPS_EDAC_NR_CHANS; + layers[1].is_virt_csrow = false; + + mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, + sizeof(struct synps_edac_priv)); + if (!mci) { + edac_printk(KERN_ERR, EDAC_MC, + "Failed memory allocation for mc instance\n"); + return -ENOMEM; + } + + priv = mci->pvt_info; + priv->baseaddr = baseaddr; + rc = synps_edac_mc_init(mci, pdev); + if (rc) { + edac_printk(KERN_ERR, EDAC_MC, + "Failed to initialize instance\n"); + goto free_edac_mc; + } + + rc = edac_mc_add_mc(mci); + if (rc) { + edac_printk(KERN_ERR, EDAC_MC, + "Failed to register with EDAC core\n"); + goto free_edac_mc; + } + + /* + * Start capturing the correctable and uncorrectable errors. A write of + * 0 starts the counters. + */ + writel(0x0, baseaddr + ECC_CTRL_OFST); + return rc; + +free_edac_mc: + edac_mc_free(mci); + + return rc; +} + +/** + * synps_edac_mc_remove - Unbind driver from controller + * @pdev: Pointer to the platform_device struct + * + * Return: Unconditionally 0 + */ +static int synps_edac_mc_remove(struct platform_device *pdev) +{ + struct mem_ctl_info *mci = platform_get_drvdata(pdev); + + edac_mc_del_mc(&pdev->dev); + edac_mc_free(mci); + + return 0; +} + +static struct of_device_id synps_edac_match[] = { + { .compatible = "xlnx,zynq-ddrc-a05", }, + { /* end of table */ } +}; + +MODULE_DEVICE_TABLE(of, synps_edac_match); + +static struct platform_driver synps_edac_mc_driver = { + .driver = { + .name = "synopsys-edac", + .of_match_table = synps_edac_match, + }, + .probe = synps_edac_mc_probe, + .remove = synps_edac_mc_remove, +}; + +module_platform_driver(synps_edac_mc_driver); + +MODULE_AUTHOR("Xilinx Inc"); +MODULE_DESCRIPTION("Synopsys DDR ECC driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 91d1179212161f220938198b742c328ad38fd0a3 Mon Sep 17 00:00:00 2001 From: Gavin Li Date: Tue, 6 Jan 2015 18:47:23 -0800 Subject: irqchip: atmel-aic-common: Prevent clobbering of priority when changing IRQ type This patch makes the bitmask for AIC_SRCTYPE consistent with that of its valid values, and prevents the priority field at bits 2:0 from being clobbered by an incorrect AND with the AIC_SRCTYPE mask. Signed-off-by: Gavin Li Cc: # v3.17+ Acked-by: Boris Brezillon Acked-by: Nicolas Ferre Link: https://lkml.kernel.org/r/1420598843-8409-1-git-send-email-gavinli@thegavinli.com Signed-off-by: Jason Cooper diff --git a/drivers/irqchip/irq-atmel-aic-common.c b/drivers/irqchip/irq-atmel-aic-common.c index d111ac7..63cd031 100644 --- a/drivers/irqchip/irq-atmel-aic-common.c +++ b/drivers/irqchip/irq-atmel-aic-common.c @@ -28,7 +28,7 @@ #define AT91_AIC_IRQ_MIN_PRIORITY 0 #define AT91_AIC_IRQ_MAX_PRIORITY 7 -#define AT91_AIC_SRCTYPE GENMASK(7, 6) +#define AT91_AIC_SRCTYPE GENMASK(6, 5) #define AT91_AIC_SRCTYPE_LOW (0 << 5) #define AT91_AIC_SRCTYPE_FALLING (1 << 5) #define AT91_AIC_SRCTYPE_HIGH (2 << 5) @@ -74,7 +74,7 @@ int aic_common_set_type(struct irq_data *d, unsigned type, unsigned *val) return -EINVAL; } - *val &= AT91_AIC_SRCTYPE; + *val &= ~AT91_AIC_SRCTYPE; *val |= aic_type; return 0; -- cgit v0.10.2 From 79144954278d4bb5989f8b903adcac7a20ff2a5a Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 7 Jan 2015 13:46:16 +0100 Subject: udf: Remove repeated loads blocksize Store blocksize in a local variable in udf_fill_inode() since it is used a lot of times. Signed-off-by: Jan Kara diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 5bc71d9..95cb697 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1288,6 +1288,7 @@ static int udf_read_inode(struct inode *inode, bool hidden_inode) struct kernel_lb_addr *iloc = &iinfo->i_location; unsigned int link_count; unsigned int indirections = 0; + int bs = inode->i_sb->s_blocksize; int ret = -EIO; reread: @@ -1374,38 +1375,35 @@ reread: if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_EFE)) { iinfo->i_efe = 1; iinfo->i_use = 0; - ret = udf_alloc_i_data(inode, inode->i_sb->s_blocksize - + ret = udf_alloc_i_data(inode, bs - sizeof(struct extendedFileEntry)); if (ret) goto out; memcpy(iinfo->i_ext.i_data, bh->b_data + sizeof(struct extendedFileEntry), - inode->i_sb->s_blocksize - - sizeof(struct extendedFileEntry)); + bs - sizeof(struct extendedFileEntry)); } else if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_FE)) { iinfo->i_efe = 0; iinfo->i_use = 0; - ret = udf_alloc_i_data(inode, inode->i_sb->s_blocksize - - sizeof(struct fileEntry)); + ret = udf_alloc_i_data(inode, bs - sizeof(struct fileEntry)); if (ret) goto out; memcpy(iinfo->i_ext.i_data, bh->b_data + sizeof(struct fileEntry), - inode->i_sb->s_blocksize - sizeof(struct fileEntry)); + bs - sizeof(struct fileEntry)); } else if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_USE)) { iinfo->i_efe = 0; iinfo->i_use = 1; iinfo->i_lenAlloc = le32_to_cpu( ((struct unallocSpaceEntry *)bh->b_data)-> lengthAllocDescs); - ret = udf_alloc_i_data(inode, inode->i_sb->s_blocksize - + ret = udf_alloc_i_data(inode, bs - sizeof(struct unallocSpaceEntry)); if (ret) goto out; memcpy(iinfo->i_ext.i_data, bh->b_data + sizeof(struct unallocSpaceEntry), - inode->i_sb->s_blocksize - - sizeof(struct unallocSpaceEntry)); + bs - sizeof(struct unallocSpaceEntry)); return 0; } @@ -1498,8 +1496,7 @@ reread: if (iinfo->i_lenAlloc != inode->i_size) goto out; /* File in ICB has to fit in there... */ - if (inode->i_size > inode->i_sb->s_blocksize - - udf_file_entry_alloc_offset(inode)) + if (inode->i_size > bs - udf_file_entry_alloc_offset(inode)) goto out; } -- cgit v0.10.2 From 23b133bdc452aa441fcb9b82cbf6dd05cfd342d0 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 7 Jan 2015 13:49:08 +0100 Subject: udf: Check length of extended attributes and allocation descriptors Check length of extended attributes and allocation descriptors when loading inodes from disk. Otherwise corrupted filesystems could confuse the code and make the kernel oops. Reported-by: Carl Henrik Lunde CC: stable@vger.kernel.org Signed-off-by: Jan Kara diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 95cb697..7b72b7d 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1487,6 +1487,15 @@ reread: } inode->i_generation = iinfo->i_unique; + /* + * Sanity check length of allocation descriptors and extended attrs to + * avoid integer overflows + */ + if (iinfo->i_lenEAttr > bs || iinfo->i_lenAlloc > bs) + goto out; + /* Now do exact checks */ + if (udf_file_entry_alloc_offset(inode) + iinfo->i_lenAlloc > bs) + goto out; /* Sanity checks for files in ICB so that we don't get confused later */ if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { /* -- cgit v0.10.2 From cc72f6e227b8091e0b8297a6be266bedcb20a5aa Mon Sep 17 00:00:00 2001 From: John Linville Date: Tue, 6 Jan 2015 14:39:33 -0500 Subject: mac80211: uninitialized return val in __ieee80211_sta_handle_tspec_ac_params The return value should be initialized to false so that there's a valid return value when there are no sessions that need work to be done on them. Luckily, the side effect of using the uninitialized value is an extra harmless driver call. Coverity: CID 1260096 Fixes: 02219b3abca59 ("mac80211: add WMM admission control support") Signed-off-by: John W. Linville [extend commit message] Signed-off-by: Johannes Berg diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2c36c47..837a406 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1643,7 +1643,7 @@ __ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - bool ret; + bool ret = false; int ac; if (local->hw.queues < IEEE80211_NUM_ACS) -- cgit v0.10.2 From db12847ca84b7a315a3ba77c939c9d08df17d54f Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Tue, 6 Jan 2015 08:39:02 -0500 Subject: mac80211: Re-fix accounting of the tailroom-needed counter When hw acceleration is enabled, the GENERATE_IV or PUT_IV_SPACE flags only require headroom space. Therefore, the tailroom-needed counter can safely be decremented for most drivers. The older incarnation of this patch (ca34e3b5) assumed that the above holds true for all drivers. As reported by Christopher Chavez and researched by Christian Lamparter and Larry Finger, this isn't a valid assumption for p54 and cw1200. Drivers that still require tailroom for ICV/MIC even when HW encryption is enabled can use IEEE80211_KEY_FLAG_RESERVE_TAILROOM to indicate it. Signed-off-by: Ido Yariv Cc: Christopher Chavez Cc: Christian Lamparter Cc: Larry Finger Cc: Solomon Peachy Signed-off-by: Johannes Berg diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c index a1e3237..4a47c7f 100644 --- a/drivers/net/wireless/cw1200/sta.c +++ b/drivers/net/wireless/cw1200/sta.c @@ -709,7 +709,8 @@ int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, if (sta) peer_addr = sta->addr; - key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE | + IEEE80211_KEY_FLAG_RESERVE_TAILROOM; switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 97aeff0..13a30c4 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -575,6 +575,8 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, key->hw_key_idx = 0xff; goto out_unlock; } + + key->flags |= IEEE80211_KEY_FLAG_RESERVE_TAILROOM; } else { slot = key->hw_key_idx; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ece1a54..555a845 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1281,7 +1281,8 @@ struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev); * * @IEEE80211_KEY_FLAG_GENERATE_IV: This flag should be set by the * driver to indicate that it requires IV generation for this - * particular key. + * particular key. Setting this flag does not necessarily mean that SKBs + * will have sufficient tailroom for ICV or MIC. * @IEEE80211_KEY_FLAG_GENERATE_MMIC: This flag should be set by * the driver for a TKIP key if it requires Michael MIC * generation in software. @@ -1293,7 +1294,9 @@ struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev); * @IEEE80211_KEY_FLAG_PUT_IV_SPACE: This flag should be set by the driver * if space should be prepared for the IV, but the IV * itself should not be generated. Do not set together with - * @IEEE80211_KEY_FLAG_GENERATE_IV on the same key. + * @IEEE80211_KEY_FLAG_GENERATE_IV on the same key. Setting this flag does + * not necessarily mean that SKBs will have sufficient tailroom for ICV or + * MIC. * @IEEE80211_KEY_FLAG_RX_MGMT: This key will be used to decrypt received * management frames. The flag can help drivers that have a hardware * crypto implementation that doesn't deal with management frames @@ -1304,6 +1307,9 @@ struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev); * @IEEE80211_KEY_FLAG_GENERATE_IV_MGMT: This flag should be set by the * driver for a CCMP key to indicate that is requires IV generation * only for managment frames (MFP). + * @IEEE80211_KEY_FLAG_RESERVE_TAILROOM: This flag should be set by the + * driver for a key to indicate that sufficient tailroom must always + * be reserved for ICV or MIC, even when HW encryption is enabled. */ enum ieee80211_key_flags { IEEE80211_KEY_FLAG_GENERATE_IV_MGMT = BIT(0), @@ -1313,6 +1319,7 @@ enum ieee80211_key_flags { IEEE80211_KEY_FLAG_SW_MGMT_TX = BIT(4), IEEE80211_KEY_FLAG_PUT_IV_SPACE = BIT(5), IEEE80211_KEY_FLAG_RX_MGMT = BIT(6), + IEEE80211_KEY_FLAG_RESERVE_TAILROOM = BIT(7), }; /** diff --git a/net/mac80211/key.c b/net/mac80211/key.c index bd4e46e..f8d9f0e 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -141,8 +141,7 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || - (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) || - (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))) + (key->conf.flags & IEEE80211_KEY_FLAG_RESERVE_TAILROOM))) sdata->crypto_tx_tailroom_needed_cnt--; WARN_ON((key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) && @@ -191,8 +190,7 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) sdata = key->sdata; if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || - (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) || - (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))) + (key->conf.flags & IEEE80211_KEY_FLAG_RESERVE_TAILROOM))) increment_tailroom_need_count(sdata); ret = drv_set_key(key->local, DISABLE_KEY, sdata, @@ -889,8 +887,7 @@ void ieee80211_remove_key(struct ieee80211_key_conf *keyconf) key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || - (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) || - (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))) + (key->conf.flags & IEEE80211_KEY_FLAG_RESERVE_TAILROOM))) increment_tailroom_need_count(key->sdata); } -- cgit v0.10.2 From 20658702e08ecd693236b443837d28863b93e872 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 29 Dec 2014 11:59:59 +0200 Subject: cfg80211: fix deadlock during reg chan check If a P2P GO is active, the cfg80211_reg_can_beacon function will take the wdev lock, in its call to cfg80211_go_permissive_chan. But the wdev lock is already taken by the parent channel-checking function, causing a deadlock. Split the checking code into two parts. The first part will check if the wdev is active and saves the channel under the wdev lock. The second part will check actual channel validity according to type. Signed-off-by: Arik Nemtsov Reviewed-by: Ilan Peer Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 7b83098..d39d1cb 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1530,45 +1530,40 @@ static void reg_call_notifier(struct wiphy *wiphy, static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) { - struct ieee80211_channel *ch; struct cfg80211_chan_def chandef; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - bool ret = true; + enum nl80211_iftype iftype; wdev_lock(wdev); + iftype = wdev->iftype; + /* make sure the interface is active */ if (!wdev->netdev || !netif_running(wdev->netdev)) - goto out; + goto wdev_inactive_unlock; - switch (wdev->iftype) { + switch (iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: if (!wdev->beacon_interval) - goto out; - - ret = cfg80211_reg_can_beacon(wiphy, - &wdev->chandef, wdev->iftype); + goto wdev_inactive_unlock; + chandef = wdev->chandef; break; case NL80211_IFTYPE_ADHOC: if (!wdev->ssid_len) - goto out; - - ret = cfg80211_reg_can_beacon(wiphy, - &wdev->chandef, wdev->iftype); + goto wdev_inactive_unlock; + chandef = wdev->chandef; break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: if (!wdev->current_bss || !wdev->current_bss->pub.channel) - goto out; + goto wdev_inactive_unlock; - ch = wdev->current_bss->pub.channel; - if (rdev->ops->get_channel && - !rdev_get_channel(rdev, wdev, &chandef)) - ret = cfg80211_chandef_usable(wiphy, &chandef, - IEEE80211_CHAN_DISABLED); - else - ret = !(ch->flags & IEEE80211_CHAN_DISABLED); + if (!rdev->ops->get_channel || + rdev_get_channel(rdev, wdev, &chandef)) + cfg80211_chandef_create(&chandef, + wdev->current_bss->pub.channel, + NL80211_CHAN_NO_HT); break; case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_AP_VLAN: @@ -1581,9 +1576,26 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) break; } -out: wdev_unlock(wdev); - return ret; + + switch (iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_ADHOC: + return cfg80211_reg_can_beacon(wiphy, &chandef, iftype); + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + return cfg80211_chandef_usable(wiphy, &chandef, + IEEE80211_CHAN_DISABLED); + default: + break; + } + + return true; + +wdev_inactive_unlock: + wdev_unlock(wdev); + return true; } static void reg_leave_invalid_chans(struct wiphy *wiphy) -- cgit v0.10.2 From 322b8af16f267a2bc027297ffa9b3b681325ed37 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 7 Jan 2015 14:41:58 +0100 Subject: ALSA: hda - Print codec->chip_name in autoconfig This will help end users figure out what HDA codec they have. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 1ede822..6ec3775 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -409,10 +409,10 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, /* * debug prints of the parsed results */ - codec_info(codec, "autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n", - cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1], - cfg->line_out_pins[2], cfg->line_out_pins[3], - cfg->line_out_pins[4], + codec_info(codec, "autoconfig for %s: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n", + codec->chip_name, cfg->line_outs, cfg->line_out_pins[0], + cfg->line_out_pins[1], cfg->line_out_pins[2], + cfg->line_out_pins[3], cfg->line_out_pins[4], cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" : (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ? "speaker" : "line")); -- cgit v0.10.2 From 4f7946eca787baa0f66b4a508595b768a4772f3f Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 7 Jan 2015 14:41:59 +0100 Subject: ALSA: hda - Debug output which type of fixup was selected Our fixup system is becoming increasingly complex, so it's becoming time consuming to figure out which way the fix was applied. This patch adds a few debug prints to aid when debugging why a specific fixup was selected. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 6ec3775..3f8706b 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -920,6 +920,8 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec, codec->fixup_id = pq->value; #ifdef CONFIG_SND_DEBUG_VERBOSE codec->fixup_name = pq->name; + codec_dbg(codec, "%s: picked fixup %s (pin match)\n", + codec->chip_name, codec->fixup_name); #endif codec->fixup_list = fixlist; return; @@ -960,6 +962,8 @@ void snd_hda_pick_fixup(struct hda_codec *codec, codec->fixup_list = NULL; codec->fixup_name = NULL; codec->fixup_id = HDA_FIXUP_ID_NO_FIXUP; + codec_dbg(codec, "%s: picked no fixup (nofixup specified)\n", + codec->chip_name); return; } @@ -969,6 +973,8 @@ void snd_hda_pick_fixup(struct hda_codec *codec, codec->fixup_id = models->id; codec->fixup_name = models->name; codec->fixup_list = fixlist; + codec_dbg(codec, "%s: picked fixup %s (model specified)\n", + codec->chip_name, codec->fixup_name); return; } models++; @@ -980,6 +986,8 @@ void snd_hda_pick_fixup(struct hda_codec *codec, id = q->value; #ifdef CONFIG_SND_DEBUG_VERBOSE name = q->name; + codec_dbg(codec, "%s: picked fixup %s (PCI SSID%s)\n", + codec->chip_name, name, q->subdevice_mask ? "" : " - vendor generic"); #endif } } @@ -992,6 +1000,8 @@ void snd_hda_pick_fixup(struct hda_codec *codec, id = q->value; #ifdef CONFIG_SND_DEBUG_VERBOSE name = q->name; + codec_dbg(codec, "%s: picked fixup %s (codec SSID)\n", + codec->chip_name, name); #endif break; } -- cgit v0.10.2 From 5618955c4269b07c8177f88a8075d4879e74cd27 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Jan 2015 01:40:17 +0200 Subject: ALSA: fm801: move to pcim_* and devm_* functions The managed functions allow to get ->probe() and ->remove() simplier. This patch converts code to use managed functions. While here remove the dead code and fix the value printed in error message. [removed pci_release_regions() as it's managed in pcim_release(), too -- tiwai] Signed-off-by: Andy Shevchenko Signed-off-by: Takashi Iwai diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index d167aff..dd6f23f 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -1186,12 +1186,6 @@ static int snd_fm801_free(struct fm801 *chip) v4l2_device_unregister(&chip->v4l2_dev); } #endif - if (chip->irq >= 0) - free_irq(chip->irq, chip); - pci_release_regions(chip->pci); - pci_disable_device(chip->pci); - - kfree(chip); return 0; } @@ -1214,28 +1208,23 @@ static int snd_fm801_create(struct snd_card *card, }; *rchip = NULL; - if ((err = pci_enable_device(pci)) < 0) + if ((err = pcim_enable_device(pci)) < 0) return err; - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (chip == NULL) { - pci_disable_device(pci); + chip = devm_kzalloc(&pci->dev, sizeof(*chip), GFP_KERNEL); + if (chip == NULL) return -ENOMEM; - } spin_lock_init(&chip->reg_lock); chip->card = card; chip->pci = pci; chip->irq = -1; chip->tea575x_tuner = tea575x_tuner; - if ((err = pci_request_regions(pci, "FM801")) < 0) { - kfree(chip); - pci_disable_device(pci); + if ((err = pci_request_regions(pci, "FM801")) < 0) return err; - } chip->port = pci_resource_start(pci, 0); if ((tea575x_tuner & TUNER_ONLY) == 0) { - if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_SHARED, - KBUILD_MODNAME, chip)) { - dev_err(card->dev, "unable to grab IRQ %d\n", chip->irq); + if (devm_request_irq(&pci->dev, pci->irq, snd_fm801_interrupt, + IRQF_SHARED, KBUILD_MODNAME, chip)) { + dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); snd_fm801_free(chip); return -EBUSY; } @@ -1250,12 +1239,6 @@ static int snd_fm801_create(struct snd_card *card, /* init might set tuner access method */ tea575x_tuner = chip->tea575x_tuner; - if (chip->irq >= 0 && (tea575x_tuner & TUNER_ONLY)) { - pci_clear_master(pci); - free_irq(chip->irq, chip); - chip->irq = -1; - } - if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { snd_fm801_free(chip); return err; -- cgit v0.10.2 From 33f4acd3b214fee9662cbaf569a4876b8604f152 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 7 Jan 2015 15:50:13 +0100 Subject: ALSA: hda - Enable mic mute hotkey and LEDs for an HP machine On this machine, the mic mute hotkey is connected to GPIO2. We therefore create an extra input device for this key. Also enable LEDs connected to GPIO3 and GPIO4. (Note: if this patch is backported to older kernels, where KEY_MIC_MUTE is not available, change the KEY_MIC_MUTE to KEY_F20, which was previously used for mic mute keys.) BugLink: https://bugs.launchpad.net/bugs/1408295 Tested-by: Keng-Yu Lin Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 65f1f4e..09d2131 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include "hda_codec.h" @@ -120,6 +121,9 @@ struct alc_spec { hda_nid_t pll_nid; unsigned int pll_coef_idx, pll_coef_bit; unsigned int coef0; +#if IS_ENABLED(CONFIG_INPUT) + struct input_dev *kb_dev; +#endif }; /* @@ -3472,6 +3476,84 @@ static void alc280_fixup_hp_gpio4(struct hda_codec *codec, } } +#if IS_ENABLED(CONFIG_INPUT) +static void gpio2_mic_hotkey_event(struct hda_codec *codec, + struct hda_jack_callback *event) +{ + struct alc_spec *spec = codec->spec; + + /* GPIO2 just toggles on a keypress/keyrelease cycle. Therefore + send both key on and key off event for every interrupt. */ + input_report_key(spec->kb_dev, KEY_MICMUTE, 1); + input_sync(spec->kb_dev); + input_report_key(spec->kb_dev, KEY_MICMUTE, 0); + input_sync(spec->kb_dev); +} +#endif + +static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ +#if IS_ENABLED(CONFIG_INPUT) + /* GPIO1 = set according to SKU external amp + GPIO2 = mic mute hotkey + GPIO3 = mute LED + GPIO4 = mic mute LED */ + static const struct hda_verb gpio_init[] = { + { 0x01, AC_VERB_SET_GPIO_MASK, 0x1e }, + { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x1a }, + { 0x01, AC_VERB_SET_GPIO_DATA, 0x02 }, + {} + }; + + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->kb_dev = input_allocate_device(); + if (!spec->kb_dev) { + codec_err(codec, "Out of memory (input_allocate_device)\n"); + return; + } + spec->kb_dev->name = "Microphone Mute Button"; + spec->kb_dev->evbit[0] = BIT_MASK(EV_KEY); + spec->kb_dev->keybit[BIT_WORD(KEY_MICMUTE)] = BIT_MASK(KEY_MICMUTE); + if (input_register_device(spec->kb_dev)) { + codec_err(codec, "input_register_device failed\n"); + input_free_device(spec->kb_dev); + spec->kb_dev = NULL; + return; + } + + snd_hda_add_verbs(codec, gpio_init); + snd_hda_codec_write_cache(codec, codec->afg, 0, + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x04); + snd_hda_jack_detect_enable_callback(codec, codec->afg, + gpio2_mic_hotkey_event); + + spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; + spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook; + spec->gpio_led = 0; + spec->mute_led_polarity = 0; + spec->gpio_mute_led_mask = 0x08; + spec->gpio_mic_led_mask = 0x10; + return; + } + + if (!spec->kb_dev) + return; + + switch (action) { + case HDA_FIXUP_ACT_PROBE: + spec->init_amp = ALC_INIT_DEFAULT; + break; + case HDA_FIXUP_ACT_FREE: + input_unregister_device(spec->kb_dev); + input_free_device(spec->kb_dev); + spec->kb_dev = NULL; + } +#endif +} + static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -4341,6 +4423,7 @@ enum { ALC282_FIXUP_ASPIRE_V5_PINS, ALC280_FIXUP_HP_GPIO4, ALC286_FIXUP_HP_GPIO_LED, + ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY, }; static const struct hda_fixup alc269_fixups[] = { @@ -4814,6 +4897,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc286_fixup_hp_gpio_led, }, + [ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc280_fixup_hp_gpio2_mic_hotkey, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -4843,6 +4930,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x225f, "HP", ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY), /* ALC282 */ SND_PCI_QUIRK(0x103c, 0x2210, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2214, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), -- cgit v0.10.2 From 9d9acda9ba956a5d37e61e0ce6c579a67610bd05 Mon Sep 17 00:00:00 2001 From: Nicholas Krause Date: Wed, 7 Jan 2015 00:44:52 -0500 Subject: libata: Remove FIXME comment in atapi_eh_request_sense Remove the FIXME comment in atapi_eh_request_sense () asking whether memset of sense buffer is necessary. The buffer may be partially or fully filled by the device. We want it to be cleared. Signed-off-by: Nicholas Krause Signed-off-by: Tejun Heo diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 8d00c26..a9f5aed 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1635,7 +1635,6 @@ unsigned int atapi_eh_request_sense(struct ata_device *dev, DPRINTK("ATAPI request sense\n"); - /* FIXME: is this needed? */ memset(sense_buf, 0, SCSI_SENSE_BUFFERSIZE); /* initialize sense_buf with the error register, -- cgit v0.10.2 From 4aaa71873ddb9faf4b0c4826579e2f6d18ff9ab4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Jan 2015 15:24:19 +0200 Subject: sata_dwc_460ex: fix resource leak on error path DMA mapped IO should be unmapped on the error path in probe() and unconditionally on remove(). Fixes: 62936009f35a ([libata] Add 460EX on-chip SATA driver, sata_dwc_460ex) Signed-off-by: Andy Shevchenko Signed-off-by: Tejun Heo diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index c7ddef8..8e824817 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -797,7 +797,7 @@ static int dma_dwc_init(struct sata_dwc_device *hsdev, int irq) if (err) { dev_err(host_pvt.dwc_dev, "%s: dma_request_interrupts returns" " %d\n", __func__, err); - goto error_out; + return err; } /* Enabe DMA */ @@ -808,11 +808,6 @@ static int dma_dwc_init(struct sata_dwc_device *hsdev, int irq) sata_dma_regs); return 0; - -error_out: - dma_dwc_exit(hsdev); - - return err; } static int sata_dwc_scr_read(struct ata_link *link, unsigned int scr, u32 *val) @@ -1662,7 +1657,7 @@ static int sata_dwc_probe(struct platform_device *ofdev) char *ver = (char *)&versionr; u8 *base = NULL; int err = 0; - int irq, rc; + int irq; struct ata_host *host; struct ata_port_info pi = sata_dwc_port_info[0]; const struct ata_port_info *ppi[] = { &pi, NULL }; @@ -1725,7 +1720,7 @@ static int sata_dwc_probe(struct platform_device *ofdev) if (irq == NO_IRQ) { dev_err(&ofdev->dev, "no SATA DMA irq\n"); err = -ENODEV; - goto error_out; + goto error_iomap; } /* Get physical SATA DMA register base address */ @@ -1734,14 +1729,16 @@ static int sata_dwc_probe(struct platform_device *ofdev) dev_err(&ofdev->dev, "ioremap failed for AHBDMA register" " address\n"); err = -ENODEV; - goto error_out; + goto error_iomap; } /* Save dev for later use in dev_xxx() routines */ host_pvt.dwc_dev = &ofdev->dev; /* Initialize AHB DMAC */ - dma_dwc_init(hsdev, irq); + err = dma_dwc_init(hsdev, irq); + if (err) + goto error_dma_iomap; /* Enable SATA Interrupts */ sata_dwc_enable_interrupts(hsdev); @@ -1759,9 +1756,8 @@ static int sata_dwc_probe(struct platform_device *ofdev) * device discovery process, invoking our port_start() handler & * error_handler() to execute a dummy Softreset EH session */ - rc = ata_host_activate(host, irq, sata_dwc_isr, 0, &sata_dwc_sht); - - if (rc != 0) + err = ata_host_activate(host, irq, sata_dwc_isr, 0, &sata_dwc_sht); + if (err) dev_err(&ofdev->dev, "failed to activate host"); dev_set_drvdata(&ofdev->dev, host); @@ -1770,7 +1766,8 @@ static int sata_dwc_probe(struct platform_device *ofdev) error_out: /* Free SATA DMA resources */ dma_dwc_exit(hsdev); - +error_dma_iomap: + iounmap((void __iomem *)host_pvt.sata_dma_regs); error_iomap: iounmap(base); error_kmalloc: @@ -1791,6 +1788,7 @@ static int sata_dwc_remove(struct platform_device *ofdev) /* Free SATA DMA resources */ dma_dwc_exit(hsdev); + iounmap((void __iomem *)host_pvt.sata_dma_regs); iounmap(hsdev->reg_base); kfree(hsdev); kfree(host); -- cgit v0.10.2 From d4812e169de44f4ab53ff671c6193c67de24da62 Mon Sep 17 00:00:00 2001 From: "Luck, Tony" Date: Mon, 5 Jan 2015 16:44:42 -0800 Subject: x86, mce: Get rid of TIF_MCE_NOTIFY and associated mce tricks We now switch to the kernel stack when a machine check interrupts during user mode. This means that we can perform recovery actions in the tail of do_machine_check() Acked-by: Borislav Petkov Signed-off-by: Tony Luck Signed-off-by: Andy Lutomirski diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h index 51b26e89..9b3de99 100644 --- a/arch/x86/include/asm/mce.h +++ b/arch/x86/include/asm/mce.h @@ -190,7 +190,6 @@ enum mcp_flags { void machine_check_poll(enum mcp_flags flags, mce_banks_t *b); int mce_notify_irq(void); -void mce_notify_process(void); DECLARE_PER_CPU(struct mce, injectm); diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index 8b13b0fb..e82e95a 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -75,7 +75,6 @@ struct thread_info { #define TIF_SYSCALL_EMU 6 /* syscall emulation active */ #define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */ #define TIF_SECCOMP 8 /* secure computing */ -#define TIF_MCE_NOTIFY 10 /* notify userspace of an MCE */ #define TIF_USER_RETURN_NOTIFY 11 /* notify kernel of userspace return */ #define TIF_UPROBE 12 /* breakpointed or singlestepping */ #define TIF_NOTSC 16 /* TSC is not accessible in userland */ @@ -100,7 +99,6 @@ struct thread_info { #define _TIF_SYSCALL_EMU (1 << TIF_SYSCALL_EMU) #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) #define _TIF_SECCOMP (1 << TIF_SECCOMP) -#define _TIF_MCE_NOTIFY (1 << TIF_MCE_NOTIFY) #define _TIF_USER_RETURN_NOTIFY (1 << TIF_USER_RETURN_NOTIFY) #define _TIF_UPROBE (1 << TIF_UPROBE) #define _TIF_NOTSC (1 << TIF_NOTSC) @@ -140,7 +138,7 @@ struct thread_info { /* Only used for 64 bit */ #define _TIF_DO_NOTIFY_MASK \ - (_TIF_SIGPENDING | _TIF_MCE_NOTIFY | _TIF_NOTIFY_RESUME | \ + (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | \ _TIF_USER_RETURN_NOTIFY | _TIF_UPROBE) /* flags to check in __switch_to() */ diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index 800d423..d231799 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -1004,51 +1004,6 @@ static void mce_clear_state(unsigned long *toclear) } /* - * Need to save faulting physical address associated with a process - * in the machine check handler some place where we can grab it back - * later in mce_notify_process() - */ -#define MCE_INFO_MAX 16 - -struct mce_info { - atomic_t inuse; - struct task_struct *t; - __u64 paddr; - int restartable; -} mce_info[MCE_INFO_MAX]; - -static void mce_save_info(__u64 addr, int c) -{ - struct mce_info *mi; - - for (mi = mce_info; mi < &mce_info[MCE_INFO_MAX]; mi++) { - if (atomic_cmpxchg(&mi->inuse, 0, 1) == 0) { - mi->t = current; - mi->paddr = addr; - mi->restartable = c; - return; - } - } - - mce_panic("Too many concurrent recoverable errors", NULL, NULL); -} - -static struct mce_info *mce_find_info(void) -{ - struct mce_info *mi; - - for (mi = mce_info; mi < &mce_info[MCE_INFO_MAX]; mi++) - if (atomic_read(&mi->inuse) && mi->t == current) - return mi; - return NULL; -} - -static void mce_clear_info(struct mce_info *mi) -{ - atomic_set(&mi->inuse, 0); -} - -/* * The actual machine check handler. This only handles real * exceptions when something got corrupted coming in through int 18. * @@ -1086,6 +1041,8 @@ void do_machine_check(struct pt_regs *regs, long error_code) DECLARE_BITMAP(toclear, MAX_NR_BANKS); DECLARE_BITMAP(valid_banks, MAX_NR_BANKS); char *msg = "Unknown"; + u64 recover_paddr = ~0ull; + int flags = MF_ACTION_REQUIRED; prev_state = ist_enter(regs); @@ -1207,9 +1164,9 @@ void do_machine_check(struct pt_regs *regs, long error_code) if (no_way_out) mce_panic("Fatal machine check on current CPU", &m, msg); if (worst == MCE_AR_SEVERITY) { - /* schedule action before return to userland */ - mce_save_info(m.addr, m.mcgstatus & MCG_STATUS_RIPV); - set_thread_flag(TIF_MCE_NOTIFY); + recover_paddr = m.addr; + if (!(m.mcgstatus & MCG_STATUS_RIPV)) + flags |= MF_MUST_KILL; } else if (kill_it) { force_sig(SIGBUS, current); } @@ -1220,6 +1177,26 @@ void do_machine_check(struct pt_regs *regs, long error_code) mce_wrmsrl(MSR_IA32_MCG_STATUS, 0); out: sync_core(); + + if (recover_paddr == ~0ull) + goto done; + + pr_err("Uncorrected hardware memory error in user-access at %llx", + recover_paddr); + /* + * We must call memory_failure() here even if the current process is + * doomed. We still need to mark the page as poisoned and alert any + * other users of the page. + */ + ist_begin_non_atomic(regs); + local_irq_enable(); + if (memory_failure(recover_paddr >> PAGE_SHIFT, MCE_VECTOR, flags) < 0) { + pr_err("Memory error not recovered"); + force_sig(SIGBUS, current); + } + local_irq_disable(); + ist_end_non_atomic(); +done: ist_exit(regs, prev_state); } EXPORT_SYMBOL_GPL(do_machine_check); @@ -1238,42 +1215,6 @@ int memory_failure(unsigned long pfn, int vector, int flags) #endif /* - * Called in process context that interrupted by MCE and marked with - * TIF_MCE_NOTIFY, just before returning to erroneous userland. - * This code is allowed to sleep. - * Attempt possible recovery such as calling the high level VM handler to - * process any corrupted pages, and kill/signal current process if required. - * Action required errors are handled here. - */ -void mce_notify_process(void) -{ - unsigned long pfn; - struct mce_info *mi = mce_find_info(); - int flags = MF_ACTION_REQUIRED; - - if (!mi) - mce_panic("Lost physical address for unconsumed uncorrectable error", NULL, NULL); - pfn = mi->paddr >> PAGE_SHIFT; - - clear_thread_flag(TIF_MCE_NOTIFY); - - pr_err("Uncorrected hardware memory error in user-access at %llx", - mi->paddr); - /* - * We must call memory_failure() here even if the current process is - * doomed. We still need to mark the page as poisoned and alert any - * other users of the page. - */ - if (!mi->restartable) - flags |= MF_MUST_KILL; - if (memory_failure(pfn, MCE_VECTOR, flags) < 0) { - pr_err("Memory error not recovered"); - force_sig(SIGBUS, current); - } - mce_clear_info(mi); -} - -/* * Action optional processing happens here (picking up * from the list of faulting pages that do_machine_check() * placed into the "ring"). diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c index ed37a76..2a33c8f 100644 --- a/arch/x86/kernel/signal.c +++ b/arch/x86/kernel/signal.c @@ -740,12 +740,6 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) { user_exit(); -#ifdef CONFIG_X86_MCE - /* notify userspace of pending MCEs */ - if (thread_info_flags & _TIF_MCE_NOTIFY) - mce_notify_process(); -#endif /* CONFIG_X86_64 && CONFIG_X86_MCE */ - if (thread_info_flags & _TIF_UPROBE) uprobe_notify_resume(regs); -- cgit v0.10.2 From 193924472a2514f7d9e4d08cf184e9a5f2b9309d Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 21 Jul 2014 10:03:59 +0200 Subject: batman-adv: avoid useless return in void functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Linus Lüssing Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c index a12e25e..1485091 100644 --- a/net/batman-adv/debugfs.c +++ b/net/batman-adv/debugfs.c @@ -233,7 +233,6 @@ static int batadv_debug_log_setup(struct batadv_priv *bat_priv) static void batadv_debug_log_cleanup(struct batadv_priv *bat_priv) { - return; } #endif diff --git a/net/batman-adv/multicast.h b/net/batman-adv/multicast.h index 73b5d45..3a44ebd 100644 --- a/net/batman-adv/multicast.h +++ b/net/batman-adv/multicast.h @@ -50,7 +50,6 @@ void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node); static inline void batadv_mcast_mla_update(struct batadv_priv *bat_priv) { - return; } static inline enum batadv_forw_mode @@ -67,12 +66,10 @@ static inline int batadv_mcast_init(struct batadv_priv *bat_priv) static inline void batadv_mcast_free(struct batadv_priv *bat_priv) { - return; } static inline void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node) { - return; } #endif /* CONFIG_BATMAN_ADV_MCAST */ -- cgit v0.10.2 From 320b936678f57107524063e4fe3c29d1d4795810 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Wed, 16 Jul 2014 12:23:10 +0200 Subject: batman-adv: remove obsolete variable primary_iface from orig_node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This variable became obsolete when changing to the new bonding mechanism based on the multi interface optimization. Since its not used anywhere, remove it. Reported-by: Linus Lüssing Signed-off-by: Simon Wunderlich Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 8854c05..462a70c 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -199,7 +199,6 @@ struct batadv_orig_bat_iv { /** * struct batadv_orig_node - structure for orig_list maintaining nodes of mesh * @orig: originator ethernet address - * @primary_addr: hosts primary interface address * @ifinfo_list: list for routers per outgoing interface * @last_bonding_candidate: pointer to last ifinfo of last used router * @batadv_dat_addr_t: address of the orig node in the distributed hash @@ -244,7 +243,6 @@ struct batadv_orig_bat_iv { */ struct batadv_orig_node { uint8_t orig[ETH_ALEN]; - uint8_t primary_addr[ETH_ALEN]; struct hlist_head ifinfo_list; struct batadv_orig_ifinfo *last_bonding_candidate; #ifdef CONFIG_BATMAN_ADV_DAT -- cgit v0.10.2 From 95298a91f989f69921b59269f3b87c0ac9073d96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Hundeb=C3=B8ll?= Date: Tue, 15 Jul 2014 09:41:05 +0200 Subject: batman-adv: kernel doc fixes for bat_iv_ogm.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Hundebøll Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 1e80539..73209f3 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -28,7 +28,7 @@ /** - * batadv_dup_status - duplicate status + * enum batadv_dup_status - duplicate status * @BATADV_NO_DUP: the packet is a duplicate * @BATADV_ORIG_DUP: OGM is a duplicate in the originator (but not for the * neighbor) @@ -517,7 +517,7 @@ out: * @bat_priv: the bat priv with all the soft interface information * @packet_len: (total) length of the OGM * @send_time: timestamp (jiffies) when the packet is to be sent - * @direktlink: true if this is a direct link packet + * @directlink: true if this is a direct link packet * @if_incoming: interface where the packet was received * @if_outgoing: interface for which the retransmission should be considered * @forw_packet: the forwarded packet which should be checked @@ -1366,6 +1366,7 @@ out: /** * batadv_iv_ogm_process_per_outif - process a batman iv OGM for an outgoing if * @skb: the skb containing the OGM + * @ogm_offset: offset from skb->data to start of ogm header * @orig_node: the (cached) orig node for the originator of this OGM * @if_incoming: the interface where this packet was received * @if_outgoing: the interface for which the packet should be considered @@ -1902,10 +1903,10 @@ out: * batadv_iv_ogm_neigh_is_eob - check if neigh1 is equally good or better than * neigh2 from the metric prospective * @neigh1: the first neighbor object of the comparison - * @if_outgoing: outgoing interface for the first neighbor + * @if_outgoing1: outgoing interface for the first neighbor * @neigh2: the second neighbor object of the comparison * @if_outgoing2: outgoing interface for the second neighbor - + * * Returns true if the metric via neigh1 is equally good or better than * the metric via neigh2, false otherwise. */ -- cgit v0.10.2 From e33571898832fb1ee44df15b1f98cc728497072d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Hundeb=C3=B8ll?= Date: Tue, 15 Jul 2014 09:41:06 +0200 Subject: batman-adv: kernel doc fixes for bridge_loop_avoidance.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Hundebøll Acked-by: Simon Wunderlich Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index a957c81..0f0ca43 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -252,7 +252,7 @@ batadv_bla_del_backbone_claims(struct batadv_bla_backbone_gw *backbone_gw) /** * batadv_bla_send_claim - sends a claim frame according to the provided info * @bat_priv: the bat priv with all the soft interface information - * @orig: the mac address to be announced within the claim + * @mac: the mac address to be announced within the claim * @vid: the VLAN ID * @claimtype: the type of the claim (CLAIM, UNCLAIM, ANNOUNCE, ...) */ @@ -364,6 +364,7 @@ out: * @bat_priv: the bat priv with all the soft interface information * @orig: the mac address of the originator * @vid: the VLAN ID + * @own_backbone: set if the requested backbone is local * * searches for the backbone gw or creates a new one if it could not * be found. @@ -454,6 +455,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv, /** * batadv_bla_answer_request - answer a bla request by sending own claims * @bat_priv: the bat priv with all the soft interface information + * @primary_if: interface where the request came on * @vid: the vid where the request came on * * Repeat all of our own claims, and finally send an ANNOUNCE frame @@ -775,6 +777,7 @@ static int batadv_handle_claim(struct batadv_priv *bat_priv, /** * batadv_check_claim_group * @bat_priv: the bat priv with all the soft interface information + * @primary_if: the primary interface of this batman interface * @hw_src: the Hardware source in the ARP Header * @hw_dst: the Hardware destination in the ARP Header * @ethhdr: pointer to the Ethernet header of the claim frame @@ -850,6 +853,7 @@ static int batadv_check_claim_group(struct batadv_priv *bat_priv, /** * batadv_bla_process_claim * @bat_priv: the bat priv with all the soft interface information + * @primary_if: the primary hard interface of this batman soft interface * @skb: the frame to be checked * * Check if this is a claim frame, and process it accordingly. -- cgit v0.10.2 From ba97abb86302d7380bc6738aa9313a1c460c4f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Hundeb=C3=B8ll?= Date: Tue, 15 Jul 2014 09:41:07 +0200 Subject: batman-adv: kernel doc fix for distributed-arp-table.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Hundebøll Acked-by: Antonio Quartulli Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli diff --git a/net/batman-adv/distributed-arp-table.h b/net/batman-adv/distributed-arp-table.h index d76e1d0..2fe0764 100644 --- a/net/batman-adv/distributed-arp-table.h +++ b/net/batman-adv/distributed-arp-table.h @@ -25,9 +25,7 @@ #include -/** - * BATADV_DAT_ADDR_MAX - maximum address value in the DHT space - */ +/* BATADV_DAT_ADDR_MAX - maximum address value in the DHT space */ #define BATADV_DAT_ADDR_MAX ((batadv_dat_addr_t)~(batadv_dat_addr_t)0) void batadv_dat_status_update(struct net_device *net_dev); -- cgit v0.10.2 From a0e287750562969db8a891e9805fef55aeb05df0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Hundeb=C3=B8ll?= Date: Tue, 15 Jul 2014 09:41:08 +0200 Subject: batman-adv: kernel doc fixes for main.{c, h} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Hundebøll Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index d1183e8..2cdd25a 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -651,7 +651,7 @@ static struct batadv_tvlv_handler /** * batadv_tvlv_container_free_ref - decrement the tvlv container refcounter and * possibly free it - * @tvlv_handler: the tvlv container to free + * @tvlv: the tvlv container to free */ static void batadv_tvlv_container_free_ref(struct batadv_tvlv_container *tvlv) { @@ -800,7 +800,7 @@ void batadv_tvlv_container_register(struct batadv_priv *bat_priv, * requested packet size * @packet_buff: packet buffer * @packet_buff_len: packet buffer size - * @packet_min_len: requested packet minimum size + * @min_packet_len: requested packet minimum size * @additional_packet_len: requested additional packet size on top of minimum * size * diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index a1fcd88..2a59b04 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -92,9 +92,8 @@ /* numbers of originator to contact for any PUT/GET DHT operation */ #define BATADV_DAT_CANDIDATES_NUM 3 -/** - * BATADV_TQ_SIMILARITY_THRESHOLD - TQ points that a secondary metric can differ - * at most from the primary one in order to be still considered acceptable +/* BATADV_TQ_SIMILARITY_THRESHOLD - TQ points that a secondary metric can differ + * at most from the primary one in order to be still considered acceptable */ #define BATADV_TQ_SIMILARITY_THRESHOLD 50 -- cgit v0.10.2 From 24820df1449d4d7cf71bb66e7885ab7394c7b4fb Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 1 Sep 2014 14:37:25 +0200 Subject: batman-adv: checkpatch - else is not generally useful after a break or return Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner diff --git a/net/batman-adv/bitarray.h b/net/batman-adv/bitarray.h index cc24073..2acaafe 100644 --- a/net/batman-adv/bitarray.h +++ b/net/batman-adv/bitarray.h @@ -29,8 +29,7 @@ static inline int batadv_test_bit(const unsigned long *seq_bits, diff = last_seqno - curr_seqno; if (diff < 0 || diff >= BATADV_TQ_LOCAL_WINDOW_SIZE) return 0; - else - return test_bit(diff, seq_bits) != 0; + return test_bit(diff, seq_bits) != 0; } /* turn corresponding bit on, so we can remember that we got the packet */ diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h index 5d7a0e6..d848cf6 100644 --- a/net/batman-adv/fragmentation.h +++ b/net/batman-adv/fragmentation.h @@ -41,8 +41,7 @@ batadv_frag_check_entry(struct batadv_frag_table_entry *frags_entry) if (!hlist_empty(&frags_entry->head) && batadv_has_timed_out(frags_entry->timestamp, BATADV_FRAG_TIMEOUT)) return true; - else - return false; + return false; } #endif /* _NET_BATMAN_ADV_FRAGMENTATION_H_ */ diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c index fab47f1..127cc4d 100644 --- a/net/batman-adv/network-coding.c +++ b/net/batman-adv/network-coding.c @@ -1212,8 +1212,7 @@ static bool batadv_nc_skb_coding_possible(struct sk_buff *skb, { if (BATADV_SKB_CB(skb)->decoded && !batadv_compare_eth(dst, src)) return false; - else - return true; + return true; } /** diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 5f59e7f..38a804e 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -2769,9 +2769,8 @@ static bool batadv_send_tt_response(struct batadv_priv *bat_priv, { if (batadv_is_my_mac(bat_priv, req_dst)) return batadv_send_my_tt_response(bat_priv, tt_data, req_src); - else - return batadv_send_other_tt_response(bat_priv, tt_data, - req_src, req_dst); + return batadv_send_other_tt_response(bat_priv, tt_data, req_src, + req_dst); } static void _batadv_tt_update_changes(struct batadv_priv *bat_priv, -- cgit v0.10.2 From 9687a31c84d7eca038a5335d8d804c11bd5d8653 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 1 Sep 2014 14:37:26 +0200 Subject: batman-adv: checkpatch - No space is necessary after a cast Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 2a59b04..18286f8 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -312,10 +312,10 @@ static inline bool batadv_has_timed_out(unsigned long timestamp, * - when adding 128 - it is neither a predecessor nor a successor, * - after adding more than 127 to the starting value - it is a successor */ -#define batadv_seq_before(x, y) ({typeof(x) _d1 = (x); \ - typeof(y) _d2 = (y); \ - typeof(x) _dummy = (_d1 - _d2); \ - (void) (&_d1 == &_d2); \ +#define batadv_seq_before(x, y) ({typeof(x)_d1 = (x); \ + typeof(y)_d2 = (y); \ + typeof(x)_dummy = (_d1 - _d2); \ + (void)(&_d1 == &_d2); \ _dummy > batadv_smallest_signed_int(_dummy); }) #define batadv_seq_after(x, y) batadv_seq_before(y, x) -- cgit v0.10.2 From aa143d2837325d66150e1566b22b61f0c7a843c9 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 1 Sep 2014 14:37:27 +0200 Subject: batman-adv: checkpatch - Please use a blank line after declarations Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c index 1485091..a4972874 100644 --- a/net/batman-adv/debugfs.c +++ b/net/batman-adv/debugfs.c @@ -404,6 +404,7 @@ struct batadv_debuginfo batadv_hardif_debuginfo_##_name = { \ .release = single_release, \ }, \ } + static BATADV_HARDIF_DEBUGINFO(originators, S_IRUGO, batadv_originators_hardif_open); diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index b598111..aad022d 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -1100,6 +1100,7 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, batadv_dat_send_data(bat_priv, skb, ip_src, BATADV_P_DAT_DHT_PUT); batadv_dat_send_data(bat_priv, skb, ip_dst, BATADV_P_DAT_DHT_PUT); } + /** * batadv_dat_snoop_incoming_arp_reply - snoop the ARP reply and fill the local * DAT storage only diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index e0bcf9e..27649e8 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -775,6 +775,7 @@ batadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsigned int *header_len, return ret; } + /** * batadv_gw_out_of_range - check if the dhcp request destination is the best gw * @bat_priv: the bat priv with all the soft interface information diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h index 34e096d..facd1fe 100644 --- a/net/batman-adv/packet.h +++ b/net/batman-adv/packet.h @@ -198,6 +198,7 @@ struct batadv_bla_claim_dst { uint8_t type; /* bla_claimframe */ __be16 group; /* group id */ }; + #pragma pack() /** -- cgit v0.10.2 From 8a3f8b6ac5c5791a04d0d30636190bfc68f2e37b Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 1 Sep 2014 14:37:28 +0200 Subject: batman-adv: checkpatch - Please don't use multiple blank lines Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 73209f3..4dd7c40 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -26,7 +26,6 @@ #include "bat_algo.h" #include "network-coding.h" - /** * enum batadv_dup_status - duplicate status * @BATADV_NO_DUP: the packet is a duplicate @@ -1362,7 +1361,6 @@ out: return ret; } - /** * batadv_iv_ogm_process_per_outif - process a batman iv OGM for an outgoing if * @skb: the skb containing the OGM diff --git a/net/batman-adv/bitarray.c b/net/batman-adv/bitarray.c index 9586750..e3da07a 100644 --- a/net/batman-adv/bitarray.c +++ b/net/batman-adv/bitarray.c @@ -29,7 +29,6 @@ static void batadv_bitmap_shift_left(unsigned long *seq_bits, int32_t n) bitmap_shift_left(seq_bits, seq_bits, n, BATADV_TQ_LOCAL_WINDOW_SIZE); } - /* receive and process one packet within the sequence number window. * * returns: diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 0f0ca43..4fc6cab 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -69,7 +69,6 @@ static inline uint32_t batadv_choose_backbone_gw(const void *data, return hash % size; } - /* compares address and vid of two backbone gws */ static int batadv_compare_backbone_gw(const struct hlist_node *node, const void *data2) @@ -662,7 +661,6 @@ static int batadv_handle_announce(struct batadv_priv *bat_priv, if (unlikely(!backbone_gw)) return 1; - /* handle as ANNOUNCE frame */ backbone_gw->lasttime = jiffies; crc = ntohs(*((__be16 *)(&an_addr[4]))); @@ -849,7 +847,6 @@ static int batadv_check_claim_group(struct batadv_priv *bat_priv, return 2; } - /** * batadv_bla_process_claim * @bat_priv: the bat priv with all the soft interface information @@ -1347,8 +1344,6 @@ out: return ret; } - - /** * batadv_bla_is_backbone_gw_orig * @bat_priv: the bat priv with all the soft interface information @@ -1390,7 +1385,6 @@ bool batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig, return false; } - /** * batadv_bla_is_backbone_gw * @skb: the frame to be checked @@ -1480,7 +1474,6 @@ int batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, if (!atomic_read(&bat_priv->bridge_loop_avoidance)) goto allow; - if (unlikely(atomic_read(&bat_priv->bla.num_requests))) /* don't allow broadcasts while requests are in flight */ if (is_multicast_ether_addr(ethhdr->h_dest) && is_bcast) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 00f9e14..3d1dcaa 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -23,7 +23,6 @@ #include "hard-interface.h" #include "soft-interface.h" - /** * batadv_frag_clear_chain - delete entries in the fragment buffer chain * @head: head of chain with entries. diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 2cdd25a..d4079cd 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -41,7 +41,6 @@ #include "network-coding.h" #include "fragmentation.h" - /* List manipulations on hardif_list have to be rtnl_lock()'ed, * list traversals just rcu-locked */ diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index bea8198..90e805a 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -797,7 +797,6 @@ batadv_purge_orig_ifinfo(struct batadv_priv *bat_priv, return ifinfo_purged; } - /** * batadv_purge_orig_neighbors - purges neighbors from originator * @bat_priv: the bat priv with all the soft interface information diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h index db3a9ed..aa4a436 100644 --- a/net/batman-adv/originator.h +++ b/net/batman-adv/originator.h @@ -70,7 +70,6 @@ batadv_orig_node_vlan_get(struct batadv_orig_node *orig_node, unsigned short vid); void batadv_orig_node_vlan_free_ref(struct batadv_orig_node_vlan *orig_vlan); - /* hashfunction to choose an entry in a hash table of given size * hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 6648f32..139d2f6 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -292,7 +292,6 @@ out: return ret; } - int batadv_recv_icmp_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if) { diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 5467955..5ec31d7 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -36,7 +36,6 @@ #include "bridge_loop_avoidance.h" #include "network-coding.h" - static int batadv_get_settings(struct net_device *dev, struct ethtool_cmd *cmd); static void batadv_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info); diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c index f40cb04..a75dc12 100644 --- a/net/batman-adv/sysfs.c +++ b/net/batman-adv/sysfs.c @@ -151,7 +151,6 @@ ssize_t batadv_show_##_name(struct kobject *kobj, \ static BATADV_ATTR(_name, _mode, batadv_show_##_name, \ batadv_store_##_name) - #define BATADV_ATTR_SIF_STORE_UINT(_name, _min, _max, _post_func) \ ssize_t batadv_store_##_name(struct kobject *kobj, \ struct attribute *attr, char *buff, \ diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 38a804e..84e6f01 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -1780,7 +1780,6 @@ static void batadv_tt_global_del(struct batadv_priv *bat_priv, batadv_tt_global_del_roaming(bat_priv, tt_global_entry, orig_node, message); - out: if (tt_global_entry) batadv_tt_global_entry_free_ref(tt_global_entry); -- cgit v0.10.2 From 32db6aaafef317be799265a5be1840b28bee8c78 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 1 Sep 2014 14:37:29 +0200 Subject: batman-adv: checkpatch - remove unnecessary parentheses Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 4dd7c40..00e00e0 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -878,7 +878,7 @@ batadv_iv_ogm_slide_own_bcast_window(struct batadv_hard_iface *hard_iface) hlist_for_each_entry_rcu(orig_node, head, hash_entry) { spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock); word_index = hard_iface->if_num * BATADV_NUM_WORDS; - word = &(orig_node->bat_iv.bcast_own[word_index]); + word = &orig_node->bat_iv.bcast_own[word_index]; batadv_bit_get_packet(bat_priv, word, 1, 0); if_num = hard_iface->if_num; @@ -1663,7 +1663,7 @@ static void batadv_iv_ogm_process(const struct sk_buff *skb, int ogm_offset, offset = if_num * BATADV_NUM_WORDS; spin_lock_bh(&orig_neigh_node->bat_iv.ogm_cnt_lock); - word = &(orig_neigh_node->bat_iv.bcast_own[offset]); + word = &orig_neigh_node->bat_iv.bcast_own[offset]; bit_pos = if_incoming_seqno - 2; bit_pos -= ntohl(ogm_packet->seqno); batadv_set_bit(word, bit_pos); -- cgit v0.10.2 From e0d9677ea387e386e9ce983f287d99b278ea4930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Hundeb=C3=B8ll?= Date: Wed, 17 Sep 2014 08:56:19 +0200 Subject: batman-adv: clear control block of received socket buffers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since other network components (and some drivers) uses the control block provided in skb's, the network coding feature might wrongly assume that an SKB has been decoded, and thus not try to code it with another packet again. This happens for instance when batman-adv is running on a bridge device. Fix this by clearing the control block for every received SKB. Introduced by 3c12de9a5c756b23fe7c9ab332474ece1568914c ("batman-adv: network coding - code and transmit packets if possible") Signed-off-by: Martin Hundebøll Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index d4079cd..3bcd847 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -402,6 +402,9 @@ int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev, goto err_free; } + /* reset control block to avoid left overs from previous users */ + memset(skb->cb, 0, sizeof(struct batadv_skb_cb)); + /* all receive handlers return whether they received or reused * the supplied skb. if not, we have to free the skb. */ -- cgit v0.10.2 From 3f68785e614bf327d63395e03a3ebfd1d847331a Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Sun, 2 Nov 2014 11:29:56 +0100 Subject: batman-adv: fix misspelled words Reported-by: checkpatch Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 4fc6cab..ac4b96e 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -244,7 +244,7 @@ batadv_bla_del_backbone_claims(struct batadv_bla_backbone_gw *backbone_gw) spin_unlock_bh(list_lock); } - /* all claims gone, intialize CRC */ + /* all claims gone, initialize CRC */ backbone_gw->crc = BATADV_BLA_CRC_INIT; } @@ -1328,7 +1328,7 @@ int batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv, goto out; } /* not found, add a new entry (overwrite the oldest entry) - * and allow it, its the first occurence. + * and allow it, its the first occurrence. */ curr = (bat_priv->bla.bcast_duplist_curr + BATADV_DUPLIST_SIZE - 1); curr %= BATADV_DUPLIST_SIZE; diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 3bcd847..12fc77b 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -798,7 +798,7 @@ void batadv_tvlv_container_register(struct batadv_priv *bat_priv, } /** - * batadv_tvlv_realloc_packet_buff - reallocate packet buffer to accomodate + * batadv_tvlv_realloc_packet_buff - reallocate packet buffer to accommodate * requested packet size * @packet_buff: packet buffer * @packet_buff_len: packet buffer size diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h index facd1fe..b81fbbf 100644 --- a/net/batman-adv/packet.h +++ b/net/batman-adv/packet.h @@ -377,7 +377,7 @@ struct batadv_frag_packet { uint8_t reserved:4; uint8_t no:4; #else -#error "unknown bitfield endianess" +#error "unknown bitfield endianness" #endif uint8_t dest[ETH_ALEN]; uint8_t orig[ETH_ALEN]; @@ -453,7 +453,7 @@ struct batadv_coded_packet { * @src: address of the source * @dst: address of the destination * @tvlv_len: length of tvlv data following the unicast tvlv header - * @align: 2 bytes to align the header to a 4 byte boundry + * @align: 2 bytes to align the header to a 4 byte boundary */ struct batadv_unicast_tvlv_packet { uint8_t packet_type; diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 139d2f6..da83982 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -456,7 +456,7 @@ batadv_find_router(struct batadv_priv *bat_priv, * the last chosen bonding candidate (next_candidate). If no such * router is found, use the first candidate found (the previously * chosen bonding candidate might have been the last one in the list). - * If this can't be found either, return the previously choosen + * If this can't be found either, return the previously chosen * router - obviously there are no other candidates. */ rcu_read_lock(); diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 84e6f01..07b263a 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -2852,7 +2852,7 @@ static void batadv_tt_update_changes(struct batadv_priv *bat_priv, /** * batadv_is_my_client - check if a client is served by the local node * @bat_priv: the bat priv with all the soft interface information - * @addr: the mac adress of the client to check + * @addr: the mac address of the client to check * @vid: VLAN identifier * * Returns true if the client is served by this node, false otherwise. diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 462a70c..9398c3f 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -968,7 +968,7 @@ struct batadv_tt_orig_list_entry { }; /** - * struct batadv_tt_change_node - structure for tt changes occured + * struct batadv_tt_change_node - structure for tt changes occurred * @list: list node for batadv_priv_tt::changes_list * @change: holds the actual translation table diff data */ -- cgit v0.10.2 From dcba6c9b45a8c91ebb1b16a3595011404a19cf98 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Tue, 30 Dec 2014 02:20:14 +0100 Subject: batman-adv: Start new development cycle Signed-off-by: Simon Wunderlich Signed-off-by: Antonio Quartulli diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 18286f8..4d23188 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -24,7 +24,7 @@ #define BATADV_DRIVER_DEVICE "batman-adv" #ifndef BATADV_SOURCE_VERSION -#define BATADV_SOURCE_VERSION "2014.4.0" +#define BATADV_SOURCE_VERSION "2015.0" #endif /* B.A.T.M.A.N. parameters */ -- cgit v0.10.2 From 432fbf3c6a856726f21011edfd53311bf373dba9 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 4 Sep 2014 17:12:49 -0700 Subject: documentation: Record limitations of bitfields and small variables This commit documents the fact that it is not safe to use bitfields as shared variables in synchronization algorithms. It also documents that CPUs must be able to concurrently load from and store to adjacent one-byte and two-byte variables, which is in fact required by the C11 standard (Section 3.14). Signed-off-by: Paul E. McKenney diff --git a/Documentation/memory-barriers.txt b/Documentation/memory-barriers.txt index 70a09f8..a10f7cd 100644 --- a/Documentation/memory-barriers.txt +++ b/Documentation/memory-barriers.txt @@ -269,6 +269,50 @@ And there are a number of things that _must_ or _must_not_ be assumed: STORE *(A + 4) = Y; STORE *A = X; STORE {*A, *(A + 4) } = {X, Y}; +And there are anti-guarantees: + + (*) These guarantees do not apply to bitfields, because compilers often + generate code to modify these using non-atomic read-modify-write + sequences. Do not attempt to use bitfields to synchronize parallel + algorithms. + + (*) Even in cases where bitfields are protected by locks, all fields + in a given bitfield must be protected by one lock. If two fields + in a given bitfield are protected by different locks, the compiler's + non-atomic read-modify-write sequences can cause an update to one + field to corrupt the value of an adjacent field. + + (*) These guarantees apply only to properly aligned and sized scalar + variables. "Properly sized" currently means variables that are + the same size as "char", "short", "int" and "long". "Properly + aligned" means the natural alignment, thus no constraints for + "char", two-byte alignment for "short", four-byte alignment for + "int", and either four-byte or eight-byte alignment for "long", + on 32-bit and 64-bit systems, respectively. Note that these + guarantees were introduced into the C11 standard, so beware when + using older pre-C11 compilers (for example, gcc 4.6). The portion + of the standard containing this guarantee is Section 3.14, which + defines "memory location" as follows: + + memory location + either an object of scalar type, or a maximal sequence + of adjacent bit-fields all having nonzero width + + NOTE 1: Two threads of execution can update and access + separate memory locations without interfering with + each other. + + NOTE 2: A bit-field and an adjacent non-bit-field member + are in separate memory locations. The same applies + to two bit-fields, if one is declared inside a nested + structure declaration and the other is not, or if the two + are separated by a zero-length bit-field declaration, + or if they are separated by a non-bit-field member + declaration. It is not safe to concurrently update two + bit-fields in the same structure if all members declared + between them are also bit-fields, no matter what the + sizes of those intervening bit-fields happen to be. + ========================= WHAT ARE MEMORY BARRIERS? -- cgit v0.10.2 From d87510c5a6e34e84835814b7b8c319918171276e Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Sun, 28 Dec 2014 01:11:16 -0800 Subject: documentation: Fix smp typo in memory-barriers.txt Signed-off-by: Davidlohr Bueso Signed-off-by: Paul E. McKenney diff --git a/Documentation/memory-barriers.txt b/Documentation/memory-barriers.txt index a10f7cd..ca2387e 100644 --- a/Documentation/memory-barriers.txt +++ b/Documentation/memory-barriers.txt @@ -794,7 +794,7 @@ In summary: However, they do -not- guarantee any other sort of ordering: Not prior loads against later loads, nor prior stores against later anything. If you need these other forms of ordering, - use smb_rmb(), smp_wmb(), or, in the case of prior stores and + use smp_rmb(), smp_wmb(), or, in the case of prior stores and later loads, smp_mb(). (*) If both legs of the "if" statement begin with identical stores -- cgit v0.10.2 From c8935ef0f2a8e8c5c440c62fb194d40c4415f456 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 7 Jan 2015 16:37:25 +0100 Subject: spi: sh-msiof: Use async pm_runtime_put() in sh_msiof_spi_setup() There's no need to use the synchronous version. Reported-by: Sergei Shtylyov Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index c58c249..9a884e6 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -546,7 +546,7 @@ static int sh_msiof_spi_setup(struct spi_device *spi) gpio_set_value(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); - pm_runtime_put_sync(&p->pdev->dev); + pm_runtime_put(&p->pdev->dev); return 0; } -- cgit v0.10.2 From f81677b4d1acc0e7cd74a43bfd9900d9512b90ae Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Wed, 7 Jan 2015 22:07:05 +0800 Subject: ASoC: Intel: Add NULL checks for the stream pointer We should not send IPC stream commands to FW when the stream is NULL, dereference the NULL pointer may also occur without precheck. Here add NULL pointer checks for these stream APIs. Signed-off-by: Jie Yang Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c index 3f8c482..5bf1404 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/sst-haswell-ipc.c @@ -1228,6 +1228,11 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream) struct sst_dsp *sst = hsw->dsp; unsigned long flags; + if (!stream) { + dev_warn(hsw->dev, "warning: stream is NULL, no stream to free, ignore it.\n"); + return 0; + } + /* dont free DSP streams that are not commited */ if (!stream->commited) goto out; @@ -1415,6 +1420,16 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream) u32 header; int ret; + if (!stream) { + dev_warn(hsw->dev, "warning: stream is NULL, no stream to commit, ignore it.\n"); + return 0; + } + + if (stream->commited) { + dev_warn(hsw->dev, "warning: stream is already committed, ignore it.\n"); + return 0; + } + trace_ipc_request("stream alloc", stream->host_id); header = IPC_GLB_TYPE(IPC_GLB_ALLOCATE_STREAM); @@ -1519,6 +1534,11 @@ int sst_hsw_stream_pause(struct sst_hsw *hsw, struct sst_hsw_stream *stream, { int ret; + if (!stream) { + dev_warn(hsw->dev, "warning: stream is NULL, no stream to pause, ignore it.\n"); + return 0; + } + trace_ipc_request("stream pause", stream->reply.stream_hw_id); ret = sst_hsw_stream_operations(hsw, IPC_STR_PAUSE, @@ -1535,6 +1555,11 @@ int sst_hsw_stream_resume(struct sst_hsw *hsw, struct sst_hsw_stream *stream, { int ret; + if (!stream) { + dev_warn(hsw->dev, "warning: stream is NULL, no stream to resume, ignore it.\n"); + return 0; + } + trace_ipc_request("stream resume", stream->reply.stream_hw_id); ret = sst_hsw_stream_operations(hsw, IPC_STR_RESUME, @@ -1550,6 +1575,11 @@ int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream) { int ret, tries = 10; + if (!stream) { + dev_warn(hsw->dev, "warning: stream is NULL, no stream to reset, ignore it.\n"); + return 0; + } + /* dont reset streams that are not commited */ if (!stream->commited) return 0; -- cgit v0.10.2 From b7ed9f1d26f04272127a98248338177e5bac233e Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 7 Jan 2015 10:19:06 +0800 Subject: ASoC: rt5670: redefine ASRC control registers 0x84 and 0x85 The previous definition of registers 0x84 and 0x85 doesn't match the datasheet. So this patch removes the wrong definition and writes a new one for the two registers. Signed-off-by: Bard Liao Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h index d11b9c2..ec38d8f 100644 --- a/sound/soc/codecs/rt5670.h +++ b/sound/soc/codecs/rt5670.h @@ -1023,50 +1023,33 @@ #define RT5670_DMIC_2_M_NOR (0x0 << 8) #define RT5670_DMIC_2_M_ASYN (0x1 << 8) +/* ASRC clock source selection (0x84, 0x85) */ +#define RT5670_CLK_SEL_SYS (0x0) +#define RT5670_CLK_SEL_I2S1_ASRC (0x1) +#define RT5670_CLK_SEL_I2S2_ASRC (0x2) +#define RT5670_CLK_SEL_I2S3_ASRC (0x3) +#define RT5670_CLK_SEL_SYS2 (0x5) +#define RT5670_CLK_SEL_SYS3 (0x6) + /* ASRC Control 2 (0x84) */ -#define RT5670_MDA_L_M_MASK (0x1 << 15) -#define RT5670_MDA_L_M_SFT 15 -#define RT5670_MDA_L_M_NOR (0x0 << 15) -#define RT5670_MDA_L_M_ASYN (0x1 << 15) -#define RT5670_MDA_R_M_MASK (0x1 << 14) -#define RT5670_MDA_R_M_SFT 14 -#define RT5670_MDA_R_M_NOR (0x0 << 14) -#define RT5670_MDA_R_M_ASYN (0x1 << 14) -#define RT5670_MAD_L_M_MASK (0x1 << 13) -#define RT5670_MAD_L_M_SFT 13 -#define RT5670_MAD_L_M_NOR (0x0 << 13) -#define RT5670_MAD_L_M_ASYN (0x1 << 13) -#define RT5670_MAD_R_M_MASK (0x1 << 12) -#define RT5670_MAD_R_M_SFT 12 -#define RT5670_MAD_R_M_NOR (0x0 << 12) -#define RT5670_MAD_R_M_ASYN (0x1 << 12) -#define RT5670_ADC_M_MASK (0x1 << 11) -#define RT5670_ADC_M_SFT 11 -#define RT5670_ADC_M_NOR (0x0 << 11) -#define RT5670_ADC_M_ASYN (0x1 << 11) -#define RT5670_STO_DAC_M_MASK (0x1 << 5) -#define RT5670_STO_DAC_M_SFT 5 -#define RT5670_STO_DAC_M_NOR (0x0 << 5) -#define RT5670_STO_DAC_M_ASYN (0x1 << 5) -#define RT5670_I2S1_R_D_MASK (0x1 << 4) -#define RT5670_I2S1_R_D_SFT 4 -#define RT5670_I2S1_R_D_DIS (0x0 << 4) -#define RT5670_I2S1_R_D_EN (0x1 << 4) -#define RT5670_I2S2_R_D_MASK (0x1 << 3) -#define RT5670_I2S2_R_D_SFT 3 -#define RT5670_I2S2_R_D_DIS (0x0 << 3) -#define RT5670_I2S2_R_D_EN (0x1 << 3) -#define RT5670_PRE_SCLK_MASK (0x3) -#define RT5670_PRE_SCLK_SFT 0 -#define RT5670_PRE_SCLK_512 (0x0) -#define RT5670_PRE_SCLK_1024 (0x1) -#define RT5670_PRE_SCLK_2048 (0x2) +#define RT5670_DA_STO_CLK_SEL_MASK (0xf << 12) +#define RT5670_DA_STO_CLK_SEL_SFT 12 +#define RT5670_DA_MONOL_CLK_SEL_MASK (0xf << 8) +#define RT5670_DA_MONOL_CLK_SEL_SFT 8 +#define RT5670_DA_MONOR_CLK_SEL_MASK (0xf << 4) +#define RT5670_DA_MONOR_CLK_SEL_SFT 4 +#define RT5670_AD_STO1_CLK_SEL_MASK (0xf << 0) +#define RT5670_AD_STO1_CLK_SEL_SFT 0 /* ASRC Control 3 (0x85) */ -#define RT5670_I2S1_RATE_MASK (0xf << 12) -#define RT5670_I2S1_RATE_SFT 12 -#define RT5670_I2S2_RATE_MASK (0xf << 8) -#define RT5670_I2S2_RATE_SFT 8 +#define RT5670_UP_CLK_SEL_MASK (0xf << 12) +#define RT5670_UP_CLK_SEL_SFT 12 +#define RT5670_DOWN_CLK_SEL_MASK (0xf << 8) +#define RT5670_DOWN_CLK_SEL_SFT 8 +#define RT5670_AD_MONOL_CLK_SEL_MASK (0xf << 4) +#define RT5670_AD_MONOL_CLK_SEL_SFT 4 +#define RT5670_AD_MONOR_CLK_SEL_MASK (0xf << 0) +#define RT5670_AD_MONOR_CLK_SEL_SFT 0 /* ASRC Control 4 (0x89) */ #define RT5670_I2S1_PD_MASK (0x7 << 12) -- cgit v0.10.2 From e8c47ba3cadcc3649f18e4710804bb6c3791eac2 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Wed, 7 Jan 2015 10:19:12 +0800 Subject: ASoC: rt5670: add API to select ASRC clock source When codec is in slave mode, ASRC can suppress noise for asynchronous MCLK and LRCLK or special I2S format. This patch defines an API to select the clock source for specified filters. And the codec driver will turn on ASRC for these filters if ASRC is selected as their clock source. Signed-off-by: Bard Liao Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 0a027bc..0632b74 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -590,6 +590,89 @@ static int can_use_asrc(struct snd_soc_dapm_widget *source, return 0; } + +/** + * rt5670_sel_asrc_clk_src - select ASRC clock source for a set of filters + * @codec: SoC audio codec device. + * @filter_mask: mask of filters. + * @clk_src: clock source + * + * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5670 can + * only support standard 32fs or 64fs i2s format, ASRC should be enabled to + * support special i2s clock format such as Intel's 100fs(100 * sampling rate). + * ASRC function will track i2s clock and generate a corresponding system clock + * for codec. This function provides an API to select the clock source for a + * set of filters specified by the mask. And the codec driver will turn on ASRC + * for these filters if ASRC is selected as their clock source. + */ +int rt5670_sel_asrc_clk_src(struct snd_soc_codec *codec, + unsigned int filter_mask, unsigned int clk_src) +{ + unsigned int asrc2_mask = 0, asrc2_value = 0; + unsigned int asrc3_mask = 0, asrc3_value = 0; + + if (clk_src > RT5670_CLK_SEL_SYS3) + return -EINVAL; + + if (filter_mask & RT5670_DA_STEREO_FILTER) { + asrc2_mask |= RT5670_DA_STO_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5670_DA_STO_CLK_SEL_MASK) + | (clk_src << RT5670_DA_STO_CLK_SEL_SFT); + } + + if (filter_mask & RT5670_DA_MONO_L_FILTER) { + asrc2_mask |= RT5670_DA_MONOL_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5670_DA_MONOL_CLK_SEL_MASK) + | (clk_src << RT5670_DA_MONOL_CLK_SEL_SFT); + } + + if (filter_mask & RT5670_DA_MONO_R_FILTER) { + asrc2_mask |= RT5670_DA_MONOR_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5670_DA_MONOR_CLK_SEL_MASK) + | (clk_src << RT5670_DA_MONOR_CLK_SEL_SFT); + } + + if (filter_mask & RT5670_AD_STEREO_FILTER) { + asrc2_mask |= RT5670_AD_STO1_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5670_AD_STO1_CLK_SEL_MASK) + | (clk_src << RT5670_AD_STO1_CLK_SEL_SFT); + } + + if (filter_mask & RT5670_AD_MONO_L_FILTER) { + asrc3_mask |= RT5670_AD_MONOL_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5670_AD_MONOL_CLK_SEL_MASK) + | (clk_src << RT5670_AD_MONOL_CLK_SEL_SFT); + } + + if (filter_mask & RT5670_AD_MONO_R_FILTER) { + asrc3_mask |= RT5670_AD_MONOR_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5670_AD_MONOR_CLK_SEL_MASK) + | (clk_src << RT5670_AD_MONOR_CLK_SEL_SFT); + } + + if (filter_mask & RT5670_UP_RATE_FILTER) { + asrc3_mask |= RT5670_UP_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5670_UP_CLK_SEL_MASK) + | (clk_src << RT5670_UP_CLK_SEL_SFT); + } + + if (filter_mask & RT5670_DOWN_RATE_FILTER) { + asrc3_mask |= RT5670_DOWN_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5670_DOWN_CLK_SEL_MASK) + | (clk_src << RT5670_DOWN_CLK_SEL_SFT); + } + + if (asrc2_mask) + snd_soc_update_bits(codec, RT5670_ASRC_2, + asrc2_mask, asrc2_value); + + if (asrc3_mask) + snd_soc_update_bits(codec, RT5670_ASRC_3, + asrc3_mask, asrc3_value); + return 0; +} +EXPORT_SYMBOL_GPL(rt5670_sel_asrc_clk_src); + /* Digital Mixer */ static const struct snd_kcontrol_new rt5670_sto1_adc_l_mix[] = { SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO1_ADC_MIXER, diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h index ec38d8f..21f8e18 100644 --- a/sound/soc/codecs/rt5670.h +++ b/sound/soc/codecs/rt5670.h @@ -1966,6 +1966,21 @@ enum { RT5670_DMIC_DATA_GPIO5, }; +/* filter mask */ +enum { + RT5670_DA_STEREO_FILTER = 0x1, + RT5670_DA_MONO_L_FILTER = (0x1 << 1), + RT5670_DA_MONO_R_FILTER = (0x1 << 2), + RT5670_AD_STEREO_FILTER = (0x1 << 3), + RT5670_AD_MONO_L_FILTER = (0x1 << 4), + RT5670_AD_MONO_R_FILTER = (0x1 << 5), + RT5670_UP_RATE_FILTER = (0x1 << 6), + RT5670_DOWN_RATE_FILTER = (0x1 << 7), +}; + +int rt5670_sel_asrc_clk_src(struct snd_soc_codec *codec, + unsigned int filter_mask, unsigned int clk_src); + struct rt5670_priv { struct snd_soc_codec *codec; struct rt5670_platform_data pdata; -- cgit v0.10.2 From eb55fab997c0bf1b1ddf0a7199da0ceb18432b96 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Wed, 7 Jan 2015 10:19:23 +0800 Subject: ASoC: Intel: Select RT5672 ASRC clock source on Cherrytrail and Braswell On Cherrytrail and Braswell, the I2S BCLK is 100FS which cannot be supported by RT5672 in slave mode and can cause noise. This patch selects codec ASRC clock source to track I2S1 clock so that codec ASRC can be enabled to suppress the noise. Signed-off-by: Mengdong Lin Reviewed-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/intel/cht_bsw_rt5672.c b/sound/soc/intel/cht_bsw_rt5672.c index a406c610..ff01662 100644 --- a/sound/soc/intel/cht_bsw_rt5672.c +++ b/sound/soc/intel/cht_bsw_rt5672.c @@ -140,6 +140,7 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) { int ret; struct snd_soc_dai *codec_dai = runtime->codec_dai; + struct snd_soc_codec *codec = codec_dai->codec; /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); @@ -148,6 +149,19 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) return ret; } + /* Select codec ASRC clock source to track I2S1 clock, because codec + * is in slave mode and 100fs I2S format (BCLK = 100 * LRCLK) cannot + * be supported by RT5672. Otherwise, ASRC will be disabled and cause + * noise. + */ + rt5670_sel_asrc_clk_src(codec, + RT5670_DA_STEREO_FILTER + | RT5670_DA_MONO_L_FILTER + | RT5670_DA_MONO_R_FILTER + | RT5670_AD_STEREO_FILTER + | RT5670_AD_MONO_L_FILTER + | RT5670_AD_MONO_R_FILTER, + RT5670_CLK_SEL_I2S1_ASRC); return 0; } -- cgit v0.10.2 From 6ada1fc0e1c4775de0e043e1bd3ae9d065491aa5 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 3 Dec 2014 19:22:48 -0500 Subject: time: settimeofday: Validate the values of tv from user An unvalidated user input is multiplied by a constant, which can result in an undefined behaviour for large values. While this is validated later, we should avoid triggering undefined behaviour. Cc: Thomas Gleixner Cc: Ingo Molnar Cc: stable Signed-off-by: Sasha Levin [jstultz: include trivial milisecond->microsecond correction noticed by Andy] Signed-off-by: John Stultz diff --git a/include/linux/time.h b/include/linux/time.h index 8c42cf8..5989b0e 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -99,6 +99,19 @@ static inline bool timespec_valid_strict(const struct timespec *ts) return true; } +static inline bool timeval_valid(const struct timeval *tv) +{ + /* Dates before 1970 are bogus */ + if (tv->tv_sec < 0) + return false; + + /* Can't have more microseconds then a second */ + if (tv->tv_usec < 0 || tv->tv_usec >= USEC_PER_SEC) + return false; + + return true; +} + extern struct timespec timespec_trunc(struct timespec t, unsigned gran); #define CURRENT_TIME (current_kernel_time()) diff --git a/kernel/time/time.c b/kernel/time/time.c index a9ae20f..22d5d3b 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -196,6 +196,10 @@ SYSCALL_DEFINE2(settimeofday, struct timeval __user *, tv, if (tv) { if (copy_from_user(&user_tv, tv, sizeof(*tv))) return -EFAULT; + + if (!timeval_valid(&user_tv)) + return -EINVAL; + new_ts.tv_sec = user_tv.tv_sec; new_ts.tv_nsec = user_tv.tv_usec * NSEC_PER_USEC; } -- cgit v0.10.2 From 5e5aeb4367b450a28f447f6d5ab57d8f2ab16a5f Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 3 Dec 2014 19:25:05 -0500 Subject: time: adjtimex: Validate the ADJ_FREQUENCY values Verify that the frequency value from userspace is valid and makes sense. Unverified values can cause overflows later on. Cc: Thomas Gleixner Cc: Ingo Molnar Cc: stable Signed-off-by: Sasha Levin [jstultz: Fix up bug for negative values and drop redunent cap check] Signed-off-by: John Stultz diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index 87a346f..28bf91c 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -633,6 +633,13 @@ int ntp_validate_timex(struct timex *txc) if ((txc->modes & ADJ_SETOFFSET) && (!capable(CAP_SYS_TIME))) return -EPERM; + if (txc->modes & ADJ_FREQUENCY) { + if (LONG_MIN / PPM_SCALE > txc->freq) + return -EINVAL; + if (LONG_MAX / PPM_SCALE < txc->freq) + return -EINVAL; + } + return 0; } -- cgit v0.10.2 From 3dbb3b98e854bceed38fbde930fb8cf8701def73 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Jan 2015 16:56:54 +0200 Subject: spi: dw: amend warning message In case of warning message in ->probe() we have to use HW device name instead of master because last is not defined yet. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 1a0f266..8edcd1b 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -673,7 +673,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) if (dws->dma_ops && dws->dma_ops->dma_init) { ret = dws->dma_ops->dma_init(dws); if (ret) { - dev_warn(&master->dev, "DMA init failed\n"); + dev_warn(dev, "DMA init failed\n"); dws->dma_inited = 0; } } -- cgit v0.10.2 From 30b4b703a5d5aa9977f5ad3aba3d77b6b8d0d255 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Jan 2015 16:56:55 +0200 Subject: spi: dw: print debug message with FIFO size When autodetection is used it would be useful to know what the FIFO size is. The patch adds a debug message for this purpose. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 8edcd1b..5a97a62 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -608,7 +608,7 @@ static void dw_spi_cleanup(struct spi_device *spi) } /* Restart the controller, disable all interrupts, clean rx fifo */ -static void spi_hw_init(struct dw_spi *dws) +static void spi_hw_init(struct device *dev, struct dw_spi *dws) { spi_enable_chip(dws, 0); spi_mask_intr(dws, 0xff); @@ -626,9 +626,10 @@ static void spi_hw_init(struct dw_spi *dws) if (fifo != dw_readw(dws, DW_SPI_TXFLTR)) break; } + dw_writew(dws, DW_SPI_TXFLTR, 0); dws->fifo_len = (fifo == 2) ? 0 : fifo - 1; - dw_writew(dws, DW_SPI_TXFLTR, 0); + dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len); } } @@ -668,7 +669,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) master->dev.of_node = dev->of_node; /* Basic HW init */ - spi_hw_init(dws); + spi_hw_init(dev, dws); if (dws->dma_ops && dws->dma_ops->dma_init) { ret = dws->dma_ops->dma_init(dws); @@ -731,7 +732,7 @@ int dw_spi_resume_host(struct dw_spi *dws) { int ret; - spi_hw_init(dws); + spi_hw_init(&dws->master->dev, dws); ret = spi_master_resume(dws->master); if (ret) dev_err(&dws->master->dev, "fail to start queue (%d)\n", ret); -- cgit v0.10.2 From d58cf5ff6500522880683ce90d9caa79af385ed8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Jan 2015 17:15:00 +0200 Subject: spi: dw-pci: describe Intel MID controllers better There are more that one SPI controller on the Intel MID boards. This patch describes the status and IDs of them. From now on we also have to care about bus number that must be unique per host. According to the specification the SPI1 has 5 bits for chip selects and SPI2 only 2 bits. The patch makes it depend to PCI ID. The first controller (SPI1) is DMA capable, meanwhile SPI2 can share same channels (via software switch) such functionality is not in the scope of this patch. Thus, attempt to init DMA for SPI2 will always fail for now. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown diff --git a/arch/x86/pci/intel_mid_pci.c b/arch/x86/pci/intel_mid_pci.c index 44b9271..852aa4c 100644 --- a/arch/x86/pci/intel_mid_pci.c +++ b/arch/x86/pci/intel_mid_pci.c @@ -293,7 +293,6 @@ static void mrst_power_off_unused_dev(struct pci_dev *dev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0801, mrst_power_off_unused_dev); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0809, mrst_power_off_unused_dev); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x080C, mrst_power_off_unused_dev); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0812, mrst_power_off_unused_dev); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0815, mrst_power_off_unused_dev); /* diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index a67d37c..47a8e65 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -270,8 +270,6 @@ int dw_spi_mid_init(struct dw_spi *dws) dws->max_freq = MRST_SPI_CLK_BASE / (clk_cdiv + 1); iounmap(clk_reg); - dws->num_cs = 16; - #ifdef CONFIG_SPI_DW_MID_DMA dws->dma_priv = kzalloc(sizeof(struct mid_dma), GFP_KERNEL); if (!dws->dma_priv) diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index ba68da1..64f3efa 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -30,10 +30,20 @@ struct dw_spi_pci { struct spi_pci_desc { int (*setup)(struct dw_spi *); + u16 num_cs; + u16 bus_num; }; -static struct spi_pci_desc spi_pci_mid_desc = { +static struct spi_pci_desc spi_pci_mid_desc_1 = { .setup = dw_spi_mid_init, + .num_cs = 32, + .bus_num = 0, +}; + +static struct spi_pci_desc spi_pci_mid_desc_2 = { + .setup = dw_spi_mid_init, + .num_cs = 4, + .bus_num = 1, }; static int spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -65,18 +75,23 @@ static int spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dws->regs = pcim_iomap_table(pdev)[pci_bar]; - dws->bus_num = 0; - dws->num_cs = 4; dws->irq = pdev->irq; /* * Specific handling for paltforms, like dma setup, * clock rate, FIFO depth. */ - if (desc && desc->setup) { - ret = desc->setup(dws); - if (ret) - return ret; + if (desc) { + if (desc->setup) { + ret = desc->setup(dws); + if (ret) + return ret; + } + + dws->num_cs = desc->num_cs; + dws->bus_num = desc->bus_num; + } else { + return -ENODEV; } ret = dw_spi_add_host(&pdev->dev, dws); @@ -121,7 +136,14 @@ static SIMPLE_DEV_PM_OPS(dw_spi_pm_ops, spi_suspend, spi_resume); static const struct pci_device_id pci_ids[] = { /* Intel MID platform SPI controller 0 */ - { PCI_VDEVICE(INTEL, 0x0800), (kernel_ulong_t)&spi_pci_mid_desc}, + /* + * The access to the device 8086:0801 is disabled by HW, since it's + * exclusively used by SCU to communicate with MSIC. + */ + /* Intel MID platform SPI controller 1 */ + { PCI_VDEVICE(INTEL, 0x0800), (kernel_ulong_t)&spi_pci_mid_desc_1}, + /* Intel MID platform SPI controller 2 */ + { PCI_VDEVICE(INTEL, 0x0812), (kernel_ulong_t)&spi_pci_mid_desc_2}, {}, }; -- cgit v0.10.2 From ce64c8b9cf5be2a93508af4667110dbe90904557 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 6 Jan 2015 15:17:20 +0100 Subject: ASoC: Add helper function for changing the DAI link format For some setups it is necessary to change the DAI link format at runtime. This patch factors out the code that does the initial static DAI link format configuration into a separate helper function which can be used board drivers as well. This allows board drivers that have to change the DAI link format at runtime to reuse it instead of having to manually change the format on all DAIs. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index b4fca9a..edd4a0a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -429,6 +429,9 @@ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd); void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream); void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream); +int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, + unsigned int dai_fmt); + /* Utility functions to get clock rates from various things */ int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots); int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 985052b..80ea358 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1427,11 +1427,75 @@ static int snd_soc_init_codec_cache(struct snd_soc_codec *codec) return 0; } +/** + * snd_soc_runtime_set_dai_fmt() - Change DAI link format for a ASoC runtime + * @rtd: The runtime for which the DAI link format should be changed + * @dai_fmt: The new DAI link format + * + * This function updates the DAI link format for all DAIs connected to the DAI + * link for the specified runtime. + * + * Note: For setups with a static format set the dai_fmt field in the + * corresponding snd_dai_link struct instead of using this function. + * + * Returns 0 on success, otherwise a negative error code. + */ +int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, + unsigned int dai_fmt) +{ + struct snd_soc_dai **codec_dais = rtd->codec_dais; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + unsigned int i; + int ret; + + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *codec_dai = codec_dais[i]; + + ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt); + if (ret != 0 && ret != -ENOTSUPP) { + dev_warn(codec_dai->dev, + "ASoC: Failed to set DAI format: %d\n", ret); + return ret; + } + } + + /* Flip the polarity for the "CPU" end of a CODEC<->CODEC link */ + if (cpu_dai->codec) { + unsigned int inv_dai_fmt; + + inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK; + switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + break; + case SND_SOC_DAIFMT_CBM_CFS: + inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; + break; + case SND_SOC_DAIFMT_CBS_CFM: + inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; + break; + case SND_SOC_DAIFMT_CBS_CFS: + inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + break; + } + + dai_fmt = inv_dai_fmt; + } + + ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt); + if (ret != 0 && ret != -ENOTSUPP) { + dev_warn(cpu_dai->dev, + "ASoC: Failed to set DAI format: %d\n", ret); + return ret; + } + + return 0; +} + static int snd_soc_instantiate_card(struct snd_soc_card *card) { struct snd_soc_codec *codec; - struct snd_soc_dai_link *dai_link; - int ret, i, order, dai_fmt; + int ret, i, order; mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); @@ -1542,60 +1606,9 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) card->num_dapm_routes); for (i = 0; i < card->num_links; i++) { - struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; - dai_link = &card->dai_link[i]; - dai_fmt = dai_link->dai_fmt; - - if (dai_fmt) { - struct snd_soc_dai **codec_dais = rtd->codec_dais; - int j; - - for (j = 0; j < rtd->num_codecs; j++) { - struct snd_soc_dai *codec_dai = codec_dais[j]; - - ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt); - if (ret != 0 && ret != -ENOTSUPP) - dev_warn(codec_dai->dev, - "ASoC: Failed to set DAI format: %d\n", - ret); - } - } - - /* If this is a regular CPU link there will be a platform */ - if (dai_fmt && - (dai_link->platform_name || dai_link->platform_of_node)) { - ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai, - dai_fmt); - if (ret != 0 && ret != -ENOTSUPP) - dev_warn(card->rtd[i].cpu_dai->dev, - "ASoC: Failed to set DAI format: %d\n", - ret); - } else if (dai_fmt) { - /* Flip the polarity for the "CPU" end */ - dai_fmt &= ~SND_SOC_DAIFMT_MASTER_MASK; - switch (dai_link->dai_fmt & - SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; - break; - case SND_SOC_DAIFMT_CBM_CFS: - dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; - break; - case SND_SOC_DAIFMT_CBS_CFM: - dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; - break; - case SND_SOC_DAIFMT_CBS_CFS: - dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; - break; - } - - ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai, - dai_fmt); - if (ret != 0 && ret != -ENOTSUPP) - dev_warn(card->rtd[i].cpu_dai->dev, - "ASoC: Failed to set DAI format: %d\n", - ret); - } + if (card->dai_link[i].dai_fmt) + snd_soc_runtime_set_dai_fmt(&card->rtd[i], + card->dai_link[i].dai_fmt); } snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname), -- cgit v0.10.2 From dc1b36b24990b9d87c6de88819c112c4db91dbc8 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 6 Jan 2015 15:17:21 +0100 Subject: ASoC: omap-twl4030: Use snd_soc_runtime_set_dai_fmt() Use snd_soc_runtime_set_dai_fmt() to configure the format for the DAI link rather than configuring each DAI individually. Signed-off-by: Lars-Peter Clausen Acked-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/omap/omap-twl4030.c b/sound/soc/omap/omap-twl4030.c index 5e551c7..fb1f6bb 100644 --- a/sound/soc/omap/omap-twl4030.c +++ b/sound/soc/omap/omap-twl4030.c @@ -53,11 +53,7 @@ static int omap_twl4030_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_card *card = rtd->card; unsigned int fmt; - int ret; switch (params_channels(params)) { case 2: /* Stereo I2S mode */ @@ -74,21 +70,7 @@ static int omap_twl4030_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - /* Set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, fmt); - if (ret < 0) { - dev_err(card->dev, "can't set codec DAI configuration\n"); - return ret; - } - - /* Set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, fmt); - if (ret < 0) { - dev_err(card->dev, "can't set cpu DAI configuration\n"); - return ret; - } - - return 0; + return snd_soc_runtime_set_dai_fmt(rtd, fmt); } static struct snd_soc_ops omap_twl4030_ops = { -- cgit v0.10.2 From c853679a51b9bb76a3e06e64a0b4c6276f391921 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 6 Jan 2015 15:17:22 +0100 Subject: ASoC: mop500_ab8500: Use snd_soc_runtime_set_dai_fmt() Use snd_soc_runtime_set_dai_fmt() to configure the format for the DAI link rather than configuring each DAI individually. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c index be4f1ac7..aa65370 100644 --- a/sound/soc/ux500/mop500_ab8500.c +++ b/sound/soc/ux500/mop500_ab8500.c @@ -290,21 +290,9 @@ static int mop500_ab8500_hw_params(struct snd_pcm_substream *substream, SND_SOC_DAIFMT_GATED; } - ret = snd_soc_dai_set_fmt(codec_dai, fmt); - if (ret < 0) { - dev_err(dev, - "%s: ERROR: snd_soc_dai_set_fmt failed for codec_dai (ret = %d)!\n", - __func__, ret); - return ret; - } - - ret = snd_soc_dai_set_fmt(cpu_dai, fmt); - if (ret < 0) { - dev_err(dev, - "%s: ERROR: snd_soc_dai_set_fmt failed for cpu_dai (ret = %d)!\n", - __func__, ret); + ret = snd_soc_runtime_set_dai_fmt(rtd, fmt); + if (ret) return ret; - } /* Setup TDM-slots */ -- cgit v0.10.2 From a37f1b8fdc912600c24f9d0d45d7046e50a031e4 Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Tue, 30 Dec 2014 11:12:35 -0800 Subject: ASoC: tegra: Add platform driver for rt5677 audio codec The driver supports NVIDIA Tegra Ryu board Sponsored: Google ChromeOS Signed-off-by: Anatol Pomozov Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.txt new file mode 100644 index 0000000..a4589cd --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5677.txt @@ -0,0 +1,67 @@ +NVIDIA Tegra audio complex, with RT5677 CODEC + +Required properties: +- compatible : "nvidia,tegra-audio-rt5677" +- clocks : Must contain an entry for each entry in clock-names. + See ../clocks/clock-bindings.txt for details. +- clock-names : Must include the following entries: + - pll_a + - pll_a_out0 + - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) +- nvidia,model : The user-visible name of this sound complex. +- nvidia,audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names for sources and + sinks are the RT5677's pins (as documented in its binding), and the jacks + on the board: + + * Headphone + * Speaker + * Headset Mic + * Internal Mic 1 + * Internal Mic 2 + +- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's + connected to the CODEC. +- nvidia,audio-codec : The phandle of the RT5677 audio codec. This binding + assumes that AIF1 on the CODEC is connected to Tegra. + +Optional properties: +- nvidia,hp-det-gpios : The GPIO that detects headphones are plugged in +- nvidia,hp-en-gpios : The GPIO that enables headphone amplifier +- nvidia,mic-present-gpios: The GPIO that mic jack is plugged in +- nvidia,dmic-clk-en-gpios : The GPIO that gates DMIC clock signal + +Example: + +sound { + compatible = "nvidia,tegra-audio-rt5677-ryu", + "nvidia,tegra-audio-rt5677"; + nvidia,model = "NVIDIA Tegra Ryu"; + + nvidia,audio-routing = + "Headphone", "LOUT2", + "Headphone", "LOUT1", + "Headset Mic", "MICBIAS1", + "IN1P", "Headset Mic", + "IN1N", "Headset Mic", + "DMIC L1", "Internal Mic 1", + "DMIC R1", "Internal Mic 1", + "DMIC L2", "Internal Mic 2", + "DMIC R2", "Internal Mic 2", + "Speaker", "PDM1L", + "Speaker", "PDM1R"; + + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&rt5677>; + + nvidia,hp-det-gpios = <&gpio TEGRA_GPIO(R, 7) GPIO_ACTIVE_HIGH>; + nvidia,mic-present-gpios = <&gpio TEGRA_GPIO(O, 5) GPIO_ACTIVE_LOW>; + nvidia,hp-en-gpios = <&rt5677 1 GPIO_ACTIVE_HIGH>; + nvidia,dmic-clk-en-gpios = <&rt5677 2 GPIO_ACTIVE_HIGH>; + + clocks = <&tegra_car TEGRA124_CLK_PLL_A>, + <&tegra_car TEGRA124_CLK_PLL_A_OUT0>, + <&tegra_car TEGRA124_CLK_EXTERN1>; + clock-names = "pll_a", "pll_a_out0", "mclk"; +}; diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 31198cf7..a6768f8 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -128,3 +128,13 @@ config SND_SOC_TEGRA_MAX98090 help Say Y or M here if you want to add support for SoC audio on Tegra boards using the MAX98090 codec, such as Venice2. + +config SND_SOC_TEGRA_RT5677 + tristate "SoC Audio support for Tegra boards using a RT5677 codec" + depends on SND_SOC_TEGRA && I2C && GPIOLIB + select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC + select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC + select SND_SOC_RT5677 + help + Say Y or M here if you want to add support for SoC audio on Tegra + boards using the RT5677 codec, such as Ryu. diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 5ae588c..9171655 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o # Tegra machine Support snd-soc-tegra-rt5640-objs := tegra_rt5640.o +snd-soc-tegra-rt5677-objs := tegra_rt5677.o snd-soc-tegra-wm8753-objs := tegra_wm8753.o snd-soc-tegra-wm8903-objs := tegra_wm8903.o snd-soc-tegra-wm9712-objs := tegra_wm9712.o @@ -27,6 +28,7 @@ snd-soc-tegra-alc5632-objs := tegra_alc5632.o snd-soc-tegra-max98090-objs := tegra_max98090.o obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.o +obj-$(CONFIG_SND_SOC_TEGRA_RT5677) += snd-soc-tegra-rt5677.o obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o diff --git a/sound/soc/tegra/tegra_rt5677.c b/sound/soc/tegra/tegra_rt5677.c new file mode 100644 index 0000000..e4cf978 --- /dev/null +++ b/sound/soc/tegra/tegra_rt5677.c @@ -0,0 +1,347 @@ +/* +* tegra_rt5677.c - Tegra machine ASoC driver for boards using RT5677 codec. + * + * Copyright (c) 2014, The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Based on code copyright/by: + * + * Copyright (C) 2010-2012 - NVIDIA, Inc. + * Copyright (C) 2011 The AC100 Kernel Team + * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd. + * Copyright 2007 Wolfson Microelectronics PLC. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../codecs/rt5677.h" + +#include "tegra_asoc_utils.h" + +#define DRV_NAME "tegra-snd-rt5677" + +struct tegra_rt5677 { + struct tegra_asoc_utils_data util_data; + int gpio_hp_det; + int gpio_hp_en; + int gpio_mic_present; + int gpio_dmic_clk_en; +}; + +static int tegra_rt5677_asoc_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_card *card = rtd->card; + struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card); + int srate, mclk, err; + + srate = params_rate(params); + mclk = 256 * srate; + + err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); + if (err < 0) { + dev_err(card->dev, "Can't configure clocks\n"); + return err; + } + + err = snd_soc_dai_set_sysclk(codec_dai, RT5677_SCLK_S_MCLK, mclk, + SND_SOC_CLOCK_IN); + if (err < 0) { + dev_err(card->dev, "codec_dai clock not set\n"); + return err; + } + + return 0; +} + +static int tegra_rt5677_event_hp(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card); + + if (!gpio_is_valid(machine->gpio_hp_en)) + return 0; + + gpio_set_value_cansleep(machine->gpio_hp_en, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static struct snd_soc_ops tegra_rt5677_ops = { + .hw_params = tegra_rt5677_asoc_hw_params, +}; + +static struct snd_soc_jack tegra_rt5677_hp_jack; + +static struct snd_soc_jack_pin tegra_rt5677_hp_jack_pins = { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, +}; +static struct snd_soc_jack_gpio tegra_rt5677_hp_jack_gpio = { + .name = "Headphone detection", + .report = SND_JACK_HEADPHONE, + .debounce_time = 150, +}; + +static struct snd_soc_jack tegra_rt5677_mic_jack; + +static struct snd_soc_jack_pin tegra_rt5677_mic_jack_pins = { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, +}; + +static struct snd_soc_jack_gpio tegra_rt5677_mic_jack_gpio = { + .name = "Headset Mic detection", + .report = SND_JACK_MICROPHONE, + .debounce_time = 150, + .invert = 1 +}; + +static const struct snd_soc_dapm_widget tegra_rt5677_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_HP("Headphone", tegra_rt5677_event_hp), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Internal Mic 1", NULL), + SND_SOC_DAPM_MIC("Internal Mic 2", NULL), +}; + +static const struct snd_kcontrol_new tegra_rt5677_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Internal Mic 1"), + SOC_DAPM_PIN_SWITCH("Internal Mic 2"), +}; + +static int tegra_rt5677_asoc_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_codec *codec = codec_dai->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(rtd->card); + + snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, + &tegra_rt5677_hp_jack); + snd_soc_jack_add_pins(&tegra_rt5677_hp_jack, 1, + &tegra_rt5677_hp_jack_pins); + + if (gpio_is_valid(machine->gpio_hp_det)) { + tegra_rt5677_hp_jack_gpio.gpio = machine->gpio_hp_det; + snd_soc_jack_add_gpios(&tegra_rt5677_hp_jack, 1, + &tegra_rt5677_hp_jack_gpio); + } + + + snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE, + &tegra_rt5677_mic_jack); + snd_soc_jack_add_pins(&tegra_rt5677_mic_jack, 1, + &tegra_rt5677_mic_jack_pins); + + if (gpio_is_valid(machine->gpio_mic_present)) { + tegra_rt5677_mic_jack_gpio.gpio = machine->gpio_mic_present; + snd_soc_jack_add_gpios(&tegra_rt5677_mic_jack, 1, + &tegra_rt5677_mic_jack_gpio); + } + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + + return 0; +} + +static int tegra_rt5677_card_remove(struct snd_soc_card *card) +{ + struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card); + + if (gpio_is_valid(machine->gpio_hp_det)) { + snd_soc_jack_free_gpios(&tegra_rt5677_hp_jack, 1, + &tegra_rt5677_hp_jack_gpio); + } + + if (gpio_is_valid(machine->gpio_mic_present)) { + snd_soc_jack_free_gpios(&tegra_rt5677_mic_jack, 1, + &tegra_rt5677_mic_jack_gpio); + } + + return 0; +} + +static struct snd_soc_dai_link tegra_rt5677_dai = { + .name = "RT5677", + .stream_name = "RT5677 PCM", + .codec_dai_name = "rt5677-aif1", + .init = tegra_rt5677_asoc_init, + .ops = &tegra_rt5677_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, +}; + +static struct snd_soc_card snd_soc_tegra_rt5677 = { + .name = "tegra-rt5677", + .owner = THIS_MODULE, + .remove = tegra_rt5677_card_remove, + .dai_link = &tegra_rt5677_dai, + .num_links = 1, + .controls = tegra_rt5677_controls, + .num_controls = ARRAY_SIZE(tegra_rt5677_controls), + .dapm_widgets = tegra_rt5677_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra_rt5677_dapm_widgets), + .fully_routed = true, +}; + +static int tegra_rt5677_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct snd_soc_card *card = &snd_soc_tegra_rt5677; + struct tegra_rt5677 *machine; + int ret; + + machine = devm_kzalloc(&pdev->dev, + sizeof(struct tegra_rt5677), GFP_KERNEL); + if (!machine) + return -ENOMEM; + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, machine); + + machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0); + if (machine->gpio_hp_det == -EPROBE_DEFER) + return -EPROBE_DEFER; + + machine->gpio_mic_present = of_get_named_gpio(np, + "nvidia,mic-present-gpios", 0); + if (machine->gpio_mic_present == -EPROBE_DEFER) + return -EPROBE_DEFER; + + machine->gpio_hp_en = of_get_named_gpio(np, "nvidia,hp-en-gpios", 0); + if (machine->gpio_hp_en == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (gpio_is_valid(machine->gpio_hp_en)) { + ret = devm_gpio_request_one(&pdev->dev, machine->gpio_hp_en, + GPIOF_OUT_INIT_LOW, "hp_en"); + if (ret) { + dev_err(card->dev, "cannot get hp_en gpio\n"); + return ret; + } + } + + machine->gpio_dmic_clk_en = of_get_named_gpio(np, + "nvidia,dmic-clk-en-gpios", 0); + if (machine->gpio_dmic_clk_en == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (gpio_is_valid(machine->gpio_dmic_clk_en)) { + ret = devm_gpio_request_one(&pdev->dev, + machine->gpio_dmic_clk_en, + GPIOF_OUT_INIT_HIGH, "dmic_clk_en"); + if (ret) { + dev_err(card->dev, "cannot get dmic_clk_en gpio\n"); + return ret; + } + } + + ret = snd_soc_of_parse_card_name(card, "nvidia,model"); + if (ret) + goto err; + + ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); + if (ret) + goto err; + + tegra_rt5677_dai.codec_of_node = of_parse_phandle(np, + "nvidia,audio-codec", 0); + if (!tegra_rt5677_dai.codec_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,audio-codec' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + tegra_rt5677_dai.cpu_of_node = of_parse_phandle(np, + "nvidia,i2s-controller", 0); + if (!tegra_rt5677_dai.cpu_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,i2s-controller' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + tegra_rt5677_dai.platform_of_node = tegra_rt5677_dai.cpu_of_node; + + ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); + if (ret) + goto err; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err_fini_utils; + } + + return 0; + +err_fini_utils: + tegra_asoc_utils_fini(&machine->util_data); +err: + return ret; +} + +static int tegra_rt5677_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card); + + snd_soc_unregister_card(card); + + tegra_asoc_utils_fini(&machine->util_data); + + return 0; +} + +static const struct of_device_id tegra_rt5677_of_match[] = { + { .compatible = "nvidia,tegra-audio-rt5677", }, + {}, +}; + +static struct platform_driver tegra_rt5677_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = tegra_rt5677_of_match, + }, + .probe = tegra_rt5677_probe, + .remove = tegra_rt5677_remove, +}; +module_platform_driver(tegra_rt5677_driver); + +MODULE_AUTHOR("Anatol Pomozov "); +MODULE_DESCRIPTION("Tegra+RT5677 machine ASoC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra_rt5677_of_match); -- cgit v0.10.2 From 67db10344816c74709271c30905bb83781e7050c Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sat, 13 Dec 2014 09:11:40 -0500 Subject: nfsd: fi_delegees doesn't need to be an atomic_t fi_delegees is always handled under the fi_lock, so there's no need to use an atomic_t for this field. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index c06a1ba..277f8b8 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -688,7 +688,7 @@ static void nfs4_put_deleg_lease(struct nfs4_file *fp) struct file *filp = NULL; spin_lock(&fp->fi_lock); - if (fp->fi_deleg_file && atomic_dec_and_test(&fp->fi_delegees)) + if (fp->fi_deleg_file && --fp->fi_delegees == 0) swap(filp, fp->fi_deleg_file); spin_unlock(&fp->fi_lock); @@ -3855,12 +3855,12 @@ static int nfs4_setlease(struct nfs4_delegation *dp) /* Race breaker */ if (fp->fi_deleg_file) { status = 0; - atomic_inc(&fp->fi_delegees); + ++fp->fi_delegees; hash_delegation_locked(dp, fp); goto out_unlock; } fp->fi_deleg_file = filp; - atomic_set(&fp->fi_delegees, 1); + fp->fi_delegees = 1; hash_delegation_locked(dp, fp); spin_unlock(&fp->fi_lock); spin_unlock(&state_lock); @@ -3901,7 +3901,7 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh, status = -EAGAIN; goto out_unlock; } - atomic_inc(&fp->fi_delegees); + ++fp->fi_delegees; hash_delegation_locked(dp, fp); status = 0; out_unlock: diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 9d3be37..dab6553 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -493,7 +493,7 @@ struct nfs4_file { atomic_t fi_access[2]; u32 fi_share_deny; struct file *fi_deleg_file; - atomic_t fi_delegees; + int fi_delegees; struct knfsd_fh fi_fhandle; bool fi_had_conflict; }; -- cgit v0.10.2 From 0ec016e3e02fe07e7250b87daffab611f219e7f1 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 19 Dec 2014 18:01:35 -0500 Subject: nfsd4: tweak rd_dircount accounting RFC 3530 14.2.24 says This value represents the length of the names of the directory entries and the cookie value for these entries. This length represents the XDR encoding of the data (names and cookies)... The "xdr encoding" of the name should probably include the 4 bytes for the length. But this is all just a hint so not worth e.g. backporting to stable. Also reshuffle some lines to more clearly group together the dircount-related code. Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 15f7b73..91f7a36 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -2768,16 +2768,17 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen, if (entry_bytes > cd->rd_maxcount) goto fail; cd->rd_maxcount -= entry_bytes; - if (!cd->rd_dircount) - goto fail; /* * RFC 3530 14.2.24 describes rd_dircount as only a "hint", so * let's always let through the first entry, at least: */ - name_and_cookie = 4 * XDR_QUADLEN(namlen) + 8; + if (!cd->rd_dircount) + goto fail; + name_and_cookie = 4 + 4 * XDR_QUADLEN(namlen) + 8; if (name_and_cookie > cd->rd_dircount && cd->cookie_offset) goto fail; cd->rd_dircount -= min(cd->rd_dircount, name_and_cookie); + cd->cookie_offset = cookie_offset; skip_entry: cd->common.err = nfs_ok; -- cgit v0.10.2 From 9535395640edb23ba5cec2abb026c4ee51899722 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Sat, 29 Nov 2014 19:07:46 +0100 Subject: batman-adv: Kconfig, Add missing DEBUG_FS dependency BATMAN_ADV_DEBUG is using debugfs files for the debugging log. So it depends on DEBUG_FS which is missing as dependency in the Kconfig file. Signed-off-by: Markus Pargmann Signed-off-by: Antonio Quartulli diff --git a/net/batman-adv/Kconfig b/net/batman-adv/Kconfig index 11660a3..c6fc8f7 100644 --- a/net/batman-adv/Kconfig +++ b/net/batman-adv/Kconfig @@ -62,6 +62,7 @@ config BATMAN_ADV_MCAST config BATMAN_ADV_DEBUG bool "B.A.T.M.A.N. debugging" depends on BATMAN_ADV + depends on DEBUG_FS help This is an option for use by developers; most people should say N here. This enables compilation of support for -- cgit v0.10.2 From 061a5ad7c9b938fd730672b21347b69896a27973 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sun, 4 Jan 2015 14:28:46 -0800 Subject: Input: imx_keypad - use the preferred form for passing a size of a struct According to Documentation/CodingStyle - Chapter 14: "The preferred form for passing a size of a struct is the following: p = kmalloc(sizeof(*p), ...); The alternative form where struct name is spelled out hurts readability and introduces an opportunity for a bug when the pointer variable type is changed but the corresponding sizeof that is passed to a memory allocator is not." So do it as recommended. Signed-off-by: Fabio Estevam Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index 20a99c3..3b681c8 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -448,8 +448,7 @@ static int imx_keypad_probe(struct platform_device *pdev) return -ENOMEM; } - keypad = devm_kzalloc(&pdev->dev, sizeof(struct imx_keypad), - GFP_KERNEL); + keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL); if (!keypad) { dev_err(&pdev->dev, "not enough memory for driver data\n"); return -ENOMEM; -- cgit v0.10.2 From 4f0d4fac24405098ad2f65f3f996f525e7d04703 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 29 Dec 2014 15:58:04 -0800 Subject: MAINTAINERS: remove Dmitry Torokhov's alternate address I am using gmail.com exclusively and mail.ru was a backup that so far was not needed. To avoid getting 2 copies of the same message let's drop mail.ru from MAINTAINERS file. Signed-off-by: Dmitry Torokhov diff --git a/MAINTAINERS b/MAINTAINERS index f73bb41..494901b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4698,7 +4698,6 @@ F: include/uapi/linux/inotify.h INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN) DRIVERS M: Dmitry Torokhov -M: Dmitry Torokhov L: linux-input@vger.kernel.org Q: http://patchwork.kernel.org/project/linux-input/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git -- cgit v0.10.2 From 148e9a711e034e06310a8c36b64957934ebe30f2 Mon Sep 17 00:00:00 2001 From: Srihari Vijayaraghavan Date: Wed, 7 Jan 2015 16:25:53 -0800 Subject: Input: i8042 - reset keyboard to fix Elantech touchpad detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some laptops, keyboard needs to be reset in order to successfully detect touchpad (e.g., some Gigabyte laptop models with Elantech touchpads). Without resettin keyboard touchpad pretends to be completely dead. Based on the original patch by Mateusz Jończyk this version has been expanded to include DMI based detection & application of the fix automatically on the affected models of laptops. This has been confirmed to fix problem by three users already on three different models of laptops. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=81331 Cc: stable@vger.kernel.org Signed-off-by: Srihari Vijayaraghavan Acked-by: Mateusz Jończyk Tested-by: Srihari Vijayaraghavan Tested by: Zakariya Dehlawi Tested-by: Guillaum Bouchard Signed-off-by: Dmitry Torokhov diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 10d51c2..8013b4d 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1243,6 +1243,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. i8042.notimeout [HW] Ignore timeout condition signalled by controller i8042.reset [HW] Reset the controller during init and cleanup i8042.unlock [HW] Unlock (ignore) the keylock + i8042.kbdreset [HW] Reset device connected to KBD port i810= [HW,DRM] diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index c66d1b5..97cdc58 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -745,6 +745,35 @@ static const struct dmi_system_id __initconst i8042_dmi_dritek_table[] = { { } }; +/* + * Some laptops need keyboard reset before probing for the trackpad to get + * it detected, initialised & finally work. + */ +static const struct dmi_system_id __initconst i8042_dmi_kbdreset_table[] = { + { + /* Gigabyte P35 v2 - Elantech touchpad */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "P35V2"), + }, + }, + { + /* Aorus branded Gigabyte X3 Plus - Elantech touchpad */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "X3"), + }, + }, + { + /* Gigabyte P34 - Elantech touchpad */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "P34"), + }, + }, + { } +}; + #endif /* CONFIG_X86 */ #ifdef CONFIG_PNP @@ -1040,6 +1069,9 @@ static int __init i8042_platform_init(void) if (dmi_check_system(i8042_dmi_dritek_table)) i8042_dritek = true; + if (dmi_check_system(i8042_dmi_kbdreset_table)) + i8042_kbdreset = true; + /* * A20 was already enabled during early kernel init. But some buggy * BIOSes (in MSI Laptops) require A20 to be enabled using 8042 to diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index f5a98af..804d2e0 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -67,6 +67,10 @@ static bool i8042_notimeout; module_param_named(notimeout, i8042_notimeout, bool, 0); MODULE_PARM_DESC(notimeout, "Ignore timeouts signalled by i8042"); +static bool i8042_kbdreset; +module_param_named(kbdreset, i8042_kbdreset, bool, 0); +MODULE_PARM_DESC(kbdreset, "Reset device connected to KBD port"); + #ifdef CONFIG_X86 static bool i8042_dritek; module_param_named(dritek, i8042_dritek, bool, 0); @@ -790,6 +794,16 @@ static int __init i8042_check_aux(void) return -1; /* + * Reset keyboard (needed on some laptops to successfully detect + * touchpad, e.g., some Gigabyte laptop models with Elantech + * touchpads). + */ + if (i8042_kbdreset) { + pr_warn("Attempting to reset device connected to KBD port\n"); + i8042_kbd_write(NULL, (unsigned char) 0xff); + } + +/* * Test AUX IRQ delivery to make sure BIOS did not grab the IRQ and * used it for a PCI card or somethig else. */ -- cgit v0.10.2 From 0e4ca02b3fc0e33e96eef1f4da227eab2c67b8ac Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 9 Dec 2014 16:14:11 -0500 Subject: efi: Update the URLs for efibootmgr Matt Domsch changed the dell page to point to the new upstream quite some time ago; kernel should reflect that here as well. Cc: Matt Domsch Signed-off-by: Peter Jones Signed-off-by: Ricardo Neri diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index f712d47..8de4da5 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -12,11 +12,11 @@ config EFI_VARS Note that using this driver in concert with efibootmgr requires at least test release version 0.5.0-test3 or later, which is - available from Matt Domsch's website located at: + available from: Subsequent efibootmgr releases may be found at: - + config EFI_VARS_PSTORE tristate "Register efivars backend for pstore" -- cgit v0.10.2 From 26e022727f5e88c6e5054e14d954425deacbe56a Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Thu, 18 Dec 2014 16:02:17 +0100 Subject: efi: Rename efi_guid_unparse to efi_guid_to_str Call it what it does - "unparse" is plain-misleading. Signed-off-by: Borislav Petkov Signed-off-by: Ricardo Neri diff --git a/block/partitions/efi.c b/block/partitions/efi.c index 56d08fd..26cb624 100644 --- a/block/partitions/efi.c +++ b/block/partitions/efi.c @@ -715,7 +715,7 @@ int efi_partition(struct parsed_partitions *state) state->parts[i + 1].flags = ADDPART_FLAG_RAID; info = &state->parts[i + 1].info; - efi_guid_unparse(&ptes[i].unique_partition_guid, info->uuid); + efi_guid_to_str(&ptes[i].unique_partition_guid, info->uuid); /* Naively convert UTF16-LE to 7 bits. */ label_max = min(ARRAY_SIZE(info->volname) - 1, diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 8590099..ff0bbe3 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -272,10 +272,10 @@ static __init int match_config_table(efi_guid_t *guid, int i; if (table_types) { - efi_guid_unparse(guid, str); + efi_guid_to_str(guid, str); for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) { - efi_guid_unparse(&table_types[i].guid, str); + efi_guid_to_str(&table_types[i].guid, str); if (!efi_guidcmp(*guid, table_types[i].guid)) { *(table_types[i].ptr) = table; diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index f256ecd..7b2e049 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c @@ -39,7 +39,7 @@ * fix locking per Peter Chubb's findings * * 25 Mar 2002 - Matt Domsch - * move uuid_unparse() to include/asm-ia64/efi.h:efi_guid_unparse() + * move uuid_unparse() to include/asm-ia64/efi.h:efi_guid_to_str() * * 12 Feb 2002 - Matt Domsch * use list_for_each_safe when deleting vars. @@ -128,7 +128,7 @@ efivar_guid_read(struct efivar_entry *entry, char *buf) if (!entry || !buf) return 0; - efi_guid_unparse(&var->VendorGuid, str); + efi_guid_to_str(&var->VendorGuid, str); str += strlen(str); str += sprintf(str, "\n"); @@ -569,7 +569,7 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var) private variables from another's. */ *(short_name + strlen(short_name)) = '-'; - efi_guid_unparse(&new_var->var.VendorGuid, + efi_guid_to_str(&new_var->var.VendorGuid, short_name + strlen(short_name)); new_var->kobj.kset = efivars_kset; diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index 6dad117..ddbce42 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c @@ -140,7 +140,7 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor, name[len] = '-'; - efi_guid_unparse(&entry->var.VendorGuid, name + len + 1); + efi_guid_to_str(&entry->var.VendorGuid, name + len + 1); name[len + EFI_VARIABLE_GUID_LEN+1] = '\0'; diff --git a/include/linux/efi.h b/include/linux/efi.h index 0949f9c..d762c81 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -844,7 +844,7 @@ efi_guidcmp (efi_guid_t left, efi_guid_t right) } static inline char * -efi_guid_unparse(efi_guid_t *guid, char *out) +efi_guid_to_str(efi_guid_t *guid, char *out) { sprintf(out, "%pUl", guid->b); return out; -- cgit v0.10.2 From 6ee0ad2a7f27f7dc365576b748bc98684f02882d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Thu, 8 Jan 2015 13:27:15 +0900 Subject: drm/amdkfd: Drop interrupt SW ring buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The work queue couldn't reliably prevent the SW ring buffer from overflowing, so dmesg was spammed by kfd kfd: Interrupt ring overflow, dropping interrupt. messages when running e.g. the Atlantis Substance demo from https://wiki.unrealengine.com/Linux_Demos on Kaveri. Since the SW ring buffer doesn't actually do anything at this point, just remove it for now. When actual interrupt processing code is added to amdkfd, it should try to do things immediately and only defer to work queues when necessary. Signed-off-by: Michel Dänzer Reviewed-by: Christian König Signed-off-by: Oded Gabbay diff --git a/drivers/gpu/drm/amd/amdkfd/Makefile b/drivers/gpu/drm/amd/amdkfd/Makefile index be6246d..307a309 100644 --- a/drivers/gpu/drm/amd/amdkfd/Makefile +++ b/drivers/gpu/drm/amd/amdkfd/Makefile @@ -8,7 +8,6 @@ amdkfd-y := kfd_module.o kfd_device.o kfd_chardev.o kfd_topology.o \ kfd_pasid.o kfd_doorbell.o kfd_flat_memory.o \ kfd_process.o kfd_queue.o kfd_mqd_manager.o \ kfd_kernel_queue.o kfd_packet_manager.o \ - kfd_process_queue_manager.o kfd_device_queue_manager.o \ - kfd_interrupt.o + kfd_process_queue_manager.o kfd_device_queue_manager.o obj-$(CONFIG_HSA_AMD) += amdkfd.o diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c index 43884eb..633532a 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c @@ -192,13 +192,6 @@ bool kgd2kfd_device_init(struct kfd_dev *kfd, goto kfd_topology_add_device_error; } - if (kfd_interrupt_init(kfd)) { - dev_err(kfd_device, - "Error initializing interrupts for device (%x:%x)\n", - kfd->pdev->vendor, kfd->pdev->device); - goto kfd_interrupt_error; - } - if (!device_iommu_pasid_init(kfd)) { dev_err(kfd_device, "Error initializing iommuv2 for device (%x:%x)\n", @@ -237,8 +230,6 @@ dqm_start_error: device_queue_manager_error: amd_iommu_free_device(kfd->pdev); device_iommu_pasid_error: - kfd_interrupt_exit(kfd); -kfd_interrupt_error: kfd_topology_remove_device(kfd); kfd_topology_add_device_error: kfd2kgd->fini_sa_manager(kfd->kgd); @@ -254,7 +245,6 @@ void kgd2kfd_device_exit(struct kfd_dev *kfd) if (kfd->init_complete) { device_queue_manager_uninit(kfd->dqm); amd_iommu_free_device(kfd->pdev); - kfd_interrupt_exit(kfd); kfd_topology_remove_device(kfd); } @@ -296,13 +286,5 @@ int kgd2kfd_resume(struct kfd_dev *kfd) /* This is called directly from KGD at ISR. */ void kgd2kfd_interrupt(struct kfd_dev *kfd, const void *ih_ring_entry) { - if (kfd->init_complete) { - spin_lock(&kfd->interrupt_lock); - - if (kfd->interrupts_active - && enqueue_ih_ring_entry(kfd, ih_ring_entry)) - schedule_work(&kfd->interrupt_work); - - spin_unlock(&kfd->interrupt_lock); - } + /* Process interrupts / schedule work as necessary */ } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c b/drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c deleted file mode 100644 index 5b99909..0000000 --- a/drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2014 Advanced Micro Devices, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. - */ - -/* - * KFD Interrupts. - * - * AMD GPUs deliver interrupts by pushing an interrupt description onto the - * interrupt ring and then sending an interrupt. KGD receives the interrupt - * in ISR and sends us a pointer to each new entry on the interrupt ring. - * - * We generally can't process interrupt-signaled events from ISR, so we call - * out to each interrupt client module (currently only the scheduler) to ask if - * each interrupt is interesting. If they return true, then it requires further - * processing so we copy it to an internal interrupt ring and call each - * interrupt client again from a work-queue. - * - * There's no acknowledgment for the interrupts we use. The hardware simply - * queues a new interrupt each time without waiting. - * - * The fixed-size internal queue means that it's possible for us to lose - * interrupts because we have no back-pressure to the hardware. - */ - -#include -#include -#include "kfd_priv.h" - -#define KFD_INTERRUPT_RING_SIZE 256 - -static void interrupt_wq(struct work_struct *); - -int kfd_interrupt_init(struct kfd_dev *kfd) -{ - void *interrupt_ring = kmalloc_array(KFD_INTERRUPT_RING_SIZE, - kfd->device_info->ih_ring_entry_size, - GFP_KERNEL); - if (!interrupt_ring) - return -ENOMEM; - - kfd->interrupt_ring = interrupt_ring; - kfd->interrupt_ring_size = - KFD_INTERRUPT_RING_SIZE * kfd->device_info->ih_ring_entry_size; - atomic_set(&kfd->interrupt_ring_wptr, 0); - atomic_set(&kfd->interrupt_ring_rptr, 0); - - spin_lock_init(&kfd->interrupt_lock); - - INIT_WORK(&kfd->interrupt_work, interrupt_wq); - - kfd->interrupts_active = true; - - /* - * After this function returns, the interrupt will be enabled. This - * barrier ensures that the interrupt running on a different processor - * sees all the above writes. - */ - smp_wmb(); - - return 0; -} - -void kfd_interrupt_exit(struct kfd_dev *kfd) -{ - /* - * Stop the interrupt handler from writing to the ring and scheduling - * workqueue items. The spinlock ensures that any interrupt running - * after we have unlocked sees interrupts_active = false. - */ - unsigned long flags; - - spin_lock_irqsave(&kfd->interrupt_lock, flags); - kfd->interrupts_active = false; - spin_unlock_irqrestore(&kfd->interrupt_lock, flags); - - /* - * Flush_scheduled_work ensures that there are no outstanding - * work-queue items that will access interrupt_ring. New work items - * can't be created because we stopped interrupt handling above. - */ - flush_scheduled_work(); - - kfree(kfd->interrupt_ring); -} - -/* - * This assumes that it can't be called concurrently with itself - * but only with dequeue_ih_ring_entry. - */ -bool enqueue_ih_ring_entry(struct kfd_dev *kfd, const void *ih_ring_entry) -{ - unsigned int rptr = atomic_read(&kfd->interrupt_ring_rptr); - unsigned int wptr = atomic_read(&kfd->interrupt_ring_wptr); - - if ((rptr - wptr) % kfd->interrupt_ring_size == - kfd->device_info->ih_ring_entry_size) { - /* This is very bad, the system is likely to hang. */ - dev_err_ratelimited(kfd_chardev(), - "Interrupt ring overflow, dropping interrupt.\n"); - return false; - } - - memcpy(kfd->interrupt_ring + wptr, ih_ring_entry, - kfd->device_info->ih_ring_entry_size); - - wptr = (wptr + kfd->device_info->ih_ring_entry_size) % - kfd->interrupt_ring_size; - smp_wmb(); /* Ensure memcpy'd data is visible before wptr update. */ - atomic_set(&kfd->interrupt_ring_wptr, wptr); - - return true; -} - -/* - * This assumes that it can't be called concurrently with itself - * but only with enqueue_ih_ring_entry. - */ -static bool dequeue_ih_ring_entry(struct kfd_dev *kfd, void *ih_ring_entry) -{ - /* - * Assume that wait queues have an implicit barrier, i.e. anything that - * happened in the ISR before it queued work is visible. - */ - - unsigned int wptr = atomic_read(&kfd->interrupt_ring_wptr); - unsigned int rptr = atomic_read(&kfd->interrupt_ring_rptr); - - if (rptr == wptr) - return false; - - memcpy(ih_ring_entry, kfd->interrupt_ring + rptr, - kfd->device_info->ih_ring_entry_size); - - rptr = (rptr + kfd->device_info->ih_ring_entry_size) % - kfd->interrupt_ring_size; - - /* - * Ensure the rptr write update is not visible until - * memcpy has finished reading. - */ - smp_mb(); - atomic_set(&kfd->interrupt_ring_rptr, rptr); - - return true; -} - -static void interrupt_wq(struct work_struct *work) -{ - struct kfd_dev *dev = container_of(work, struct kfd_dev, - interrupt_work); - - uint32_t ih_ring_entry[DIV_ROUND_UP( - dev->device_info->ih_ring_entry_size, - sizeof(uint32_t))]; - - while (dequeue_ih_ring_entry(dev, ih_ring_entry)) - ; -} diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index a5edb29..b3dc13c 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -135,22 +135,10 @@ struct kfd_dev { struct kgd2kfd_shared_resources shared_resources; - void *interrupt_ring; - size_t interrupt_ring_size; - atomic_t interrupt_ring_rptr; - atomic_t interrupt_ring_wptr; - struct work_struct interrupt_work; - spinlock_t interrupt_lock; - /* QCM Device instance */ struct device_queue_manager *dqm; bool init_complete; - /* - * Interrupts of interest to KFD are copied - * from the HW ring into a SW ring. - */ - bool interrupts_active; }; /* KGD2KFD callbacks */ @@ -531,10 +519,7 @@ struct kfd_dev *kfd_device_by_pci_dev(const struct pci_dev *pdev); struct kfd_dev *kfd_topology_enum_kfd_devices(uint8_t idx); /* Interrupts */ -int kfd_interrupt_init(struct kfd_dev *dev); -void kfd_interrupt_exit(struct kfd_dev *dev); void kgd2kfd_interrupt(struct kfd_dev *kfd, const void *ih_ring_entry); -bool enqueue_ih_ring_entry(struct kfd_dev *kfd, const void *ih_ring_entry); /* Power Management */ void kgd2kfd_suspend(struct kfd_dev *kfd); -- cgit v0.10.2 From 0333d6dd3bc17468f42d0c455113fd9d50f7746e Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Tue, 6 Jan 2015 21:36:55 -0800 Subject: Bluetooth: btmrvl: use msecs_to_jiffies within macro definition This change improves readability and fixes allignment problem. Signed-off-by: Amitkumar Karwar Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h index 5513204..e75f8ee 100644 --- a/drivers/bluetooth/btmrvl_drv.h +++ b/drivers/bluetooth/btmrvl_drv.h @@ -28,9 +28,9 @@ #define BTM_UPLD_SIZE 2312 /* Time to wait until Host Sleep state change in millisecond */ -#define WAIT_UNTIL_HS_STATE_CHANGED 5000 +#define WAIT_UNTIL_HS_STATE_CHANGED msecs_to_jiffies(5000) /* Time to wait for command response in millisecond */ -#define WAIT_UNTIL_CMD_RESP 5000 +#define WAIT_UNTIL_CMD_RESP msecs_to_jiffies(5000) enum rdwr_status { RDWR_STATUS_SUCCESS = 0, diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index c4a542c..2578b29 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -207,8 +207,8 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode, wake_up_interruptible(&priv->main_thread.wait_q); if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q, - priv->adapter->cmd_complete, - msecs_to_jiffies(WAIT_UNTIL_CMD_RESP))) + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) return -ETIMEDOUT; return 0; @@ -293,7 +293,7 @@ int btmrvl_enable_hs(struct btmrvl_private *priv) ret = wait_event_interruptible_timeout(adapter->event_hs_wait_q, adapter->hs_state, - msecs_to_jiffies(WAIT_UNTIL_HS_STATE_CHANGED)); + WAIT_UNTIL_HS_STATE_CHANGED); if (ret < 0) { BT_ERR("event_hs_wait_q terminated (%d): %d,%d,%d", ret, adapter->hs_state, adapter->ps_state, -- cgit v0.10.2 From a9cc32d96c62b230c664a362b5f3206d3bcf35b8 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Tue, 6 Jan 2015 21:36:56 -0800 Subject: Bluetooth: btmrvl: fix card reset and suspend race issue Sometimes suspend thread queues a command and wait for it's response, meanwhile WLAN driver power cycles the card which leads to crash. This patch makes sure that suspend thread is woken up in remove path. Signed-off-by: Amitkumar Karwar Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index 2578b29..4135977 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -207,10 +207,14 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode, wake_up_interruptible(&priv->main_thread.wait_q); if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q, - priv->adapter->cmd_complete, + priv->adapter->cmd_complete || + priv->surprise_removed, WAIT_UNTIL_CMD_RESP)) return -ETIMEDOUT; + if (priv->surprise_removed) + return -EFAULT; + return 0; } @@ -292,9 +296,10 @@ int btmrvl_enable_hs(struct btmrvl_private *priv) } ret = wait_event_interruptible_timeout(adapter->event_hs_wait_q, - adapter->hs_state, + adapter->hs_state || + priv->surprise_removed, WAIT_UNTIL_HS_STATE_CHANGED); - if (ret < 0) { + if (ret < 0 || priv->surprise_removed) { BT_ERR("event_hs_wait_q terminated (%d): %d,%d,%d", ret, adapter->hs_state, adapter->ps_state, adapter->wakeup_tries); -- cgit v0.10.2 From ea81ac2e7050798109356150ea16e71622a5c329 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Sun, 4 Jan 2015 17:10:53 +0100 Subject: ieee802154: create 6lowpan sub-directory This patch creates an 6lowpan sub-directory inside ieee802154. Additional we move all ieee802154 6lowpan relevant files into this sub-directory instead of placing the 6lowpan related files inside ieee802154. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/6lowpan/6lowpan_rtnl.c b/net/ieee802154/6lowpan/6lowpan_rtnl.c new file mode 100644 index 0000000..27eaa65e --- /dev/null +++ b/net/ieee802154/6lowpan/6lowpan_rtnl.c @@ -0,0 +1,729 @@ +/* Copyright 2011, Siemens AG + * written by Alexander Smirnov + */ + +/* Based on patches from Jon Smirl + * Copyright (c) 2011 Jon Smirl + * + * 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. + */ + +/* Jon's code is based on 6lowpan implementation for Contiki which is: + * Copyright (c) 2008, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "reassembly.h" + +static LIST_HEAD(lowpan_devices); +static int lowpan_open_count; + +/* private device info */ +struct lowpan_dev_info { + struct net_device *real_dev; /* real WPAN device ptr */ + struct mutex dev_list_mtx; /* mutex for list ops */ + u16 fragment_tag; +}; + +struct lowpan_dev_record { + struct net_device *ldev; + struct list_head list; +}; + +/* don't save pan id, it's intra pan */ +struct lowpan_addr { + u8 mode; + union { + /* IPv6 needs big endian here */ + __be64 extended_addr; + __be16 short_addr; + } u; +}; + +struct lowpan_addr_info { + struct lowpan_addr daddr; + struct lowpan_addr saddr; +}; + +static inline struct +lowpan_dev_info *lowpan_dev_info(const struct net_device *dev) +{ + return netdev_priv(dev); +} + +static inline struct +lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb) +{ + WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct lowpan_addr_info)); + return (struct lowpan_addr_info *)(skb->data - + sizeof(struct lowpan_addr_info)); +} + +static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *_daddr, + const void *_saddr, unsigned int len) +{ + const u8 *saddr = _saddr; + const u8 *daddr = _daddr; + struct lowpan_addr_info *info; + + /* TODO: + * if this package isn't ipv6 one, where should it be routed? + */ + if (type != ETH_P_IPV6) + return 0; + + if (!saddr) + saddr = dev->dev_addr; + + raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8); + raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8); + + info = lowpan_skb_priv(skb); + + /* TODO: Currently we only support extended_addr */ + info->daddr.mode = IEEE802154_ADDR_LONG; + memcpy(&info->daddr.u.extended_addr, daddr, + sizeof(info->daddr.u.extended_addr)); + info->saddr.mode = IEEE802154_ADDR_LONG; + memcpy(&info->saddr.u.extended_addr, saddr, + sizeof(info->daddr.u.extended_addr)); + + return 0; +} + +static int lowpan_give_skb_to_devices(struct sk_buff *skb, + struct net_device *dev) +{ + struct lowpan_dev_record *entry; + struct sk_buff *skb_cp; + int stat = NET_RX_SUCCESS; + + skb->protocol = htons(ETH_P_IPV6); + skb->pkt_type = PACKET_HOST; + + rcu_read_lock(); + list_for_each_entry_rcu(entry, &lowpan_devices, list) + if (lowpan_dev_info(entry->ldev)->real_dev == skb->dev) { + skb_cp = skb_copy(skb, GFP_ATOMIC); + if (!skb_cp) { + kfree_skb(skb); + rcu_read_unlock(); + return NET_RX_DROP; + } + + skb_cp->dev = entry->ldev; + stat = netif_rx(skb_cp); + if (stat == NET_RX_DROP) + break; + } + rcu_read_unlock(); + + consume_skb(skb); + + return stat; +} + +static int +iphc_decompress(struct sk_buff *skb, const struct ieee802154_hdr *hdr) +{ + u8 iphc0, iphc1; + struct ieee802154_addr_sa sa, da; + void *sap, *dap; + + raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len); + /* at least two bytes will be used for the encoding */ + if (skb->len < 2) + return -EINVAL; + + if (lowpan_fetch_skb_u8(skb, &iphc0)) + return -EINVAL; + + if (lowpan_fetch_skb_u8(skb, &iphc1)) + return -EINVAL; + + ieee802154_addr_to_sa(&sa, &hdr->source); + ieee802154_addr_to_sa(&da, &hdr->dest); + + if (sa.addr_type == IEEE802154_ADDR_SHORT) + sap = &sa.short_addr; + else + sap = &sa.hwaddr; + + if (da.addr_type == IEEE802154_ADDR_SHORT) + dap = &da.short_addr; + else + dap = &da.hwaddr; + + return lowpan_header_decompress(skb, skb->dev, sap, sa.addr_type, + IEEE802154_ADDR_LEN, dap, da.addr_type, + IEEE802154_ADDR_LEN, iphc0, iphc1); +} + +static struct sk_buff* +lowpan_alloc_frag(struct sk_buff *skb, int size, + const struct ieee802154_hdr *master_hdr) +{ + struct net_device *real_dev = lowpan_dev_info(skb->dev)->real_dev; + struct sk_buff *frag; + int rc; + + frag = alloc_skb(real_dev->hard_header_len + + real_dev->needed_tailroom + size, + GFP_ATOMIC); + + if (likely(frag)) { + frag->dev = real_dev; + frag->priority = skb->priority; + skb_reserve(frag, real_dev->hard_header_len); + skb_reset_network_header(frag); + *mac_cb(frag) = *mac_cb(skb); + + rc = dev_hard_header(frag, real_dev, 0, &master_hdr->dest, + &master_hdr->source, size); + if (rc < 0) { + kfree_skb(frag); + return ERR_PTR(rc); + } + } else { + frag = ERR_PTR(-ENOMEM); + } + + return frag; +} + +static int +lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr, + u8 *frag_hdr, int frag_hdrlen, + int offset, int len) +{ + struct sk_buff *frag; + + raw_dump_inline(__func__, " fragment header", frag_hdr, frag_hdrlen); + + frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr); + if (IS_ERR(frag)) + return -PTR_ERR(frag); + + memcpy(skb_put(frag, frag_hdrlen), frag_hdr, frag_hdrlen); + memcpy(skb_put(frag, len), skb_network_header(skb) + offset, len); + + raw_dump_table(__func__, " fragment dump", frag->data, frag->len); + + return dev_queue_xmit(frag); +} + +static int +lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev, + const struct ieee802154_hdr *wpan_hdr) +{ + u16 dgram_size, dgram_offset; + __be16 frag_tag; + u8 frag_hdr[5]; + int frag_cap, frag_len, payload_cap, rc; + int skb_unprocessed, skb_offset; + + dgram_size = lowpan_uncompress_size(skb, &dgram_offset) - + skb->mac_len; + frag_tag = htons(lowpan_dev_info(dev)->fragment_tag); + lowpan_dev_info(dev)->fragment_tag++; + + frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07); + frag_hdr[1] = dgram_size & 0xff; + memcpy(frag_hdr + 2, &frag_tag, sizeof(frag_tag)); + + payload_cap = ieee802154_max_payload(wpan_hdr); + + frag_len = round_down(payload_cap - LOWPAN_FRAG1_HEAD_SIZE - + skb_network_header_len(skb), 8); + + skb_offset = skb_network_header_len(skb); + skb_unprocessed = skb->len - skb->mac_len - skb_offset; + + rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, + LOWPAN_FRAG1_HEAD_SIZE, 0, + frag_len + skb_network_header_len(skb)); + if (rc) { + pr_debug("%s unable to send FRAG1 packet (tag: %d)", + __func__, ntohs(frag_tag)); + goto err; + } + + frag_hdr[0] &= ~LOWPAN_DISPATCH_FRAG1; + frag_hdr[0] |= LOWPAN_DISPATCH_FRAGN; + frag_cap = round_down(payload_cap - LOWPAN_FRAGN_HEAD_SIZE, 8); + + do { + dgram_offset += frag_len; + skb_offset += frag_len; + skb_unprocessed -= frag_len; + frag_len = min(frag_cap, skb_unprocessed); + + frag_hdr[4] = dgram_offset >> 3; + + rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, + LOWPAN_FRAGN_HEAD_SIZE, skb_offset, + frag_len); + if (rc) { + pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n", + __func__, ntohs(frag_tag), skb_offset); + goto err; + } + } while (skb_unprocessed > frag_cap); + + consume_skb(skb); + return NET_XMIT_SUCCESS; + +err: + kfree_skb(skb); + return rc; +} + +static int lowpan_header(struct sk_buff *skb, struct net_device *dev) +{ + struct ieee802154_addr sa, da; + struct ieee802154_mac_cb *cb = mac_cb_init(skb); + struct lowpan_addr_info info; + void *daddr, *saddr; + + memcpy(&info, lowpan_skb_priv(skb), sizeof(info)); + + /* TODO: Currently we only support extended_addr */ + daddr = &info.daddr.u.extended_addr; + saddr = &info.saddr.u.extended_addr; + + lowpan_header_compress(skb, dev, ETH_P_IPV6, daddr, saddr, skb->len); + + cb->type = IEEE802154_FC_TYPE_DATA; + + /* prepare wpan address data */ + sa.mode = IEEE802154_ADDR_LONG; + sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); + sa.extended_addr = ieee802154_devaddr_from_raw(saddr); + + /* intra-PAN communications */ + da.pan_id = sa.pan_id; + + /* if the destination address is the broadcast address, use the + * corresponding short address + */ + if (lowpan_is_addr_broadcast((const u8 *)daddr)) { + da.mode = IEEE802154_ADDR_SHORT; + da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); + cb->ackreq = false; + } else { + da.mode = IEEE802154_ADDR_LONG; + da.extended_addr = ieee802154_devaddr_from_raw(daddr); + cb->ackreq = true; + } + + return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev, + ETH_P_IPV6, (void *)&da, (void *)&sa, 0); +} + +static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ieee802154_hdr wpan_hdr; + int max_single, ret; + + pr_debug("package xmit\n"); + + /* We must take a copy of the skb before we modify/replace the ipv6 + * header as the header could be used elsewhere + */ + skb = skb_unshare(skb, GFP_ATOMIC); + if (!skb) + return NET_XMIT_DROP; + + ret = lowpan_header(skb, dev); + if (ret < 0) { + kfree_skb(skb); + return NET_XMIT_DROP; + } + + if (ieee802154_hdr_peek(skb, &wpan_hdr) < 0) { + kfree_skb(skb); + return NET_XMIT_DROP; + } + + max_single = ieee802154_max_payload(&wpan_hdr); + + if (skb_tail_pointer(skb) - skb_network_header(skb) <= max_single) { + skb->dev = lowpan_dev_info(dev)->real_dev; + return dev_queue_xmit(skb); + } else { + netdev_tx_t rc; + + pr_debug("frame is too big, fragmentation is needed\n"); + rc = lowpan_xmit_fragmented(skb, dev, &wpan_hdr); + + return rc < 0 ? NET_XMIT_DROP : rc; + } +} + +static __le16 lowpan_get_pan_id(const struct net_device *dev) +{ + struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; + + return ieee802154_mlme_ops(real_dev)->get_pan_id(real_dev); +} + +static __le16 lowpan_get_short_addr(const struct net_device *dev) +{ + struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; + + return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev); +} + +static u8 lowpan_get_dsn(const struct net_device *dev) +{ + struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; + + return ieee802154_mlme_ops(real_dev)->get_dsn(real_dev); +} + +static struct header_ops lowpan_header_ops = { + .create = lowpan_header_create, +}; + +static struct lock_class_key lowpan_tx_busylock; +static struct lock_class_key lowpan_netdev_xmit_lock_key; + +static void lowpan_set_lockdep_class_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) +{ + lockdep_set_class(&txq->_xmit_lock, + &lowpan_netdev_xmit_lock_key); +} + +static int lowpan_dev_init(struct net_device *dev) +{ + netdev_for_each_tx_queue(dev, lowpan_set_lockdep_class_one, NULL); + dev->qdisc_tx_busylock = &lowpan_tx_busylock; + return 0; +} + +static const struct net_device_ops lowpan_netdev_ops = { + .ndo_init = lowpan_dev_init, + .ndo_start_xmit = lowpan_xmit, +}; + +static struct ieee802154_mlme_ops lowpan_mlme = { + .get_pan_id = lowpan_get_pan_id, + .get_short_addr = lowpan_get_short_addr, + .get_dsn = lowpan_get_dsn, +}; + +static void lowpan_setup(struct net_device *dev) +{ + dev->addr_len = IEEE802154_ADDR_LEN; + memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN); + dev->type = ARPHRD_IEEE802154; + /* Frame Control + Sequence Number + Address fields + Security Header */ + dev->hard_header_len = 2 + 1 + 20 + 14; + dev->needed_tailroom = 2; /* FCS */ + dev->mtu = IPV6_MIN_MTU; + dev->tx_queue_len = 0; + dev->flags = IFF_BROADCAST | IFF_MULTICAST; + dev->watchdog_timeo = 0; + + dev->netdev_ops = &lowpan_netdev_ops; + dev->header_ops = &lowpan_header_ops; + dev->ml_priv = &lowpan_mlme; + dev->destructor = free_netdev; +} + +static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[]) +{ + if (tb[IFLA_ADDRESS]) { + if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN) + return -EINVAL; + } + return 0; +} + +static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + struct ieee802154_hdr hdr; + int ret; + + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + goto drop; + + if (!netif_running(dev)) + goto drop_skb; + + if (skb->pkt_type == PACKET_OTHERHOST) + goto drop_skb; + + if (dev->type != ARPHRD_IEEE802154) + goto drop_skb; + + if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) + goto drop_skb; + + /* check that it's our buffer */ + if (skb->data[0] == LOWPAN_DISPATCH_IPV6) { + /* Pull off the 1-byte of 6lowpan header. */ + skb_pull(skb, 1); + return lowpan_give_skb_to_devices(skb, NULL); + } else { + switch (skb->data[0] & 0xe0) { + case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ + ret = iphc_decompress(skb, &hdr); + if (ret < 0) + goto drop_skb; + + return lowpan_give_skb_to_devices(skb, NULL); + case LOWPAN_DISPATCH_FRAG1: /* first fragment header */ + ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1); + if (ret == 1) { + ret = iphc_decompress(skb, &hdr); + if (ret < 0) + goto drop_skb; + + return lowpan_give_skb_to_devices(skb, NULL); + } else if (ret == -1) { + return NET_RX_DROP; + } else { + return NET_RX_SUCCESS; + } + case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */ + ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN); + if (ret == 1) { + ret = iphc_decompress(skb, &hdr); + if (ret < 0) + goto drop_skb; + + return lowpan_give_skb_to_devices(skb, NULL); + } else if (ret == -1) { + return NET_RX_DROP; + } else { + return NET_RX_SUCCESS; + } + default: + break; + } + } + +drop_skb: + kfree_skb(skb); +drop: + return NET_RX_DROP; +} + +static struct packet_type lowpan_packet_type = { + .type = htons(ETH_P_IEEE802154), + .func = lowpan_rcv, +}; + +static int lowpan_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + struct net_device *real_dev; + struct lowpan_dev_record *entry; + int ret; + + ASSERT_RTNL(); + + pr_debug("adding new link\n"); + + if (!tb[IFLA_LINK]) + return -EINVAL; + /* find and hold real wpan device */ + real_dev = dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); + if (!real_dev) + return -ENODEV; + if (real_dev->type != ARPHRD_IEEE802154) { + dev_put(real_dev); + return -EINVAL; + } + + lowpan_dev_info(dev)->real_dev = real_dev; + mutex_init(&lowpan_dev_info(dev)->dev_list_mtx); + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + dev_put(real_dev); + lowpan_dev_info(dev)->real_dev = NULL; + return -ENOMEM; + } + + entry->ldev = dev; + + /* Set the lowpan hardware address to the wpan hardware address. */ + memcpy(dev->dev_addr, real_dev->dev_addr, IEEE802154_ADDR_LEN); + + mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); + INIT_LIST_HEAD(&entry->list); + list_add_tail(&entry->list, &lowpan_devices); + mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); + + ret = register_netdevice(dev); + if (ret >= 0) { + if (!lowpan_open_count) + dev_add_pack(&lowpan_packet_type); + lowpan_open_count++; + } + + return ret; +} + +static void lowpan_dellink(struct net_device *dev, struct list_head *head) +{ + struct lowpan_dev_info *lowpan_dev = lowpan_dev_info(dev); + struct net_device *real_dev = lowpan_dev->real_dev; + struct lowpan_dev_record *entry, *tmp; + + ASSERT_RTNL(); + + lowpan_open_count--; + if (!lowpan_open_count) + dev_remove_pack(&lowpan_packet_type); + + mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); + list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { + if (entry->ldev == dev) { + list_del(&entry->list); + kfree(entry); + } + } + mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); + + mutex_destroy(&lowpan_dev_info(dev)->dev_list_mtx); + + unregister_netdevice_queue(dev, head); + + dev_put(real_dev); +} + +static struct rtnl_link_ops lowpan_link_ops __read_mostly = { + .kind = "lowpan", + .priv_size = sizeof(struct lowpan_dev_info), + .setup = lowpan_setup, + .newlink = lowpan_newlink, + .dellink = lowpan_dellink, + .validate = lowpan_validate, +}; + +static inline int __init lowpan_netlink_init(void) +{ + return rtnl_link_register(&lowpan_link_ops); +} + +static inline void lowpan_netlink_fini(void) +{ + rtnl_link_unregister(&lowpan_link_ops); +} + +static int lowpan_device_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + LIST_HEAD(del_list); + struct lowpan_dev_record *entry, *tmp; + + if (dev->type != ARPHRD_IEEE802154) + goto out; + + if (event == NETDEV_UNREGISTER) { + list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { + if (lowpan_dev_info(entry->ldev)->real_dev == dev) + lowpan_dellink(entry->ldev, &del_list); + } + + unregister_netdevice_many(&del_list); + } + +out: + return NOTIFY_DONE; +} + +static struct notifier_block lowpan_dev_notifier = { + .notifier_call = lowpan_device_event, +}; + +static int __init lowpan_init_module(void) +{ + int err = 0; + + err = lowpan_net_frag_init(); + if (err < 0) + goto out; + + err = lowpan_netlink_init(); + if (err < 0) + goto out_frag; + + err = register_netdevice_notifier(&lowpan_dev_notifier); + if (err < 0) + goto out_pack; + + return 0; + +out_pack: + lowpan_netlink_fini(); +out_frag: + lowpan_net_frag_exit(); +out: + return err; +} + +static void __exit lowpan_cleanup_module(void) +{ + lowpan_netlink_fini(); + + lowpan_net_frag_exit(); + + unregister_netdevice_notifier(&lowpan_dev_notifier); +} + +module_init(lowpan_init_module); +module_exit(lowpan_cleanup_module); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_RTNL_LINK("lowpan"); diff --git a/net/ieee802154/6lowpan/Kconfig b/net/ieee802154/6lowpan/Kconfig new file mode 100644 index 0000000..d24f985 --- /dev/null +++ b/net/ieee802154/6lowpan/Kconfig @@ -0,0 +1,5 @@ +config IEEE802154_6LOWPAN + tristate "6lowpan support over IEEE 802.15.4" + depends on 6LOWPAN + ---help--- + IPv6 compression over IEEE 802.15.4. diff --git a/net/ieee802154/6lowpan/Makefile b/net/ieee802154/6lowpan/Makefile new file mode 100644 index 0000000..936959b --- /dev/null +++ b/net/ieee802154/6lowpan/Makefile @@ -0,0 +1,3 @@ +obj-y += ieee802154_6lowpan.o + +ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c new file mode 100644 index 0000000..9d980ed --- /dev/null +++ b/net/ieee802154/6lowpan/reassembly.c @@ -0,0 +1,585 @@ +/* 6LoWPAN fragment reassembly + * + * + * Authors: + * Alexander Aring + * + * Based on: net/ipv6/reassembly.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "6LoWPAN: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "reassembly.h" + +static const char lowpan_frags_cache_name[] = "lowpan-frags"; + +struct lowpan_frag_info { + u16 d_tag; + u16 d_size; + u8 d_offset; +}; + +static struct lowpan_frag_info *lowpan_cb(struct sk_buff *skb) +{ + return (struct lowpan_frag_info *)skb->cb; +} + +static struct inet_frags lowpan_frags; + +static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, + struct sk_buff *prev, struct net_device *dev); + +static unsigned int lowpan_hash_frag(u16 tag, u16 d_size, + const struct ieee802154_addr *saddr, + const struct ieee802154_addr *daddr) +{ + net_get_random_once(&lowpan_frags.rnd, sizeof(lowpan_frags.rnd)); + return jhash_3words(ieee802154_addr_hash(saddr), + ieee802154_addr_hash(daddr), + (__force u32)(tag + (d_size << 16)), + lowpan_frags.rnd); +} + +static unsigned int lowpan_hashfn(const struct inet_frag_queue *q) +{ + const struct lowpan_frag_queue *fq; + + fq = container_of(q, struct lowpan_frag_queue, q); + return lowpan_hash_frag(fq->tag, fq->d_size, &fq->saddr, &fq->daddr); +} + +static bool lowpan_frag_match(const struct inet_frag_queue *q, const void *a) +{ + const struct lowpan_frag_queue *fq; + const struct lowpan_create_arg *arg = a; + + fq = container_of(q, struct lowpan_frag_queue, q); + return fq->tag == arg->tag && fq->d_size == arg->d_size && + ieee802154_addr_equal(&fq->saddr, arg->src) && + ieee802154_addr_equal(&fq->daddr, arg->dst); +} + +static void lowpan_frag_init(struct inet_frag_queue *q, const void *a) +{ + const struct lowpan_create_arg *arg = a; + struct lowpan_frag_queue *fq; + + fq = container_of(q, struct lowpan_frag_queue, q); + + fq->tag = arg->tag; + fq->d_size = arg->d_size; + fq->saddr = *arg->src; + fq->daddr = *arg->dst; +} + +static void lowpan_frag_expire(unsigned long data) +{ + struct frag_queue *fq; + struct net *net; + + fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q); + net = container_of(fq->q.net, struct net, ieee802154_lowpan.frags); + + spin_lock(&fq->q.lock); + + if (fq->q.flags & INET_FRAG_COMPLETE) + goto out; + + inet_frag_kill(&fq->q, &lowpan_frags); +out: + spin_unlock(&fq->q.lock); + inet_frag_put(&fq->q, &lowpan_frags); +} + +static inline struct lowpan_frag_queue * +fq_find(struct net *net, const struct lowpan_frag_info *frag_info, + const struct ieee802154_addr *src, + const struct ieee802154_addr *dst) +{ + struct inet_frag_queue *q; + struct lowpan_create_arg arg; + unsigned int hash; + struct netns_ieee802154_lowpan *ieee802154_lowpan = + net_ieee802154_lowpan(net); + + arg.tag = frag_info->d_tag; + arg.d_size = frag_info->d_size; + arg.src = src; + arg.dst = dst; + + hash = lowpan_hash_frag(frag_info->d_tag, frag_info->d_size, src, dst); + + q = inet_frag_find(&ieee802154_lowpan->frags, + &lowpan_frags, &arg, hash); + if (IS_ERR_OR_NULL(q)) { + inet_frag_maybe_warn_overflow(q, pr_fmt()); + return NULL; + } + return container_of(q, struct lowpan_frag_queue, q); +} + +static int lowpan_frag_queue(struct lowpan_frag_queue *fq, + struct sk_buff *skb, const u8 frag_type) +{ + struct sk_buff *prev, *next; + struct net_device *dev; + int end, offset; + + if (fq->q.flags & INET_FRAG_COMPLETE) + goto err; + + offset = lowpan_cb(skb)->d_offset << 3; + end = lowpan_cb(skb)->d_size; + + /* Is this the final fragment? */ + if (offset + skb->len == end) { + /* If we already have some bits beyond end + * or have different end, the segment is corrupted. + */ + if (end < fq->q.len || + ((fq->q.flags & INET_FRAG_LAST_IN) && end != fq->q.len)) + goto err; + fq->q.flags |= INET_FRAG_LAST_IN; + fq->q.len = end; + } else { + if (end > fq->q.len) { + /* Some bits beyond end -> corruption. */ + if (fq->q.flags & INET_FRAG_LAST_IN) + goto err; + fq->q.len = end; + } + } + + /* Find out which fragments are in front and at the back of us + * in the chain of fragments so far. We must know where to put + * this fragment, right? + */ + prev = fq->q.fragments_tail; + if (!prev || lowpan_cb(prev)->d_offset < lowpan_cb(skb)->d_offset) { + next = NULL; + goto found; + } + prev = NULL; + for (next = fq->q.fragments; next != NULL; next = next->next) { + if (lowpan_cb(next)->d_offset >= lowpan_cb(skb)->d_offset) + break; /* bingo! */ + prev = next; + } + +found: + /* Insert this fragment in the chain of fragments. */ + skb->next = next; + if (!next) + fq->q.fragments_tail = skb; + if (prev) + prev->next = skb; + else + fq->q.fragments = skb; + + dev = skb->dev; + if (dev) + skb->dev = NULL; + + fq->q.stamp = skb->tstamp; + if (frag_type == LOWPAN_DISPATCH_FRAG1) { + /* Calculate uncomp. 6lowpan header to estimate full size */ + fq->q.meat += lowpan_uncompress_size(skb, NULL); + fq->q.flags |= INET_FRAG_FIRST_IN; + } else { + fq->q.meat += skb->len; + } + add_frag_mem_limit(&fq->q, skb->truesize); + + if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && + fq->q.meat == fq->q.len) { + int res; + unsigned long orefdst = skb->_skb_refdst; + + skb->_skb_refdst = 0UL; + res = lowpan_frag_reasm(fq, prev, dev); + skb->_skb_refdst = orefdst; + return res; + } + + return -1; +err: + kfree_skb(skb); + return -1; +} + +/* Check if this packet is complete. + * Returns NULL on failure by any reason, and pointer + * to current nexthdr field in reassembled frame. + * + * It is called with locked fq, and caller must check that + * queue is eligible for reassembly i.e. it is not COMPLETE, + * the last and the first frames arrived and all the bits are here. + */ +static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *prev, + struct net_device *dev) +{ + struct sk_buff *fp, *head = fq->q.fragments; + int sum_truesize; + + inet_frag_kill(&fq->q, &lowpan_frags); + + /* Make the one we just received the head. */ + if (prev) { + head = prev->next; + fp = skb_clone(head, GFP_ATOMIC); + + if (!fp) + goto out_oom; + + fp->next = head->next; + if (!fp->next) + fq->q.fragments_tail = fp; + prev->next = fp; + + skb_morph(head, fq->q.fragments); + head->next = fq->q.fragments->next; + + consume_skb(fq->q.fragments); + fq->q.fragments = head; + } + + /* Head of list must not be cloned. */ + if (skb_unclone(head, GFP_ATOMIC)) + goto out_oom; + + /* If the first fragment is fragmented itself, we split + * it to two chunks: the first with data and paged part + * and the second, holding only fragments. + */ + if (skb_has_frag_list(head)) { + struct sk_buff *clone; + int i, plen = 0; + + clone = alloc_skb(0, GFP_ATOMIC); + if (!clone) + goto out_oom; + clone->next = head->next; + head->next = clone; + skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list; + skb_frag_list_init(head); + for (i = 0; i < skb_shinfo(head)->nr_frags; i++) + plen += skb_frag_size(&skb_shinfo(head)->frags[i]); + clone->len = head->data_len - plen; + clone->data_len = clone->len; + head->data_len -= clone->len; + head->len -= clone->len; + add_frag_mem_limit(&fq->q, clone->truesize); + } + + WARN_ON(head == NULL); + + sum_truesize = head->truesize; + for (fp = head->next; fp;) { + bool headstolen; + int delta; + struct sk_buff *next = fp->next; + + sum_truesize += fp->truesize; + if (skb_try_coalesce(head, fp, &headstolen, &delta)) { + kfree_skb_partial(fp, headstolen); + } else { + if (!skb_shinfo(head)->frag_list) + skb_shinfo(head)->frag_list = fp; + head->data_len += fp->len; + head->len += fp->len; + head->truesize += fp->truesize; + } + fp = next; + } + sub_frag_mem_limit(&fq->q, sum_truesize); + + head->next = NULL; + head->dev = dev; + head->tstamp = fq->q.stamp; + + fq->q.fragments = NULL; + fq->q.fragments_tail = NULL; + + return 1; +out_oom: + net_dbg_ratelimited("lowpan_frag_reasm: no memory for reassembly\n"); + return -1; +} + +static int lowpan_get_frag_info(struct sk_buff *skb, const u8 frag_type, + struct lowpan_frag_info *frag_info) +{ + bool fail; + u8 pattern = 0, low = 0; + __be16 d_tag = 0; + + fail = lowpan_fetch_skb(skb, &pattern, 1); + fail |= lowpan_fetch_skb(skb, &low, 1); + frag_info->d_size = (pattern & 7) << 8 | low; + fail |= lowpan_fetch_skb(skb, &d_tag, 2); + frag_info->d_tag = ntohs(d_tag); + + if (frag_type == LOWPAN_DISPATCH_FRAGN) { + fail |= lowpan_fetch_skb(skb, &frag_info->d_offset, 1); + } else { + skb_reset_network_header(skb); + frag_info->d_offset = 0; + } + + if (unlikely(fail)) + return -EIO; + + return 0; +} + +int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type) +{ + struct lowpan_frag_queue *fq; + struct net *net = dev_net(skb->dev); + struct lowpan_frag_info *frag_info = lowpan_cb(skb); + struct ieee802154_addr source, dest; + int err; + + source = mac_cb(skb)->source; + dest = mac_cb(skb)->dest; + + err = lowpan_get_frag_info(skb, frag_type, frag_info); + if (err < 0) + goto err; + + if (frag_info->d_size > IPV6_MIN_MTU) { + net_warn_ratelimited("lowpan_frag_rcv: datagram size exceeds MTU\n"); + goto err; + } + + fq = fq_find(net, frag_info, &source, &dest); + if (fq != NULL) { + int ret; + + spin_lock(&fq->q.lock); + ret = lowpan_frag_queue(fq, skb, frag_type); + spin_unlock(&fq->q.lock); + + inet_frag_put(&fq->q, &lowpan_frags); + return ret; + } + +err: + kfree_skb(skb); + return -1; +} +EXPORT_SYMBOL(lowpan_frag_rcv); + +#ifdef CONFIG_SYSCTL +static int zero; + +static struct ctl_table lowpan_frags_ns_ctl_table[] = { + { + .procname = "6lowpanfrag_high_thresh", + .data = &init_net.ieee802154_lowpan.frags.high_thresh, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &init_net.ieee802154_lowpan.frags.low_thresh + }, + { + .procname = "6lowpanfrag_low_thresh", + .data = &init_net.ieee802154_lowpan.frags.low_thresh, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &init_net.ieee802154_lowpan.frags.high_thresh + }, + { + .procname = "6lowpanfrag_time", + .data = &init_net.ieee802154_lowpan.frags.timeout, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_jiffies, + }, + { } +}; + +/* secret interval has been deprecated */ +static int lowpan_frags_secret_interval_unused; +static struct ctl_table lowpan_frags_ctl_table[] = { + { + .procname = "6lowpanfrag_secret_interval", + .data = &lowpan_frags_secret_interval_unused, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_jiffies, + }, + { } +}; + +static int __net_init lowpan_frags_ns_sysctl_register(struct net *net) +{ + struct ctl_table *table; + struct ctl_table_header *hdr; + struct netns_ieee802154_lowpan *ieee802154_lowpan = + net_ieee802154_lowpan(net); + + table = lowpan_frags_ns_ctl_table; + if (!net_eq(net, &init_net)) { + table = kmemdup(table, sizeof(lowpan_frags_ns_ctl_table), + GFP_KERNEL); + if (table == NULL) + goto err_alloc; + + table[0].data = &ieee802154_lowpan->frags.high_thresh; + table[0].extra1 = &ieee802154_lowpan->frags.low_thresh; + table[0].extra2 = &init_net.ieee802154_lowpan.frags.high_thresh; + table[1].data = &ieee802154_lowpan->frags.low_thresh; + table[1].extra2 = &ieee802154_lowpan->frags.high_thresh; + table[2].data = &ieee802154_lowpan->frags.timeout; + + /* Don't export sysctls to unprivileged users */ + if (net->user_ns != &init_user_ns) + table[0].procname = NULL; + } + + hdr = register_net_sysctl(net, "net/ieee802154/6lowpan", table); + if (hdr == NULL) + goto err_reg; + + ieee802154_lowpan->sysctl.frags_hdr = hdr; + return 0; + +err_reg: + if (!net_eq(net, &init_net)) + kfree(table); +err_alloc: + return -ENOMEM; +} + +static void __net_exit lowpan_frags_ns_sysctl_unregister(struct net *net) +{ + struct ctl_table *table; + struct netns_ieee802154_lowpan *ieee802154_lowpan = + net_ieee802154_lowpan(net); + + table = ieee802154_lowpan->sysctl.frags_hdr->ctl_table_arg; + unregister_net_sysctl_table(ieee802154_lowpan->sysctl.frags_hdr); + if (!net_eq(net, &init_net)) + kfree(table); +} + +static struct ctl_table_header *lowpan_ctl_header; + +static int __init lowpan_frags_sysctl_register(void) +{ + lowpan_ctl_header = register_net_sysctl(&init_net, + "net/ieee802154/6lowpan", + lowpan_frags_ctl_table); + return lowpan_ctl_header == NULL ? -ENOMEM : 0; +} + +static void lowpan_frags_sysctl_unregister(void) +{ + unregister_net_sysctl_table(lowpan_ctl_header); +} +#else +static inline int lowpan_frags_ns_sysctl_register(struct net *net) +{ + return 0; +} + +static inline void lowpan_frags_ns_sysctl_unregister(struct net *net) +{ +} + +static inline int __init lowpan_frags_sysctl_register(void) +{ + return 0; +} + +static inline void lowpan_frags_sysctl_unregister(void) +{ +} +#endif + +static int __net_init lowpan_frags_init_net(struct net *net) +{ + struct netns_ieee802154_lowpan *ieee802154_lowpan = + net_ieee802154_lowpan(net); + + ieee802154_lowpan->frags.high_thresh = IPV6_FRAG_HIGH_THRESH; + ieee802154_lowpan->frags.low_thresh = IPV6_FRAG_LOW_THRESH; + ieee802154_lowpan->frags.timeout = IPV6_FRAG_TIMEOUT; + + inet_frags_init_net(&ieee802154_lowpan->frags); + + return lowpan_frags_ns_sysctl_register(net); +} + +static void __net_exit lowpan_frags_exit_net(struct net *net) +{ + struct netns_ieee802154_lowpan *ieee802154_lowpan = + net_ieee802154_lowpan(net); + + lowpan_frags_ns_sysctl_unregister(net); + inet_frags_exit_net(&ieee802154_lowpan->frags, &lowpan_frags); +} + +static struct pernet_operations lowpan_frags_ops = { + .init = lowpan_frags_init_net, + .exit = lowpan_frags_exit_net, +}; + +int __init lowpan_net_frag_init(void) +{ + int ret; + + ret = lowpan_frags_sysctl_register(); + if (ret) + return ret; + + ret = register_pernet_subsys(&lowpan_frags_ops); + if (ret) + goto err_pernet; + + lowpan_frags.hashfn = lowpan_hashfn; + lowpan_frags.constructor = lowpan_frag_init; + lowpan_frags.destructor = NULL; + lowpan_frags.skb_free = NULL; + lowpan_frags.qsize = sizeof(struct frag_queue); + lowpan_frags.match = lowpan_frag_match; + lowpan_frags.frag_expire = lowpan_frag_expire; + lowpan_frags.frags_cache_name = lowpan_frags_cache_name; + ret = inet_frags_init(&lowpan_frags); + if (ret) + goto err_pernet; + + return ret; +err_pernet: + lowpan_frags_sysctl_unregister(); + return ret; +} + +void lowpan_net_frag_exit(void) +{ + inet_frags_fini(&lowpan_frags); + lowpan_frags_sysctl_unregister(); + unregister_pernet_subsys(&lowpan_frags_ops); +} diff --git a/net/ieee802154/6lowpan/reassembly.h b/net/ieee802154/6lowpan/reassembly.h new file mode 100644 index 0000000..836b16f --- /dev/null +++ b/net/ieee802154/6lowpan/reassembly.h @@ -0,0 +1,41 @@ +#ifndef __IEEE802154_6LOWPAN_REASSEMBLY_H__ +#define __IEEE802154_6LOWPAN_REASSEMBLY_H__ + +#include + +struct lowpan_create_arg { + u16 tag; + u16 d_size; + const struct ieee802154_addr *src; + const struct ieee802154_addr *dst; +}; + +/* Equivalent of ipv4 struct ip + */ +struct lowpan_frag_queue { + struct inet_frag_queue q; + + u16 tag; + u16 d_size; + struct ieee802154_addr saddr; + struct ieee802154_addr daddr; +}; + +static inline u32 ieee802154_addr_hash(const struct ieee802154_addr *a) +{ + switch (a->mode) { + case IEEE802154_ADDR_LONG: + return (((__force u64)a->extended_addr) >> 32) ^ + (((__force u64)a->extended_addr) & 0xffffffff); + case IEEE802154_ADDR_SHORT: + return (__force u32)(a->short_addr); + default: + return 0; + } +} + +int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type); +void lowpan_net_frag_exit(void); +int lowpan_net_frag_init(void); + +#endif /* __IEEE802154_6LOWPAN_REASSEMBLY_H__ */ diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c deleted file mode 100644 index 27eaa65e..0000000 --- a/net/ieee802154/6lowpan_rtnl.c +++ /dev/null @@ -1,729 +0,0 @@ -/* Copyright 2011, Siemens AG - * written by Alexander Smirnov - */ - -/* Based on patches from Jon Smirl - * Copyright (c) 2011 Jon Smirl - * - * 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. - */ - -/* Jon's code is based on 6lowpan implementation for Contiki which is: - * Copyright (c) 2008, Swedish Institute of Computer Science. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "reassembly.h" - -static LIST_HEAD(lowpan_devices); -static int lowpan_open_count; - -/* private device info */ -struct lowpan_dev_info { - struct net_device *real_dev; /* real WPAN device ptr */ - struct mutex dev_list_mtx; /* mutex for list ops */ - u16 fragment_tag; -}; - -struct lowpan_dev_record { - struct net_device *ldev; - struct list_head list; -}; - -/* don't save pan id, it's intra pan */ -struct lowpan_addr { - u8 mode; - union { - /* IPv6 needs big endian here */ - __be64 extended_addr; - __be16 short_addr; - } u; -}; - -struct lowpan_addr_info { - struct lowpan_addr daddr; - struct lowpan_addr saddr; -}; - -static inline struct -lowpan_dev_info *lowpan_dev_info(const struct net_device *dev) -{ - return netdev_priv(dev); -} - -static inline struct -lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb) -{ - WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct lowpan_addr_info)); - return (struct lowpan_addr_info *)(skb->data - - sizeof(struct lowpan_addr_info)); -} - -static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, - unsigned short type, const void *_daddr, - const void *_saddr, unsigned int len) -{ - const u8 *saddr = _saddr; - const u8 *daddr = _daddr; - struct lowpan_addr_info *info; - - /* TODO: - * if this package isn't ipv6 one, where should it be routed? - */ - if (type != ETH_P_IPV6) - return 0; - - if (!saddr) - saddr = dev->dev_addr; - - raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8); - raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8); - - info = lowpan_skb_priv(skb); - - /* TODO: Currently we only support extended_addr */ - info->daddr.mode = IEEE802154_ADDR_LONG; - memcpy(&info->daddr.u.extended_addr, daddr, - sizeof(info->daddr.u.extended_addr)); - info->saddr.mode = IEEE802154_ADDR_LONG; - memcpy(&info->saddr.u.extended_addr, saddr, - sizeof(info->daddr.u.extended_addr)); - - return 0; -} - -static int lowpan_give_skb_to_devices(struct sk_buff *skb, - struct net_device *dev) -{ - struct lowpan_dev_record *entry; - struct sk_buff *skb_cp; - int stat = NET_RX_SUCCESS; - - skb->protocol = htons(ETH_P_IPV6); - skb->pkt_type = PACKET_HOST; - - rcu_read_lock(); - list_for_each_entry_rcu(entry, &lowpan_devices, list) - if (lowpan_dev_info(entry->ldev)->real_dev == skb->dev) { - skb_cp = skb_copy(skb, GFP_ATOMIC); - if (!skb_cp) { - kfree_skb(skb); - rcu_read_unlock(); - return NET_RX_DROP; - } - - skb_cp->dev = entry->ldev; - stat = netif_rx(skb_cp); - if (stat == NET_RX_DROP) - break; - } - rcu_read_unlock(); - - consume_skb(skb); - - return stat; -} - -static int -iphc_decompress(struct sk_buff *skb, const struct ieee802154_hdr *hdr) -{ - u8 iphc0, iphc1; - struct ieee802154_addr_sa sa, da; - void *sap, *dap; - - raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len); - /* at least two bytes will be used for the encoding */ - if (skb->len < 2) - return -EINVAL; - - if (lowpan_fetch_skb_u8(skb, &iphc0)) - return -EINVAL; - - if (lowpan_fetch_skb_u8(skb, &iphc1)) - return -EINVAL; - - ieee802154_addr_to_sa(&sa, &hdr->source); - ieee802154_addr_to_sa(&da, &hdr->dest); - - if (sa.addr_type == IEEE802154_ADDR_SHORT) - sap = &sa.short_addr; - else - sap = &sa.hwaddr; - - if (da.addr_type == IEEE802154_ADDR_SHORT) - dap = &da.short_addr; - else - dap = &da.hwaddr; - - return lowpan_header_decompress(skb, skb->dev, sap, sa.addr_type, - IEEE802154_ADDR_LEN, dap, da.addr_type, - IEEE802154_ADDR_LEN, iphc0, iphc1); -} - -static struct sk_buff* -lowpan_alloc_frag(struct sk_buff *skb, int size, - const struct ieee802154_hdr *master_hdr) -{ - struct net_device *real_dev = lowpan_dev_info(skb->dev)->real_dev; - struct sk_buff *frag; - int rc; - - frag = alloc_skb(real_dev->hard_header_len + - real_dev->needed_tailroom + size, - GFP_ATOMIC); - - if (likely(frag)) { - frag->dev = real_dev; - frag->priority = skb->priority; - skb_reserve(frag, real_dev->hard_header_len); - skb_reset_network_header(frag); - *mac_cb(frag) = *mac_cb(skb); - - rc = dev_hard_header(frag, real_dev, 0, &master_hdr->dest, - &master_hdr->source, size); - if (rc < 0) { - kfree_skb(frag); - return ERR_PTR(rc); - } - } else { - frag = ERR_PTR(-ENOMEM); - } - - return frag; -} - -static int -lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr, - u8 *frag_hdr, int frag_hdrlen, - int offset, int len) -{ - struct sk_buff *frag; - - raw_dump_inline(__func__, " fragment header", frag_hdr, frag_hdrlen); - - frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr); - if (IS_ERR(frag)) - return -PTR_ERR(frag); - - memcpy(skb_put(frag, frag_hdrlen), frag_hdr, frag_hdrlen); - memcpy(skb_put(frag, len), skb_network_header(skb) + offset, len); - - raw_dump_table(__func__, " fragment dump", frag->data, frag->len); - - return dev_queue_xmit(frag); -} - -static int -lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev, - const struct ieee802154_hdr *wpan_hdr) -{ - u16 dgram_size, dgram_offset; - __be16 frag_tag; - u8 frag_hdr[5]; - int frag_cap, frag_len, payload_cap, rc; - int skb_unprocessed, skb_offset; - - dgram_size = lowpan_uncompress_size(skb, &dgram_offset) - - skb->mac_len; - frag_tag = htons(lowpan_dev_info(dev)->fragment_tag); - lowpan_dev_info(dev)->fragment_tag++; - - frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07); - frag_hdr[1] = dgram_size & 0xff; - memcpy(frag_hdr + 2, &frag_tag, sizeof(frag_tag)); - - payload_cap = ieee802154_max_payload(wpan_hdr); - - frag_len = round_down(payload_cap - LOWPAN_FRAG1_HEAD_SIZE - - skb_network_header_len(skb), 8); - - skb_offset = skb_network_header_len(skb); - skb_unprocessed = skb->len - skb->mac_len - skb_offset; - - rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, - LOWPAN_FRAG1_HEAD_SIZE, 0, - frag_len + skb_network_header_len(skb)); - if (rc) { - pr_debug("%s unable to send FRAG1 packet (tag: %d)", - __func__, ntohs(frag_tag)); - goto err; - } - - frag_hdr[0] &= ~LOWPAN_DISPATCH_FRAG1; - frag_hdr[0] |= LOWPAN_DISPATCH_FRAGN; - frag_cap = round_down(payload_cap - LOWPAN_FRAGN_HEAD_SIZE, 8); - - do { - dgram_offset += frag_len; - skb_offset += frag_len; - skb_unprocessed -= frag_len; - frag_len = min(frag_cap, skb_unprocessed); - - frag_hdr[4] = dgram_offset >> 3; - - rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, - LOWPAN_FRAGN_HEAD_SIZE, skb_offset, - frag_len); - if (rc) { - pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n", - __func__, ntohs(frag_tag), skb_offset); - goto err; - } - } while (skb_unprocessed > frag_cap); - - consume_skb(skb); - return NET_XMIT_SUCCESS; - -err: - kfree_skb(skb); - return rc; -} - -static int lowpan_header(struct sk_buff *skb, struct net_device *dev) -{ - struct ieee802154_addr sa, da; - struct ieee802154_mac_cb *cb = mac_cb_init(skb); - struct lowpan_addr_info info; - void *daddr, *saddr; - - memcpy(&info, lowpan_skb_priv(skb), sizeof(info)); - - /* TODO: Currently we only support extended_addr */ - daddr = &info.daddr.u.extended_addr; - saddr = &info.saddr.u.extended_addr; - - lowpan_header_compress(skb, dev, ETH_P_IPV6, daddr, saddr, skb->len); - - cb->type = IEEE802154_FC_TYPE_DATA; - - /* prepare wpan address data */ - sa.mode = IEEE802154_ADDR_LONG; - sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); - sa.extended_addr = ieee802154_devaddr_from_raw(saddr); - - /* intra-PAN communications */ - da.pan_id = sa.pan_id; - - /* if the destination address is the broadcast address, use the - * corresponding short address - */ - if (lowpan_is_addr_broadcast((const u8 *)daddr)) { - da.mode = IEEE802154_ADDR_SHORT; - da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); - cb->ackreq = false; - } else { - da.mode = IEEE802154_ADDR_LONG; - da.extended_addr = ieee802154_devaddr_from_raw(daddr); - cb->ackreq = true; - } - - return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev, - ETH_P_IPV6, (void *)&da, (void *)&sa, 0); -} - -static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct ieee802154_hdr wpan_hdr; - int max_single, ret; - - pr_debug("package xmit\n"); - - /* We must take a copy of the skb before we modify/replace the ipv6 - * header as the header could be used elsewhere - */ - skb = skb_unshare(skb, GFP_ATOMIC); - if (!skb) - return NET_XMIT_DROP; - - ret = lowpan_header(skb, dev); - if (ret < 0) { - kfree_skb(skb); - return NET_XMIT_DROP; - } - - if (ieee802154_hdr_peek(skb, &wpan_hdr) < 0) { - kfree_skb(skb); - return NET_XMIT_DROP; - } - - max_single = ieee802154_max_payload(&wpan_hdr); - - if (skb_tail_pointer(skb) - skb_network_header(skb) <= max_single) { - skb->dev = lowpan_dev_info(dev)->real_dev; - return dev_queue_xmit(skb); - } else { - netdev_tx_t rc; - - pr_debug("frame is too big, fragmentation is needed\n"); - rc = lowpan_xmit_fragmented(skb, dev, &wpan_hdr); - - return rc < 0 ? NET_XMIT_DROP : rc; - } -} - -static __le16 lowpan_get_pan_id(const struct net_device *dev) -{ - struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; - - return ieee802154_mlme_ops(real_dev)->get_pan_id(real_dev); -} - -static __le16 lowpan_get_short_addr(const struct net_device *dev) -{ - struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; - - return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev); -} - -static u8 lowpan_get_dsn(const struct net_device *dev) -{ - struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; - - return ieee802154_mlme_ops(real_dev)->get_dsn(real_dev); -} - -static struct header_ops lowpan_header_ops = { - .create = lowpan_header_create, -}; - -static struct lock_class_key lowpan_tx_busylock; -static struct lock_class_key lowpan_netdev_xmit_lock_key; - -static void lowpan_set_lockdep_class_one(struct net_device *dev, - struct netdev_queue *txq, - void *_unused) -{ - lockdep_set_class(&txq->_xmit_lock, - &lowpan_netdev_xmit_lock_key); -} - -static int lowpan_dev_init(struct net_device *dev) -{ - netdev_for_each_tx_queue(dev, lowpan_set_lockdep_class_one, NULL); - dev->qdisc_tx_busylock = &lowpan_tx_busylock; - return 0; -} - -static const struct net_device_ops lowpan_netdev_ops = { - .ndo_init = lowpan_dev_init, - .ndo_start_xmit = lowpan_xmit, -}; - -static struct ieee802154_mlme_ops lowpan_mlme = { - .get_pan_id = lowpan_get_pan_id, - .get_short_addr = lowpan_get_short_addr, - .get_dsn = lowpan_get_dsn, -}; - -static void lowpan_setup(struct net_device *dev) -{ - dev->addr_len = IEEE802154_ADDR_LEN; - memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN); - dev->type = ARPHRD_IEEE802154; - /* Frame Control + Sequence Number + Address fields + Security Header */ - dev->hard_header_len = 2 + 1 + 20 + 14; - dev->needed_tailroom = 2; /* FCS */ - dev->mtu = IPV6_MIN_MTU; - dev->tx_queue_len = 0; - dev->flags = IFF_BROADCAST | IFF_MULTICAST; - dev->watchdog_timeo = 0; - - dev->netdev_ops = &lowpan_netdev_ops; - dev->header_ops = &lowpan_header_ops; - dev->ml_priv = &lowpan_mlme; - dev->destructor = free_netdev; -} - -static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[]) -{ - if (tb[IFLA_ADDRESS]) { - if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN) - return -EINVAL; - } - return 0; -} - -static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) -{ - struct ieee802154_hdr hdr; - int ret; - - skb = skb_share_check(skb, GFP_ATOMIC); - if (!skb) - goto drop; - - if (!netif_running(dev)) - goto drop_skb; - - if (skb->pkt_type == PACKET_OTHERHOST) - goto drop_skb; - - if (dev->type != ARPHRD_IEEE802154) - goto drop_skb; - - if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) - goto drop_skb; - - /* check that it's our buffer */ - if (skb->data[0] == LOWPAN_DISPATCH_IPV6) { - /* Pull off the 1-byte of 6lowpan header. */ - skb_pull(skb, 1); - return lowpan_give_skb_to_devices(skb, NULL); - } else { - switch (skb->data[0] & 0xe0) { - case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ - ret = iphc_decompress(skb, &hdr); - if (ret < 0) - goto drop_skb; - - return lowpan_give_skb_to_devices(skb, NULL); - case LOWPAN_DISPATCH_FRAG1: /* first fragment header */ - ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1); - if (ret == 1) { - ret = iphc_decompress(skb, &hdr); - if (ret < 0) - goto drop_skb; - - return lowpan_give_skb_to_devices(skb, NULL); - } else if (ret == -1) { - return NET_RX_DROP; - } else { - return NET_RX_SUCCESS; - } - case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */ - ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN); - if (ret == 1) { - ret = iphc_decompress(skb, &hdr); - if (ret < 0) - goto drop_skb; - - return lowpan_give_skb_to_devices(skb, NULL); - } else if (ret == -1) { - return NET_RX_DROP; - } else { - return NET_RX_SUCCESS; - } - default: - break; - } - } - -drop_skb: - kfree_skb(skb); -drop: - return NET_RX_DROP; -} - -static struct packet_type lowpan_packet_type = { - .type = htons(ETH_P_IEEE802154), - .func = lowpan_rcv, -}; - -static int lowpan_newlink(struct net *src_net, struct net_device *dev, - struct nlattr *tb[], struct nlattr *data[]) -{ - struct net_device *real_dev; - struct lowpan_dev_record *entry; - int ret; - - ASSERT_RTNL(); - - pr_debug("adding new link\n"); - - if (!tb[IFLA_LINK]) - return -EINVAL; - /* find and hold real wpan device */ - real_dev = dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); - if (!real_dev) - return -ENODEV; - if (real_dev->type != ARPHRD_IEEE802154) { - dev_put(real_dev); - return -EINVAL; - } - - lowpan_dev_info(dev)->real_dev = real_dev; - mutex_init(&lowpan_dev_info(dev)->dev_list_mtx); - - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) { - dev_put(real_dev); - lowpan_dev_info(dev)->real_dev = NULL; - return -ENOMEM; - } - - entry->ldev = dev; - - /* Set the lowpan hardware address to the wpan hardware address. */ - memcpy(dev->dev_addr, real_dev->dev_addr, IEEE802154_ADDR_LEN); - - mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); - INIT_LIST_HEAD(&entry->list); - list_add_tail(&entry->list, &lowpan_devices); - mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); - - ret = register_netdevice(dev); - if (ret >= 0) { - if (!lowpan_open_count) - dev_add_pack(&lowpan_packet_type); - lowpan_open_count++; - } - - return ret; -} - -static void lowpan_dellink(struct net_device *dev, struct list_head *head) -{ - struct lowpan_dev_info *lowpan_dev = lowpan_dev_info(dev); - struct net_device *real_dev = lowpan_dev->real_dev; - struct lowpan_dev_record *entry, *tmp; - - ASSERT_RTNL(); - - lowpan_open_count--; - if (!lowpan_open_count) - dev_remove_pack(&lowpan_packet_type); - - mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); - list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { - if (entry->ldev == dev) { - list_del(&entry->list); - kfree(entry); - } - } - mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); - - mutex_destroy(&lowpan_dev_info(dev)->dev_list_mtx); - - unregister_netdevice_queue(dev, head); - - dev_put(real_dev); -} - -static struct rtnl_link_ops lowpan_link_ops __read_mostly = { - .kind = "lowpan", - .priv_size = sizeof(struct lowpan_dev_info), - .setup = lowpan_setup, - .newlink = lowpan_newlink, - .dellink = lowpan_dellink, - .validate = lowpan_validate, -}; - -static inline int __init lowpan_netlink_init(void) -{ - return rtnl_link_register(&lowpan_link_ops); -} - -static inline void lowpan_netlink_fini(void) -{ - rtnl_link_unregister(&lowpan_link_ops); -} - -static int lowpan_device_event(struct notifier_block *unused, - unsigned long event, void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - LIST_HEAD(del_list); - struct lowpan_dev_record *entry, *tmp; - - if (dev->type != ARPHRD_IEEE802154) - goto out; - - if (event == NETDEV_UNREGISTER) { - list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { - if (lowpan_dev_info(entry->ldev)->real_dev == dev) - lowpan_dellink(entry->ldev, &del_list); - } - - unregister_netdevice_many(&del_list); - } - -out: - return NOTIFY_DONE; -} - -static struct notifier_block lowpan_dev_notifier = { - .notifier_call = lowpan_device_event, -}; - -static int __init lowpan_init_module(void) -{ - int err = 0; - - err = lowpan_net_frag_init(); - if (err < 0) - goto out; - - err = lowpan_netlink_init(); - if (err < 0) - goto out_frag; - - err = register_netdevice_notifier(&lowpan_dev_notifier); - if (err < 0) - goto out_pack; - - return 0; - -out_pack: - lowpan_netlink_fini(); -out_frag: - lowpan_net_frag_exit(); -out: - return err; -} - -static void __exit lowpan_cleanup_module(void) -{ - lowpan_netlink_fini(); - - lowpan_net_frag_exit(); - - unregister_netdevice_notifier(&lowpan_dev_notifier); -} - -module_init(lowpan_init_module); -module_exit(lowpan_cleanup_module); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_RTNL_LINK("lowpan"); diff --git a/net/ieee802154/Kconfig b/net/ieee802154/Kconfig index 9ea0af4..1370d5b 100644 --- a/net/ieee802154/Kconfig +++ b/net/ieee802154/Kconfig @@ -20,10 +20,6 @@ config IEEE802154_SOCKET for 802.15.4 dataframes. Also RAW socket interface to build MAC header from userspace. -config IEEE802154_6LOWPAN - tristate "6lowpan support over IEEE 802.15.4" - depends on 6LOWPAN - ---help--- - IPv6 compression over IEEE 802.15.4. +source "net/ieee802154/6lowpan/Kconfig" endif diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile index 88bde78..3653397 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile @@ -1,8 +1,7 @@ obj-$(CONFIG_IEEE802154) += ieee802154.o obj-$(CONFIG_IEEE802154_SOCKET) += ieee802154_socket.o -obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o +obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan/ -ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \ header_ops.o sysfs.o nl802154.o ieee802154_socket-y := socket.o diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c deleted file mode 100644 index 9d980ed..0000000 --- a/net/ieee802154/reassembly.c +++ /dev/null @@ -1,585 +0,0 @@ -/* 6LoWPAN fragment reassembly - * - * - * Authors: - * Alexander Aring - * - * Based on: net/ipv6/reassembly.c - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#define pr_fmt(fmt) "6LoWPAN: " fmt - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "reassembly.h" - -static const char lowpan_frags_cache_name[] = "lowpan-frags"; - -struct lowpan_frag_info { - u16 d_tag; - u16 d_size; - u8 d_offset; -}; - -static struct lowpan_frag_info *lowpan_cb(struct sk_buff *skb) -{ - return (struct lowpan_frag_info *)skb->cb; -} - -static struct inet_frags lowpan_frags; - -static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, - struct sk_buff *prev, struct net_device *dev); - -static unsigned int lowpan_hash_frag(u16 tag, u16 d_size, - const struct ieee802154_addr *saddr, - const struct ieee802154_addr *daddr) -{ - net_get_random_once(&lowpan_frags.rnd, sizeof(lowpan_frags.rnd)); - return jhash_3words(ieee802154_addr_hash(saddr), - ieee802154_addr_hash(daddr), - (__force u32)(tag + (d_size << 16)), - lowpan_frags.rnd); -} - -static unsigned int lowpan_hashfn(const struct inet_frag_queue *q) -{ - const struct lowpan_frag_queue *fq; - - fq = container_of(q, struct lowpan_frag_queue, q); - return lowpan_hash_frag(fq->tag, fq->d_size, &fq->saddr, &fq->daddr); -} - -static bool lowpan_frag_match(const struct inet_frag_queue *q, const void *a) -{ - const struct lowpan_frag_queue *fq; - const struct lowpan_create_arg *arg = a; - - fq = container_of(q, struct lowpan_frag_queue, q); - return fq->tag == arg->tag && fq->d_size == arg->d_size && - ieee802154_addr_equal(&fq->saddr, arg->src) && - ieee802154_addr_equal(&fq->daddr, arg->dst); -} - -static void lowpan_frag_init(struct inet_frag_queue *q, const void *a) -{ - const struct lowpan_create_arg *arg = a; - struct lowpan_frag_queue *fq; - - fq = container_of(q, struct lowpan_frag_queue, q); - - fq->tag = arg->tag; - fq->d_size = arg->d_size; - fq->saddr = *arg->src; - fq->daddr = *arg->dst; -} - -static void lowpan_frag_expire(unsigned long data) -{ - struct frag_queue *fq; - struct net *net; - - fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q); - net = container_of(fq->q.net, struct net, ieee802154_lowpan.frags); - - spin_lock(&fq->q.lock); - - if (fq->q.flags & INET_FRAG_COMPLETE) - goto out; - - inet_frag_kill(&fq->q, &lowpan_frags); -out: - spin_unlock(&fq->q.lock); - inet_frag_put(&fq->q, &lowpan_frags); -} - -static inline struct lowpan_frag_queue * -fq_find(struct net *net, const struct lowpan_frag_info *frag_info, - const struct ieee802154_addr *src, - const struct ieee802154_addr *dst) -{ - struct inet_frag_queue *q; - struct lowpan_create_arg arg; - unsigned int hash; - struct netns_ieee802154_lowpan *ieee802154_lowpan = - net_ieee802154_lowpan(net); - - arg.tag = frag_info->d_tag; - arg.d_size = frag_info->d_size; - arg.src = src; - arg.dst = dst; - - hash = lowpan_hash_frag(frag_info->d_tag, frag_info->d_size, src, dst); - - q = inet_frag_find(&ieee802154_lowpan->frags, - &lowpan_frags, &arg, hash); - if (IS_ERR_OR_NULL(q)) { - inet_frag_maybe_warn_overflow(q, pr_fmt()); - return NULL; - } - return container_of(q, struct lowpan_frag_queue, q); -} - -static int lowpan_frag_queue(struct lowpan_frag_queue *fq, - struct sk_buff *skb, const u8 frag_type) -{ - struct sk_buff *prev, *next; - struct net_device *dev; - int end, offset; - - if (fq->q.flags & INET_FRAG_COMPLETE) - goto err; - - offset = lowpan_cb(skb)->d_offset << 3; - end = lowpan_cb(skb)->d_size; - - /* Is this the final fragment? */ - if (offset + skb->len == end) { - /* If we already have some bits beyond end - * or have different end, the segment is corrupted. - */ - if (end < fq->q.len || - ((fq->q.flags & INET_FRAG_LAST_IN) && end != fq->q.len)) - goto err; - fq->q.flags |= INET_FRAG_LAST_IN; - fq->q.len = end; - } else { - if (end > fq->q.len) { - /* Some bits beyond end -> corruption. */ - if (fq->q.flags & INET_FRAG_LAST_IN) - goto err; - fq->q.len = end; - } - } - - /* Find out which fragments are in front and at the back of us - * in the chain of fragments so far. We must know where to put - * this fragment, right? - */ - prev = fq->q.fragments_tail; - if (!prev || lowpan_cb(prev)->d_offset < lowpan_cb(skb)->d_offset) { - next = NULL; - goto found; - } - prev = NULL; - for (next = fq->q.fragments; next != NULL; next = next->next) { - if (lowpan_cb(next)->d_offset >= lowpan_cb(skb)->d_offset) - break; /* bingo! */ - prev = next; - } - -found: - /* Insert this fragment in the chain of fragments. */ - skb->next = next; - if (!next) - fq->q.fragments_tail = skb; - if (prev) - prev->next = skb; - else - fq->q.fragments = skb; - - dev = skb->dev; - if (dev) - skb->dev = NULL; - - fq->q.stamp = skb->tstamp; - if (frag_type == LOWPAN_DISPATCH_FRAG1) { - /* Calculate uncomp. 6lowpan header to estimate full size */ - fq->q.meat += lowpan_uncompress_size(skb, NULL); - fq->q.flags |= INET_FRAG_FIRST_IN; - } else { - fq->q.meat += skb->len; - } - add_frag_mem_limit(&fq->q, skb->truesize); - - if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && - fq->q.meat == fq->q.len) { - int res; - unsigned long orefdst = skb->_skb_refdst; - - skb->_skb_refdst = 0UL; - res = lowpan_frag_reasm(fq, prev, dev); - skb->_skb_refdst = orefdst; - return res; - } - - return -1; -err: - kfree_skb(skb); - return -1; -} - -/* Check if this packet is complete. - * Returns NULL on failure by any reason, and pointer - * to current nexthdr field in reassembled frame. - * - * It is called with locked fq, and caller must check that - * queue is eligible for reassembly i.e. it is not COMPLETE, - * the last and the first frames arrived and all the bits are here. - */ -static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *prev, - struct net_device *dev) -{ - struct sk_buff *fp, *head = fq->q.fragments; - int sum_truesize; - - inet_frag_kill(&fq->q, &lowpan_frags); - - /* Make the one we just received the head. */ - if (prev) { - head = prev->next; - fp = skb_clone(head, GFP_ATOMIC); - - if (!fp) - goto out_oom; - - fp->next = head->next; - if (!fp->next) - fq->q.fragments_tail = fp; - prev->next = fp; - - skb_morph(head, fq->q.fragments); - head->next = fq->q.fragments->next; - - consume_skb(fq->q.fragments); - fq->q.fragments = head; - } - - /* Head of list must not be cloned. */ - if (skb_unclone(head, GFP_ATOMIC)) - goto out_oom; - - /* If the first fragment is fragmented itself, we split - * it to two chunks: the first with data and paged part - * and the second, holding only fragments. - */ - if (skb_has_frag_list(head)) { - struct sk_buff *clone; - int i, plen = 0; - - clone = alloc_skb(0, GFP_ATOMIC); - if (!clone) - goto out_oom; - clone->next = head->next; - head->next = clone; - skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list; - skb_frag_list_init(head); - for (i = 0; i < skb_shinfo(head)->nr_frags; i++) - plen += skb_frag_size(&skb_shinfo(head)->frags[i]); - clone->len = head->data_len - plen; - clone->data_len = clone->len; - head->data_len -= clone->len; - head->len -= clone->len; - add_frag_mem_limit(&fq->q, clone->truesize); - } - - WARN_ON(head == NULL); - - sum_truesize = head->truesize; - for (fp = head->next; fp;) { - bool headstolen; - int delta; - struct sk_buff *next = fp->next; - - sum_truesize += fp->truesize; - if (skb_try_coalesce(head, fp, &headstolen, &delta)) { - kfree_skb_partial(fp, headstolen); - } else { - if (!skb_shinfo(head)->frag_list) - skb_shinfo(head)->frag_list = fp; - head->data_len += fp->len; - head->len += fp->len; - head->truesize += fp->truesize; - } - fp = next; - } - sub_frag_mem_limit(&fq->q, sum_truesize); - - head->next = NULL; - head->dev = dev; - head->tstamp = fq->q.stamp; - - fq->q.fragments = NULL; - fq->q.fragments_tail = NULL; - - return 1; -out_oom: - net_dbg_ratelimited("lowpan_frag_reasm: no memory for reassembly\n"); - return -1; -} - -static int lowpan_get_frag_info(struct sk_buff *skb, const u8 frag_type, - struct lowpan_frag_info *frag_info) -{ - bool fail; - u8 pattern = 0, low = 0; - __be16 d_tag = 0; - - fail = lowpan_fetch_skb(skb, &pattern, 1); - fail |= lowpan_fetch_skb(skb, &low, 1); - frag_info->d_size = (pattern & 7) << 8 | low; - fail |= lowpan_fetch_skb(skb, &d_tag, 2); - frag_info->d_tag = ntohs(d_tag); - - if (frag_type == LOWPAN_DISPATCH_FRAGN) { - fail |= lowpan_fetch_skb(skb, &frag_info->d_offset, 1); - } else { - skb_reset_network_header(skb); - frag_info->d_offset = 0; - } - - if (unlikely(fail)) - return -EIO; - - return 0; -} - -int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type) -{ - struct lowpan_frag_queue *fq; - struct net *net = dev_net(skb->dev); - struct lowpan_frag_info *frag_info = lowpan_cb(skb); - struct ieee802154_addr source, dest; - int err; - - source = mac_cb(skb)->source; - dest = mac_cb(skb)->dest; - - err = lowpan_get_frag_info(skb, frag_type, frag_info); - if (err < 0) - goto err; - - if (frag_info->d_size > IPV6_MIN_MTU) { - net_warn_ratelimited("lowpan_frag_rcv: datagram size exceeds MTU\n"); - goto err; - } - - fq = fq_find(net, frag_info, &source, &dest); - if (fq != NULL) { - int ret; - - spin_lock(&fq->q.lock); - ret = lowpan_frag_queue(fq, skb, frag_type); - spin_unlock(&fq->q.lock); - - inet_frag_put(&fq->q, &lowpan_frags); - return ret; - } - -err: - kfree_skb(skb); - return -1; -} -EXPORT_SYMBOL(lowpan_frag_rcv); - -#ifdef CONFIG_SYSCTL -static int zero; - -static struct ctl_table lowpan_frags_ns_ctl_table[] = { - { - .procname = "6lowpanfrag_high_thresh", - .data = &init_net.ieee802154_lowpan.frags.high_thresh, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &init_net.ieee802154_lowpan.frags.low_thresh - }, - { - .procname = "6lowpanfrag_low_thresh", - .data = &init_net.ieee802154_lowpan.frags.low_thresh, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &zero, - .extra2 = &init_net.ieee802154_lowpan.frags.high_thresh - }, - { - .procname = "6lowpanfrag_time", - .data = &init_net.ieee802154_lowpan.frags.timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, - { } -}; - -/* secret interval has been deprecated */ -static int lowpan_frags_secret_interval_unused; -static struct ctl_table lowpan_frags_ctl_table[] = { - { - .procname = "6lowpanfrag_secret_interval", - .data = &lowpan_frags_secret_interval_unused, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, - { } -}; - -static int __net_init lowpan_frags_ns_sysctl_register(struct net *net) -{ - struct ctl_table *table; - struct ctl_table_header *hdr; - struct netns_ieee802154_lowpan *ieee802154_lowpan = - net_ieee802154_lowpan(net); - - table = lowpan_frags_ns_ctl_table; - if (!net_eq(net, &init_net)) { - table = kmemdup(table, sizeof(lowpan_frags_ns_ctl_table), - GFP_KERNEL); - if (table == NULL) - goto err_alloc; - - table[0].data = &ieee802154_lowpan->frags.high_thresh; - table[0].extra1 = &ieee802154_lowpan->frags.low_thresh; - table[0].extra2 = &init_net.ieee802154_lowpan.frags.high_thresh; - table[1].data = &ieee802154_lowpan->frags.low_thresh; - table[1].extra2 = &ieee802154_lowpan->frags.high_thresh; - table[2].data = &ieee802154_lowpan->frags.timeout; - - /* Don't export sysctls to unprivileged users */ - if (net->user_ns != &init_user_ns) - table[0].procname = NULL; - } - - hdr = register_net_sysctl(net, "net/ieee802154/6lowpan", table); - if (hdr == NULL) - goto err_reg; - - ieee802154_lowpan->sysctl.frags_hdr = hdr; - return 0; - -err_reg: - if (!net_eq(net, &init_net)) - kfree(table); -err_alloc: - return -ENOMEM; -} - -static void __net_exit lowpan_frags_ns_sysctl_unregister(struct net *net) -{ - struct ctl_table *table; - struct netns_ieee802154_lowpan *ieee802154_lowpan = - net_ieee802154_lowpan(net); - - table = ieee802154_lowpan->sysctl.frags_hdr->ctl_table_arg; - unregister_net_sysctl_table(ieee802154_lowpan->sysctl.frags_hdr); - if (!net_eq(net, &init_net)) - kfree(table); -} - -static struct ctl_table_header *lowpan_ctl_header; - -static int __init lowpan_frags_sysctl_register(void) -{ - lowpan_ctl_header = register_net_sysctl(&init_net, - "net/ieee802154/6lowpan", - lowpan_frags_ctl_table); - return lowpan_ctl_header == NULL ? -ENOMEM : 0; -} - -static void lowpan_frags_sysctl_unregister(void) -{ - unregister_net_sysctl_table(lowpan_ctl_header); -} -#else -static inline int lowpan_frags_ns_sysctl_register(struct net *net) -{ - return 0; -} - -static inline void lowpan_frags_ns_sysctl_unregister(struct net *net) -{ -} - -static inline int __init lowpan_frags_sysctl_register(void) -{ - return 0; -} - -static inline void lowpan_frags_sysctl_unregister(void) -{ -} -#endif - -static int __net_init lowpan_frags_init_net(struct net *net) -{ - struct netns_ieee802154_lowpan *ieee802154_lowpan = - net_ieee802154_lowpan(net); - - ieee802154_lowpan->frags.high_thresh = IPV6_FRAG_HIGH_THRESH; - ieee802154_lowpan->frags.low_thresh = IPV6_FRAG_LOW_THRESH; - ieee802154_lowpan->frags.timeout = IPV6_FRAG_TIMEOUT; - - inet_frags_init_net(&ieee802154_lowpan->frags); - - return lowpan_frags_ns_sysctl_register(net); -} - -static void __net_exit lowpan_frags_exit_net(struct net *net) -{ - struct netns_ieee802154_lowpan *ieee802154_lowpan = - net_ieee802154_lowpan(net); - - lowpan_frags_ns_sysctl_unregister(net); - inet_frags_exit_net(&ieee802154_lowpan->frags, &lowpan_frags); -} - -static struct pernet_operations lowpan_frags_ops = { - .init = lowpan_frags_init_net, - .exit = lowpan_frags_exit_net, -}; - -int __init lowpan_net_frag_init(void) -{ - int ret; - - ret = lowpan_frags_sysctl_register(); - if (ret) - return ret; - - ret = register_pernet_subsys(&lowpan_frags_ops); - if (ret) - goto err_pernet; - - lowpan_frags.hashfn = lowpan_hashfn; - lowpan_frags.constructor = lowpan_frag_init; - lowpan_frags.destructor = NULL; - lowpan_frags.skb_free = NULL; - lowpan_frags.qsize = sizeof(struct frag_queue); - lowpan_frags.match = lowpan_frag_match; - lowpan_frags.frag_expire = lowpan_frag_expire; - lowpan_frags.frags_cache_name = lowpan_frags_cache_name; - ret = inet_frags_init(&lowpan_frags); - if (ret) - goto err_pernet; - - return ret; -err_pernet: - lowpan_frags_sysctl_unregister(); - return ret; -} - -void lowpan_net_frag_exit(void) -{ - inet_frags_fini(&lowpan_frags); - lowpan_frags_sysctl_unregister(); - unregister_pernet_subsys(&lowpan_frags_ops); -} diff --git a/net/ieee802154/reassembly.h b/net/ieee802154/reassembly.h deleted file mode 100644 index 836b16f..0000000 --- a/net/ieee802154/reassembly.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef __IEEE802154_6LOWPAN_REASSEMBLY_H__ -#define __IEEE802154_6LOWPAN_REASSEMBLY_H__ - -#include - -struct lowpan_create_arg { - u16 tag; - u16 d_size; - const struct ieee802154_addr *src; - const struct ieee802154_addr *dst; -}; - -/* Equivalent of ipv4 struct ip - */ -struct lowpan_frag_queue { - struct inet_frag_queue q; - - u16 tag; - u16 d_size; - struct ieee802154_addr saddr; - struct ieee802154_addr daddr; -}; - -static inline u32 ieee802154_addr_hash(const struct ieee802154_addr *a) -{ - switch (a->mode) { - case IEEE802154_ADDR_LONG: - return (((__force u64)a->extended_addr) >> 32) ^ - (((__force u64)a->extended_addr) & 0xffffffff); - case IEEE802154_ADDR_SHORT: - return (__force u32)(a->short_addr); - default: - return 0; - } -} - -int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type); -void lowpan_net_frag_exit(void); -int lowpan_net_frag_init(void); - -#endif /* __IEEE802154_6LOWPAN_REASSEMBLY_H__ */ -- cgit v0.10.2 From 8691ee592c9299847b28350692eda1d5d6990581 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Sun, 4 Jan 2015 17:10:54 +0100 Subject: ieee802154: 6lowpan: rename internal header This patch renames the internal header for af802154. This naming convention is like ieee802154_i.h in mac802154 and avoids naming confusing with the global af802154 header. Furthermore this header contains more ieee802154 specific definitions. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/6lowpan/6lowpan_i.h b/net/ieee802154/6lowpan/6lowpan_i.h new file mode 100644 index 0000000..edbe803 --- /dev/null +++ b/net/ieee802154/6lowpan/6lowpan_i.h @@ -0,0 +1,41 @@ +#ifndef __IEEE802154_6LOWPAN_I_H__ +#define __IEEE802154_6LOWPAN_I_H__ + +#include + +struct lowpan_create_arg { + u16 tag; + u16 d_size; + const struct ieee802154_addr *src; + const struct ieee802154_addr *dst; +}; + +/* Equivalent of ipv4 struct ip + */ +struct lowpan_frag_queue { + struct inet_frag_queue q; + + u16 tag; + u16 d_size; + struct ieee802154_addr saddr; + struct ieee802154_addr daddr; +}; + +static inline u32 ieee802154_addr_hash(const struct ieee802154_addr *a) +{ + switch (a->mode) { + case IEEE802154_ADDR_LONG: + return (((__force u64)a->extended_addr) >> 32) ^ + (((__force u64)a->extended_addr) & 0xffffffff); + case IEEE802154_ADDR_SHORT: + return (__force u32)(a->short_addr); + default: + return 0; + } +} + +int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type); +void lowpan_net_frag_exit(void); +int lowpan_net_frag_init(void); + +#endif /* __IEEE802154_6LOWPAN_I_H__ */ diff --git a/net/ieee802154/6lowpan/6lowpan_rtnl.c b/net/ieee802154/6lowpan/6lowpan_rtnl.c index 27eaa65e..1918c43 100644 --- a/net/ieee802154/6lowpan/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan/6lowpan_rtnl.c @@ -55,7 +55,7 @@ #include #include -#include "reassembly.h" +#include "6lowpan_i.h" static LIST_HEAD(lowpan_devices); static int lowpan_open_count; diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c index 9d980ed..f46e4d1 100644 --- a/net/ieee802154/6lowpan/reassembly.c +++ b/net/ieee802154/6lowpan/reassembly.c @@ -28,7 +28,7 @@ #include #include -#include "reassembly.h" +#include "6lowpan_i.h" static const char lowpan_frags_cache_name[] = "lowpan-frags"; diff --git a/net/ieee802154/6lowpan/reassembly.h b/net/ieee802154/6lowpan/reassembly.h deleted file mode 100644 index 836b16f..0000000 --- a/net/ieee802154/6lowpan/reassembly.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef __IEEE802154_6LOWPAN_REASSEMBLY_H__ -#define __IEEE802154_6LOWPAN_REASSEMBLY_H__ - -#include - -struct lowpan_create_arg { - u16 tag; - u16 d_size; - const struct ieee802154_addr *src; - const struct ieee802154_addr *dst; -}; - -/* Equivalent of ipv4 struct ip - */ -struct lowpan_frag_queue { - struct inet_frag_queue q; - - u16 tag; - u16 d_size; - struct ieee802154_addr saddr; - struct ieee802154_addr daddr; -}; - -static inline u32 ieee802154_addr_hash(const struct ieee802154_addr *a) -{ - switch (a->mode) { - case IEEE802154_ADDR_LONG: - return (((__force u64)a->extended_addr) >> 32) ^ - (((__force u64)a->extended_addr) & 0xffffffff); - case IEEE802154_ADDR_SHORT: - return (__force u32)(a->short_addr); - default: - return 0; - } -} - -int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type); -void lowpan_net_frag_exit(void); -int lowpan_net_frag_init(void); - -#endif /* __IEEE802154_6LOWPAN_REASSEMBLY_H__ */ -- cgit v0.10.2 From 4662a0da544c8ed7ecf79ef0f6c4dc65be081352 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Sun, 4 Jan 2015 17:10:55 +0100 Subject: ieee802154: 6lowpan: move receive functionality This patch moves all relevant receive functionality into a separate rx.c file. We can simple separate this functionality like we did it in mac802154. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/6lowpan/6lowpan_i.h b/net/ieee802154/6lowpan/6lowpan_i.h index edbe803..29ec61b 100644 --- a/net/ieee802154/6lowpan/6lowpan_i.h +++ b/net/ieee802154/6lowpan/6lowpan_i.h @@ -1,6 +1,8 @@ #ifndef __IEEE802154_6LOWPAN_I_H__ #define __IEEE802154_6LOWPAN_I_H__ +#include + #include struct lowpan_create_arg { @@ -34,8 +36,31 @@ static inline u32 ieee802154_addr_hash(const struct ieee802154_addr *a) } } +struct lowpan_dev_record { + struct net_device *ldev; + struct list_head list; +}; + +/* private device info */ +struct lowpan_dev_info { + struct net_device *real_dev; /* real WPAN device ptr */ + struct mutex dev_list_mtx; /* mutex for list ops */ + u16 fragment_tag; +}; + +static inline struct +lowpan_dev_info *lowpan_dev_info(const struct net_device *dev) +{ + return netdev_priv(dev); +} + +extern struct list_head lowpan_devices; + int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type); void lowpan_net_frag_exit(void); int lowpan_net_frag_init(void); +void lowpan_rx_init(void); +void lowpan_rx_exit(void); + #endif /* __IEEE802154_6LOWPAN_I_H__ */ diff --git a/net/ieee802154/6lowpan/6lowpan_rtnl.c b/net/ieee802154/6lowpan/6lowpan_rtnl.c index 1918c43..9dce20e 100644 --- a/net/ieee802154/6lowpan/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan/6lowpan_rtnl.c @@ -57,21 +57,9 @@ #include "6lowpan_i.h" -static LIST_HEAD(lowpan_devices); +LIST_HEAD(lowpan_devices); static int lowpan_open_count; -/* private device info */ -struct lowpan_dev_info { - struct net_device *real_dev; /* real WPAN device ptr */ - struct mutex dev_list_mtx; /* mutex for list ops */ - u16 fragment_tag; -}; - -struct lowpan_dev_record { - struct net_device *ldev; - struct list_head list; -}; - /* don't save pan id, it's intra pan */ struct lowpan_addr { u8 mode; @@ -88,12 +76,6 @@ struct lowpan_addr_info { }; static inline struct -lowpan_dev_info *lowpan_dev_info(const struct net_device *dev) -{ - return netdev_priv(dev); -} - -static inline struct lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb) { WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct lowpan_addr_info)); @@ -134,74 +116,6 @@ static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, return 0; } -static int lowpan_give_skb_to_devices(struct sk_buff *skb, - struct net_device *dev) -{ - struct lowpan_dev_record *entry; - struct sk_buff *skb_cp; - int stat = NET_RX_SUCCESS; - - skb->protocol = htons(ETH_P_IPV6); - skb->pkt_type = PACKET_HOST; - - rcu_read_lock(); - list_for_each_entry_rcu(entry, &lowpan_devices, list) - if (lowpan_dev_info(entry->ldev)->real_dev == skb->dev) { - skb_cp = skb_copy(skb, GFP_ATOMIC); - if (!skb_cp) { - kfree_skb(skb); - rcu_read_unlock(); - return NET_RX_DROP; - } - - skb_cp->dev = entry->ldev; - stat = netif_rx(skb_cp); - if (stat == NET_RX_DROP) - break; - } - rcu_read_unlock(); - - consume_skb(skb); - - return stat; -} - -static int -iphc_decompress(struct sk_buff *skb, const struct ieee802154_hdr *hdr) -{ - u8 iphc0, iphc1; - struct ieee802154_addr_sa sa, da; - void *sap, *dap; - - raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len); - /* at least two bytes will be used for the encoding */ - if (skb->len < 2) - return -EINVAL; - - if (lowpan_fetch_skb_u8(skb, &iphc0)) - return -EINVAL; - - if (lowpan_fetch_skb_u8(skb, &iphc1)) - return -EINVAL; - - ieee802154_addr_to_sa(&sa, &hdr->source); - ieee802154_addr_to_sa(&da, &hdr->dest); - - if (sa.addr_type == IEEE802154_ADDR_SHORT) - sap = &sa.short_addr; - else - sap = &sa.hwaddr; - - if (da.addr_type == IEEE802154_ADDR_SHORT) - dap = &da.short_addr; - else - dap = &da.hwaddr; - - return lowpan_header_decompress(skb, skb->dev, sap, sa.addr_type, - IEEE802154_ADDR_LEN, dap, da.addr_type, - IEEE802154_ADDR_LEN, iphc0, iphc1); -} - static struct sk_buff* lowpan_alloc_frag(struct sk_buff *skb, int size, const struct ieee802154_hdr *master_hdr) @@ -485,83 +399,6 @@ static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[]) return 0; } -static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) -{ - struct ieee802154_hdr hdr; - int ret; - - skb = skb_share_check(skb, GFP_ATOMIC); - if (!skb) - goto drop; - - if (!netif_running(dev)) - goto drop_skb; - - if (skb->pkt_type == PACKET_OTHERHOST) - goto drop_skb; - - if (dev->type != ARPHRD_IEEE802154) - goto drop_skb; - - if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) - goto drop_skb; - - /* check that it's our buffer */ - if (skb->data[0] == LOWPAN_DISPATCH_IPV6) { - /* Pull off the 1-byte of 6lowpan header. */ - skb_pull(skb, 1); - return lowpan_give_skb_to_devices(skb, NULL); - } else { - switch (skb->data[0] & 0xe0) { - case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ - ret = iphc_decompress(skb, &hdr); - if (ret < 0) - goto drop_skb; - - return lowpan_give_skb_to_devices(skb, NULL); - case LOWPAN_DISPATCH_FRAG1: /* first fragment header */ - ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1); - if (ret == 1) { - ret = iphc_decompress(skb, &hdr); - if (ret < 0) - goto drop_skb; - - return lowpan_give_skb_to_devices(skb, NULL); - } else if (ret == -1) { - return NET_RX_DROP; - } else { - return NET_RX_SUCCESS; - } - case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */ - ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN); - if (ret == 1) { - ret = iphc_decompress(skb, &hdr); - if (ret < 0) - goto drop_skb; - - return lowpan_give_skb_to_devices(skb, NULL); - } else if (ret == -1) { - return NET_RX_DROP; - } else { - return NET_RX_SUCCESS; - } - default: - break; - } - } - -drop_skb: - kfree_skb(skb); -drop: - return NET_RX_DROP; -} - -static struct packet_type lowpan_packet_type = { - .type = htons(ETH_P_IEEE802154), - .func = lowpan_rcv, -}; - static int lowpan_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { @@ -607,7 +444,7 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev, ret = register_netdevice(dev); if (ret >= 0) { if (!lowpan_open_count) - dev_add_pack(&lowpan_packet_type); + lowpan_rx_init(); lowpan_open_count++; } @@ -624,7 +461,7 @@ static void lowpan_dellink(struct net_device *dev, struct list_head *head) lowpan_open_count--; if (!lowpan_open_count) - dev_remove_pack(&lowpan_packet_type); + lowpan_rx_exit(); mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { diff --git a/net/ieee802154/6lowpan/Makefile b/net/ieee802154/6lowpan/Makefile index 936959b..8e83971 100644 --- a/net/ieee802154/6lowpan/Makefile +++ b/net/ieee802154/6lowpan/Makefile @@ -1,3 +1,3 @@ obj-y += ieee802154_6lowpan.o -ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o +ieee802154_6lowpan-y := 6lowpan_rtnl.o rx.o reassembly.o diff --git a/net/ieee802154/6lowpan/rx.c b/net/ieee802154/6lowpan/rx.c new file mode 100644 index 0000000..4be1d28 --- /dev/null +++ b/net/ieee802154/6lowpan/rx.c @@ -0,0 +1,171 @@ +/* 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. + */ + +#include + +#include +#include + +#include "6lowpan_i.h" + +static int lowpan_give_skb_to_devices(struct sk_buff *skb, + struct net_device *dev) +{ + struct lowpan_dev_record *entry; + struct sk_buff *skb_cp; + int stat = NET_RX_SUCCESS; + + skb->protocol = htons(ETH_P_IPV6); + skb->pkt_type = PACKET_HOST; + + rcu_read_lock(); + list_for_each_entry_rcu(entry, &lowpan_devices, list) + if (lowpan_dev_info(entry->ldev)->real_dev == skb->dev) { + skb_cp = skb_copy(skb, GFP_ATOMIC); + if (!skb_cp) { + kfree_skb(skb); + rcu_read_unlock(); + return NET_RX_DROP; + } + + skb_cp->dev = entry->ldev; + stat = netif_rx(skb_cp); + if (stat == NET_RX_DROP) + break; + } + rcu_read_unlock(); + + consume_skb(skb); + + return stat; +} + +static int +iphc_decompress(struct sk_buff *skb, const struct ieee802154_hdr *hdr) +{ + u8 iphc0, iphc1; + struct ieee802154_addr_sa sa, da; + void *sap, *dap; + + raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len); + /* at least two bytes will be used for the encoding */ + if (skb->len < 2) + return -EINVAL; + + if (lowpan_fetch_skb_u8(skb, &iphc0)) + return -EINVAL; + + if (lowpan_fetch_skb_u8(skb, &iphc1)) + return -EINVAL; + + ieee802154_addr_to_sa(&sa, &hdr->source); + ieee802154_addr_to_sa(&da, &hdr->dest); + + if (sa.addr_type == IEEE802154_ADDR_SHORT) + sap = &sa.short_addr; + else + sap = &sa.hwaddr; + + if (da.addr_type == IEEE802154_ADDR_SHORT) + dap = &da.short_addr; + else + dap = &da.hwaddr; + + return lowpan_header_decompress(skb, skb->dev, sap, sa.addr_type, + IEEE802154_ADDR_LEN, dap, da.addr_type, + IEEE802154_ADDR_LEN, iphc0, iphc1); +} + +static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + struct ieee802154_hdr hdr; + int ret; + + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + goto drop; + + if (!netif_running(dev)) + goto drop_skb; + + if (skb->pkt_type == PACKET_OTHERHOST) + goto drop_skb; + + if (dev->type != ARPHRD_IEEE802154) + goto drop_skb; + + if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) + goto drop_skb; + + /* check that it's our buffer */ + if (skb->data[0] == LOWPAN_DISPATCH_IPV6) { + /* Pull off the 1-byte of 6lowpan header. */ + skb_pull(skb, 1); + return lowpan_give_skb_to_devices(skb, NULL); + } else { + switch (skb->data[0] & 0xe0) { + case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ + ret = iphc_decompress(skb, &hdr); + if (ret < 0) + goto drop_skb; + + return lowpan_give_skb_to_devices(skb, NULL); + case LOWPAN_DISPATCH_FRAG1: /* first fragment header */ + ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1); + if (ret == 1) { + ret = iphc_decompress(skb, &hdr); + if (ret < 0) + goto drop_skb; + + return lowpan_give_skb_to_devices(skb, NULL); + } else if (ret == -1) { + return NET_RX_DROP; + } else { + return NET_RX_SUCCESS; + } + case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */ + ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN); + if (ret == 1) { + ret = iphc_decompress(skb, &hdr); + if (ret < 0) + goto drop_skb; + + return lowpan_give_skb_to_devices(skb, NULL); + } else if (ret == -1) { + return NET_RX_DROP; + } else { + return NET_RX_SUCCESS; + } + default: + break; + } + } + +drop_skb: + kfree_skb(skb); +drop: + return NET_RX_DROP; +} + +static struct packet_type lowpan_packet_type = { + .type = htons(ETH_P_IEEE802154), + .func = lowpan_rcv, +}; + +void lowpan_rx_init(void) +{ + dev_add_pack(&lowpan_packet_type); +} + +void lowpan_rx_exit(void) +{ + dev_remove_pack(&lowpan_packet_type); +} -- cgit v0.10.2 From 4dc315e267fe4a3c863fec21f26aa0a418c3f07a Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Sun, 4 Jan 2015 17:10:56 +0100 Subject: ieee802154: 6lowpan: move transmit functionality This patch moves all relevant transmit functionality into a separate tx.c file. We can simple separate this functionality like we did it in mac802154. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/6lowpan/6lowpan_i.h b/net/ieee802154/6lowpan/6lowpan_i.h index 29ec61b..e50f69d 100644 --- a/net/ieee802154/6lowpan/6lowpan_i.h +++ b/net/ieee802154/6lowpan/6lowpan_i.h @@ -3,6 +3,7 @@ #include +#include #include struct lowpan_create_arg { @@ -63,4 +64,9 @@ int lowpan_net_frag_init(void); void lowpan_rx_init(void); void lowpan_rx_exit(void); +int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *_daddr, + const void *_saddr, unsigned int len); +netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev); + #endif /* __IEEE802154_6LOWPAN_I_H__ */ diff --git a/net/ieee802154/6lowpan/6lowpan_rtnl.c b/net/ieee802154/6lowpan/6lowpan_rtnl.c index 9dce20e..055fbb7 100644 --- a/net/ieee802154/6lowpan/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan/6lowpan_rtnl.c @@ -44,15 +44,10 @@ * SUCH DAMAGE. */ -#include -#include #include -#include #include #include -#include -#include -#include + #include #include "6lowpan_i.h" @@ -60,263 +55,6 @@ LIST_HEAD(lowpan_devices); static int lowpan_open_count; -/* don't save pan id, it's intra pan */ -struct lowpan_addr { - u8 mode; - union { - /* IPv6 needs big endian here */ - __be64 extended_addr; - __be16 short_addr; - } u; -}; - -struct lowpan_addr_info { - struct lowpan_addr daddr; - struct lowpan_addr saddr; -}; - -static inline struct -lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb) -{ - WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct lowpan_addr_info)); - return (struct lowpan_addr_info *)(skb->data - - sizeof(struct lowpan_addr_info)); -} - -static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, - unsigned short type, const void *_daddr, - const void *_saddr, unsigned int len) -{ - const u8 *saddr = _saddr; - const u8 *daddr = _daddr; - struct lowpan_addr_info *info; - - /* TODO: - * if this package isn't ipv6 one, where should it be routed? - */ - if (type != ETH_P_IPV6) - return 0; - - if (!saddr) - saddr = dev->dev_addr; - - raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8); - raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8); - - info = lowpan_skb_priv(skb); - - /* TODO: Currently we only support extended_addr */ - info->daddr.mode = IEEE802154_ADDR_LONG; - memcpy(&info->daddr.u.extended_addr, daddr, - sizeof(info->daddr.u.extended_addr)); - info->saddr.mode = IEEE802154_ADDR_LONG; - memcpy(&info->saddr.u.extended_addr, saddr, - sizeof(info->daddr.u.extended_addr)); - - return 0; -} - -static struct sk_buff* -lowpan_alloc_frag(struct sk_buff *skb, int size, - const struct ieee802154_hdr *master_hdr) -{ - struct net_device *real_dev = lowpan_dev_info(skb->dev)->real_dev; - struct sk_buff *frag; - int rc; - - frag = alloc_skb(real_dev->hard_header_len + - real_dev->needed_tailroom + size, - GFP_ATOMIC); - - if (likely(frag)) { - frag->dev = real_dev; - frag->priority = skb->priority; - skb_reserve(frag, real_dev->hard_header_len); - skb_reset_network_header(frag); - *mac_cb(frag) = *mac_cb(skb); - - rc = dev_hard_header(frag, real_dev, 0, &master_hdr->dest, - &master_hdr->source, size); - if (rc < 0) { - kfree_skb(frag); - return ERR_PTR(rc); - } - } else { - frag = ERR_PTR(-ENOMEM); - } - - return frag; -} - -static int -lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr, - u8 *frag_hdr, int frag_hdrlen, - int offset, int len) -{ - struct sk_buff *frag; - - raw_dump_inline(__func__, " fragment header", frag_hdr, frag_hdrlen); - - frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr); - if (IS_ERR(frag)) - return -PTR_ERR(frag); - - memcpy(skb_put(frag, frag_hdrlen), frag_hdr, frag_hdrlen); - memcpy(skb_put(frag, len), skb_network_header(skb) + offset, len); - - raw_dump_table(__func__, " fragment dump", frag->data, frag->len); - - return dev_queue_xmit(frag); -} - -static int -lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev, - const struct ieee802154_hdr *wpan_hdr) -{ - u16 dgram_size, dgram_offset; - __be16 frag_tag; - u8 frag_hdr[5]; - int frag_cap, frag_len, payload_cap, rc; - int skb_unprocessed, skb_offset; - - dgram_size = lowpan_uncompress_size(skb, &dgram_offset) - - skb->mac_len; - frag_tag = htons(lowpan_dev_info(dev)->fragment_tag); - lowpan_dev_info(dev)->fragment_tag++; - - frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07); - frag_hdr[1] = dgram_size & 0xff; - memcpy(frag_hdr + 2, &frag_tag, sizeof(frag_tag)); - - payload_cap = ieee802154_max_payload(wpan_hdr); - - frag_len = round_down(payload_cap - LOWPAN_FRAG1_HEAD_SIZE - - skb_network_header_len(skb), 8); - - skb_offset = skb_network_header_len(skb); - skb_unprocessed = skb->len - skb->mac_len - skb_offset; - - rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, - LOWPAN_FRAG1_HEAD_SIZE, 0, - frag_len + skb_network_header_len(skb)); - if (rc) { - pr_debug("%s unable to send FRAG1 packet (tag: %d)", - __func__, ntohs(frag_tag)); - goto err; - } - - frag_hdr[0] &= ~LOWPAN_DISPATCH_FRAG1; - frag_hdr[0] |= LOWPAN_DISPATCH_FRAGN; - frag_cap = round_down(payload_cap - LOWPAN_FRAGN_HEAD_SIZE, 8); - - do { - dgram_offset += frag_len; - skb_offset += frag_len; - skb_unprocessed -= frag_len; - frag_len = min(frag_cap, skb_unprocessed); - - frag_hdr[4] = dgram_offset >> 3; - - rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, - LOWPAN_FRAGN_HEAD_SIZE, skb_offset, - frag_len); - if (rc) { - pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n", - __func__, ntohs(frag_tag), skb_offset); - goto err; - } - } while (skb_unprocessed > frag_cap); - - consume_skb(skb); - return NET_XMIT_SUCCESS; - -err: - kfree_skb(skb); - return rc; -} - -static int lowpan_header(struct sk_buff *skb, struct net_device *dev) -{ - struct ieee802154_addr sa, da; - struct ieee802154_mac_cb *cb = mac_cb_init(skb); - struct lowpan_addr_info info; - void *daddr, *saddr; - - memcpy(&info, lowpan_skb_priv(skb), sizeof(info)); - - /* TODO: Currently we only support extended_addr */ - daddr = &info.daddr.u.extended_addr; - saddr = &info.saddr.u.extended_addr; - - lowpan_header_compress(skb, dev, ETH_P_IPV6, daddr, saddr, skb->len); - - cb->type = IEEE802154_FC_TYPE_DATA; - - /* prepare wpan address data */ - sa.mode = IEEE802154_ADDR_LONG; - sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); - sa.extended_addr = ieee802154_devaddr_from_raw(saddr); - - /* intra-PAN communications */ - da.pan_id = sa.pan_id; - - /* if the destination address is the broadcast address, use the - * corresponding short address - */ - if (lowpan_is_addr_broadcast((const u8 *)daddr)) { - da.mode = IEEE802154_ADDR_SHORT; - da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); - cb->ackreq = false; - } else { - da.mode = IEEE802154_ADDR_LONG; - da.extended_addr = ieee802154_devaddr_from_raw(daddr); - cb->ackreq = true; - } - - return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev, - ETH_P_IPV6, (void *)&da, (void *)&sa, 0); -} - -static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct ieee802154_hdr wpan_hdr; - int max_single, ret; - - pr_debug("package xmit\n"); - - /* We must take a copy of the skb before we modify/replace the ipv6 - * header as the header could be used elsewhere - */ - skb = skb_unshare(skb, GFP_ATOMIC); - if (!skb) - return NET_XMIT_DROP; - - ret = lowpan_header(skb, dev); - if (ret < 0) { - kfree_skb(skb); - return NET_XMIT_DROP; - } - - if (ieee802154_hdr_peek(skb, &wpan_hdr) < 0) { - kfree_skb(skb); - return NET_XMIT_DROP; - } - - max_single = ieee802154_max_payload(&wpan_hdr); - - if (skb_tail_pointer(skb) - skb_network_header(skb) <= max_single) { - skb->dev = lowpan_dev_info(dev)->real_dev; - return dev_queue_xmit(skb); - } else { - netdev_tx_t rc; - - pr_debug("frame is too big, fragmentation is needed\n"); - rc = lowpan_xmit_fragmented(skb, dev, &wpan_hdr); - - return rc < 0 ? NET_XMIT_DROP : rc; - } -} - static __le16 lowpan_get_pan_id(const struct net_device *dev) { struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; diff --git a/net/ieee802154/6lowpan/Makefile b/net/ieee802154/6lowpan/Makefile index 8e83971..7f11c13 100644 --- a/net/ieee802154/6lowpan/Makefile +++ b/net/ieee802154/6lowpan/Makefile @@ -1,3 +1,3 @@ obj-y += ieee802154_6lowpan.o -ieee802154_6lowpan-y := 6lowpan_rtnl.o rx.o reassembly.o +ieee802154_6lowpan-y := 6lowpan_rtnl.o rx.o reassembly.o tx.o diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c new file mode 100644 index 0000000..2349070 --- /dev/null +++ b/net/ieee802154/6lowpan/tx.c @@ -0,0 +1,271 @@ +/* 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. + */ + +#include +#include + +#include "6lowpan_i.h" + +/* don't save pan id, it's intra pan */ +struct lowpan_addr { + u8 mode; + union { + /* IPv6 needs big endian here */ + __be64 extended_addr; + __be16 short_addr; + } u; +}; + +struct lowpan_addr_info { + struct lowpan_addr daddr; + struct lowpan_addr saddr; +}; + +static inline struct +lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb) +{ + WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct lowpan_addr_info)); + return (struct lowpan_addr_info *)(skb->data - + sizeof(struct lowpan_addr_info)); +} + +int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *_daddr, + const void *_saddr, unsigned int len) +{ + const u8 *saddr = _saddr; + const u8 *daddr = _daddr; + struct lowpan_addr_info *info; + + /* TODO: + * if this package isn't ipv6 one, where should it be routed? + */ + if (type != ETH_P_IPV6) + return 0; + + if (!saddr) + saddr = dev->dev_addr; + + raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8); + raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8); + + info = lowpan_skb_priv(skb); + + /* TODO: Currently we only support extended_addr */ + info->daddr.mode = IEEE802154_ADDR_LONG; + memcpy(&info->daddr.u.extended_addr, daddr, + sizeof(info->daddr.u.extended_addr)); + info->saddr.mode = IEEE802154_ADDR_LONG; + memcpy(&info->saddr.u.extended_addr, saddr, + sizeof(info->daddr.u.extended_addr)); + + return 0; +} + +static struct sk_buff* +lowpan_alloc_frag(struct sk_buff *skb, int size, + const struct ieee802154_hdr *master_hdr) +{ + struct net_device *real_dev = lowpan_dev_info(skb->dev)->real_dev; + struct sk_buff *frag; + int rc; + + frag = alloc_skb(real_dev->hard_header_len + + real_dev->needed_tailroom + size, + GFP_ATOMIC); + + if (likely(frag)) { + frag->dev = real_dev; + frag->priority = skb->priority; + skb_reserve(frag, real_dev->hard_header_len); + skb_reset_network_header(frag); + *mac_cb(frag) = *mac_cb(skb); + + rc = dev_hard_header(frag, real_dev, 0, &master_hdr->dest, + &master_hdr->source, size); + if (rc < 0) { + kfree_skb(frag); + return ERR_PTR(rc); + } + } else { + frag = ERR_PTR(-ENOMEM); + } + + return frag; +} + +static int +lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr, + u8 *frag_hdr, int frag_hdrlen, + int offset, int len) +{ + struct sk_buff *frag; + + raw_dump_inline(__func__, " fragment header", frag_hdr, frag_hdrlen); + + frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr); + if (IS_ERR(frag)) + return -PTR_ERR(frag); + + memcpy(skb_put(frag, frag_hdrlen), frag_hdr, frag_hdrlen); + memcpy(skb_put(frag, len), skb_network_header(skb) + offset, len); + + raw_dump_table(__func__, " fragment dump", frag->data, frag->len); + + return dev_queue_xmit(frag); +} + +static int +lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev, + const struct ieee802154_hdr *wpan_hdr) +{ + u16 dgram_size, dgram_offset; + __be16 frag_tag; + u8 frag_hdr[5]; + int frag_cap, frag_len, payload_cap, rc; + int skb_unprocessed, skb_offset; + + dgram_size = lowpan_uncompress_size(skb, &dgram_offset) - + skb->mac_len; + frag_tag = htons(lowpan_dev_info(dev)->fragment_tag); + lowpan_dev_info(dev)->fragment_tag++; + + frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07); + frag_hdr[1] = dgram_size & 0xff; + memcpy(frag_hdr + 2, &frag_tag, sizeof(frag_tag)); + + payload_cap = ieee802154_max_payload(wpan_hdr); + + frag_len = round_down(payload_cap - LOWPAN_FRAG1_HEAD_SIZE - + skb_network_header_len(skb), 8); + + skb_offset = skb_network_header_len(skb); + skb_unprocessed = skb->len - skb->mac_len - skb_offset; + + rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, + LOWPAN_FRAG1_HEAD_SIZE, 0, + frag_len + skb_network_header_len(skb)); + if (rc) { + pr_debug("%s unable to send FRAG1 packet (tag: %d)", + __func__, ntohs(frag_tag)); + goto err; + } + + frag_hdr[0] &= ~LOWPAN_DISPATCH_FRAG1; + frag_hdr[0] |= LOWPAN_DISPATCH_FRAGN; + frag_cap = round_down(payload_cap - LOWPAN_FRAGN_HEAD_SIZE, 8); + + do { + dgram_offset += frag_len; + skb_offset += frag_len; + skb_unprocessed -= frag_len; + frag_len = min(frag_cap, skb_unprocessed); + + frag_hdr[4] = dgram_offset >> 3; + + rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, + LOWPAN_FRAGN_HEAD_SIZE, skb_offset, + frag_len); + if (rc) { + pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n", + __func__, ntohs(frag_tag), skb_offset); + goto err; + } + } while (skb_unprocessed > frag_cap); + + consume_skb(skb); + return NET_XMIT_SUCCESS; + +err: + kfree_skb(skb); + return rc; +} + +static int lowpan_header(struct sk_buff *skb, struct net_device *dev) +{ + struct ieee802154_addr sa, da; + struct ieee802154_mac_cb *cb = mac_cb_init(skb); + struct lowpan_addr_info info; + void *daddr, *saddr; + + memcpy(&info, lowpan_skb_priv(skb), sizeof(info)); + + /* TODO: Currently we only support extended_addr */ + daddr = &info.daddr.u.extended_addr; + saddr = &info.saddr.u.extended_addr; + + lowpan_header_compress(skb, dev, ETH_P_IPV6, daddr, saddr, skb->len); + + cb->type = IEEE802154_FC_TYPE_DATA; + + /* prepare wpan address data */ + sa.mode = IEEE802154_ADDR_LONG; + sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); + sa.extended_addr = ieee802154_devaddr_from_raw(saddr); + + /* intra-PAN communications */ + da.pan_id = sa.pan_id; + + /* if the destination address is the broadcast address, use the + * corresponding short address + */ + if (lowpan_is_addr_broadcast((const u8 *)daddr)) { + da.mode = IEEE802154_ADDR_SHORT; + da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); + cb->ackreq = false; + } else { + da.mode = IEEE802154_ADDR_LONG; + da.extended_addr = ieee802154_devaddr_from_raw(daddr); + cb->ackreq = true; + } + + return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev, + ETH_P_IPV6, (void *)&da, (void *)&sa, 0); +} + +netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ieee802154_hdr wpan_hdr; + int max_single, ret; + + pr_debug("package xmit\n"); + + /* We must take a copy of the skb before we modify/replace the ipv6 + * header as the header could be used elsewhere + */ + skb = skb_unshare(skb, GFP_ATOMIC); + if (!skb) + return NET_XMIT_DROP; + + ret = lowpan_header(skb, dev); + if (ret < 0) { + kfree_skb(skb); + return NET_XMIT_DROP; + } + + if (ieee802154_hdr_peek(skb, &wpan_hdr) < 0) { + kfree_skb(skb); + return NET_XMIT_DROP; + } + + max_single = ieee802154_max_payload(&wpan_hdr); + + if (skb_tail_pointer(skb) - skb_network_header(skb) <= max_single) { + skb->dev = lowpan_dev_info(dev)->real_dev; + return dev_queue_xmit(skb); + } else { + netdev_tx_t rc; + + pr_debug("frame is too big, fragmentation is needed\n"); + rc = lowpan_xmit_fragmented(skb, dev, &wpan_hdr); + + return rc < 0 ? NET_XMIT_DROP : rc; + } +} -- cgit v0.10.2 From 52d84ffc540cc14010c7b0d9df20063445f89187 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Sun, 4 Jan 2015 17:10:57 +0100 Subject: ieee802154: 6lowpan: rename to core This patch renames the 6lowpan_rtnl.c file to core.c. 6lowpan_rtnl.c contains functionality to put all 802.15.4 6LoWPAN functionality together. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/6lowpan/6lowpan_rtnl.c b/net/ieee802154/6lowpan/6lowpan_rtnl.c deleted file mode 100644 index 055fbb7..0000000 --- a/net/ieee802154/6lowpan/6lowpan_rtnl.c +++ /dev/null @@ -1,304 +0,0 @@ -/* Copyright 2011, Siemens AG - * written by Alexander Smirnov - */ - -/* Based on patches from Jon Smirl - * Copyright (c) 2011 Jon Smirl - * - * 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. - */ - -/* Jon's code is based on 6lowpan implementation for Contiki which is: - * Copyright (c) 2008, Swedish Institute of Computer Science. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include -#include - -#include - -#include "6lowpan_i.h" - -LIST_HEAD(lowpan_devices); -static int lowpan_open_count; - -static __le16 lowpan_get_pan_id(const struct net_device *dev) -{ - struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; - - return ieee802154_mlme_ops(real_dev)->get_pan_id(real_dev); -} - -static __le16 lowpan_get_short_addr(const struct net_device *dev) -{ - struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; - - return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev); -} - -static u8 lowpan_get_dsn(const struct net_device *dev) -{ - struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; - - return ieee802154_mlme_ops(real_dev)->get_dsn(real_dev); -} - -static struct header_ops lowpan_header_ops = { - .create = lowpan_header_create, -}; - -static struct lock_class_key lowpan_tx_busylock; -static struct lock_class_key lowpan_netdev_xmit_lock_key; - -static void lowpan_set_lockdep_class_one(struct net_device *dev, - struct netdev_queue *txq, - void *_unused) -{ - lockdep_set_class(&txq->_xmit_lock, - &lowpan_netdev_xmit_lock_key); -} - -static int lowpan_dev_init(struct net_device *dev) -{ - netdev_for_each_tx_queue(dev, lowpan_set_lockdep_class_one, NULL); - dev->qdisc_tx_busylock = &lowpan_tx_busylock; - return 0; -} - -static const struct net_device_ops lowpan_netdev_ops = { - .ndo_init = lowpan_dev_init, - .ndo_start_xmit = lowpan_xmit, -}; - -static struct ieee802154_mlme_ops lowpan_mlme = { - .get_pan_id = lowpan_get_pan_id, - .get_short_addr = lowpan_get_short_addr, - .get_dsn = lowpan_get_dsn, -}; - -static void lowpan_setup(struct net_device *dev) -{ - dev->addr_len = IEEE802154_ADDR_LEN; - memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN); - dev->type = ARPHRD_IEEE802154; - /* Frame Control + Sequence Number + Address fields + Security Header */ - dev->hard_header_len = 2 + 1 + 20 + 14; - dev->needed_tailroom = 2; /* FCS */ - dev->mtu = IPV6_MIN_MTU; - dev->tx_queue_len = 0; - dev->flags = IFF_BROADCAST | IFF_MULTICAST; - dev->watchdog_timeo = 0; - - dev->netdev_ops = &lowpan_netdev_ops; - dev->header_ops = &lowpan_header_ops; - dev->ml_priv = &lowpan_mlme; - dev->destructor = free_netdev; -} - -static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[]) -{ - if (tb[IFLA_ADDRESS]) { - if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN) - return -EINVAL; - } - return 0; -} - -static int lowpan_newlink(struct net *src_net, struct net_device *dev, - struct nlattr *tb[], struct nlattr *data[]) -{ - struct net_device *real_dev; - struct lowpan_dev_record *entry; - int ret; - - ASSERT_RTNL(); - - pr_debug("adding new link\n"); - - if (!tb[IFLA_LINK]) - return -EINVAL; - /* find and hold real wpan device */ - real_dev = dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); - if (!real_dev) - return -ENODEV; - if (real_dev->type != ARPHRD_IEEE802154) { - dev_put(real_dev); - return -EINVAL; - } - - lowpan_dev_info(dev)->real_dev = real_dev; - mutex_init(&lowpan_dev_info(dev)->dev_list_mtx); - - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) { - dev_put(real_dev); - lowpan_dev_info(dev)->real_dev = NULL; - return -ENOMEM; - } - - entry->ldev = dev; - - /* Set the lowpan hardware address to the wpan hardware address. */ - memcpy(dev->dev_addr, real_dev->dev_addr, IEEE802154_ADDR_LEN); - - mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); - INIT_LIST_HEAD(&entry->list); - list_add_tail(&entry->list, &lowpan_devices); - mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); - - ret = register_netdevice(dev); - if (ret >= 0) { - if (!lowpan_open_count) - lowpan_rx_init(); - lowpan_open_count++; - } - - return ret; -} - -static void lowpan_dellink(struct net_device *dev, struct list_head *head) -{ - struct lowpan_dev_info *lowpan_dev = lowpan_dev_info(dev); - struct net_device *real_dev = lowpan_dev->real_dev; - struct lowpan_dev_record *entry, *tmp; - - ASSERT_RTNL(); - - lowpan_open_count--; - if (!lowpan_open_count) - lowpan_rx_exit(); - - mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); - list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { - if (entry->ldev == dev) { - list_del(&entry->list); - kfree(entry); - } - } - mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); - - mutex_destroy(&lowpan_dev_info(dev)->dev_list_mtx); - - unregister_netdevice_queue(dev, head); - - dev_put(real_dev); -} - -static struct rtnl_link_ops lowpan_link_ops __read_mostly = { - .kind = "lowpan", - .priv_size = sizeof(struct lowpan_dev_info), - .setup = lowpan_setup, - .newlink = lowpan_newlink, - .dellink = lowpan_dellink, - .validate = lowpan_validate, -}; - -static inline int __init lowpan_netlink_init(void) -{ - return rtnl_link_register(&lowpan_link_ops); -} - -static inline void lowpan_netlink_fini(void) -{ - rtnl_link_unregister(&lowpan_link_ops); -} - -static int lowpan_device_event(struct notifier_block *unused, - unsigned long event, void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - LIST_HEAD(del_list); - struct lowpan_dev_record *entry, *tmp; - - if (dev->type != ARPHRD_IEEE802154) - goto out; - - if (event == NETDEV_UNREGISTER) { - list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { - if (lowpan_dev_info(entry->ldev)->real_dev == dev) - lowpan_dellink(entry->ldev, &del_list); - } - - unregister_netdevice_many(&del_list); - } - -out: - return NOTIFY_DONE; -} - -static struct notifier_block lowpan_dev_notifier = { - .notifier_call = lowpan_device_event, -}; - -static int __init lowpan_init_module(void) -{ - int err = 0; - - err = lowpan_net_frag_init(); - if (err < 0) - goto out; - - err = lowpan_netlink_init(); - if (err < 0) - goto out_frag; - - err = register_netdevice_notifier(&lowpan_dev_notifier); - if (err < 0) - goto out_pack; - - return 0; - -out_pack: - lowpan_netlink_fini(); -out_frag: - lowpan_net_frag_exit(); -out: - return err; -} - -static void __exit lowpan_cleanup_module(void) -{ - lowpan_netlink_fini(); - - lowpan_net_frag_exit(); - - unregister_netdevice_notifier(&lowpan_dev_notifier); -} - -module_init(lowpan_init_module); -module_exit(lowpan_cleanup_module); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_RTNL_LINK("lowpan"); diff --git a/net/ieee802154/6lowpan/Makefile b/net/ieee802154/6lowpan/Makefile index 7f11c13..2f1b75a 100644 --- a/net/ieee802154/6lowpan/Makefile +++ b/net/ieee802154/6lowpan/Makefile @@ -1,3 +1,3 @@ obj-y += ieee802154_6lowpan.o -ieee802154_6lowpan-y := 6lowpan_rtnl.o rx.o reassembly.o tx.o +ieee802154_6lowpan-y := core.o rx.o reassembly.o tx.o diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c new file mode 100644 index 0000000..055fbb7 --- /dev/null +++ b/net/ieee802154/6lowpan/core.c @@ -0,0 +1,304 @@ +/* Copyright 2011, Siemens AG + * written by Alexander Smirnov + */ + +/* Based on patches from Jon Smirl + * Copyright (c) 2011 Jon Smirl + * + * 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. + */ + +/* Jon's code is based on 6lowpan implementation for Contiki which is: + * Copyright (c) 2008, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +#include + +#include "6lowpan_i.h" + +LIST_HEAD(lowpan_devices); +static int lowpan_open_count; + +static __le16 lowpan_get_pan_id(const struct net_device *dev) +{ + struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; + + return ieee802154_mlme_ops(real_dev)->get_pan_id(real_dev); +} + +static __le16 lowpan_get_short_addr(const struct net_device *dev) +{ + struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; + + return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev); +} + +static u8 lowpan_get_dsn(const struct net_device *dev) +{ + struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; + + return ieee802154_mlme_ops(real_dev)->get_dsn(real_dev); +} + +static struct header_ops lowpan_header_ops = { + .create = lowpan_header_create, +}; + +static struct lock_class_key lowpan_tx_busylock; +static struct lock_class_key lowpan_netdev_xmit_lock_key; + +static void lowpan_set_lockdep_class_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) +{ + lockdep_set_class(&txq->_xmit_lock, + &lowpan_netdev_xmit_lock_key); +} + +static int lowpan_dev_init(struct net_device *dev) +{ + netdev_for_each_tx_queue(dev, lowpan_set_lockdep_class_one, NULL); + dev->qdisc_tx_busylock = &lowpan_tx_busylock; + return 0; +} + +static const struct net_device_ops lowpan_netdev_ops = { + .ndo_init = lowpan_dev_init, + .ndo_start_xmit = lowpan_xmit, +}; + +static struct ieee802154_mlme_ops lowpan_mlme = { + .get_pan_id = lowpan_get_pan_id, + .get_short_addr = lowpan_get_short_addr, + .get_dsn = lowpan_get_dsn, +}; + +static void lowpan_setup(struct net_device *dev) +{ + dev->addr_len = IEEE802154_ADDR_LEN; + memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN); + dev->type = ARPHRD_IEEE802154; + /* Frame Control + Sequence Number + Address fields + Security Header */ + dev->hard_header_len = 2 + 1 + 20 + 14; + dev->needed_tailroom = 2; /* FCS */ + dev->mtu = IPV6_MIN_MTU; + dev->tx_queue_len = 0; + dev->flags = IFF_BROADCAST | IFF_MULTICAST; + dev->watchdog_timeo = 0; + + dev->netdev_ops = &lowpan_netdev_ops; + dev->header_ops = &lowpan_header_ops; + dev->ml_priv = &lowpan_mlme; + dev->destructor = free_netdev; +} + +static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[]) +{ + if (tb[IFLA_ADDRESS]) { + if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN) + return -EINVAL; + } + return 0; +} + +static int lowpan_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + struct net_device *real_dev; + struct lowpan_dev_record *entry; + int ret; + + ASSERT_RTNL(); + + pr_debug("adding new link\n"); + + if (!tb[IFLA_LINK]) + return -EINVAL; + /* find and hold real wpan device */ + real_dev = dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); + if (!real_dev) + return -ENODEV; + if (real_dev->type != ARPHRD_IEEE802154) { + dev_put(real_dev); + return -EINVAL; + } + + lowpan_dev_info(dev)->real_dev = real_dev; + mutex_init(&lowpan_dev_info(dev)->dev_list_mtx); + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + dev_put(real_dev); + lowpan_dev_info(dev)->real_dev = NULL; + return -ENOMEM; + } + + entry->ldev = dev; + + /* Set the lowpan hardware address to the wpan hardware address. */ + memcpy(dev->dev_addr, real_dev->dev_addr, IEEE802154_ADDR_LEN); + + mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); + INIT_LIST_HEAD(&entry->list); + list_add_tail(&entry->list, &lowpan_devices); + mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); + + ret = register_netdevice(dev); + if (ret >= 0) { + if (!lowpan_open_count) + lowpan_rx_init(); + lowpan_open_count++; + } + + return ret; +} + +static void lowpan_dellink(struct net_device *dev, struct list_head *head) +{ + struct lowpan_dev_info *lowpan_dev = lowpan_dev_info(dev); + struct net_device *real_dev = lowpan_dev->real_dev; + struct lowpan_dev_record *entry, *tmp; + + ASSERT_RTNL(); + + lowpan_open_count--; + if (!lowpan_open_count) + lowpan_rx_exit(); + + mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); + list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { + if (entry->ldev == dev) { + list_del(&entry->list); + kfree(entry); + } + } + mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); + + mutex_destroy(&lowpan_dev_info(dev)->dev_list_mtx); + + unregister_netdevice_queue(dev, head); + + dev_put(real_dev); +} + +static struct rtnl_link_ops lowpan_link_ops __read_mostly = { + .kind = "lowpan", + .priv_size = sizeof(struct lowpan_dev_info), + .setup = lowpan_setup, + .newlink = lowpan_newlink, + .dellink = lowpan_dellink, + .validate = lowpan_validate, +}; + +static inline int __init lowpan_netlink_init(void) +{ + return rtnl_link_register(&lowpan_link_ops); +} + +static inline void lowpan_netlink_fini(void) +{ + rtnl_link_unregister(&lowpan_link_ops); +} + +static int lowpan_device_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + LIST_HEAD(del_list); + struct lowpan_dev_record *entry, *tmp; + + if (dev->type != ARPHRD_IEEE802154) + goto out; + + if (event == NETDEV_UNREGISTER) { + list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { + if (lowpan_dev_info(entry->ldev)->real_dev == dev) + lowpan_dellink(entry->ldev, &del_list); + } + + unregister_netdevice_many(&del_list); + } + +out: + return NOTIFY_DONE; +} + +static struct notifier_block lowpan_dev_notifier = { + .notifier_call = lowpan_device_event, +}; + +static int __init lowpan_init_module(void) +{ + int err = 0; + + err = lowpan_net_frag_init(); + if (err < 0) + goto out; + + err = lowpan_netlink_init(); + if (err < 0) + goto out_frag; + + err = register_netdevice_notifier(&lowpan_dev_notifier); + if (err < 0) + goto out_pack; + + return 0; + +out_pack: + lowpan_netlink_fini(); +out_frag: + lowpan_net_frag_exit(); +out: + return err; +} + +static void __exit lowpan_cleanup_module(void) +{ + lowpan_netlink_fini(); + + lowpan_net_frag_exit(); + + unregister_netdevice_notifier(&lowpan_dev_notifier); +} + +module_init(lowpan_init_module); +module_exit(lowpan_cleanup_module); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_RTNL_LINK("lowpan"); -- cgit v0.10.2 From 3db084fd0af5407eebeb9b7ebd5d0425b362c519 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 8 Jan 2015 00:39:17 +0200 Subject: ALSA: fm801: PCI core handles power state for us There is no need to repeat the work that is already done in the PCI driver core. The patch removes excerpts from suspend and resume callbacks. Note that there is no more calls performed to enable or disable a PCI device during suspend-resume cycle. Nowadays they seems to be superflous. Someone can read more in [1]. [1] https://www.kernel.org/doc/ols/2009/ols2009-pages-319-330.pdf Signed-off-by: Andy Shevchenko Signed-off-by: Takashi Iwai diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index dd6f23f..1a0494e 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -1375,7 +1375,6 @@ static unsigned char saved_regs[] = { static int snd_fm801_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct fm801 *chip = card->private_data; int i; @@ -1387,29 +1386,15 @@ static int snd_fm801_suspend(struct device *dev) for (i = 0; i < ARRAY_SIZE(saved_regs); i++) chip->saved_regs[i] = inw(chip->port + saved_regs[i]); /* FIXME: tea575x suspend */ - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int snd_fm801_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct fm801 *chip = card->private_data; int i; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - snd_fm801_chip_init(chip, 1); snd_ac97_resume(chip->ac97); snd_ac97_resume(chip->ac97_sec); -- cgit v0.10.2 From e9f49369722bd7dd53f90467196df4b952eac1b6 Mon Sep 17 00:00:00 2001 From: Paul Bonser Date: Wed, 7 Jan 2015 23:34:16 -0600 Subject: ALSA: usb-audio: Add support for Akai MPC Element USB MIDI controller The Akai MPC Element incorrectly reports its bInterfaceClass as 255, but otherwise implements the USB MIDI spec correctly. This adds a quirks-table.h entry which allows the device to be recognized as a standard USB MIDI device. Signed-off-by: Paul Bonser Signed-off-by: Takashi Iwai diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 0a598af..67d4765 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2486,6 +2486,28 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, +{ + /* Akai MPC Element */ + USB_DEVICE(0x09e8, 0x0021), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_MIDI_STANDARD_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, + /* TerraTec devices */ { USB_DEVICE_VENDOR_SPEC(0x0ccd, 0x0012), -- cgit v0.10.2 From 2558cd8cab793e9c8c3b17bdf06552bfb98d49e5 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 18 Dec 2014 15:38:00 +0100 Subject: microblaze: Use unsigned type for limit comparison in cache.c The patch removes warnings: arch/microblaze/kernel/cpu/cache.c:146:14: warning: comparison of unsigned expression < 0 is always false [-Wtype-limits] Signed-off-by: Michal Simek diff --git a/arch/microblaze/kernel/cpu/cache.c b/arch/microblaze/kernel/cpu/cache.c index a6e44410..0bde47e 100644 --- a/arch/microblaze/kernel/cpu/cache.c +++ b/arch/microblaze/kernel/cpu/cache.c @@ -140,10 +140,10 @@ do { \ /* It is used only first parameter for OP - for wic, wdc */ #define CACHE_RANGE_LOOP_1(start, end, line_length, op) \ do { \ - int volatile temp = 0; \ - int align = ~(line_length - 1); \ + unsigned int volatile temp = 0; \ + unsigned int align = ~(line_length - 1); \ end = ((end & align) == end) ? end - line_length : end & align; \ - WARN_ON(end - start < 0); \ + WARN_ON(end < start); \ \ __asm__ __volatile__ (" 1: " #op " %1, r0;" \ "cmpu %0, %1, %2;" \ -- cgit v0.10.2 From ed4602e13f8d7b764308d6db58ff9932353f16a3 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 18 Dec 2014 15:38:07 +0100 Subject: microblaze: Fix variable types to remove W=1 warning This patch removes this warning: arch/microblaze/kernel/signal.c: In function 'setup_rt_frame': arch/microblaze/kernel/signal.c:177:3: warning: signed and unsigned type in conditional expression [-Wsign-compare] Signed-off-by: Michal Simek diff --git a/arch/microblaze/kernel/signal.c b/arch/microblaze/kernel/signal.c index 8955a38..2357060 100644 --- a/arch/microblaze/kernel/signal.c +++ b/arch/microblaze/kernel/signal.c @@ -158,7 +158,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, { struct rt_sigframe __user *frame; int err = 0, sig = ksig->sig; - int signal; + unsigned long signal; unsigned long address = 0; #ifdef CONFIG_MMU pmd_t *pmdp; @@ -174,7 +174,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, && current_thread_info()->exec_domain->signal_invmap && sig < 32 ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; + : (unsigned long)sig; if (ksig->ka.sa.sa_flags & SA_SIGINFO) err |= copy_siginfo_to_user(&frame->info, &ksig->info); -- cgit v0.10.2 From 81653edd99ee2297ad6ab49a4f91a1d5dce577f1 Mon Sep 17 00:00:00 2001 From: Erico Nunes Date: Fri, 2 Jan 2015 00:40:33 -0200 Subject: microblaze: Add missing PVR version codes PVR version code was missing in the cpu_ver_lookup table for the following versions: 8.50.b 8.50.c 9.2 9.3 This caused /proc/cpuinfo to display "CPU-Ver: Unknown" for these versions. This was detected and the patch tested with MicroBlaze version 8.50.c. The other codes were taken from the Xilinx MicroBlaze Processor Reference Guides UG081 (v14.7) and UG984 (v2014.1). Signed-off-by: Erico Nunes Signed-off-by: Michal Simek diff --git a/arch/microblaze/kernel/cpu/cpuinfo.c b/arch/microblaze/kernel/cpu/cpuinfo.c index 234acad..b60442d 100644 --- a/arch/microblaze/kernel/cpu/cpuinfo.c +++ b/arch/microblaze/kernel/cpu/cpuinfo.c @@ -41,8 +41,12 @@ const struct cpu_ver_key cpu_ver_lookup[] = { {"8.40.a", 0x18}, {"8.40.b", 0x19}, {"8.50.a", 0x1a}, + {"8.50.b", 0x1c}, + {"8.50.c", 0x1e}, {"9.0", 0x1b}, {"9.1", 0x1d}, + {"9.2", 0x1f}, + {"9.3", 0x20}, {NULL, 0}, }; -- cgit v0.10.2 From ed89466f2368fc15c72ce5344d582c640dcf53a6 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 5 Jan 2015 12:01:17 +0100 Subject: microblaze: Add target architecture Add missing target architectures - virtex7, ultrascale virtex and ultrascale kintex. Signed-off-by: Michal Simek diff --git a/arch/microblaze/kernel/cpu/cpuinfo.c b/arch/microblaze/kernel/cpu/cpuinfo.c index b60442d..d1dd6e8 100644 --- a/arch/microblaze/kernel/cpu/cpuinfo.c +++ b/arch/microblaze/kernel/cpu/cpuinfo.c @@ -65,11 +65,14 @@ const struct family_string_key family_string_lookup[] = { {"spartan3adsp", 0xc}, {"spartan6", 0xd}, {"virtex6", 0xe}, + {"virtex7", 0xf}, /* FIXME There is no key code defined for spartan2 */ {"spartan2", 0xf0}, {"kintex7", 0x10}, {"artix7", 0x11}, {"zynq7000", 0x12}, + {"UltraScale Virtex", 0x13}, + {"UltraScale Kintex", 0x14}, {NULL, 0}, }; -- cgit v0.10.2 From 2c80a072a6c1cc4490fbbde5ba82bc7faf0374f0 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Fri, 19 Dec 2014 10:21:04 -0800 Subject: microblaze: intc: Don't override error codes Just pass on error codes instead of overriding them. Signed-off-by: Soren Brinkmann Signed-off-by: Michal Simek diff --git a/arch/microblaze/kernel/intc.c b/arch/microblaze/kernel/intc.c index 15c7c12..01ae730 100644 --- a/arch/microblaze/kernel/intc.c +++ b/arch/microblaze/kernel/intc.c @@ -148,13 +148,13 @@ static int __init xilinx_intc_of_init(struct device_node *intc, ret = of_property_read_u32(intc, "xlnx,num-intr-inputs", &nr_irq); if (ret < 0) { pr_err("%s: unable to read xlnx,num-intr-inputs\n", __func__); - return -EINVAL; + return ret; } ret = of_property_read_u32(intc, "xlnx,kind-of-intr", &intr_mask); if (ret < 0) { pr_err("%s: unable to read xlnx,kind-of-intr\n", __func__); - return -EINVAL; + return ret; } if (intr_mask > (u32)((1ULL << nr_irq) - 1)) -- cgit v0.10.2 From d50466c90724d5aae4d38a9a9bc163bea6f94098 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Fri, 19 Dec 2014 10:21:05 -0800 Subject: microblaze: intc: Refactor DT sanity check Avoid funky casts and arithmetic. Signed-off-by: Soren Brinkmann Signed-off-by: Michal Simek diff --git a/arch/microblaze/kernel/intc.c b/arch/microblaze/kernel/intc.c index 01ae730..8965fd3 100644 --- a/arch/microblaze/kernel/intc.c +++ b/arch/microblaze/kernel/intc.c @@ -157,7 +157,7 @@ static int __init xilinx_intc_of_init(struct device_node *intc, return ret; } - if (intr_mask > (u32)((1ULL << nr_irq) - 1)) + if (intr_mask >> nr_irq) pr_info(" ERROR: Mismatch in kind-of-intr param\n"); pr_info("%s: num_irq=%d, edge=0x%x\n", -- cgit v0.10.2 From 231856ae7ccb5ee49b2f722e1d8ba7a45df1a978 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Fri, 19 Dec 2014 10:21:06 -0800 Subject: microblaze: intc: Reformat output A message was using pr_info level output for a message including "ERROR" which is not really a fatal error. Remove the 'ERROR' from that message, use pr_warn loglevel and add the function name to the output to give users a chance to find the culprit in case the warning triggers. Signed-off-by: Soren Brinkmann Signed-off-by: Michal Simek diff --git a/arch/microblaze/kernel/intc.c b/arch/microblaze/kernel/intc.c index 8965fd3..719feee 100644 --- a/arch/microblaze/kernel/intc.c +++ b/arch/microblaze/kernel/intc.c @@ -158,7 +158,7 @@ static int __init xilinx_intc_of_init(struct device_node *intc, } if (intr_mask >> nr_irq) - pr_info(" ERROR: Mismatch in kind-of-intr param\n"); + pr_warn("%s: mismatch in kind-of-intr param\n", __func__); pr_info("%s: num_irq=%d, edge=0x%x\n", intc->full_name, nr_irq, intr_mask); -- cgit v0.10.2 From 0774bf6a8b49ccd35fad58a1eed0d2382f34912e Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 17:44:02 +0200 Subject: microblaze/uaccess: fix sparse errors virtio wants to read bitwise types from userspace using get_user. At the moment this triggers sparse errors, since the value is passed through an integer. Fix that up using __force. Signed-off-by: Michael S. Tsirkin Signed-off-by: Michal Simek diff --git a/arch/microblaze/include/asm/uaccess.h b/arch/microblaze/include/asm/uaccess.h index 59a89a6..e41bebf 100644 --- a/arch/microblaze/include/asm/uaccess.h +++ b/arch/microblaze/include/asm/uaccess.h @@ -220,7 +220,7 @@ extern long __user_bad(void); } else { \ __gu_err = -EFAULT; \ } \ - x = (typeof(*(ptr)))__gu_val; \ + x = (__force typeof(*(ptr)))__gu_val; \ __gu_err; \ }) @@ -242,7 +242,7 @@ extern long __user_bad(void); default: \ /* __gu_val = 0; __gu_err = -EINVAL;*/ __gu_err = __user_bad();\ } \ - x = (__typeof__(*(ptr))) __gu_val; \ + x = (__force __typeof__(*(ptr))) __gu_val; \ __gu_err; \ }) -- cgit v0.10.2 From 132d5dfc04698e4226ca9787214bd4e277ed39f2 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 17:45:03 +0200 Subject: microblaze: whitespace fix Align using tabs to make code prettier. Signed-off-by: Michael S. Tsirkin Signed-off-by: Michal Simek diff --git a/arch/microblaze/include/asm/uaccess.h b/arch/microblaze/include/asm/uaccess.h index e41bebf..62942fd 100644 --- a/arch/microblaze/include/asm/uaccess.h +++ b/arch/microblaze/include/asm/uaccess.h @@ -306,7 +306,7 @@ extern long __user_bad(void); #define __put_user_check(x, ptr, size) \ ({ \ - typeof(*(ptr)) volatile __pu_val = x; \ + typeof(*(ptr)) volatile __pu_val = x; \ typeof(*(ptr)) __user *__pu_addr = (ptr); \ int __pu_err = 0; \ \ -- cgit v0.10.2 From 58498ee3e573dc03be651b6839dbdf865ae7ee38 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 9 Dec 2014 10:18:49 +0100 Subject: s390/ftrace: add code replacement sanity checks Always verify that the to be replaced code matches what we expect to see. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/ftrace.c b/arch/s390/kernel/ftrace.c index b86bb88..3dabcae 100644 --- a/arch/s390/kernel/ftrace.c +++ b/arch/s390/kernel/ftrace.c @@ -59,62 +59,65 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long addr) { - struct ftrace_insn insn; - unsigned short op; - void *from, *to; - size_t size; - - ftrace_generate_nop_insn(&insn); - size = sizeof(insn); - from = &insn; - to = (void *) rec->ip; - if (probe_kernel_read(&op, (void *) rec->ip, sizeof(op))) + struct ftrace_insn orig, new, old; + + if (probe_kernel_read(&old, (void *) rec->ip, sizeof(old))) return -EFAULT; - /* - * If we find a breakpoint instruction, a kprobe has been placed - * at the beginning of the function. We write the constant - * KPROBE_ON_FTRACE_NOP into the remaining four bytes of the original - * instruction so that the kprobes handler can execute a nop, if it - * reaches this breakpoint. - */ - if (op == BREAKPOINT_INSTRUCTION) { - size -= 2; - from += 2; - to += 2; - insn.disp = KPROBE_ON_FTRACE_NOP; + if (addr == MCOUNT_ADDR) { + /* Initial code replacement; we expect to see stg r14,8(r15) */ + orig.opc = 0xe3e0; + orig.disp = 0xf0080024; + ftrace_generate_nop_insn(&new); + } else if (old.opc == BREAKPOINT_INSTRUCTION) { + /* + * If we find a breakpoint instruction, a kprobe has been + * placed at the beginning of the function. We write the + * constant KPROBE_ON_FTRACE_NOP into the remaining four + * bytes of the original instruction so that the kprobes + * handler can execute a nop, if it reaches this breakpoint. + */ + new.opc = orig.opc = BREAKPOINT_INSTRUCTION; + orig.disp = KPROBE_ON_FTRACE_CALL; + new.disp = KPROBE_ON_FTRACE_NOP; + } else { + /* Replace ftrace call with a nop. */ + ftrace_generate_call_insn(&orig, rec->ip); + ftrace_generate_nop_insn(&new); } - if (probe_kernel_write(to, from, size)) + /* Verify that the to be replaced code matches what we expect. */ + if (memcmp(&orig, &old, sizeof(old))) + return -EINVAL; + if (probe_kernel_write((void *) rec->ip, &new, sizeof(new))) return -EPERM; return 0; } int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) { - struct ftrace_insn insn; - unsigned short op; - void *from, *to; - size_t size; - - ftrace_generate_call_insn(&insn, rec->ip); - size = sizeof(insn); - from = &insn; - to = (void *) rec->ip; - if (probe_kernel_read(&op, (void *) rec->ip, sizeof(op))) + struct ftrace_insn orig, new, old; + + if (probe_kernel_read(&old, (void *) rec->ip, sizeof(old))) return -EFAULT; - /* - * If we find a breakpoint instruction, a kprobe has been placed - * at the beginning of the function. We write the constant - * KPROBE_ON_FTRACE_CALL into the remaining four bytes of the original - * instruction so that the kprobes handler can execute a brasl if it - * reaches this breakpoint. - */ - if (op == BREAKPOINT_INSTRUCTION) { - size -= 2; - from += 2; - to += 2; - insn.disp = KPROBE_ON_FTRACE_CALL; + if (old.opc == BREAKPOINT_INSTRUCTION) { + /* + * If we find a breakpoint instruction, a kprobe has been + * placed at the beginning of the function. We write the + * constant KPROBE_ON_FTRACE_CALL into the remaining four + * bytes of the original instruction so that the kprobes + * handler can execute a brasl if it reaches this breakpoint. + */ + new.opc = orig.opc = BREAKPOINT_INSTRUCTION; + orig.disp = KPROBE_ON_FTRACE_NOP; + new.disp = KPROBE_ON_FTRACE_CALL; + } else { + /* Replace nop with an ftrace call. */ + ftrace_generate_nop_insn(&orig); + ftrace_generate_call_insn(&new, rec->ip); } - if (probe_kernel_write(to, from, size)) + /* Verify that the to be replaced code matches what we expect. */ + if (memcmp(&orig, &old, sizeof(old))) + return -EINVAL; + if (probe_kernel_write((void *) rec->ip, &new, sizeof(new))) return -EPERM; return 0; } -- cgit v0.10.2 From eba8452525e3fd0b982f78365dea8bd2ce11a20a Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 12 Dec 2014 12:46:35 +0100 Subject: s390/pci: add missing address space annotation Signed-off-by: Heiko Carstens diff --git a/arch/s390/pci/pci_mmio.c b/arch/s390/pci/pci_mmio.c index 62c5ea6..8aa271b 100644 --- a/arch/s390/pci/pci_mmio.c +++ b/arch/s390/pci/pci_mmio.c @@ -55,7 +55,7 @@ SYSCALL_DEFINE3(s390_pci_mmio_write, unsigned long, mmio_addr, ret = get_pfn(mmio_addr, VM_WRITE, &pfn); if (ret) goto out; - io_addr = (void *)((pfn << PAGE_SHIFT) | (mmio_addr & ~PAGE_MASK)); + io_addr = (void __iomem *)((pfn << PAGE_SHIFT) | (mmio_addr & ~PAGE_MASK)); ret = -EFAULT; if ((unsigned long) io_addr < ZPCI_IOMAP_ADDR_BASE) @@ -96,7 +96,7 @@ SYSCALL_DEFINE3(s390_pci_mmio_read, unsigned long, mmio_addr, ret = get_pfn(mmio_addr, VM_READ, &pfn); if (ret) goto out; - io_addr = (void *)((pfn << PAGE_SHIFT) | (mmio_addr & ~PAGE_MASK)); + io_addr = (void __iomem *)((pfn << PAGE_SHIFT) | (mmio_addr & ~PAGE_MASK)); ret = -EFAULT; if ((unsigned long) io_addr < ZPCI_IOMAP_ADDR_BASE) -- cgit v0.10.2 From 8d1f211ebbdfd57843a52fa7efe34251530beec1 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 12 Dec 2014 12:52:00 +0100 Subject: s390/disassembler: remove indentical initializer Remove one of the two identical initializer entries. Signed-off-by: Heiko Carstens diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c index f376293..d46d0b0 100644 --- a/arch/s390/kernel/dis.c +++ b/arch/s390/kernel/dis.c @@ -226,7 +226,6 @@ static const struct s390_operand operands[] = [U16_32] = { 16, 32, 0 }, [J16_16] = { 16, 16, OPERAND_PCREL }, [J16_32] = { 16, 32, OPERAND_PCREL }, - [I16_32] = { 16, 32, OPERAND_SIGNED }, [I24_24] = { 24, 24, OPERAND_SIGNED }, [J32_16] = { 32, 16, OPERAND_PCREL }, [I32_16] = { 32, 16, OPERAND_SIGNED }, -- cgit v0.10.2 From 925dfc020a41ce484172a43b603437e58aecd1c1 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 12 Dec 2014 13:04:21 +0100 Subject: s390/pgtable: add unsigned long casts Get rid of warnings like this one: warning: constant 0xffe0000000000000 is so big it is unsigned long Signed-off-by: Heiko Carstens diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index 601deb8..47cbca0 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -527,7 +527,7 @@ int __gmap_link(struct gmap *gmap, unsigned long gaddr, unsigned long vmaddr) table += (gaddr >> 53) & 0x7ff; if ((*table & _REGION_ENTRY_INVALID) && gmap_alloc_table(gmap, table, _REGION2_ENTRY_EMPTY, - gaddr & 0xffe0000000000000)) + gaddr & 0xffe0000000000000UL)) return -ENOMEM; table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN); } @@ -535,7 +535,7 @@ int __gmap_link(struct gmap *gmap, unsigned long gaddr, unsigned long vmaddr) table += (gaddr >> 42) & 0x7ff; if ((*table & _REGION_ENTRY_INVALID) && gmap_alloc_table(gmap, table, _REGION3_ENTRY_EMPTY, - gaddr & 0xfffffc0000000000)) + gaddr & 0xfffffc0000000000UL)) return -ENOMEM; table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN); } @@ -543,7 +543,7 @@ int __gmap_link(struct gmap *gmap, unsigned long gaddr, unsigned long vmaddr) table += (gaddr >> 31) & 0x7ff; if ((*table & _REGION_ENTRY_INVALID) && gmap_alloc_table(gmap, table, _SEGMENT_ENTRY_EMPTY, - gaddr & 0xffffffff80000000)) + gaddr & 0xffffffff80000000UL)) return -ENOMEM; table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN); } -- cgit v0.10.2 From e0a50545480de0936ab867168d9bd086e56f465c Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 12 Dec 2014 13:11:08 +0100 Subject: s390/signal: add sys_sigreturn and sys_rt_sigreturn declarations Get rid of sparse warnings like this one: arch/s390/kernel/signal.c:244:1: warning: symbol 'sys_sigreturn' was not declared. Should it be static? Signed-off-by: Heiko Carstens diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h index 8e61393..834df04 100644 --- a/arch/s390/kernel/entry.h +++ b/arch/s390/kernel/entry.h @@ -71,9 +71,11 @@ struct s390_mmap_arg_struct; struct fadvise64_64_args; struct old_sigaction; +long sys_rt_sigreturn(void); +long sys_sigreturn(void); + long sys_s390_personality(unsigned int personality); long sys_s390_runtime_instr(int command, int signum); - long sys_s390_pci_mmio_write(unsigned long, const void __user *, size_t); long sys_s390_pci_mmio_read(unsigned long, void __user *, size_t); #endif /* _ENTRY_H */ -- cgit v0.10.2 From 8ebd51a705c56520481f2b813790dc5afdb0a751 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sat, 20 Dec 2014 13:27:49 +0100 Subject: s390/cio: idset.c: remove some unused functions Removes some functions that are not used anywhere: idset_clear() idset_sch_get_first() This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/idset.c b/drivers/s390/cio/idset.c index 5a99908..b3e06a7 100644 --- a/drivers/s390/cio/idset.c +++ b/drivers/s390/cio/idset.c @@ -38,11 +38,6 @@ void idset_free(struct idset *set) vfree(set); } -void idset_clear(struct idset *set) -{ - memset(set->bitmap, 0, bitmap_size(set->num_ssid, set->num_id)); -} - void idset_fill(struct idset *set) { memset(set->bitmap, 0xff, bitmap_size(set->num_ssid, set->num_id)); @@ -103,21 +98,6 @@ int idset_sch_contains(struct idset *set, struct subchannel_id schid) return idset_contains(set, schid.ssid, schid.sch_no); } -int idset_sch_get_first(struct idset *set, struct subchannel_id *schid) -{ - int ssid = 0; - int id = 0; - int rc; - - rc = idset_get_first(set, &ssid, &id); - if (rc) { - init_subchannel_id(schid); - schid->ssid = ssid; - schid->sch_no = id; - } - return rc; -} - int idset_is_empty(struct idset *set) { return bitmap_empty(set->bitmap, set->num_ssid * set->num_id); diff --git a/drivers/s390/cio/idset.h b/drivers/s390/cio/idset.h index 06d3bc0..22b5810 100644 --- a/drivers/s390/cio/idset.h +++ b/drivers/s390/cio/idset.h @@ -11,7 +11,6 @@ struct idset; void idset_free(struct idset *set); -void idset_clear(struct idset *set); void idset_fill(struct idset *set); struct idset *idset_sch_new(void); @@ -19,7 +18,6 @@ void idset_sch_add(struct idset *set, struct subchannel_id id); void idset_sch_del(struct idset *set, struct subchannel_id id); void idset_sch_del_subseq(struct idset *set, struct subchannel_id schid); int idset_sch_contains(struct idset *set, struct subchannel_id id); -int idset_sch_get_first(struct idset *set, struct subchannel_id *id); int idset_is_empty(struct idset *set); void idset_add_set(struct idset *to, struct idset *from); -- cgit v0.10.2 From 47523c983f55448c3a09cc3f1a885bf81cd422e3 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 22 Dec 2014 10:07:04 +0100 Subject: s390: keep Kconfig sorted Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 68b68d7..e79c3ea 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -66,6 +66,7 @@ config S390 select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE select ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS select ARCH_HAS_GCOV_PROFILE_ALL + select ARCH_HAS_SG_CHAIN select ARCH_HAVE_NMI_SAFE_CMPXCHG select ARCH_INLINE_READ_LOCK select ARCH_INLINE_READ_LOCK_BH @@ -151,7 +152,6 @@ config S390 select TTY select VIRT_CPU_ACCOUNTING select VIRT_TO_BUS - select ARCH_HAS_SG_CHAIN config SCHED_OMIT_FRAME_POINTER def_bool y -- cgit v0.10.2 From fbf87dff6706d412fe69b8158f7ae415e5e7380b Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Sat, 3 Jan 2015 17:29:07 +0800 Subject: s390/sclp: fix declaration of _sclp_print_early() _sclp_print_early() has return value: at present, return 0 for OK, 1 for failure. It returns '%r2', so use 'long' as return value (upper caller can check '%r2' directly). The related warning: CC arch/s390/boot/compressed/misc.o arch/s390/boot/compressed/misc.c:66:8: warning: type defaults to 'int' in declaration of '_sclp_print_early' [-Wimplicit-int] extern _sclp_print_early(const char *); ^ At present, _sclp_print_early() is only used by puts(), so can still remain its declaration in 'misc.c' file. [heiko.carstens@de.ibm.com]: move declaration to sclp.h header file Signed-off-by: Chen Gang Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/boot/compressed/misc.c b/arch/s390/boot/compressed/misc.c index 57cbaff..42506b3 100644 --- a/arch/s390/boot/compressed/misc.c +++ b/arch/s390/boot/compressed/misc.c @@ -8,6 +8,7 @@ #include #include +#include #include #include "sizes.h" @@ -63,8 +64,6 @@ static unsigned long free_mem_end_ptr; #include "../../../../lib/decompress_unxz.c" #endif -extern _sclp_print_early(const char *); - static int puts(const char *s) { _sclp_print_early(s); diff --git a/arch/s390/include/asm/sclp.h b/arch/s390/include/asm/sclp.h index 1aba89b..b6f8066 100644 --- a/arch/s390/include/asm/sclp.h +++ b/arch/s390/include/asm/sclp.h @@ -68,4 +68,6 @@ void sclp_early_detect(void); int sclp_has_siif(void); unsigned int sclp_get_ibc(void); +long _sclp_print_early(const char *); + #endif /* _ASM_S390_SCLP_H */ -- cgit v0.10.2 From a7e75d434b53b45ae60779903904d4fbdbd145a5 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 5 Jan 2015 10:10:14 +0100 Subject: s390/sclp: sign extend return value of _sclp_print_early() _sclp_print_early() has a return value, but misses to sign extend it if called from 64 bit code. This is not really a bug, since currently no caller cares what the return value is. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/sclp.S b/arch/s390/kernel/sclp.S index a41f2c9..7e77e03 100644 --- a/arch/s390/kernel/sclp.S +++ b/arch/s390/kernel/sclp.S @@ -294,7 +294,8 @@ ENTRY(_sclp_print_early) #ifdef CONFIG_64BIT tm LC_AR_MODE_ID,1 jno .Lesa3 - lmh %r6,%r15,96(%r15) # store upper register halves + lgfr %r2,%r2 # sign extend return value + lmh %r6,%r15,96(%r15) # restore upper register halves ahi %r15,80 .Lesa3: #endif -- cgit v0.10.2 From 91c0837e6dee8c694c8849c70e1f0f770d92d072 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 5 Jan 2015 04:29:18 -0800 Subject: s390: remove unnecessary KERN_CONT This has no effect as KERN_CONT is an empty string, It's probably just a missing conversion artifact as the other pr_cont uses in the same file don't have this prefix. Signed-off-by: Joe Perches Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 811937b..232c14e 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -171,7 +171,7 @@ static void dump_pagetable(unsigned long asce, unsigned long address) table = table + ((address >> 20) & 0x7ff); if (bad_address(table)) goto bad; - pr_cont(KERN_CONT "S:%016lx ", *table); + pr_cont("S:%016lx ", *table); if (*table & (_SEGMENT_ENTRY_INVALID | _SEGMENT_ENTRY_LARGE)) goto out; table = (unsigned long *)(*table & _SEGMENT_ENTRY_ORIGIN); -- cgit v0.10.2 From e6a67ad0e29087201536792f7d5cecec4ff6fc64 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Thu, 1 Jan 2015 22:56:02 +0800 Subject: s390/crypto: remove 'const' to avoid compiler warnings In aes_encrypt() and aes_decrypt(), need let 'sctx->key' be modified, so remove 'const' for it. The related warnings: CC [M] arch/s390/crypto/aes_s390.o arch/s390/crypto/aes_s390.c: In function 'aes_encrypt': arch/s390/crypto/aes_s390.c:146:37: warning: passing argument 2 of 'crypt_s390_km' discards 'const' qualifier from pointer target type [-Wdiscarded-array-qualifiers] crypt_s390_km(KM_AES_128_ENCRYPT, &sctx->key, out, in, ^ ... In file included from arch/s390/crypto/aes_s390.c:29:0: arch/s390/crypto/crypt_s390.h:154:19: note: expected 'void *' but argument is of type 'const u8 (*)[32] {aka const unsigned char (*)[32]}' static inline int crypt_s390_km(long func, void *param, ^ Signed-off-by: Chen Gang Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/crypto/aes_s390.c b/arch/s390/crypto/aes_s390.c index 1f272b2..5566ce8 100644 --- a/arch/s390/crypto/aes_s390.c +++ b/arch/s390/crypto/aes_s390.c @@ -134,7 +134,7 @@ static int aes_set_key(struct crypto_tfm *tfm, const u8 *in_key, static void aes_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) { - const struct s390_aes_ctx *sctx = crypto_tfm_ctx(tfm); + struct s390_aes_ctx *sctx = crypto_tfm_ctx(tfm); if (unlikely(need_fallback(sctx->key_len))) { crypto_cipher_encrypt_one(sctx->fallback.cip, out, in); @@ -159,7 +159,7 @@ static void aes_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) static void aes_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) { - const struct s390_aes_ctx *sctx = crypto_tfm_ctx(tfm); + struct s390_aes_ctx *sctx = crypto_tfm_ctx(tfm); if (unlikely(need_fallback(sctx->key_len))) { crypto_cipher_decrypt_one(sctx->fallback.cip, out, in); -- cgit v0.10.2 From d97d929f06d0e072cd36fba6bd9d25b29bae34fd Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 8 Jan 2015 07:41:52 +0000 Subject: s390: move cacheinfo sysfs to generic cacheinfo infrastructure This patch removes the redundant sysfs cacheinfo code by reusing the newly introduced generic cacheinfo infrastructure through the commit 246246cbde5e ("drivers: base: support cpu cache information interface to userspace via sysfs") Signed-off-by: Sudeep Holla Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/cache.c b/arch/s390/kernel/cache.c index c0b03c2..fe21f07 100644 --- a/arch/s390/kernel/cache.c +++ b/arch/s390/kernel/cache.c @@ -5,37 +5,11 @@ * Author(s): Heiko Carstens */ -#include #include -#include -#include -#include #include +#include #include -struct cache { - unsigned long size; - unsigned int line_size; - unsigned int associativity; - unsigned int nr_sets; - unsigned int level : 3; - unsigned int type : 2; - unsigned int private : 1; - struct list_head list; -}; - -struct cache_dir { - struct kobject *kobj; - struct cache_index_dir *index; -}; - -struct cache_index_dir { - struct kobject kobj; - int cpu; - struct cache *cache; - struct cache_index_dir *next; -}; - enum { CACHE_SCOPE_NOTEXISTS, CACHE_SCOPE_PRIVATE, @@ -44,10 +18,10 @@ enum { }; enum { - CACHE_TYPE_SEPARATE, - CACHE_TYPE_DATA, - CACHE_TYPE_INSTRUCTION, - CACHE_TYPE_UNIFIED, + CTYPE_SEPARATE, + CTYPE_DATA, + CTYPE_INSTRUCTION, + CTYPE_UNIFIED, }; enum { @@ -70,39 +44,59 @@ struct cache_info { }; #define CACHE_MAX_LEVEL 8 - union cache_topology { struct cache_info ci[CACHE_MAX_LEVEL]; unsigned long long raw; }; static const char * const cache_type_string[] = { - "Data", + "", "Instruction", + "Data", + "", "Unified", }; -static struct cache_dir *cache_dir_cpu[NR_CPUS]; -static LIST_HEAD(cache_list); +static const enum cache_type cache_type_map[] = { + [CTYPE_SEPARATE] = CACHE_TYPE_SEPARATE, + [CTYPE_DATA] = CACHE_TYPE_DATA, + [CTYPE_INSTRUCTION] = CACHE_TYPE_INST, + [CTYPE_UNIFIED] = CACHE_TYPE_UNIFIED, +}; void show_cacheinfo(struct seq_file *m) { - struct cache *cache; - int index = 0; + int cpu = smp_processor_id(), idx; + struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); + struct cacheinfo *cache; - list_for_each_entry(cache, &cache_list, list) { - seq_printf(m, "cache%-11d: ", index); + for (idx = 0; idx < this_cpu_ci->num_leaves; idx++) { + cache = this_cpu_ci->info_list + idx; + seq_printf(m, "cache%-11d: ", idx); seq_printf(m, "level=%d ", cache->level); seq_printf(m, "type=%s ", cache_type_string[cache->type]); - seq_printf(m, "scope=%s ", cache->private ? "Private" : "Shared"); - seq_printf(m, "size=%luK ", cache->size >> 10); - seq_printf(m, "line_size=%u ", cache->line_size); - seq_printf(m, "associativity=%d", cache->associativity); + seq_printf(m, "scope=%s ", + cache->disable_sysfs ? "Shared" : "Private"); + seq_printf(m, "size=%dK ", cache->size >> 10); + seq_printf(m, "line_size=%u ", cache->coherency_line_size); + seq_printf(m, "associativity=%d", cache->ways_of_associativity); seq_puts(m, "\n"); - index++; } } +static inline enum cache_type get_cache_type(struct cache_info *ci, int level) +{ + if (level >= CACHE_MAX_LEVEL) + return CACHE_TYPE_NOCACHE; + + ci += level; + + if (ci->scope != CACHE_SCOPE_SHARED && ci->scope != CACHE_SCOPE_PRIVATE) + return CACHE_TYPE_NOCACHE; + + return cache_type_map[ci->type]; +} + static inline unsigned long ecag(int ai, int li, int ti) { unsigned long cmd, val; @@ -113,277 +107,79 @@ static inline unsigned long ecag(int ai, int li, int ti) return val; } -static int __init cache_add(int level, int private, int type) +static void ci_leaf_init(struct cacheinfo *this_leaf, int private, + enum cache_type type, unsigned int level) { - struct cache *cache; - int ti; + int ti, num_sets; + int cpu = smp_processor_id(); - cache = kzalloc(sizeof(*cache), GFP_KERNEL); - if (!cache) - return -ENOMEM; - if (type == CACHE_TYPE_INSTRUCTION) + if (type == CACHE_TYPE_INST) ti = CACHE_TI_INSTRUCTION; else ti = CACHE_TI_UNIFIED; - cache->size = ecag(EXTRACT_SIZE, level, ti); - cache->line_size = ecag(EXTRACT_LINE_SIZE, level, ti); - cache->associativity = ecag(EXTRACT_ASSOCIATIVITY, level, ti); - cache->nr_sets = cache->size / cache->associativity; - cache->nr_sets /= cache->line_size; - cache->private = private; - cache->level = level + 1; - cache->type = type - 1; - list_add_tail(&cache->list, &cache_list); - return 0; -} - -static void __init cache_build_info(void) -{ - struct cache *cache, *next; - union cache_topology ct; - int level, private, rc; - - ct.raw = ecag(EXTRACT_TOPOLOGY, 0, 0); - for (level = 0; level < CACHE_MAX_LEVEL; level++) { - switch (ct.ci[level].scope) { - case CACHE_SCOPE_SHARED: - private = 0; - break; - case CACHE_SCOPE_PRIVATE: - private = 1; - break; - default: - return; - } - if (ct.ci[level].type == CACHE_TYPE_SEPARATE) { - rc = cache_add(level, private, CACHE_TYPE_DATA); - rc |= cache_add(level, private, CACHE_TYPE_INSTRUCTION); - } else { - rc = cache_add(level, private, ct.ci[level].type); - } - if (rc) - goto error; - } - return; -error: - list_for_each_entry_safe(cache, next, &cache_list, list) { - list_del(&cache->list); - kfree(cache); - } -} - -static struct cache_dir *cache_create_cache_dir(int cpu) -{ - struct cache_dir *cache_dir; - struct kobject *kobj = NULL; - struct device *dev; - - dev = get_cpu_device(cpu); - if (!dev) - goto out; - kobj = kobject_create_and_add("cache", &dev->kobj); - if (!kobj) - goto out; - cache_dir = kzalloc(sizeof(*cache_dir), GFP_KERNEL); - if (!cache_dir) - goto out; - cache_dir->kobj = kobj; - cache_dir_cpu[cpu] = cache_dir; - return cache_dir; -out: - kobject_put(kobj); - return NULL; -} - -static struct cache_index_dir *kobj_to_cache_index_dir(struct kobject *kobj) -{ - return container_of(kobj, struct cache_index_dir, kobj); -} - -static void cache_index_release(struct kobject *kobj) -{ - struct cache_index_dir *index; - - index = kobj_to_cache_index_dir(kobj); - kfree(index); -} - -static ssize_t cache_index_show(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - struct kobj_attribute *kobj_attr; - - kobj_attr = container_of(attr, struct kobj_attribute, attr); - return kobj_attr->show(kobj, kobj_attr, buf); -} - -#define DEFINE_CACHE_ATTR(_name, _format, _value) \ -static ssize_t cache_##_name##_show(struct kobject *kobj, \ - struct kobj_attribute *attr, \ - char *buf) \ -{ \ - struct cache_index_dir *index; \ - \ - index = kobj_to_cache_index_dir(kobj); \ - return sprintf(buf, _format, _value); \ -} \ -static struct kobj_attribute cache_##_name##_attr = \ - __ATTR(_name, 0444, cache_##_name##_show, NULL); -DEFINE_CACHE_ATTR(size, "%luK\n", index->cache->size >> 10); -DEFINE_CACHE_ATTR(coherency_line_size, "%u\n", index->cache->line_size); -DEFINE_CACHE_ATTR(number_of_sets, "%u\n", index->cache->nr_sets); -DEFINE_CACHE_ATTR(ways_of_associativity, "%u\n", index->cache->associativity); -DEFINE_CACHE_ATTR(type, "%s\n", cache_type_string[index->cache->type]); -DEFINE_CACHE_ATTR(level, "%d\n", index->cache->level); + this_leaf->level = level + 1; + this_leaf->type = type; + this_leaf->coherency_line_size = ecag(EXTRACT_LINE_SIZE, level, ti); + this_leaf->ways_of_associativity = ecag(EXTRACT_ASSOCIATIVITY, + level, ti); + this_leaf->size = ecag(EXTRACT_SIZE, level, ti); -static ssize_t shared_cpu_map_func(struct kobject *kobj, int type, char *buf) -{ - struct cache_index_dir *index; - int len; - - index = kobj_to_cache_index_dir(kobj); - len = type ? - cpulist_scnprintf(buf, PAGE_SIZE - 2, cpumask_of(index->cpu)) : - cpumask_scnprintf(buf, PAGE_SIZE - 2, cpumask_of(index->cpu)); - len += sprintf(&buf[len], "\n"); - return len; -} - -static ssize_t shared_cpu_map_show(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) -{ - return shared_cpu_map_func(kobj, 0, buf); + num_sets = this_leaf->size / this_leaf->coherency_line_size; + num_sets /= this_leaf->ways_of_associativity; + this_leaf->number_of_sets = num_sets; + cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map); + if (!private) + this_leaf->disable_sysfs = true; } -static struct kobj_attribute cache_shared_cpu_map_attr = - __ATTR(shared_cpu_map, 0444, shared_cpu_map_show, NULL); -static ssize_t shared_cpu_list_show(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) +int init_cache_level(unsigned int cpu) { - return shared_cpu_map_func(kobj, 1, buf); -} -static struct kobj_attribute cache_shared_cpu_list_attr = - __ATTR(shared_cpu_list, 0444, shared_cpu_list_show, NULL); - -static struct attribute *cache_index_default_attrs[] = { - &cache_type_attr.attr, - &cache_size_attr.attr, - &cache_number_of_sets_attr.attr, - &cache_ways_of_associativity_attr.attr, - &cache_level_attr.attr, - &cache_coherency_line_size_attr.attr, - &cache_shared_cpu_map_attr.attr, - &cache_shared_cpu_list_attr.attr, - NULL, -}; - -static const struct sysfs_ops cache_index_ops = { - .show = cache_index_show, -}; - -static struct kobj_type cache_index_type = { - .sysfs_ops = &cache_index_ops, - .release = cache_index_release, - .default_attrs = cache_index_default_attrs, -}; - -static int cache_create_index_dir(struct cache_dir *cache_dir, - struct cache *cache, int index, int cpu) -{ - struct cache_index_dir *index_dir; - int rc; - - index_dir = kzalloc(sizeof(*index_dir), GFP_KERNEL); - if (!index_dir) - return -ENOMEM; - index_dir->cache = cache; - index_dir->cpu = cpu; - rc = kobject_init_and_add(&index_dir->kobj, &cache_index_type, - cache_dir->kobj, "index%d", index); - if (rc) - goto out; - index_dir->next = cache_dir->index; - cache_dir->index = index_dir; - return 0; -out: - kfree(index_dir); - return rc; -} + struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); + unsigned int level = 0, leaves = 0; + union cache_topology ct; + enum cache_type ctype; -static int cache_add_cpu(int cpu) -{ - struct cache_dir *cache_dir; - struct cache *cache; - int rc, index = 0; + if (!this_cpu_ci) + return -EINVAL; - if (list_empty(&cache_list)) - return 0; - cache_dir = cache_create_cache_dir(cpu); - if (!cache_dir) - return -ENOMEM; - list_for_each_entry(cache, &cache_list, list) { - if (!cache->private) + ct.raw = ecag(EXTRACT_TOPOLOGY, 0, 0); + do { + ctype = get_cache_type(&ct.ci[0], level); + if (ctype == CACHE_TYPE_NOCACHE) break; - rc = cache_create_index_dir(cache_dir, cache, index, cpu); - if (rc) - return rc; - index++; - } - return 0; -} + /* Separate instruction and data caches */ + leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1; + } while (++level < CACHE_MAX_LEVEL); -static void cache_remove_cpu(int cpu) -{ - struct cache_index_dir *index, *next; - struct cache_dir *cache_dir; + this_cpu_ci->num_levels = level; + this_cpu_ci->num_leaves = leaves; - cache_dir = cache_dir_cpu[cpu]; - if (!cache_dir) - return; - index = cache_dir->index; - while (index) { - next = index->next; - kobject_put(&index->kobj); - index = next; - } - kobject_put(cache_dir->kobj); - kfree(cache_dir); - cache_dir_cpu[cpu] = NULL; + return 0; } -static int cache_hotplug(struct notifier_block *nfb, unsigned long action, - void *hcpu) +int populate_cache_leaves(unsigned int cpu) { - int cpu = (long)hcpu; - int rc = 0; + unsigned int level, idx, pvt; + union cache_topology ct; + enum cache_type ctype; + struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); + struct cacheinfo *this_leaf = this_cpu_ci->info_list; - switch (action & ~CPU_TASKS_FROZEN) { - case CPU_ONLINE: - rc = cache_add_cpu(cpu); - if (rc) - cache_remove_cpu(cpu); - break; - case CPU_DEAD: - cache_remove_cpu(cpu); - break; + ct.raw = ecag(EXTRACT_TOPOLOGY, 0, 0); + for (idx = 0, level = 0; level < this_cpu_ci->num_levels && + idx < this_cpu_ci->num_leaves; idx++, level++) { + if (!this_leaf) + return -EINVAL; + + pvt = (ct.ci[level].scope == CACHE_SCOPE_PRIVATE) ? 1 : 0; + ctype = get_cache_type(&ct.ci[0], level); + if (ctype == CACHE_TYPE_SEPARATE) { + ci_leaf_init(this_leaf++, pvt, CACHE_TYPE_DATA, level); + ci_leaf_init(this_leaf++, pvt, CACHE_TYPE_INST, level); + } else { + ci_leaf_init(this_leaf++, pvt, ctype, level); + } } - return rc ? NOTIFY_BAD : NOTIFY_OK; -} - -static int __init cache_init(void) -{ - int cpu; - - if (!test_facility(34)) - return 0; - cache_build_info(); - - cpu_notifier_register_begin(); - for_each_online_cpu(cpu) - cache_add_cpu(cpu); - __hotcpu_notifier(cache_hotplug, 0); - cpu_notifier_register_done(); return 0; } -device_initcall(cache_init); -- cgit v0.10.2 From a12d159d06317420c1a6941f5657b2918a02bf74 Mon Sep 17 00:00:00 2001 From: Jianqun Xu Date: Thu, 8 Jan 2015 10:49:59 +0800 Subject: ASoC: rockchip: i2s: applys rate symmetry for CPU DAI This patch applys rate symmetry for rockchip i2s DAI. Signed-off-by: Jianqun Xu Signed-off-by: Mark Brown diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 26ec511..deced0e 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -335,6 +335,7 @@ static struct snd_soc_dai_driver rockchip_i2s_dai = { SNDRV_PCM_FMTBIT_S24_LE), }, .ops = &rockchip_i2s_dai_ops, + .symmetric_rates = 1, }; static const struct snd_soc_component_driver rockchip_i2s_component = { -- cgit v0.10.2 From 866476f323465a8afef10b14b48d5136bf5c51fe Mon Sep 17 00:00:00 2001 From: Kristian Evensen Date: Wed, 24 Dec 2014 09:57:10 +0100 Subject: netfilter: conntrack: Flush connections with a given mark This patch adds support for selective flushing of conntrack mappings. By adding CTA_MARK and CTA_MARK_MASK to a delete-message, the mark (and mask) is checked before a connection is deleted while flushing. Configuring the flush is moved out of ctnetlink_del_conntrack(), and instead of calling nf_conntrack_flush_report(), we always call nf_ct_iterate_cleanup(). This enables us to only make one call from the new ctnetlink_flush_conntrack() and makes it easy to add more filter parameters. Filtering is done in the ctnetlink_filter_match()-function, which is also called from ctnetlink_dump_table(). ctnetlink_dump_filter has been renamed ctnetlink_filter, to indicated that it is no longer only used when dumping conntrack entries. Moreover, reject mark filters with -EOPNOTSUPP if no ct mark support is available. Signed-off-by: Kristian Evensen Signed-off-by: Pablo Neira Ayuso diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 1bd9ed9..d1c2394 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -749,13 +749,47 @@ static int ctnetlink_done(struct netlink_callback *cb) return 0; } -struct ctnetlink_dump_filter { +struct ctnetlink_filter { struct { u_int32_t val; u_int32_t mask; } mark; }; +static struct ctnetlink_filter * +ctnetlink_alloc_filter(const struct nlattr * const cda[]) +{ +#ifdef CONFIG_NF_CONNTRACK_MARK + struct ctnetlink_filter *filter; + + filter = kzalloc(sizeof(*filter), GFP_KERNEL); + if (filter == NULL) + return ERR_PTR(-ENOMEM); + + filter->mark.val = ntohl(nla_get_be32(cda[CTA_MARK])); + filter->mark.mask = ntohl(nla_get_be32(cda[CTA_MARK_MASK])); + + return filter; +#else + return ERR_PTR(-EOPNOTSUPP); +#endif +} + +static int ctnetlink_filter_match(struct nf_conn *ct, void *data) +{ + struct ctnetlink_filter *filter = data; + + if (filter == NULL) + return 1; + +#ifdef CONFIG_NF_CONNTRACK_MARK + if ((ct->mark & filter->mark.mask) == filter->mark.val) + return 1; +#endif + + return 0; +} + static int ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb) { @@ -768,10 +802,6 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb) int res; spinlock_t *lockp; -#ifdef CONFIG_NF_CONNTRACK_MARK - const struct ctnetlink_dump_filter *filter = cb->data; -#endif - last = (struct nf_conn *)cb->args[1]; local_bh_disable(); @@ -798,12 +828,9 @@ restart: continue; cb->args[1] = 0; } -#ifdef CONFIG_NF_CONNTRACK_MARK - if (filter && !((ct->mark & filter->mark.mask) == - filter->mark.val)) { + if (!ctnetlink_filter_match(ct, cb->data)) continue; - } -#endif + rcu_read_lock(); res = ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).portid, @@ -1001,6 +1028,25 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = { .len = NF_CT_LABELS_MAX_SIZE }, }; +static int ctnetlink_flush_conntrack(struct net *net, + const struct nlattr * const cda[], + u32 portid, int report) +{ + struct ctnetlink_filter *filter = NULL; + + if (cda[CTA_MARK] && cda[CTA_MARK_MASK]) { + filter = ctnetlink_alloc_filter(cda); + if (IS_ERR(filter)) + return PTR_ERR(filter); + } + + nf_ct_iterate_cleanup(net, ctnetlink_filter_match, filter, + portid, report); + kfree(filter); + + return 0; +} + static int ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, @@ -1024,11 +1070,9 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb, else if (cda[CTA_TUPLE_REPLY]) err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY, u3); else { - /* Flush the whole table */ - nf_conntrack_flush_report(net, - NETLINK_CB(skb).portid, - nlmsg_report(nlh)); - return 0; + return ctnetlink_flush_conntrack(net, cda, + NETLINK_CB(skb).portid, + nlmsg_report(nlh)); } if (err < 0) @@ -1076,21 +1120,16 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, .dump = ctnetlink_dump_table, .done = ctnetlink_done, }; -#ifdef CONFIG_NF_CONNTRACK_MARK + if (cda[CTA_MARK] && cda[CTA_MARK_MASK]) { - struct ctnetlink_dump_filter *filter; + struct ctnetlink_filter *filter; - filter = kzalloc(sizeof(struct ctnetlink_dump_filter), - GFP_ATOMIC); - if (filter == NULL) - return -ENOMEM; + filter = ctnetlink_alloc_filter(cda); + if (IS_ERR(filter)) + return PTR_ERR(filter); - filter->mark.val = ntohl(nla_get_be32(cda[CTA_MARK])); - filter->mark.mask = - ntohl(nla_get_be32(cda[CTA_MARK_MASK])); c.data = filter; } -#endif return netlink_dump_start(ctnl, skb, nlh, &c); } -- cgit v0.10.2 From ae406bd0572be97a46d72e8a5e97c33c3168388c Mon Sep 17 00:00:00 2001 From: Kristian Evensen Date: Wed, 24 Dec 2014 09:57:11 +0100 Subject: netfilter: conntrack: Remove nf_ct_conntrack_flush_report The only user of nf_ct_conntrack_flush_report() was ctnetlink_del_conntrack(). After adding support for flushing connections with a given mark, this function is no longer called. Signed-off-by: Kristian Evensen Signed-off-by: Pablo Neira Ayuso diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index f0daed2..74f271a 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -191,8 +191,6 @@ __nf_conntrack_find(struct net *net, u16 zone, int nf_conntrack_hash_check_insert(struct nf_conn *ct); bool nf_ct_delete(struct nf_conn *ct, u32 pid, int report); -void nf_conntrack_flush_report(struct net *net, u32 portid, int report); - bool nf_ct_get_tuplepr(const struct sk_buff *skb, unsigned int nhoff, u_int16_t l3num, struct nf_conntrack_tuple *tuple); bool nf_ct_invert_tuplepr(struct nf_conntrack_tuple *inverse, diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index da58cd4..5a6990b 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1426,12 +1426,6 @@ void nf_ct_free_hashtable(void *hash, unsigned int size) } EXPORT_SYMBOL_GPL(nf_ct_free_hashtable); -void nf_conntrack_flush_report(struct net *net, u32 portid, int report) -{ - nf_ct_iterate_cleanup(net, kill_all, NULL, portid, report); -} -EXPORT_SYMBOL_GPL(nf_conntrack_flush_report); - static int untrack_refs(void) { int cnt = 0, cpu; -- cgit v0.10.2 From f71d148f1adf82edef467d41a2a3be22b36811f0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Jan 2015 15:24:20 +0200 Subject: sata_dwc_460ex: remove redundant dev_set_drvdata Driver core sets it to NULL upon probe failure or release. Signed-off-by: Andy Shevchenko Signed-off-by: Tejun Heo diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 8e824817..5c34395 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -1783,7 +1783,6 @@ static int sata_dwc_remove(struct platform_device *ofdev) struct sata_dwc_device *hsdev = host->private_data; ata_host_detach(host); - dev_set_drvdata(dev, NULL); /* Free SATA DMA resources */ dma_dwc_exit(hsdev); -- cgit v0.10.2 From 84683a7e081ff60e75039cd2475776654ebbf526 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Jan 2015 15:24:21 +0200 Subject: sata_dwc_460ex: enable COMPILE_TEST for the driver To test how the driver could be compiled in the non-native environment let's enable COMPILE_TEST for it. It would be useful for further work. This patch enables COMPILE_TEST for the driver and fixes compilation errors on at least x86 platforms. Signed-off-by: Andy Shevchenko Signed-off-by: Tejun Heo diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index a3a1360..5ed20e3 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -269,7 +269,7 @@ config ATA_PIIX config SATA_DWC tristate "DesignWare Cores SATA support" - depends on 460EX + depends on 460EX || COMPILE_TEST help This option enables support for the on-chip SATA controller of the AppliedMicro processor 460EX. diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 5c34395..c1723e0 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -48,6 +48,18 @@ #define DRV_NAME "sata-dwc" #define DRV_VERSION "1.3" +#ifndef out_le32 +#define out_le32(a, v) __raw_writel(__cpu_to_le32(v), (void __iomem *)(a)) +#endif + +#ifndef in_le32 +#define in_le32(a) __le32_to_cpu(__raw_readl((void __iomem *)(a))) +#endif + +#ifndef NO_IRQ +#define NO_IRQ 0 +#endif + /* SATA DMA driver Globals */ #define DMA_NUM_CHANS 1 #define DMA_NUM_CHAN_REGS 8 -- cgit v0.10.2 From d7c256e857c12597813d631a7890a19bbbb6f3a5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Jan 2015 15:24:22 +0200 Subject: sata_dwc_460ex: fix most of the sparse warnings There are a lot sparse warnings. Most of them related to __iomem keyword which sometimes absent when it's needed and vise versa. The patch fixes most of the warnings. While at it, remove the redundant sata_dwc_sht.can_queue initialization to ATA_DEF_QUEUE. tj: Added description about ATA_DEF_QUEUE init removal. Signed-off-by: Andy Shevchenko Signed-off-by: Tejun Heo diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index c1723e0..6c1b649 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -285,7 +285,7 @@ struct sata_dwc_device { struct device *dev; /* generic device struct */ struct ata_probe_ent *pe; /* ptr to probe-ent */ struct ata_host *host; - u8 *reg_base; + u8 __iomem *reg_base; struct sata_dwc_regs *sata_dwc_regs; /* DW Synopsys SATA specific */ int irq_dma; }; @@ -335,7 +335,9 @@ struct sata_dwc_host_priv { struct device *dwc_dev; int dma_channel; }; -struct sata_dwc_host_priv host_pvt; + +static struct sata_dwc_host_priv host_pvt; + /* * Prototypes */ @@ -592,9 +594,9 @@ static int map_sg_to_lli(struct scatterlist *sg, int num_elems, sms_val = 0; dms_val = 1 + host_pvt.dma_channel; - dev_dbg(host_pvt.dwc_dev, "%s: sg=%p nelem=%d lli=%p dma_lli=0x%08x" - " dmadr=0x%08x\n", __func__, sg, num_elems, lli, (u32)dma_lli, - (u32)dmadr_addr); + dev_dbg(host_pvt.dwc_dev, + "%s: sg=%p nelem=%d lli=%p dma_lli=0x%pad dmadr=0x%p\n", + __func__, sg, num_elems, lli, &dma_lli, dmadr_addr); bl = get_burst_length_encode(AHB_DMA_BRST_DFLT); @@ -785,7 +787,7 @@ static void dma_dwc_exit(struct sata_dwc_device *hsdev) { dev_dbg(host_pvt.dwc_dev, "%s:\n", __func__); if (host_pvt.sata_dma_regs) { - iounmap(host_pvt.sata_dma_regs); + iounmap((void __iomem *)host_pvt.sata_dma_regs); host_pvt.sata_dma_regs = NULL; } @@ -830,7 +832,7 @@ static int sata_dwc_scr_read(struct ata_link *link, unsigned int scr, u32 *val) return -EINVAL; } - *val = in_le32((void *)link->ap->ioaddr.scr_addr + (scr * 4)); + *val = in_le32(link->ap->ioaddr.scr_addr + (scr * 4)); dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=val=0x%08x\n", __func__, link->ap->print_id, scr, *val); @@ -846,21 +848,19 @@ static int sata_dwc_scr_write(struct ata_link *link, unsigned int scr, u32 val) __func__, scr); return -EINVAL; } - out_le32((void *)link->ap->ioaddr.scr_addr + (scr * 4), val); + out_le32(link->ap->ioaddr.scr_addr + (scr * 4), val); return 0; } static u32 core_scr_read(unsigned int scr) { - return in_le32((void __iomem *)(host_pvt.scr_addr_sstatus) +\ - (scr * 4)); + return in_le32(host_pvt.scr_addr_sstatus + (scr * 4)); } static void core_scr_write(unsigned int scr, u32 val) { - out_le32((void __iomem *)(host_pvt.scr_addr_sstatus) + (scr * 4), - val); + out_le32(host_pvt.scr_addr_sstatus + (scr * 4), val); } static void clear_serror(void) @@ -868,7 +868,6 @@ static void clear_serror(void) u32 val; val = core_scr_read(SCR_ERROR); core_scr_write(SCR_ERROR, val); - } static void clear_interrupt_bit(struct sata_dwc_device *hsdev, u32 bit) @@ -1268,24 +1267,24 @@ static void sata_dwc_enable_interrupts(struct sata_dwc_device *hsdev) static void sata_dwc_setup_port(struct ata_ioports *port, unsigned long base) { - port->cmd_addr = (void *)base + 0x00; - port->data_addr = (void *)base + 0x00; + port->cmd_addr = (void __iomem *)base + 0x00; + port->data_addr = (void __iomem *)base + 0x00; - port->error_addr = (void *)base + 0x04; - port->feature_addr = (void *)base + 0x04; + port->error_addr = (void __iomem *)base + 0x04; + port->feature_addr = (void __iomem *)base + 0x04; - port->nsect_addr = (void *)base + 0x08; + port->nsect_addr = (void __iomem *)base + 0x08; - port->lbal_addr = (void *)base + 0x0c; - port->lbam_addr = (void *)base + 0x10; - port->lbah_addr = (void *)base + 0x14; + port->lbal_addr = (void __iomem *)base + 0x0c; + port->lbam_addr = (void __iomem *)base + 0x10; + port->lbah_addr = (void __iomem *)base + 0x14; - port->device_addr = (void *)base + 0x18; - port->command_addr = (void *)base + 0x1c; - port->status_addr = (void *)base + 0x1c; + port->device_addr = (void __iomem *)base + 0x18; + port->command_addr = (void __iomem *)base + 0x1c; + port->status_addr = (void __iomem *)base + 0x1c; - port->altstatus_addr = (void *)base + 0x20; - port->ctl_addr = (void *)base + 0x20; + port->altstatus_addr = (void __iomem *)base + 0x20; + port->ctl_addr = (void __iomem *)base + 0x20; } /* @@ -1326,7 +1325,7 @@ static int sata_dwc_port_start(struct ata_port *ap) for (i = 0; i < SATA_DWC_QCMD_MAX; i++) hsdevp->cmd_issued[i] = SATA_DWC_CMD_ISSUED_NOT; - ap->bmdma_prd = 0; /* set these so libata doesn't use them */ + ap->bmdma_prd = NULL; /* set these so libata doesn't use them */ ap->bmdma_prd_dma = 0; /* @@ -1523,8 +1522,8 @@ static void sata_dwc_qc_prep_by_tag(struct ata_queued_cmd *qc, u8 tag) dma_chan = dma_dwc_xfer_setup(sg, qc->n_elem, hsdevp->llit[tag], hsdevp->llit_dma[tag], - (void *__iomem)(&hsdev->sata_dwc_regs->\ - dmadr), qc->dma_dir); + (void __iomem *)&hsdev->sata_dwc_regs->dmadr, + qc->dma_dir); if (dma_chan < 0) { dev_err(ap->dev, "%s: dma_dwc_xfer_setup returns err %d\n", __func__, dma_chan); @@ -1597,8 +1596,8 @@ static void sata_dwc_error_handler(struct ata_port *ap) ata_sff_error_handler(ap); } -int sata_dwc_hardreset(struct ata_link *link, unsigned int *class, - unsigned long deadline) +static int sata_dwc_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) { struct sata_dwc_device *hsdev = HSDEV_FROM_AP(link->ap); int ret; @@ -1630,7 +1629,7 @@ static struct scsi_host_template sata_dwc_sht = { * max of 1. This will get fixed in in a future release. */ .sg_tablesize = LIBATA_MAX_PRD, - .can_queue = ATA_DEF_QUEUE, /* ATA_MAX_QUEUE */ + /* .can_queue = ATA_MAX_QUEUE, */ .dma_boundary = ATA_DMA_BOUNDARY, }; @@ -1667,7 +1666,7 @@ static int sata_dwc_probe(struct platform_device *ofdev) struct sata_dwc_device *hsdev; u32 idr, versionr; char *ver = (char *)&versionr; - u8 *base = NULL; + u8 __iomem *base; int err = 0; int irq; struct ata_host *host; @@ -1736,7 +1735,7 @@ static int sata_dwc_probe(struct platform_device *ofdev) } /* Get physical SATA DMA register base address */ - host_pvt.sata_dma_regs = of_iomap(ofdev->dev.of_node, 1); + host_pvt.sata_dma_regs = (void *)of_iomap(ofdev->dev.of_node, 1); if (!(host_pvt.sata_dma_regs)) { dev_err(&ofdev->dev, "ioremap failed for AHBDMA register" " address\n"); -- cgit v0.10.2 From 9037908f16d6376b79f3639d89470e5ef4c7a85f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 8 Jan 2015 12:50:12 +0200 Subject: sata_dwc_460ex: use np local variable in ->probe() This patch fixes the style of usage of a node pointer. There is no functional change. Signed-off-by: Andy Shevchenko Signed-off-by: Tejun Heo diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 6c1b649..b206017 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -1691,7 +1691,7 @@ static int sata_dwc_probe(struct platform_device *ofdev) host_pvt.dma_channel = dma_chan; /* Ioremap SATA registers */ - base = of_iomap(ofdev->dev.of_node, 0); + base = of_iomap(np, 0); if (!base) { dev_err(&ofdev->dev, "ioremap failed for SATA register" " address\n"); @@ -1727,7 +1727,7 @@ static int sata_dwc_probe(struct platform_device *ofdev) idr, ver[0], ver[1], ver[2]); /* Get SATA DMA interrupt number */ - irq = irq_of_parse_and_map(ofdev->dev.of_node, 1); + irq = irq_of_parse_and_map(np, 1); if (irq == NO_IRQ) { dev_err(&ofdev->dev, "no SATA DMA irq\n"); err = -ENODEV; @@ -1735,7 +1735,7 @@ static int sata_dwc_probe(struct platform_device *ofdev) } /* Get physical SATA DMA register base address */ - host_pvt.sata_dma_regs = (void *)of_iomap(ofdev->dev.of_node, 1); + host_pvt.sata_dma_regs = (void *)of_iomap(np, 1); if (!(host_pvt.sata_dma_regs)) { dev_err(&ofdev->dev, "ioremap failed for AHBDMA register" " address\n"); @@ -1755,7 +1755,7 @@ static int sata_dwc_probe(struct platform_device *ofdev) sata_dwc_enable_interrupts(hsdev); /* Get SATA interrupt number */ - irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); + irq = irq_of_parse_and_map(np, 0); if (irq == NO_IRQ) { dev_err(&ofdev->dev, "no SATA DMA irq\n"); err = -ENODEV; -- cgit v0.10.2 From c592b74f6b8ba72cb0b5a90ceb0989fc35fc1391 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 8 Jan 2015 12:50:13 +0200 Subject: sata_dwc_460ex: remove extra message There is no need to print a message about failure of memory allocation. The caller will get an error code and may print the same. Signed-off-by: Andy Shevchenko Signed-off-by: Tejun Heo diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index b206017..169cbca 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -1677,11 +1677,8 @@ static int sata_dwc_probe(struct platform_device *ofdev) /* Allocate DWC SATA device */ hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL); - if (hsdev == NULL) { - dev_err(&ofdev->dev, "kmalloc failed for hsdev\n"); - err = -ENOMEM; - goto error; - } + if (hsdev == NULL) + return -ENOMEM; if (of_property_read_u32(np, "dma-channel", &dma_chan)) { dev_warn(&ofdev->dev, "no dma-channel property set." @@ -1783,7 +1780,6 @@ error_iomap: iounmap(base); error_kmalloc: kfree(hsdev); -error: return err; } -- cgit v0.10.2 From d537fc0c0e23ce87201cb763bf040e730834f94d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 8 Jan 2015 12:50:14 +0200 Subject: sata_dwc_460ex: convert to devm_kzalloc in ->probe() The patch converts ->probe() to use devm_kzalloc that simplifies error path. Note that ata_host_alloc_pinfo() has been using device resources already. Signed-off-by: Andy Shevchenko Signed-off-by: Tejun Heo diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 169cbca..fdb0f28 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -1676,10 +1676,13 @@ static int sata_dwc_probe(struct platform_device *ofdev) u32 dma_chan; /* Allocate DWC SATA device */ - hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL); - if (hsdev == NULL) + host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_DWC_MAX_PORTS); + hsdev = devm_kzalloc(&ofdev->dev, sizeof(*hsdev), GFP_KERNEL); + if (!host || !hsdev) return -ENOMEM; + host->private_data = hsdev; + if (of_property_read_u32(np, "dma-channel", &dma_chan)) { dev_warn(&ofdev->dev, "no dma-channel property set." " Use channel 0\n"); @@ -1692,8 +1695,7 @@ static int sata_dwc_probe(struct platform_device *ofdev) if (!base) { dev_err(&ofdev->dev, "ioremap failed for SATA register" " address\n"); - err = -ENODEV; - goto error_kmalloc; + return -ENODEV; } hsdev->reg_base = base; dev_dbg(&ofdev->dev, "ioremap done for SATA register address\n"); @@ -1701,16 +1703,6 @@ static int sata_dwc_probe(struct platform_device *ofdev) /* Synopsys DWC SATA specific Registers */ hsdev->sata_dwc_regs = (void *__iomem)(base + SATA_DWC_REG_OFFSET); - /* Allocate and fill host */ - host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_DWC_MAX_PORTS); - if (!host) { - dev_err(&ofdev->dev, "ata_host_alloc_pinfo failed\n"); - err = -ENOMEM; - goto error_iomap; - } - - host->private_data = hsdev; - /* Setup port */ host->ports[0]->ioaddr.cmd_addr = base; host->ports[0]->ioaddr.scr_addr = base + SATA_DWC_SCR_OFFSET; @@ -1778,8 +1770,6 @@ error_dma_iomap: iounmap((void __iomem *)host_pvt.sata_dma_regs); error_iomap: iounmap(base); -error_kmalloc: - kfree(hsdev); return err; } @@ -1796,8 +1786,6 @@ static int sata_dwc_remove(struct platform_device *ofdev) iounmap((void __iomem *)host_pvt.sata_dma_regs); iounmap(hsdev->reg_base); - kfree(hsdev); - kfree(host); dev_dbg(&ofdev->dev, "done\n"); return 0; } -- cgit v0.10.2 From 4ed20bebf51578229a1986efcf46344075ec8447 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Nov 2014 16:35:34 +0100 Subject: cfg80211: remove "channel" from survey names All of the survey data is (currently) per channel anyway, so having the word "channel" in the name does nothing. In the next patch I'll introduce global data to the survey, where the word "channel" is actually confusing. Signed-off-by: Johannes Berg diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index c0f3e4d..721631c 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1344,11 +1344,11 @@ static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) rx_clear_count -= ar->survey_last_rx_clear_count; survey = &ar->survey[idx]; - survey->channel_time = WMI_CHAN_INFO_MSEC(cycle_count); - survey->channel_time_rx = WMI_CHAN_INFO_MSEC(rx_clear_count); + survey->time = WMI_CHAN_INFO_MSEC(cycle_count); + survey->time_rx = WMI_CHAN_INFO_MSEC(rx_clear_count); survey->noise = noise_floor; - survey->filled = SURVEY_INFO_CHANNEL_TIME | - SURVEY_INFO_CHANNEL_TIME_RX | + survey->filled = SURVEY_INFO_TIME | + SURVEY_INFO_TIME_RX | SURVEY_INFO_NOISE_DBM; } diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index 19eab2a..3b4a646 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -672,10 +672,10 @@ ath5k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) spin_lock_bh(&common->cc_lock); ath_hw_cycle_counters_update(common); if (cc->cycles > 0) { - ah->survey.channel_time += cc->cycles / div; - ah->survey.channel_time_busy += cc->rx_busy / div; - ah->survey.channel_time_rx += cc->rx_frame / div; - ah->survey.channel_time_tx += cc->tx_frame / div; + ah->survey.time += cc->cycles / div; + ah->survey.time_busy += cc->rx_busy / div; + ah->survey.time_rx += cc->rx_frame / div; + ah->survey.time_tx += cc->tx_frame / div; } memset(cc, 0, sizeof(*cc)); spin_unlock_bh(&common->cc_lock); @@ -686,10 +686,10 @@ ath5k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) survey->noise = ah->ah_noise_floor; survey->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_IN_USE | - SURVEY_INFO_CHANNEL_TIME | - SURVEY_INFO_CHANNEL_TIME_BUSY | - SURVEY_INFO_CHANNEL_TIME_RX | - SURVEY_INFO_CHANNEL_TIME_TX; + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_TX; return 0; } diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index b829263..90631d7 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -516,14 +516,14 @@ int ath_update_survey_stats(struct ath_softc *sc) ath_hw_cycle_counters_update(common); if (cc->cycles > 0) { - survey->filled |= SURVEY_INFO_CHANNEL_TIME | - SURVEY_INFO_CHANNEL_TIME_BUSY | - SURVEY_INFO_CHANNEL_TIME_RX | - SURVEY_INFO_CHANNEL_TIME_TX; - survey->channel_time += cc->cycles / div; - survey->channel_time_busy += cc->rx_busy / div; - survey->channel_time_rx += cc->rx_frame / div; - survey->channel_time_tx += cc->tx_frame / div; + survey->filled |= SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_TX; + survey->time += cc->cycles / div; + survey->time_busy += cc->rx_busy / div; + survey->time_rx += cc->rx_frame / div; + survey->time_tx += cc->tx_frame / div; } if (cc->cycles < div) diff --git a/drivers/net/wireless/ath/carl9170/cmd.c b/drivers/net/wireless/ath/carl9170/cmd.c index 39a6387..f2b4f53 100644 --- a/drivers/net/wireless/ath/carl9170/cmd.c +++ b/drivers/net/wireless/ath/carl9170/cmd.c @@ -188,12 +188,12 @@ int carl9170_collect_tally(struct ar9170 *ar) if (ar->channel) { info = &ar->survey[ar->channel->hw_value]; - info->channel_time = ar->tally.active; - info->channel_time_busy = ar->tally.cca; - info->channel_time_tx = ar->tally.tx_time; - do_div(info->channel_time, 1000); - do_div(info->channel_time_busy, 1000); - do_div(info->channel_time_tx, 1000); + info->time = ar->tally.active; + info->time_busy = ar->tally.cca; + info->time_tx = ar->tally.tx_time; + do_div(info->time, 1000); + do_div(info->time_busy, 1000); + do_div(info->time_tx, 1000); } } return 0; diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index ef5b6dc..f1455a0 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1690,9 +1690,9 @@ found: survey->filled |= SURVEY_INFO_IN_USE; if (ar->fw.hw_counters) { - survey->filled |= SURVEY_INFO_CHANNEL_TIME | - SURVEY_INFO_CHANNEL_TIME_BUSY | - SURVEY_INFO_CHANNEL_TIME_TX; + survey->filled |= SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_TX; } return 0; diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 4a66a655..8bd446b 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1037,10 +1037,11 @@ mwifiex_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *dev, survey->channel = ieee80211_get_channel(wiphy, ieee80211_channel_to_frequency(pchan_stats[idx].chan_num, band)); survey->filled = SURVEY_INFO_NOISE_DBM | - SURVEY_INFO_CHANNEL_TIME | SURVEY_INFO_CHANNEL_TIME_BUSY; + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY; survey->noise = pchan_stats[idx].noise; - survey->channel_time = pchan_stats[idx].cca_scan_dur; - survey->channel_time_busy = pchan_stats[idx].cca_busy_dur; + survey->time = pchan_stats[idx].cca_scan_dur; + survey->time_busy = pchan_stats[idx].cca_busy_dur; return 0; } diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index b8d1e04..f9b1218 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3098,14 +3098,14 @@ static void mwl8k_update_survey(struct mwl8k_priv *priv, cca_cnt = ioread32(priv->regs + NOK_CCA_CNT_REG); cca_cnt /= 1000; /* uSecs to mSecs */ - survey->channel_time_busy = (u64) cca_cnt; + survey->time_busy = (u64) cca_cnt; rx_rdy = ioread32(priv->regs + BBU_RXRDY_CNT_REG); rx_rdy /= 1000; /* uSecs to mSecs */ - survey->channel_time_rx = (u64) rx_rdy; + survey->time_rx = (u64) rx_rdy; priv->channel_time = jiffies - priv->channel_time; - survey->channel_time = jiffies_to_msecs(priv->channel_time); + survey->time = jiffies_to_msecs(priv->channel_time); survey->channel = channel; @@ -3115,9 +3115,9 @@ static void mwl8k_update_survey(struct mwl8k_priv *priv, survey->noise = nf * -1; survey->filled = SURVEY_INFO_NOISE_DBM | - SURVEY_INFO_CHANNEL_TIME | - SURVEY_INFO_CHANNEL_TIME_BUSY | - SURVEY_INFO_CHANNEL_TIME_RX; + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_RX; } /* diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c index 0fe67d2..2fe713e 100644 --- a/drivers/net/wireless/p54/eeprom.c +++ b/drivers/net/wireless/p54/eeprom.c @@ -196,9 +196,9 @@ static int p54_generate_band(struct ieee80211_hw *dev, dest->max_power = chan->max_power; priv->survey[*chan_num].channel = &tmp->channels[j]; priv->survey[*chan_num].filled = SURVEY_INFO_NOISE_DBM | - SURVEY_INFO_CHANNEL_TIME | - SURVEY_INFO_CHANNEL_TIME_BUSY | - SURVEY_INFO_CHANNEL_TIME_TX; + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_TX; dest->hw_value = (*chan_num); j++; (*chan_num)++; diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 13a30c4..b9250d7 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -305,9 +305,9 @@ static void p54_reset_stats(struct p54_common *priv) struct survey_info *info = &priv->survey[chan->hw_value]; /* only reset channel statistics, don't touch .filled, etc. */ - info->channel_time = 0; - info->channel_time_busy = 0; - info->channel_time_tx = 0; + info->time = 0; + info->time_busy = 0; + info->time_tx = 0; } priv->update_stats = true; @@ -636,7 +636,7 @@ static int p54_get_survey(struct ieee80211_hw *dev, int idx, if (in_use) { /* test if the reported statistics are valid. */ - if (survey->channel_time != 0) { + if (survey->time != 0) { survey->filled |= SURVEY_INFO_IN_USE; } else { /* diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index 153c615..24e5ff9 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -587,13 +587,13 @@ static void p54_rx_stats(struct p54_common *priv, struct sk_buff *skb) if (chan) { struct survey_info *survey = &priv->survey[chan->hw_value]; survey->noise = clamp(priv->noise, -128, 127); - survey->channel_time = priv->survey_raw.active; - survey->channel_time_tx = priv->survey_raw.tx; - survey->channel_time_busy = priv->survey_raw.tx + + survey->time = priv->survey_raw.active; + survey->time_tx = priv->survey_raw.tx; + survey->time_busy = priv->survey_raw.tx + priv->survey_raw.cca; - do_div(survey->channel_time, 1024); - do_div(survey->channel_time_tx, 1024); - do_div(survey->channel_time_busy, 1024); + do_div(survey->time, 1024); + do_div(survey->time_tx, 1024); + do_div(survey->time_busy, 1024); } tmp = p54_find_and_unlink_skb(priv, hdr->req_id); diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 81ee481..be2d54f 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -8020,13 +8020,13 @@ int rt2800_get_survey(struct ieee80211_hw *hw, int idx, rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC, &busy_ext); if (idle || busy) { - survey->filled = SURVEY_INFO_CHANNEL_TIME | - SURVEY_INFO_CHANNEL_TIME_BUSY | - SURVEY_INFO_CHANNEL_TIME_EXT_BUSY; + survey->filled = SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_EXT_BUSY; - survey->channel_time = (idle + busy) / 1000; - survey->channel_time_busy = busy / 1000; - survey->channel_time_ext_busy = busy_ext / 1000; + survey->time = (idle + busy) / 1000; + survey->time_busy = busy / 1000; + survey->time_ext_busy = busy_ext / 1000; } if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f38645f..3b489f8 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -520,23 +520,23 @@ ieee80211_chandef_max_power(struct cfg80211_chan_def *chandef) * * @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in * @SURVEY_INFO_IN_USE: channel is currently being used - * @SURVEY_INFO_CHANNEL_TIME: channel active time (in ms) was filled in - * @SURVEY_INFO_CHANNEL_TIME_BUSY: channel busy time was filled in - * @SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: extension channel busy time was filled in - * @SURVEY_INFO_CHANNEL_TIME_RX: channel receive time was filled in - * @SURVEY_INFO_CHANNEL_TIME_TX: channel transmit time was filled in + * @SURVEY_INFO_TIME: active time (in ms) was filled in + * @SURVEY_INFO_TIME_BUSY: busy time was filled in + * @SURVEY_INFO_TIME_EXT_BUSY: extension channel busy time was filled in + * @SURVEY_INFO_TIME_RX: receive time was filled in + * @SURVEY_INFO_TIME_TX: transmit time was filled in * * Used by the driver to indicate which info in &struct survey_info * it has filled in during the get_survey(). */ enum survey_info_flags { - SURVEY_INFO_NOISE_DBM = 1<<0, - SURVEY_INFO_IN_USE = 1<<1, - SURVEY_INFO_CHANNEL_TIME = 1<<2, - SURVEY_INFO_CHANNEL_TIME_BUSY = 1<<3, - SURVEY_INFO_CHANNEL_TIME_EXT_BUSY = 1<<4, - SURVEY_INFO_CHANNEL_TIME_RX = 1<<5, - SURVEY_INFO_CHANNEL_TIME_TX = 1<<6, + SURVEY_INFO_NOISE_DBM = BIT(0), + SURVEY_INFO_IN_USE = BIT(1), + SURVEY_INFO_TIME = BIT(2), + SURVEY_INFO_TIME_BUSY = BIT(3), + SURVEY_INFO_TIME_EXT_BUSY = BIT(4), + SURVEY_INFO_TIME_RX = BIT(5), + SURVEY_INFO_TIME_TX = BIT(6), }; /** @@ -546,11 +546,11 @@ enum survey_info_flags { * @filled: bitflag of flags from &enum survey_info_flags * @noise: channel noise in dBm. This and all following fields are * optional - * @channel_time: amount of time in ms the radio spent on the channel - * @channel_time_busy: amount of time the primary channel was sensed busy - * @channel_time_ext_busy: amount of time the extension channel was sensed busy - * @channel_time_rx: amount of time the radio spent receiving data - * @channel_time_tx: amount of time the radio spent transmitting data + * @time: amount of time in ms the radio was turn on (on the channel) + * @time_busy: amount of time the primary channel was sensed busy + * @time_ext_busy: amount of time the extension channel was sensed busy + * @time_rx: amount of time the radio spent receiving data + * @time_tx: amount of time the radio spent transmitting data * * Used by dump_survey() to report back per-channel survey information. * @@ -559,11 +559,11 @@ enum survey_info_flags { */ struct survey_info { struct ieee80211_channel *channel; - u64 channel_time; - u64 channel_time_busy; - u64 channel_time_ext_busy; - u64 channel_time_rx; - u64 channel_time_tx; + u64 time; + u64 time_busy; + u64 time_ext_busy; + u64 time_rx; + u64 time_tx; u32 filled; s8 noise; }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 7ba9404..1a5acc8 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2815,15 +2815,15 @@ enum nl80211_user_reg_hint_type { * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm) * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used - * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio + * @NL80211_SURVEY_INFO_TIME: amount of time (in ms) that the radio * spent on this channel - * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary + * @NL80211_SURVEY_INFO_TIME_BUSY: amount of the time the primary * channel was sensed busy (either due to activity or energy detect) - * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension + * @NL80211_SURVEY_INFO_TIME_EXT_BUSY: amount of time the extension * channel was sensed busy - * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent + * @NL80211_SURVEY_INFO_TIME_RX: amount of time the radio spent * receiving data - * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent + * @NL80211_SURVEY_INFO_TIME_TX: amount of time the radio spent * transmitting data * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number * currently defined @@ -2834,17 +2834,24 @@ enum nl80211_survey_info { NL80211_SURVEY_INFO_FREQUENCY, NL80211_SURVEY_INFO_NOISE, NL80211_SURVEY_INFO_IN_USE, - NL80211_SURVEY_INFO_CHANNEL_TIME, - NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, - NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, - NL80211_SURVEY_INFO_CHANNEL_TIME_RX, - NL80211_SURVEY_INFO_CHANNEL_TIME_TX, + NL80211_SURVEY_INFO_TIME, + NL80211_SURVEY_INFO_TIME_BUSY, + NL80211_SURVEY_INFO_TIME_EXT_BUSY, + NL80211_SURVEY_INFO_TIME_RX, + NL80211_SURVEY_INFO_TIME_TX, /* keep last */ __NL80211_SURVEY_INFO_AFTER_LAST, NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1 }; +/* keep old names for compatibility */ +#define NL80211_SURVEY_INFO_CHANNEL_TIME NL80211_SURVEY_INFO_TIME +#define NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY NL80211_SURVEY_INFO_TIME_BUSY +#define NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY NL80211_SURVEY_INFO_TIME_EXT_BUSY +#define NL80211_SURVEY_INFO_CHANNEL_TIME_RX NL80211_SURVEY_INFO_TIME_RX +#define NL80211_SURVEY_INFO_CHANNEL_TIME_TX NL80211_SURVEY_INFO_TIME_TX + /** * enum nl80211_mntr_flags - monitor configuration flags * diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c index ebfc809..eea7427 100644 --- a/net/mac80211/ethtool.c +++ b/net/mac80211/ethtool.c @@ -175,24 +175,24 @@ do_survey: data[i++] = (u8)survey.noise; else data[i++] = -1LL; - if (survey.filled & SURVEY_INFO_CHANNEL_TIME) - data[i++] = survey.channel_time; + if (survey.filled & SURVEY_INFO_TIME) + data[i++] = survey.time; else data[i++] = -1LL; - if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY) - data[i++] = survey.channel_time_busy; + if (survey.filled & SURVEY_INFO_TIME_BUSY) + data[i++] = survey.time_busy; else data[i++] = -1LL; - if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) - data[i++] = survey.channel_time_ext_busy; + if (survey.filled & SURVEY_INFO_TIME_EXT_BUSY) + data[i++] = survey.time_ext_busy; else data[i++] = -1LL; - if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX) - data[i++] = survey.channel_time_rx; + if (survey.filled & SURVEY_INFO_TIME_RX) + data[i++] = survey.time_rx; else data[i++] = -1LL; - if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX) - data[i++] = survey.channel_time_tx; + if (survey.filled & SURVEY_INFO_TIME_TX) + data[i++] = survey.time_tx; else data[i++] = -1LL; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ad3e294..94ab201 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6641,25 +6641,25 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq, if ((survey->filled & SURVEY_INFO_IN_USE) && nla_put_flag(msg, NL80211_SURVEY_INFO_IN_USE)) goto nla_put_failure; - if ((survey->filled & SURVEY_INFO_CHANNEL_TIME) && - nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME, - survey->channel_time)) + if ((survey->filled & SURVEY_INFO_TIME) && + nla_put_u64(msg, NL80211_SURVEY_INFO_TIME, + survey->time)) goto nla_put_failure; - if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY) && - nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, - survey->channel_time_busy)) + if ((survey->filled & SURVEY_INFO_TIME_BUSY) && + nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_BUSY, + survey->time_busy)) goto nla_put_failure; - if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) && - nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, - survey->channel_time_ext_busy)) + if ((survey->filled & SURVEY_INFO_TIME_EXT_BUSY) && + nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_EXT_BUSY, + survey->time_ext_busy)) goto nla_put_failure; - if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_RX) && - nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX, - survey->channel_time_rx)) + if ((survey->filled & SURVEY_INFO_TIME_RX) && + nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_RX, + survey->time_rx)) goto nla_put_failure; - if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_TX) && - nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX, - survey->channel_time_tx)) + if ((survey->filled & SURVEY_INFO_TIME_TX) && + nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_TX, + survey->time_tx)) goto nla_put_failure; nla_nest_end(msg, infoattr); diff --git a/net/wireless/trace.h b/net/wireless/trace.h index ad38910..bbb7afc 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1604,11 +1604,11 @@ TRACE_EVENT(rdev_return_int_survey_info, WIPHY_ENTRY CHAN_ENTRY __field(int, ret) - __field(u64, channel_time) - __field(u64, channel_time_busy) - __field(u64, channel_time_ext_busy) - __field(u64, channel_time_rx) - __field(u64, channel_time_tx) + __field(u64, time) + __field(u64, time_busy) + __field(u64, time_ext_busy) + __field(u64, time_rx) + __field(u64, time_tx) __field(u32, filled) __field(s8, noise) ), @@ -1616,11 +1616,11 @@ TRACE_EVENT(rdev_return_int_survey_info, WIPHY_ASSIGN; CHAN_ASSIGN(info->channel); __entry->ret = ret; - __entry->channel_time = info->channel_time; - __entry->channel_time_busy = info->channel_time_busy; - __entry->channel_time_ext_busy = info->channel_time_ext_busy; - __entry->channel_time_rx = info->channel_time_rx; - __entry->channel_time_tx = info->channel_time_tx; + __entry->time = info->time; + __entry->time_busy = info->time_busy; + __entry->time_ext_busy = info->time_ext_busy; + __entry->time_rx = info->time_rx; + __entry->time_tx = info->time_tx; __entry->filled = info->filled; __entry->noise = info->noise; ), @@ -1629,9 +1629,9 @@ TRACE_EVENT(rdev_return_int_survey_info, "channel time extension busy: %llu, channel time rx: %llu, " "channel time tx: %llu, filled: %u, noise: %d", WIPHY_PR_ARG, __entry->ret, CHAN_PR_ARG, - __entry->channel_time, __entry->channel_time_busy, - __entry->channel_time_ext_busy, __entry->channel_time_rx, - __entry->channel_time_tx, __entry->filled, __entry->noise) + __entry->time, __entry->time_busy, + __entry->time_ext_busy, __entry->time_rx, + __entry->time_tx, __entry->filled, __entry->noise) ); TRACE_EVENT(rdev_tdls_oper, -- cgit v0.10.2 From 11f78ac32b06648c1dde9371b70323168b51a83e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Nov 2014 16:43:50 +0100 Subject: cfg80211: allow survey data to return global data Not all devices are able to report survey data (particularly time spent for various operations) per channel. As all these statistics already exist in survey data, allow such devices to report them (if userspace requested it) Signed-off-by: Johannes Berg diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 3b489f8..5a86144 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -542,7 +542,8 @@ enum survey_info_flags { /** * struct survey_info - channel survey response * - * @channel: the channel this survey record reports, mandatory + * @channel: the channel this survey record reports, may be %NULL for a single + * record to report global statistics * @filled: bitflag of flags from &enum survey_info_flags * @noise: channel noise in dBm. This and all following fields are * optional diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 1a5acc8..5e8b65f 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1727,6 +1727,14 @@ enum nl80211_commands { * is located at bit 0 of byte 0. bit index 25 would be located at bit 1 * of byte 3 (u8 array). * + * @NL80211_ATTR_SURVEY_RADIO_STATS: Request overall radio statistics to be + * returned along with other survey data. If set, @NL80211_CMD_GET_SURVEY + * may return a survey entry without a channel indicating global radio + * statistics (only some values are valid and make sense.) + * For devices that don't return such an entry even then, the information + * should be contained in the result as the sum of the respective counters + * over all channels. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2088,6 +2096,8 @@ enum nl80211_attrs { NL80211_ATTR_EXT_FEATURES, + NL80211_ATTR_SURVEY_RADIO_STATS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2816,15 +2826,15 @@ enum nl80211_user_reg_hint_type { * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm) * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used * @NL80211_SURVEY_INFO_TIME: amount of time (in ms) that the radio - * spent on this channel + * was turned on (on channel or globally) * @NL80211_SURVEY_INFO_TIME_BUSY: amount of the time the primary * channel was sensed busy (either due to activity or energy detect) * @NL80211_SURVEY_INFO_TIME_EXT_BUSY: amount of time the extension * channel was sensed busy * @NL80211_SURVEY_INFO_TIME_RX: amount of time the radio spent - * receiving data + * receiving data (on channel or globally) * @NL80211_SURVEY_INFO_TIME_TX: amount of time the radio spent - * transmitting data + * transmitting data (on channel or globally) * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number * currently defined * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 94ab201..9555ef9 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6613,12 +6613,17 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb) } static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq, - int flags, struct net_device *dev, - struct survey_info *survey) + int flags, struct net_device *dev, + bool allow_radio_stats, + struct survey_info *survey) { void *hdr; struct nlattr *infoattr; + /* skip radio stats if userspace didn't request them */ + if (!survey->channel && !allow_radio_stats) + return 0; + hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_SURVEY_RESULTS); if (!hdr) @@ -6631,7 +6636,8 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq, if (!infoattr) goto nla_put_failure; - if (nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY, + if (survey->channel && + nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY, survey->channel->center_freq)) goto nla_put_failure; @@ -6671,19 +6677,22 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq, return -EMSGSIZE; } -static int nl80211_dump_survey(struct sk_buff *skb, - struct netlink_callback *cb) +static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb) { struct survey_info survey; struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; int survey_idx = cb->args[2]; int res; + bool radio_stats; res = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); if (res) return res; + /* prepare_wdev_dump parsed the attributes */ + radio_stats = nl80211_fam.attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS]; + if (!wdev->netdev) { res = -EINVAL; goto out_err; @@ -6701,13 +6710,9 @@ static int nl80211_dump_survey(struct sk_buff *skb, if (res) goto out_err; - /* Survey without a channel doesn't make sense */ - if (!survey.channel) { - res = -EINVAL; - goto out; - } - - if (survey.channel->flags & IEEE80211_CHAN_DISABLED) { + /* don't send disabled channels, but do send non-channel data */ + if (survey.channel && + survey.channel->flags & IEEE80211_CHAN_DISABLED) { survey_idx++; continue; } @@ -6715,7 +6720,7 @@ static int nl80211_dump_survey(struct sk_buff *skb, if (nl80211_send_survey(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, - wdev->netdev, &survey) < 0) + wdev->netdev, radio_stats, &survey) < 0) goto out; survey_idx++; } -- cgit v0.10.2 From 052536abfa9144566599a7fbe8cc89e1086fa9a7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Nov 2014 16:44:11 +0100 Subject: cfg80211: add scan time to survey data Add the time spent scanning to the survey data so it can be reported by drivers that collect such information. Signed-off-by: Johannes Berg diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 5a86144..f94f0d48 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -525,6 +525,7 @@ ieee80211_chandef_max_power(struct cfg80211_chan_def *chandef) * @SURVEY_INFO_TIME_EXT_BUSY: extension channel busy time was filled in * @SURVEY_INFO_TIME_RX: receive time was filled in * @SURVEY_INFO_TIME_TX: transmit time was filled in + * @SURVEY_INFO_TIME_SCAN: scan time was filled in * * Used by the driver to indicate which info in &struct survey_info * it has filled in during the get_survey(). @@ -537,6 +538,7 @@ enum survey_info_flags { SURVEY_INFO_TIME_EXT_BUSY = BIT(4), SURVEY_INFO_TIME_RX = BIT(5), SURVEY_INFO_TIME_TX = BIT(6), + SURVEY_INFO_TIME_SCAN = BIT(7), }; /** @@ -552,6 +554,7 @@ enum survey_info_flags { * @time_ext_busy: amount of time the extension channel was sensed busy * @time_rx: amount of time the radio spent receiving data * @time_tx: amount of time the radio spent transmitting data + * @time_scan: amount of time the radio spent for scanning * * Used by dump_survey() to report back per-channel survey information. * @@ -565,6 +568,7 @@ struct survey_info { u64 time_ext_busy; u64 time_rx; u64 time_tx; + u64 time_scan; u32 filled; s8 noise; }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 5e8b65f..2f549a2 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2835,6 +2835,8 @@ enum nl80211_user_reg_hint_type { * receiving data (on channel or globally) * @NL80211_SURVEY_INFO_TIME_TX: amount of time the radio spent * transmitting data (on channel or globally) + * @NL80211_SURVEY_INFO_TIME_SCAN: time the radio spent for scan + * (on this channel or globally) * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number * currently defined * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use @@ -2849,6 +2851,7 @@ enum nl80211_survey_info { NL80211_SURVEY_INFO_TIME_EXT_BUSY, NL80211_SURVEY_INFO_TIME_RX, NL80211_SURVEY_INFO_TIME_TX, + NL80211_SURVEY_INFO_TIME_SCAN, /* keep last */ __NL80211_SURVEY_INFO_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9555ef9..f56309b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6667,6 +6667,10 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq, nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_TX, survey->time_tx)) goto nla_put_failure; + if ((survey->filled & SURVEY_INFO_TIME_SCAN) && + nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_SCAN, + survey->time_scan)) + goto nla_put_failure; nla_nest_end(msg, infoattr); diff --git a/net/wireless/trace.h b/net/wireless/trace.h index bbb7afc..b17b369 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1609,6 +1609,7 @@ TRACE_EVENT(rdev_return_int_survey_info, __field(u64, time_ext_busy) __field(u64, time_rx) __field(u64, time_tx) + __field(u64, time_scan) __field(u32, filled) __field(s8, noise) ), @@ -1621,17 +1622,19 @@ TRACE_EVENT(rdev_return_int_survey_info, __entry->time_ext_busy = info->time_ext_busy; __entry->time_rx = info->time_rx; __entry->time_tx = info->time_tx; + __entry->time_scan = info->time_scan; __entry->filled = info->filled; __entry->noise = info->noise; ), TP_printk(WIPHY_PR_FMT ", returned: %d, " CHAN_PR_FMT ", channel time: %llu, channel time busy: %llu, " "channel time extension busy: %llu, channel time rx: %llu, " - "channel time tx: %llu, filled: %u, noise: %d", + "channel time tx: %llu, scan time: %llu, filled: %u, noise: %d", WIPHY_PR_ARG, __entry->ret, CHAN_PR_ARG, __entry->time, __entry->time_busy, __entry->time_ext_busy, __entry->time_rx, - __entry->time_tx, __entry->filled, __entry->noise) + __entry->time_tx, __entry->time_scan, + __entry->filled, __entry->noise) ); TRACE_EVENT(rdev_tdls_oper, -- cgit v0.10.2 From cf5ead822d5db2d276616ccca91f00eb3b855db2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Nov 2014 17:14:00 +0100 Subject: cfg80211: allow including station info in delete event When a station is removed, its statistics may be interesting to userspace, for example for further aggregation of statistics of all stations that ever connected to an AP. Introduce a new cfg80211_del_sta_sinfo() function (and make the cfg80211_del_sta() a static inline calling it) to allow passing a struct station_info along with this, and send the data in the nl80211 event message. Signed-off-by: Johannes Berg diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f94f0d48..42e3d74 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4592,13 +4592,27 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, struct station_info *sinfo, gfp_t gfp); /** + * cfg80211_del_sta_sinfo - notify userspace about deletion of a station + * @dev: the netdev + * @mac_addr: the station's address + * @sinfo: the station information/statistics + * @gfp: allocation flags + */ +void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr, + struct station_info *sinfo, gfp_t gfp); + +/** * cfg80211_del_sta - notify userspace about deletion of a station * * @dev: the netdev * @mac_addr: the station's address * @gfp: allocation flags */ -void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp); +static inline void cfg80211_del_sta(struct net_device *dev, + const u8 *mac_addr, gfp_t gfp) +{ + cfg80211_del_sta_sinfo(dev, mac_addr, NULL, gfp); +} /** * cfg80211_conn_failed - connection request failed notification diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f56309b..a75dc91 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3650,8 +3650,8 @@ static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal, return true; } -static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, - int flags, +static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, + u32 seq, int flags, struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *mac_addr, struct station_info *sinfo) @@ -3659,7 +3659,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, void *hdr; struct nlattr *sinfoattr, *bss_param; - hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION); + hdr = nl80211hdr_put(msg, portid, seq, flags, cmd); if (!hdr) return -1; @@ -3854,7 +3854,7 @@ static int nl80211_dump_station(struct sk_buff *skb, if (err) goto out_err; - if (nl80211_send_station(skb, + if (nl80211_send_station(skb, NL80211_CMD_NEW_STATION, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, rdev, wdev->netdev, mac_addr, @@ -3901,7 +3901,8 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) if (!msg) return -ENOMEM; - if (nl80211_send_station(msg, info->snd_portid, info->snd_seq, 0, + if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION, + info->snd_portid, info->snd_seq, 0, rdev, dev, mac_addr, &sinfo) < 0) { nlmsg_free(msg); return -ENOBUFS; @@ -11687,7 +11688,7 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, if (!msg) return; - if (nl80211_send_station(msg, 0, 0, 0, + if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION, 0, 0, 0, rdev, dev, mac_addr, sinfo) < 0) { nlmsg_free(msg); return; @@ -11698,12 +11699,16 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, } EXPORT_SYMBOL(cfg80211_new_sta); -void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp) +void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr, + struct station_info *sinfo, gfp_t gfp) { struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct sk_buff *msg; - void *hdr; + struct station_info empty_sinfo = {}; + + if (!sinfo) + sinfo = &empty_sinfo; trace_cfg80211_del_sta(dev, mac_addr); @@ -11711,27 +11716,16 @@ void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp) if (!msg) return; - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION); - if (!hdr) { + if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0, + rdev, dev, mac_addr, sinfo)) { nlmsg_free(msg); return; } - if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr)) - goto nla_put_failure; - - genlmsg_end(msg, hdr); - genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, NL80211_MCGRP_MLME, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); } -EXPORT_SYMBOL(cfg80211_del_sta); +EXPORT_SYMBOL(cfg80211_del_sta_sinfo); void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr, enum nl80211_connect_failed_reason reason, -- cgit v0.10.2 From 6f7a8d26e2668e00de524d3da0122a4411047dd2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Nov 2014 20:32:57 +0100 Subject: mac80211: send statistics with delete station event Use the new cfg80211_del_sta_sinfo() function to send the statistics about the deleted station with the delete event. This lets userspace see how much traffic etc. the deleted station used. Signed-off-by: Johannes Berg diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index db8b07a..388ff0b 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -874,6 +874,7 @@ static void __sta_info_destroy_part2(struct sta_info *sta) { struct ieee80211_local *local = sta->local; struct ieee80211_sub_if_data *sdata = sta->sdata; + struct station_info sinfo = {}; int ret; /* @@ -908,7 +909,8 @@ static void __sta_info_destroy_part2(struct sta_info *sta) sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr); - cfg80211_del_sta(sdata->dev, sta->sta.addr, GFP_KERNEL); + sta_set_sinfo(sta, &sinfo); + cfg80211_del_sta_sinfo(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL); rate_control_remove_sta_debugfs(sta); ieee80211_sta_debugfs_remove(sta); -- cgit v0.10.2 From 2b9a7e1bac24df8ddb0713ad1e5807a7243bcab0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Nov 2014 11:35:23 +0100 Subject: mac80211: allow drivers to provide most station statistics In many cases, drivers can filter things like beacons that will skew statistics reported by mac80211. To get correct statistics in these cases, call drivers to obtain statistics and let them override all values, filling values from mac80211 if the driver didn't provide them. Not all of them make sense for the driver to fill, so some are still always done by mac80211. Note that this doesn't currently allow a driver to say "I know this value is wrong, don't report it at all", or to sum it up with a mac80211 value (as could be useful for "dropped misc"), that can be added if it turns out to be needed. This also gets rid of the get_rssi() method as is can now be implemented using sta_statistics(). Signed-off-by: Johannes Berg diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 2a99456..8d11b0c 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5376,14 +5376,15 @@ static void wlcore_op_sta_rc_update(struct ieee80211_hw *hw, wlcore_hw_sta_rc_update(wl, wlvif, sta, changed); } -static int wlcore_op_get_rssi(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - s8 *rssi_dbm) +static void wlcore_op_sta_statistics(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct station_info *sinfo) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); - int ret = 0; + s8 rssi_dbm; + int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 get_rssi"); @@ -5396,17 +5397,18 @@ static int wlcore_op_get_rssi(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; - ret = wlcore_acx_average_rssi(wl, wlvif, rssi_dbm); + ret = wlcore_acx_average_rssi(wl, wlvif, &rssi_dbm); if (ret < 0) goto out_sleep; + sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->signal = rssi_dbm; + out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); - - return ret; } static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw) @@ -5606,7 +5608,7 @@ static const struct ieee80211_ops wl1271_ops = { .assign_vif_chanctx = wlcore_op_assign_vif_chanctx, .unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx, .sta_rc_update = wlcore_op_sta_rc_update, - .get_rssi = wlcore_op_get_rssi, + .sta_statistics = wlcore_op_sta_statistics, CFG80211_TESTMODE_CMD(wl1271_tm_cmd) }; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 555a845..123f230 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2708,6 +2708,14 @@ enum ieee80211_reconfig_type { * is only used if the configured rate control algorithm actually uses * the new rate table API, and is therefore optional. Must be atomic. * + * @sta_statistics: Get statistics for this station. For example with beacon + * filtering, the statistics kept by mac80211 might not be accurate, so + * let the driver pre-fill the statistics. The driver can fill most of + * the values (indicating which by setting the filled bitmap), but not + * all of them make sense - see the source for which ones are possible. + * Statistics that the driver doesn't fill will be filled by mac80211. + * The callback can sleep. + * * @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max), * bursting) for a hardware TX queue. * Returns a negative error code on failure. @@ -2868,9 +2876,6 @@ enum ieee80211_reconfig_type { * @get_et_strings: Ethtool API to get a set of strings to describe stats * and perhaps other supported types of ethtool data-sets. * - * @get_rssi: Get current signal strength in dBm, the function is optional - * and can sleep. - * * @mgd_prepare_tx: Prepare for transmitting a management frame for association * before associated. In multi-channel scenarios, a virtual interface is * bound to a channel before it is associated, but as it isn't associated @@ -3071,6 +3076,10 @@ struct ieee80211_ops { void (*sta_rate_tbl_update)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); + void (*sta_statistics)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct station_info *sinfo); int (*conf_tx)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 ac, const struct ieee80211_tx_queue_params *params); @@ -3138,8 +3147,6 @@ struct ieee80211_ops { void (*get_et_strings)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 sset, u8 *data); - int (*get_rssi)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, s8 *rssi_dbm); void (*mgd_prepare_tx)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 2ebc9ea..fdeda17 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -639,6 +639,21 @@ static inline void drv_sta_rate_tbl_update(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline void drv_sta_statistics(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta, + struct station_info *sinfo) +{ + sdata = get_bss_sdata(sdata); + if (!check_sdata_in_driver(sdata)) + return; + + trace_drv_sta_statistics(local, sdata, sta); + if (local->ops->sta_statistics) + local->ops->sta_statistics(&local->hw, &sdata->vif, sta, sinfo); + trace_drv_return_void(local); +} + static inline int drv_conf_tx(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, u16 ac, const struct ieee80211_tx_queue_params *params) @@ -966,21 +981,6 @@ drv_allow_buffered_frames(struct ieee80211_local *local, trace_drv_return_void(local); } -static inline int drv_get_rssi(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - struct ieee80211_sta *sta, - s8 *rssi_dbm) -{ - int ret; - - might_sleep(); - - ret = local->ops->get_rssi(&local->hw, &sdata->vif, sta, rssi_dbm); - trace_drv_get_rssi(local, sta, *rssi_dbm, ret); - - return ret; -} - static inline void drv_mgd_prepare_tx(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 388ff0b..967b42e 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1746,7 +1746,6 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) struct ieee80211_local *local = sdata->local; struct rate_control_ref *ref = NULL; struct timespec uptime; - u64 packets = 0; u32 thr = 0; int i, ac; @@ -1755,47 +1754,74 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->generation = sdata->local->sta_generation; - sinfo->filled = STATION_INFO_INACTIVE_TIME | - STATION_INFO_RX_BYTES64 | - STATION_INFO_TX_BYTES64 | - STATION_INFO_RX_PACKETS | - STATION_INFO_TX_PACKETS | - STATION_INFO_TX_RETRIES | - STATION_INFO_TX_FAILED | - STATION_INFO_TX_BITRATE | - STATION_INFO_RX_BITRATE | - STATION_INFO_RX_DROP_MISC | - STATION_INFO_BSS_PARAM | - STATION_INFO_CONNECTED_TIME | - STATION_INFO_STA_FLAGS | - STATION_INFO_BEACON_LOSS_COUNT; + drv_sta_statistics(local, sdata, &sta->sta, sinfo); + + sinfo->filled |= STATION_INFO_INACTIVE_TIME | + STATION_INFO_STA_FLAGS | + STATION_INFO_BSS_PARAM | + STATION_INFO_CONNECTED_TIME | + STATION_INFO_RX_DROP_MISC | + STATION_INFO_BEACON_LOSS_COUNT; ktime_get_ts(&uptime); sinfo->connected_time = uptime.tv_sec - sta->last_connected; - sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); - sinfo->tx_bytes = 0; - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - sinfo->tx_bytes += sta->tx_bytes[ac]; - packets += sta->tx_packets[ac]; + + if (!(sinfo->filled & (STATION_INFO_TX_BYTES64 | + STATION_INFO_TX_BYTES))) { + sinfo->tx_bytes = 0; + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + sinfo->tx_bytes += sta->tx_bytes[ac]; + sinfo->filled |= STATION_INFO_TX_BYTES64; + } + + if (!(sinfo->filled & STATION_INFO_TX_PACKETS)) { + sinfo->tx_packets = 0; + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + sinfo->tx_packets += sta->tx_packets[ac]; + sinfo->filled |= STATION_INFO_TX_PACKETS; + } + + if (!(sinfo->filled & (STATION_INFO_RX_BYTES64 | + STATION_INFO_RX_BYTES))) { + sinfo->rx_bytes = sta->rx_bytes; + sinfo->filled |= STATION_INFO_RX_BYTES64; + } + + if (!(sinfo->filled & STATION_INFO_RX_PACKETS)) { + sinfo->rx_packets = sta->rx_packets; + sinfo->filled |= STATION_INFO_RX_PACKETS; + } + + if (!(sinfo->filled & STATION_INFO_TX_RETRIES)) { + sinfo->tx_retries = sta->tx_retry_count; + sinfo->filled |= STATION_INFO_TX_RETRIES; + } + + if (!(sinfo->filled & STATION_INFO_TX_FAILED)) { + sinfo->tx_failed = sta->tx_retry_failed; + sinfo->filled |= STATION_INFO_TX_FAILED; } - sinfo->tx_packets = packets; - sinfo->rx_bytes = sta->rx_bytes; - sinfo->rx_packets = sta->rx_packets; - sinfo->tx_retries = sta->tx_retry_count; - sinfo->tx_failed = sta->tx_retry_failed; + sinfo->rx_dropped_misc = sta->rx_dropped; sinfo->beacon_loss_count = sta->beacon_loss_count; if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) || (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) { - sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG; - if (!local->ops->get_rssi || - drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal)) + if (!(sinfo->filled & STATION_INFO_SIGNAL)) { sinfo->signal = (s8)sta->last_signal; - sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal); + sinfo->filled |= STATION_INFO_SIGNAL; + } + + if (!(sinfo->filled & STATION_INFO_SIGNAL_AVG)) { + sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal); + sinfo->filled |= STATION_INFO_SIGNAL_AVG; + } } - if (sta->chains) { + + if (sta->chains && + !(sinfo->filled & (STATION_INFO_CHAIN_SIGNAL | + STATION_INFO_CHAIN_SIGNAL_AVG))) { sinfo->filled |= STATION_INFO_CHAIN_SIGNAL | STATION_INFO_CHAIN_SIGNAL_AVG; @@ -1807,8 +1833,15 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) } } - sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); - sta_set_rate_info_rx(sta, &sinfo->rxrate); + if (!(sinfo->filled & STATION_INFO_TX_BITRATE)) { + sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); + sinfo->filled |= STATION_INFO_TX_BITRATE; + } + + if (!(sinfo->filled & STATION_INFO_RX_BITRATE)) { + sta_set_rate_info_rx(sta, &sinfo->rxrate); + sinfo->filled |= STATION_INFO_RX_BITRATE; + } if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 8e461a0..263a956 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -825,6 +825,13 @@ DECLARE_EVENT_CLASS(sta_event, ) ); +DEFINE_EVENT(sta_event, drv_sta_statistics, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta), + TP_ARGS(local, sdata, sta) +); + DEFINE_EVENT(sta_event, drv_sta_add, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, @@ -1329,32 +1336,6 @@ DEFINE_EVENT(release_evt, drv_allow_buffered_frames, TP_ARGS(local, sta, tids, num_frames, reason, more_data) ); -TRACE_EVENT(drv_get_rssi, - TP_PROTO(struct ieee80211_local *local, struct ieee80211_sta *sta, - s8 rssi, int ret), - - TP_ARGS(local, sta, rssi, ret), - - TP_STRUCT__entry( - LOCAL_ENTRY - STA_ENTRY - __field(s8, rssi) - __field(int, ret) - ), - - TP_fast_assign( - LOCAL_ASSIGN; - STA_ASSIGN; - __entry->rssi = rssi; - __entry->ret = ret; - ), - - TP_printk( - LOCAL_PR_FMT STA_PR_FMT " rssi:%d ret:%d", - LOCAL_PR_ARG, STA_PR_ARG, __entry->rssi, __entry->ret - ) -); - DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), -- cgit v0.10.2 From 319090bf6c75e3ad42a8c74973be5e78ae4f948f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Nov 2014 14:08:11 +0100 Subject: cfg80211: remove enum station_info_flags This is really just duplicating the list of information that's already available in the nl80211 attribute, so remove the list. Two small changes are needed: * remove STATION_INFO_ASSOC_REQ_IES complete, but the length (assoc_req_ies_len) can be used instead * add NL80211_STA_INFO_RX_DROP_MISC which exists internally but not in nl80211 yet This gets rid of the duplicate maintenance of the two lists. Signed-off-by: Johannes Berg diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 7a53378..44dd6ef 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1799,20 +1799,20 @@ static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev, if (vif->target_stats.rx_byte) { sinfo->rx_bytes = vif->target_stats.rx_byte; - sinfo->filled |= STATION_INFO_RX_BYTES64; + sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES64); sinfo->rx_packets = vif->target_stats.rx_pkt; - sinfo->filled |= STATION_INFO_RX_PACKETS; + sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS); } if (vif->target_stats.tx_byte) { sinfo->tx_bytes = vif->target_stats.tx_byte; - sinfo->filled |= STATION_INFO_TX_BYTES64; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES64); sinfo->tx_packets = vif->target_stats.tx_pkt; - sinfo->filled |= STATION_INFO_TX_PACKETS; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS); } sinfo->signal = vif->target_stats.cs_rssi; - sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); rate = vif->target_stats.tx_ucast_rate; @@ -1844,12 +1844,12 @@ static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev, return 0; } - sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); if (test_bit(CONNECTED, &vif->flags) && test_bit(DTIM_PERIOD_AVAIL, &vif->flags) && vif->nw_type == INFRA_NETWORK) { - sinfo->filled |= STATION_INFO_BSS_PARAM; + sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM); sinfo->bss_param.flags = 0; sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period; sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int; diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 933aef0..b42ba46 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -488,7 +488,6 @@ void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr, sinfo.assoc_req_ies = ies; sinfo.assoc_req_ies_len = ies_len; - sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; cfg80211_new_sta(vif->ndev, mac_addr, &sinfo, GFP_KERNEL); diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 38332a6..e72a95d 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -142,14 +142,14 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, sinfo->generation = wil->sinfo_gen; - sinfo->filled = STATION_INFO_RX_BYTES | - STATION_INFO_TX_BYTES | - STATION_INFO_RX_PACKETS | - STATION_INFO_TX_PACKETS | - STATION_INFO_RX_BITRATE | - STATION_INFO_TX_BITRATE | - STATION_INFO_RX_DROP_MISC | - STATION_INFO_TX_FAILED; + sinfo->filled = BIT(NL80211_STA_INFO_RX_BYTES) | + BIT(NL80211_STA_INFO_TX_BYTES) | + BIT(NL80211_STA_INFO_RX_PACKETS) | + BIT(NL80211_STA_INFO_TX_PACKETS) | + BIT(NL80211_STA_INFO_RX_BITRATE) | + BIT(NL80211_STA_INFO_TX_BITRATE) | + BIT(NL80211_STA_INFO_RX_DROP_MISC) | + BIT(NL80211_STA_INFO_TX_FAILED); sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs); @@ -163,7 +163,7 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, sinfo->tx_failed = stats->tx_errors; if (test_bit(wil_status_fwconnected, &wil->status)) { - sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); sinfo->signal = reply.evt.sqi; } diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 63476c8..8997549 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -457,7 +457,6 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) if (assoc_req_ie) { sinfo.assoc_req_ies = assoc_req_ie; sinfo.assoc_req_ies_len = assoc_req_ielen; - sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; } cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 3aecc5f..4a88b23 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -2333,10 +2333,10 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, brcmf_err("GET STA INFO failed, %d\n", err); goto done; } - sinfo->filled = STATION_INFO_INACTIVE_TIME; + sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME); sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000; if (le32_to_cpu(sta_info_le.flags) & BRCMF_STA_ASSOC) { - sinfo->filled |= STATION_INFO_CONNECTED_TIME; + sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME); sinfo->connected_time = le32_to_cpu(sta_info_le.in); } brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n", @@ -2354,7 +2354,7 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, brcmf_err("Could not get rate (%d)\n", err); goto done; } else { - sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); sinfo->txrate.legacy = rate * 5; brcmf_dbg(CONN, "Rate %d Mbps\n", rate / 2); } @@ -2369,7 +2369,7 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, goto done; } else { rssi = le32_to_cpu(scb_val.val); - sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); sinfo->signal = rssi; brcmf_dbg(CONN, "RSSI %d dBm\n", rssi); } @@ -2396,7 +2396,7 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(CONN, "DTIM peroid %d\n", dtim_period); } - sinfo->filled |= STATION_INFO_BSS_PARAM; + sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM); } } else err = -EPERM; @@ -4778,7 +4778,6 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) && (reason == BRCMF_E_STATUS_SUCCESS)) { memset(&sinfo, 0, sizeof(sinfo)); - sinfo.filled = STATION_INFO_ASSOC_REQ_IES; if (!data) { brcmf_err("No IEs present in ASSOC/REASSOC_IND"); return -EINVAL; diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 34f09ef..a92985a 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -1616,10 +1616,10 @@ static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev, lbs_deb_enter(LBS_DEB_CFG80211); - sinfo->filled |= STATION_INFO_TX_BYTES | - STATION_INFO_TX_PACKETS | - STATION_INFO_RX_BYTES | - STATION_INFO_RX_PACKETS; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES) | + BIT(NL80211_STA_INFO_TX_PACKETS) | + BIT(NL80211_STA_INFO_RX_BYTES) | + BIT(NL80211_STA_INFO_RX_PACKETS); sinfo->tx_bytes = priv->dev->stats.tx_bytes; sinfo->tx_packets = priv->dev->stats.tx_packets; sinfo->rx_bytes = priv->dev->stats.rx_bytes; @@ -1629,14 +1629,14 @@ static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev, ret = lbs_get_rssi(priv, &signal, &noise); if (ret == 0) { sinfo->signal = signal; - sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); } /* Convert priv->cur_rate from hw_value to NL80211 value */ for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) { if (priv->cur_rate == lbs_rates[i].hw_value) { sinfo->txrate.legacy = lbs_rates[i].bitrate; - sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); break; } } diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 8bd446b..71312ff 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -910,10 +910,10 @@ mwifiex_dump_station_info(struct mwifiex_private *priv, { u32 rate; - sinfo->filled = STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES | - STATION_INFO_RX_PACKETS | STATION_INFO_TX_PACKETS | - STATION_INFO_TX_BITRATE | - STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG; + sinfo->filled = BIT(NL80211_STA_INFO_RX_BYTES) | BIT(NL80211_STA_INFO_TX_BYTES) | + BIT(NL80211_STA_INFO_RX_PACKETS) | BIT(NL80211_STA_INFO_TX_PACKETS) | + BIT(NL80211_STA_INFO_TX_BITRATE) | + BIT(NL80211_STA_INFO_SIGNAL) | BIT(NL80211_STA_INFO_SIGNAL_AVG); /* Get signal information from the firmware */ if (mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, @@ -944,7 +944,7 @@ mwifiex_dump_station_info(struct mwifiex_private *priv, sinfo->txrate.legacy = rate * 5; if (priv->bss_mode == NL80211_IFTYPE_STATION) { - sinfo->filled |= STATION_INFO_BSS_PARAM; + sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM); sinfo->bss_param.flags = 0; if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & WLAN_CAPABILITY_SHORT_PREAMBLE) diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c index c54a537..3b3a970 100644 --- a/drivers/net/wireless/mwifiex/uap_event.c +++ b/drivers/net/wireless/mwifiex/uap_event.c @@ -68,7 +68,6 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) len = ETH_ALEN; if (len != -1) { - sinfo.filled = STATION_INFO_ASSOC_REQ_IES; sinfo.assoc_req_ies = &event->data[len]; len = (u8 *)sinfo.assoc_req_ies - (u8 *)&event->frame_control; diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 1a4facd..60d44ce 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -2478,7 +2478,7 @@ static void rndis_fill_station_info(struct usbnet *usbdev, ret = rndis_query_oid(usbdev, RNDIS_OID_GEN_LINK_SPEED, &linkspeed, &len); if (ret == 0) { sinfo->txrate.legacy = le32_to_cpu(linkspeed) / 1000; - sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); } len = sizeof(rssi); @@ -2486,7 +2486,7 @@ static void rndis_fill_station_info(struct usbnet *usbdev, &rssi, &len); if (ret == 0) { sinfo->signal = level_to_qual(le32_to_cpu(rssi)); - sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); } } diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 8d11b0c..a2133b1 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5401,7 +5401,7 @@ static void wlcore_op_sta_statistics(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; - sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); sinfo->signal = rssi_dbm; out_sleep: diff --git a/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c index 3d26955..c76874d 100644 --- a/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c @@ -1092,17 +1092,17 @@ static int cfg80211_rtw_get_station(struct wiphy *wiphy, goto exit; } - sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); sinfo->signal = translate_percentage_to_dbm(padapter->recvpriv. signal_strength); - sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); sinfo->txrate.legacy = rtw_get_cur_max_rate(padapter); - sinfo->filled |= STATION_INFO_RX_PACKETS; + sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS); sinfo->rx_packets = sta_rx_data_pkts(psta); - sinfo->filled |= STATION_INFO_TX_PACKETS; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS); sinfo->tx_packets = psta->sta_stats.tx_pkts; } @@ -2365,7 +2365,6 @@ void rtw_cfg80211_indicate_sta_assoc(struct rtw_adapter *padapter, u.reassoc_req.variable); sinfo.filled = 0; - sinfo.filled = STATION_INFO_ASSOC_REQ_IES; sinfo.assoc_req_ies = pmgmt_frame + ie_offset; sinfo.assoc_req_ies_len = frame_len - ie_offset; cfg80211_new_sta(ndev, hdr->addr2, &sinfo, GFP_ATOMIC); diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c index 8942dcb..7c87aec 100644 --- a/drivers/staging/wlan-ng/cfg80211.c +++ b/drivers/staging/wlan-ng/cfg80211.c @@ -325,9 +325,9 @@ static int prism2_get_station(struct wiphy *wiphy, struct net_device *dev, if (result == 0) { sinfo->txrate.legacy = quality.txrate.data; - sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); sinfo->signal = quality.level.data; - sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); } return result; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 42e3d74..91c1336 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -866,75 +866,6 @@ int cfg80211_check_station_change(struct wiphy *wiphy, enum cfg80211_station_type statype); /** - * enum station_info_flags - station information flags - * - * Used by the driver to indicate which info in &struct station_info - * it has filled in during get_station() or dump_station(). - * - * @STATION_INFO_INACTIVE_TIME: @inactive_time filled - * @STATION_INFO_RX_BYTES: @rx_bytes filled - * @STATION_INFO_TX_BYTES: @tx_bytes filled - * @STATION_INFO_RX_BYTES64: @rx_bytes filled with 64-bit value - * @STATION_INFO_TX_BYTES64: @tx_bytes filled with 64-bit value - * @STATION_INFO_LLID: @llid filled - * @STATION_INFO_PLID: @plid filled - * @STATION_INFO_PLINK_STATE: @plink_state filled - * @STATION_INFO_SIGNAL: @signal filled - * @STATION_INFO_TX_BITRATE: @txrate fields are filled - * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs) - * @STATION_INFO_RX_PACKETS: @rx_packets filled with 32-bit value - * @STATION_INFO_TX_PACKETS: @tx_packets filled with 32-bit value - * @STATION_INFO_TX_RETRIES: @tx_retries filled - * @STATION_INFO_TX_FAILED: @tx_failed filled - * @STATION_INFO_RX_DROP_MISC: @rx_dropped_misc filled - * @STATION_INFO_SIGNAL_AVG: @signal_avg filled - * @STATION_INFO_RX_BITRATE: @rxrate fields are filled - * @STATION_INFO_BSS_PARAM: @bss_param filled - * @STATION_INFO_CONNECTED_TIME: @connected_time filled - * @STATION_INFO_ASSOC_REQ_IES: @assoc_req_ies filled - * @STATION_INFO_STA_FLAGS: @sta_flags filled - * @STATION_INFO_BEACON_LOSS_COUNT: @beacon_loss_count filled - * @STATION_INFO_T_OFFSET: @t_offset filled - * @STATION_INFO_LOCAL_PM: @local_pm filled - * @STATION_INFO_PEER_PM: @peer_pm filled - * @STATION_INFO_NONPEER_PM: @nonpeer_pm filled - * @STATION_INFO_CHAIN_SIGNAL: @chain_signal filled - * @STATION_INFO_CHAIN_SIGNAL_AVG: @chain_signal_avg filled - * @STATION_INFO_EXPECTED_THROUGHPUT: @expected_throughput filled - */ -enum station_info_flags { - STATION_INFO_INACTIVE_TIME = BIT(0), - STATION_INFO_RX_BYTES = BIT(1), - STATION_INFO_TX_BYTES = BIT(2), - STATION_INFO_LLID = BIT(3), - STATION_INFO_PLID = BIT(4), - STATION_INFO_PLINK_STATE = BIT(5), - STATION_INFO_SIGNAL = BIT(6), - STATION_INFO_TX_BITRATE = BIT(7), - STATION_INFO_RX_PACKETS = BIT(8), - STATION_INFO_TX_PACKETS = BIT(9), - STATION_INFO_TX_RETRIES = BIT(10), - STATION_INFO_TX_FAILED = BIT(11), - STATION_INFO_RX_DROP_MISC = BIT(12), - STATION_INFO_SIGNAL_AVG = BIT(13), - STATION_INFO_RX_BITRATE = BIT(14), - STATION_INFO_BSS_PARAM = BIT(15), - STATION_INFO_CONNECTED_TIME = BIT(16), - STATION_INFO_ASSOC_REQ_IES = BIT(17), - STATION_INFO_STA_FLAGS = BIT(18), - STATION_INFO_BEACON_LOSS_COUNT = BIT(19), - STATION_INFO_T_OFFSET = BIT(20), - STATION_INFO_LOCAL_PM = BIT(21), - STATION_INFO_PEER_PM = BIT(22), - STATION_INFO_NONPEER_PM = BIT(23), - STATION_INFO_RX_BYTES64 = BIT(24), - STATION_INFO_TX_BYTES64 = BIT(25), - STATION_INFO_CHAIN_SIGNAL = BIT(26), - STATION_INFO_CHAIN_SIGNAL_AVG = BIT(27), - STATION_INFO_EXPECTED_THROUGHPUT = BIT(28), -}; - -/** * enum station_info_rate_flags - bitrate info flags * * Used by the driver to indicate the specific rate transmission @@ -1015,7 +946,8 @@ struct sta_bss_parameters { * * Station information filled by driver for get_station() and dump_station. * - * @filled: bitflag of flags from &enum station_info_flags + * @filled: bitflag of flags using the bits of &enum nl80211_sta_info to + * indicate the relevant values in this struct for them * @connected_time: time(in secs) since a station is last connected * @inactive_time: time since last station activity (tx/rx) in milliseconds * @rx_bytes: bytes received from this station @@ -1094,11 +1026,6 @@ struct station_info { enum nl80211_mesh_power_mode nonpeer_pm; u32 expected_throughput; - - /* - * Note: Add a new enum station_info_flags value for each new field and - * use it to check which fields are initialized. - */ }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 2f549a2..e48ca0b 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2373,6 +2373,8 @@ enum nl80211_sta_bss_param { * Same format as NL80211_STA_INFO_CHAIN_SIGNAL. * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the * 802.11 header (u32, kbps) + * @NL80211_STA_INFO_RX_DROP_MISC: RX packets dropped for unspecified reasons + * (u64) * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -2405,6 +2407,7 @@ enum nl80211_sta_info { NL80211_STA_INFO_CHAIN_SIGNAL, NL80211_STA_INFO_CHAIN_SIGNAL_AVG, NL80211_STA_INFO_EXPECTED_THROUGHPUT, + NL80211_STA_INFO_RX_DROP_MISC, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c index eea7427..52bcea6 100644 --- a/net/mac80211/ethtool.c +++ b/net/mac80211/ethtool.c @@ -117,16 +117,16 @@ static void ieee80211_get_stats(struct net_device *dev, data[i++] = sta->sta_state; - if (sinfo.filled & STATION_INFO_TX_BITRATE) + if (sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE)) data[i] = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate); i++; - if (sinfo.filled & STATION_INFO_RX_BITRATE) + if (sinfo.filled & BIT(NL80211_STA_INFO_RX_BITRATE)) data[i] = 100000 * cfg80211_calculate_bitrate(&sinfo.rxrate); i++; - if (sinfo.filled & STATION_INFO_SIGNAL_AVG) + if (sinfo.filled & BIT(NL80211_STA_INFO_SIGNAL_AVG)) data[i] = (u8)sinfo.signal_avg; i++; } else { diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 967b42e..64b53b9 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1756,51 +1756,51 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) drv_sta_statistics(local, sdata, &sta->sta, sinfo); - sinfo->filled |= STATION_INFO_INACTIVE_TIME | - STATION_INFO_STA_FLAGS | - STATION_INFO_BSS_PARAM | - STATION_INFO_CONNECTED_TIME | - STATION_INFO_RX_DROP_MISC | - STATION_INFO_BEACON_LOSS_COUNT; + sinfo->filled |= BIT(NL80211_STA_INFO_INACTIVE_TIME) | + BIT(NL80211_STA_INFO_STA_FLAGS) | + BIT(NL80211_STA_INFO_BSS_PARAM) | + BIT(NL80211_STA_INFO_CONNECTED_TIME) | + BIT(NL80211_STA_INFO_RX_DROP_MISC) | + BIT(NL80211_STA_INFO_BEACON_LOSS); ktime_get_ts(&uptime); sinfo->connected_time = uptime.tv_sec - sta->last_connected; sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); - if (!(sinfo->filled & (STATION_INFO_TX_BYTES64 | - STATION_INFO_TX_BYTES))) { + if (!(sinfo->filled & (BIT(NL80211_STA_INFO_TX_BYTES64) | + BIT(NL80211_STA_INFO_TX_BYTES)))) { sinfo->tx_bytes = 0; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) sinfo->tx_bytes += sta->tx_bytes[ac]; - sinfo->filled |= STATION_INFO_TX_BYTES64; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES64); } - if (!(sinfo->filled & STATION_INFO_TX_PACKETS)) { + if (!(sinfo->filled & BIT(NL80211_STA_INFO_TX_PACKETS))) { sinfo->tx_packets = 0; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) sinfo->tx_packets += sta->tx_packets[ac]; - sinfo->filled |= STATION_INFO_TX_PACKETS; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS); } - if (!(sinfo->filled & (STATION_INFO_RX_BYTES64 | - STATION_INFO_RX_BYTES))) { + if (!(sinfo->filled & (BIT(NL80211_STA_INFO_RX_BYTES64) | + BIT(NL80211_STA_INFO_RX_BYTES)))) { sinfo->rx_bytes = sta->rx_bytes; - sinfo->filled |= STATION_INFO_RX_BYTES64; + sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES64); } - if (!(sinfo->filled & STATION_INFO_RX_PACKETS)) { + if (!(sinfo->filled & BIT(NL80211_STA_INFO_RX_PACKETS))) { sinfo->rx_packets = sta->rx_packets; - sinfo->filled |= STATION_INFO_RX_PACKETS; + sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS); } - if (!(sinfo->filled & STATION_INFO_TX_RETRIES)) { + if (!(sinfo->filled & BIT(NL80211_STA_INFO_TX_RETRIES))) { sinfo->tx_retries = sta->tx_retry_count; - sinfo->filled |= STATION_INFO_TX_RETRIES; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_RETRIES); } - if (!(sinfo->filled & STATION_INFO_TX_FAILED)) { + if (!(sinfo->filled & BIT(NL80211_STA_INFO_TX_FAILED))) { sinfo->tx_failed = sta->tx_retry_failed; - sinfo->filled |= STATION_INFO_TX_FAILED; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED); } sinfo->rx_dropped_misc = sta->rx_dropped; @@ -1808,22 +1808,22 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) || (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) { - if (!(sinfo->filled & STATION_INFO_SIGNAL)) { + if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL))) { sinfo->signal = (s8)sta->last_signal; - sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); } - if (!(sinfo->filled & STATION_INFO_SIGNAL_AVG)) { + if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL_AVG))) { sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal); - sinfo->filled |= STATION_INFO_SIGNAL_AVG; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL_AVG); } } if (sta->chains && - !(sinfo->filled & (STATION_INFO_CHAIN_SIGNAL | - STATION_INFO_CHAIN_SIGNAL_AVG))) { - sinfo->filled |= STATION_INFO_CHAIN_SIGNAL | - STATION_INFO_CHAIN_SIGNAL_AVG; + !(sinfo->filled & (BIT(NL80211_STA_INFO_CHAIN_SIGNAL) | + BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)))) { + sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL) | + BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG); sinfo->chains = sta->chains; for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) { @@ -1833,30 +1833,30 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) } } - if (!(sinfo->filled & STATION_INFO_TX_BITRATE)) { + if (!(sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE))) { sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); - sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); } - if (!(sinfo->filled & STATION_INFO_RX_BITRATE)) { + if (!(sinfo->filled & BIT(NL80211_STA_INFO_RX_BITRATE))) { sta_set_rate_info_rx(sta, &sinfo->rxrate); - sinfo->filled |= STATION_INFO_RX_BITRATE; + sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE); } if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH - sinfo->filled |= STATION_INFO_LLID | - STATION_INFO_PLID | - STATION_INFO_PLINK_STATE | - STATION_INFO_LOCAL_PM | - STATION_INFO_PEER_PM | - STATION_INFO_NONPEER_PM; + sinfo->filled |= BIT(NL80211_STA_INFO_LLID) | + BIT(NL80211_STA_INFO_PLID) | + BIT(NL80211_STA_INFO_PLINK_STATE) | + BIT(NL80211_STA_INFO_LOCAL_PM) | + BIT(NL80211_STA_INFO_PEER_PM) | + BIT(NL80211_STA_INFO_NONPEER_PM); sinfo->llid = sta->llid; sinfo->plid = sta->plid; sinfo->plink_state = sta->plink_state; if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { - sinfo->filled |= STATION_INFO_T_OFFSET; + sinfo->filled |= BIT(NL80211_STA_INFO_T_OFFSET); sinfo->t_offset = sta->t_offset; } sinfo->local_pm = sta->local_pm; @@ -1905,7 +1905,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) thr = drv_get_expected_throughput(local, &sta->sta); if (thr != 0) { - sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT; + sinfo->filled |= BIT(NL80211_STA_INFO_EXPECTED_THROUGHPUT); sinfo->expected_throughput = thr; } } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a75dc91..68faf8a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3671,115 +3671,77 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO); if (!sinfoattr) goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_CONNECTED_TIME) && - nla_put_u32(msg, NL80211_STA_INFO_CONNECTED_TIME, - sinfo->connected_time)) - goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_INACTIVE_TIME) && - nla_put_u32(msg, NL80211_STA_INFO_INACTIVE_TIME, - sinfo->inactive_time)) - goto nla_put_failure; - if ((sinfo->filled & (STATION_INFO_RX_BYTES | - STATION_INFO_RX_BYTES64)) && + +#define PUT_SINFO(attr, memb, type) do { \ + if (sinfo->filled & BIT(NL80211_STA_INFO_ ## attr) && \ + nla_put_ ## type(msg, NL80211_STA_INFO_ ## attr, \ + sinfo->memb)) \ + goto nla_put_failure; \ + } while (0) + + PUT_SINFO(CONNECTED_TIME, connected_time, u32); + PUT_SINFO(INACTIVE_TIME, inactive_time, u32); + + if (sinfo->filled & (BIT(NL80211_STA_INFO_RX_BYTES) | + BIT(NL80211_STA_INFO_RX_BYTES64)) && nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES, (u32)sinfo->rx_bytes)) goto nla_put_failure; - if ((sinfo->filled & (STATION_INFO_TX_BYTES | - STATION_INFO_TX_BYTES64)) && + + if (sinfo->filled & (BIT(NL80211_STA_INFO_TX_BYTES) | + BIT(NL80211_STA_INFO_TX_BYTES64)) && nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES, (u32)sinfo->tx_bytes)) goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_RX_BYTES64) && - nla_put_u64(msg, NL80211_STA_INFO_RX_BYTES64, - sinfo->rx_bytes)) - goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_TX_BYTES64) && - nla_put_u64(msg, NL80211_STA_INFO_TX_BYTES64, - sinfo->tx_bytes)) - goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_LLID) && - nla_put_u16(msg, NL80211_STA_INFO_LLID, sinfo->llid)) - goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_PLID) && - nla_put_u16(msg, NL80211_STA_INFO_PLID, sinfo->plid)) - goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_PLINK_STATE) && - nla_put_u8(msg, NL80211_STA_INFO_PLINK_STATE, - sinfo->plink_state)) - goto nla_put_failure; + + PUT_SINFO(RX_BYTES64, rx_bytes, u64); + PUT_SINFO(TX_BYTES64, tx_bytes, u64); + PUT_SINFO(LLID, llid, u16); + PUT_SINFO(PLID, plid, u16); + PUT_SINFO(PLINK_STATE, plink_state, u8); + switch (rdev->wiphy.signal_type) { case CFG80211_SIGNAL_TYPE_MBM: - if ((sinfo->filled & STATION_INFO_SIGNAL) && - nla_put_u8(msg, NL80211_STA_INFO_SIGNAL, - sinfo->signal)) - goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_SIGNAL_AVG) && - nla_put_u8(msg, NL80211_STA_INFO_SIGNAL_AVG, - sinfo->signal_avg)) - goto nla_put_failure; + PUT_SINFO(SIGNAL, signal, u8); + PUT_SINFO(SIGNAL_AVG, signal_avg, u8); break; default: break; } - if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL) { + if (sinfo->filled & BIT(NL80211_STA_INFO_CHAIN_SIGNAL)) { if (!nl80211_put_signal(msg, sinfo->chains, sinfo->chain_signal, NL80211_STA_INFO_CHAIN_SIGNAL)) goto nla_put_failure; } - if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL_AVG) { + if (sinfo->filled & BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) { if (!nl80211_put_signal(msg, sinfo->chains, sinfo->chain_signal_avg, NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) goto nla_put_failure; } - if (sinfo->filled & STATION_INFO_TX_BITRATE) { + if (sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE)) { if (!nl80211_put_sta_rate(msg, &sinfo->txrate, NL80211_STA_INFO_TX_BITRATE)) goto nla_put_failure; } - if (sinfo->filled & STATION_INFO_RX_BITRATE) { + if (sinfo->filled & BIT(NL80211_STA_INFO_RX_BITRATE)) { if (!nl80211_put_sta_rate(msg, &sinfo->rxrate, NL80211_STA_INFO_RX_BITRATE)) goto nla_put_failure; } - if ((sinfo->filled & STATION_INFO_RX_PACKETS) && - nla_put_u32(msg, NL80211_STA_INFO_RX_PACKETS, - sinfo->rx_packets)) - goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_TX_PACKETS) && - nla_put_u32(msg, NL80211_STA_INFO_TX_PACKETS, - sinfo->tx_packets)) - goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_TX_RETRIES) && - nla_put_u32(msg, NL80211_STA_INFO_TX_RETRIES, - sinfo->tx_retries)) - goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_TX_FAILED) && - nla_put_u32(msg, NL80211_STA_INFO_TX_FAILED, - sinfo->tx_failed)) - goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_EXPECTED_THROUGHPUT) && - nla_put_u32(msg, NL80211_STA_INFO_EXPECTED_THROUGHPUT, - sinfo->expected_throughput)) - goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_BEACON_LOSS_COUNT) && - nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS, - sinfo->beacon_loss_count)) - goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_LOCAL_PM) && - nla_put_u32(msg, NL80211_STA_INFO_LOCAL_PM, - sinfo->local_pm)) - goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_PEER_PM) && - nla_put_u32(msg, NL80211_STA_INFO_PEER_PM, - sinfo->peer_pm)) - goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_NONPEER_PM) && - nla_put_u32(msg, NL80211_STA_INFO_NONPEER_PM, - sinfo->nonpeer_pm)) - goto nla_put_failure; - if (sinfo->filled & STATION_INFO_BSS_PARAM) { + + PUT_SINFO(RX_PACKETS, rx_packets, u32); + PUT_SINFO(TX_PACKETS, tx_packets, u32); + PUT_SINFO(TX_RETRIES, tx_retries, u32); + PUT_SINFO(TX_FAILED, tx_failed, u32); + PUT_SINFO(EXPECTED_THROUGHPUT, expected_throughput, u32); + PUT_SINFO(BEACON_LOSS, beacon_loss_count, u32); + PUT_SINFO(LOCAL_PM, local_pm, u32); + PUT_SINFO(PEER_PM, peer_pm, u32); + PUT_SINFO(NONPEER_PM, nonpeer_pm, u32); + + if (sinfo->filled & BIT(NL80211_STA_INFO_BSS_PARAM)) { bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM); if (!bss_param) goto nla_put_failure; @@ -3798,18 +3760,19 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, nla_nest_end(msg, bss_param); } - if ((sinfo->filled & STATION_INFO_STA_FLAGS) && + if ((sinfo->filled & BIT(NL80211_STA_INFO_STA_FLAGS)) && nla_put(msg, NL80211_STA_INFO_STA_FLAGS, sizeof(struct nl80211_sta_flag_update), &sinfo->sta_flags)) goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_T_OFFSET) && - nla_put_u64(msg, NL80211_STA_INFO_T_OFFSET, - sinfo->t_offset)) - goto nla_put_failure; + + PUT_SINFO(T_OFFSET, t_offset, u64); + PUT_SINFO(RX_DROP_MISC, rx_dropped_misc, u64); + +#undef PUT_SINFO nla_nest_end(msg, sinfoattr); - if ((sinfo->filled & STATION_INFO_ASSOC_REQ_IES) && + if (sinfo->assoc_req_ies_len && nla_put(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len, sinfo->assoc_req_ies)) goto nla_put_failure; diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 0f47948..5b24d39 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -1300,7 +1300,7 @@ static int cfg80211_wext_giwrate(struct net_device *dev, if (err) return err; - if (!(sinfo.filled & STATION_INFO_TX_BITRATE)) + if (!(sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE))) return -EOPNOTSUPP; rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate); @@ -1340,7 +1340,7 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) switch (rdev->wiphy.signal_type) { case CFG80211_SIGNAL_TYPE_MBM: - if (sinfo.filled & STATION_INFO_SIGNAL) { + if (sinfo.filled & BIT(NL80211_STA_INFO_SIGNAL)) { int sig = sinfo.signal; wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; @@ -1354,7 +1354,7 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) break; } case CFG80211_SIGNAL_TYPE_UNSPEC: - if (sinfo.filled & STATION_INFO_SIGNAL) { + if (sinfo.filled & BIT(NL80211_STA_INFO_SIGNAL)) { wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; wstats.qual.level = sinfo.signal; @@ -1367,9 +1367,9 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) } wstats.qual.updated |= IW_QUAL_NOISE_INVALID; - if (sinfo.filled & STATION_INFO_RX_DROP_MISC) + if (sinfo.filled & BIT(NL80211_STA_INFO_RX_DROP_MISC)) wstats.discard.misc = sinfo.rx_dropped_misc; - if (sinfo.filled & STATION_INFO_TX_FAILED) + if (sinfo.filled & BIT(NL80211_STA_INFO_TX_FAILED)) wstats.discard.retries = sinfo.tx_failed; return &wstats; -- cgit v0.10.2 From a76b1942a10293a94edf3c93c23a6231b63532f5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Nov 2014 14:12:22 +0100 Subject: cfg80211: add nl80211 beacon-only statistics Add these two values: * BEACON_RX: number of beacons received from this peer * BEACON_SIGNAL_AVG: signal strength average for beacons only These can then be used for Android Lollipop's statistics request. Signed-off-by: Johannes Berg diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 91c1336..ef26ce1 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -987,6 +987,9 @@ struct sta_bss_parameters { * @nonpeer_pm: non-peer mesh STA power save mode * @expected_throughput: expected throughput in kbps (including 802.11 headers) * towards this station. + * @rx_beacon: number of beacons received from this peer + * @rx_beacon_signal_avg: signal strength average (in dBm) for beacons received + * from this peer */ struct station_info { u32 filled; @@ -1026,6 +1029,9 @@ struct station_info { enum nl80211_mesh_power_mode nonpeer_pm; u32 expected_throughput; + + u64 rx_beacon; + u8 rx_beacon_signal_avg; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index e48ca0b..0c3d341 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2375,6 +2375,9 @@ enum nl80211_sta_bss_param { * 802.11 header (u32, kbps) * @NL80211_STA_INFO_RX_DROP_MISC: RX packets dropped for unspecified reasons * (u64) + * @NL80211_STA_INFO_BEACON_RX: number of beacons received from this peer (u64) + * @NL80211_STA_INFO_BEACON_SIGNAL_AVG: signal strength average + * for beacons only (u8, dBm) * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -2408,6 +2411,8 @@ enum nl80211_sta_info { NL80211_STA_INFO_CHAIN_SIGNAL_AVG, NL80211_STA_INFO_EXPECTED_THROUGHPUT, NL80211_STA_INFO_RX_DROP_MISC, + NL80211_STA_INFO_BEACON_RX, + NL80211_STA_INFO_BEACON_SIGNAL_AVG, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 68faf8a..42b968a1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3768,6 +3768,8 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, PUT_SINFO(T_OFFSET, t_offset, u64); PUT_SINFO(RX_DROP_MISC, rx_dropped_misc, u64); + PUT_SINFO(BEACON_RX, rx_beacon, u64); + PUT_SINFO(BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8); #undef PUT_SINFO nla_nest_end(msg, sinfoattr); -- cgit v0.10.2 From 8d791361a4698ca6f01c361a47b39b30d26bf66c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 21 Nov 2014 12:40:05 +0100 Subject: nl80211: clarify packet statistics descriptions The current statistics we keep aren't very clear, some are on MPDUs and some on MSDUs/MMPDUs. Clarify the descriptions based on the counters mac80211 keeps. Signed-off-by: Johannes Berg diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ef26ce1..95420fb 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -950,8 +950,8 @@ struct sta_bss_parameters { * indicate the relevant values in this struct for them * @connected_time: time(in secs) since a station is last connected * @inactive_time: time since last station activity (tx/rx) in milliseconds - * @rx_bytes: bytes received from this station - * @tx_bytes: bytes transmitted to this station + * @rx_bytes: bytes (size of MPDUs) received from this station + * @tx_bytes: bytes (size of MPDUs) transmitted to this station * @llid: mesh local link id * @plid: mesh peer link id * @plink_state: mesh peer link state @@ -964,10 +964,10 @@ struct sta_bss_parameters { * @chain_signal_avg: per-chain signal strength average in dBm * @txrate: current unicast bitrate from this station * @rxrate: current unicast bitrate to this station - * @rx_packets: packets received from this station - * @tx_packets: packets transmitted to this station - * @tx_retries: cumulative retry counts - * @tx_failed: number of failed transmissions (retries exceeded, no ACK) + * @rx_packets: packets (MSDUs & MMPDUs) received from this station + * @tx_packets: packets (MSDUs & MMPDUs) transmitted to this station + * @tx_retries: cumulative retry counts (MPDUs) + * @tx_failed: number of failed transmissions (MPDUs) (retries exceeded, no ACK) * @rx_dropped_misc: Dropped for un-specified reason. * @bss_param: current BSS parameters * @generation: generation number for nl80211 dumps. diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 0c3d341..b0fb5d5 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2338,18 +2338,24 @@ enum nl80211_sta_bss_param { * * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs) - * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station) - * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station) - * @NL80211_STA_INFO_RX_BYTES64: total received bytes (u64, from this station) - * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (u64, to this station) + * @NL80211_STA_INFO_RX_BYTES: total received bytes (MPDU length) + * (u32, from this station) + * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (MPDU length) + * (u32, to this station) + * @NL80211_STA_INFO_RX_BYTES64: total received bytes (MPDU length) + * (u64, from this station) + * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (MPDU length) + * (u64, to this station) * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute * containing info as possible, see &enum nl80211_rate_info - * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station) - * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this - * station) - * @NL80211_STA_INFO_TX_RETRIES: total retries (u32, to this station) - * @NL80211_STA_INFO_TX_FAILED: total failed packets (u32, to this station) + * @NL80211_STA_INFO_RX_PACKETS: total received packet (MSDUs and MMPDUs) + * (u32, from this station) + * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (MSDUs and MMPDUs) + * (u32, to this station) + * @NL80211_STA_INFO_TX_RETRIES: total retries (MPDUs) (u32, to this station) + * @NL80211_STA_INFO_TX_FAILED: total failed packets (MPDUs) + * (u32, to this station) * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm) * @NL80211_STA_INFO_LLID: the station's mesh LLID * @NL80211_STA_INFO_PLID: the station's mesh PLID -- cgit v0.10.2 From 6de39808cf1dd7b02bf42e7d8695d80f5eaf645d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 19 Dec 2014 12:34:00 +0100 Subject: nl80211: support per-TID station statistics The base for the current statistics is pretty mixed up, support exporting RX/TX statistics for MSDUs per TID. This (currently) covers received MSDUs, transmitted MSDUs and retries/failures thereof. Doing it per TID for MSDUs makes more sense than say only per AC because it's symmetric - we could export per-AC statistics for all frames (which AC we used for transmission can be determined also for management frames) but per TID is better and usually data frames are really the ones we care about. Also, on RX we can't determine the AC - but we do know the TID for any QoS MPDU we received. Signed-off-by: Johannes Berg diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 95420fb..1977357 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -939,6 +939,24 @@ struct sta_bss_parameters { u16 beacon_interval; }; +/** + * struct cfg80211_tid_stats - per-TID statistics + * @filled: bitmap of flags using the bits of &enum nl80211_tid_stats to + * indicate the relevant values in this struct are filled + * @rx_msdu: number of received MSDUs + * @tx_msdu: number of (attempted) transmitted MSDUs + * @tx_msdu_retries: number of retries (not counting the first) for + * transmitted MSDUs + * @tx_msdu_failed: number of failed transmitted MSDUs + */ +struct cfg80211_tid_stats { + u32 filled; + u64 rx_msdu; + u64 tx_msdu; + u64 tx_msdu_retries; + u64 tx_msdu_failed; +}; + #define IEEE80211_MAX_CHAINS 4 /** @@ -990,6 +1008,8 @@ struct sta_bss_parameters { * @rx_beacon: number of beacons received from this peer * @rx_beacon_signal_avg: signal strength average (in dBm) for beacons received * from this peer + * @pertid: per-TID statistics, see &struct cfg80211_tid_stats, using the last + * (IEEE80211_NUM_TIDS) index for MSDUs not encapsulated in QoS-MPDUs. */ struct station_info { u32 filled; @@ -1032,6 +1052,7 @@ struct station_info { u64 rx_beacon; u8 rx_beacon_signal_avg; + struct cfg80211_tid_stats pertid[IEEE80211_NUM_TIDS + 1]; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index b0fb5d5..a963d48 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2384,6 +2384,11 @@ enum nl80211_sta_bss_param { * @NL80211_STA_INFO_BEACON_RX: number of beacons received from this peer (u64) * @NL80211_STA_INFO_BEACON_SIGNAL_AVG: signal strength average * for beacons only (u8, dBm) + * @NL80211_STA_INFO_TID_STATS: per-TID statistics (see &enum nl80211_tid_stats) + * This is a nested attribute where each the inner attribute number is the + * TID+1 and the special TID 16 (i.e. value 17) is used for non-QoS frames; + * each one of those is again nested with &enum nl80211_tid_stats + * attributes carrying the actual values. * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -2419,6 +2424,7 @@ enum nl80211_sta_info { NL80211_STA_INFO_RX_DROP_MISC, NL80211_STA_INFO_BEACON_RX, NL80211_STA_INFO_BEACON_SIGNAL_AVG, + NL80211_STA_INFO_TID_STATS, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, @@ -2426,6 +2432,31 @@ enum nl80211_sta_info { }; /** + * enum nl80211_tid_stats - per TID statistics attributes + * @__NL80211_TID_STATS_INVALID: attribute number 0 is reserved + * @NL80211_TID_STATS_RX_MSDU: number of MSDUs received (u64) + * @NL80211_TID_STATS_TX_MSDU: number of MSDUs transmitted (or + * attempted to transmit; u64) + * @NL80211_TID_STATS_TX_MSDU_RETRIES: number of retries for + * transmitted MSDUs (not counting the first attempt; u64) + * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted + * MSDUs (u64) + * @NUM_NL80211_TID_STATS: number of attributes here + * @NL80211_TID_STATS_MAX: highest numbered attribute here + */ +enum nl80211_tid_stats { + __NL80211_TID_STATS_INVALID, + NL80211_TID_STATS_RX_MSDU, + NL80211_TID_STATS_TX_MSDU, + NL80211_TID_STATS_TX_MSDU_RETRIES, + NL80211_TID_STATS_TX_MSDU_FAILED, + + /* keep last */ + NUM_NL80211_TID_STATS, + NL80211_TID_STATS_MAX = NUM_NL80211_TID_STATS - 1 +}; + +/** * enum nl80211_mpath_flags - nl80211 mesh path flags * * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 42b968a1..7c2ce26 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3772,6 +3772,47 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, PUT_SINFO(BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8); #undef PUT_SINFO + + if (sinfo->filled & BIT(NL80211_STA_INFO_TID_STATS)) { + struct nlattr *tidsattr; + int tid; + + tidsattr = nla_nest_start(msg, NL80211_STA_INFO_TID_STATS); + if (!tidsattr) + goto nla_put_failure; + + for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) { + struct cfg80211_tid_stats *tidstats; + struct nlattr *tidattr; + + tidstats = &sinfo->pertid[tid]; + + if (!tidstats->filled) + continue; + + tidattr = nla_nest_start(msg, tid + 1); + if (!tidattr) + goto nla_put_failure; + +#define PUT_TIDVAL(attr, memb, type) do { \ + if (tidstats->filled & BIT(NL80211_TID_STATS_ ## attr) && \ + nla_put_ ## type(msg, NL80211_TID_STATS_ ## attr, \ + tidstats->memb)) \ + goto nla_put_failure; \ + } while (0) + + PUT_TIDVAL(RX_MSDU, rx_msdu, u64); + PUT_TIDVAL(TX_MSDU, tx_msdu, u64); + PUT_TIDVAL(TX_MSDU_RETRIES, tx_msdu_retries, u64); + PUT_TIDVAL(TX_MSDU_FAILED, tx_msdu_failed, u64); + +#undef PUT_TIDVAL + nla_nest_end(msg, tidattr); + } + + nla_nest_end(msg, tidsattr); + } + nla_nest_end(msg, sinfoattr); if (sinfo->assoc_req_ies_len && -- cgit v0.10.2 From 79c892b85027d5074dfa670dd451c14ee649fb88 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 21 Nov 2014 14:26:31 +0100 Subject: mac80211: provide per-TID RX/TX MSDU counters Implement the new counters cfg80211 can now advertise to userspace. The TX code is in the sequence number handler, which is a bit odd, but that place already knows the TID and frame type, so it was easiest and least impact there. Signed-off-by: Johannes Berg diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index fa5d870..3a1a3ba 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2314,6 +2314,15 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) return RX_DROP_MONITOR; + if (rx->sta) { + /* The security index has the same property as needed + * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS + * for non-QoS-data frames. Here we know it's a data + * frame, so count MSDUs. + */ + rx->sta->rx_msdu[rx->security_idx]++; + } + /* * Send unexpected-4addr-frame event to hostapd. For older versions, * also drop the frame to cooked monitor interfaces. diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 64b53b9..dc352fc 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1843,6 +1843,37 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE); } + sinfo->filled |= BIT(NL80211_STA_INFO_TID_STATS); + for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) { + struct cfg80211_tid_stats *tidstats = &sinfo->pertid[i]; + + if (!(tidstats->filled & BIT(NL80211_TID_STATS_RX_MSDU))) { + tidstats->filled |= BIT(NL80211_TID_STATS_RX_MSDU); + tidstats->rx_msdu = sta->rx_msdu[i]; + } + + if (!(tidstats->filled & BIT(NL80211_TID_STATS_TX_MSDU))) { + tidstats->filled |= BIT(NL80211_TID_STATS_TX_MSDU); + tidstats->tx_msdu = sta->tx_msdu[i]; + } + + if (!(tidstats->filled & + BIT(NL80211_TID_STATS_TX_MSDU_RETRIES)) && + local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) { + tidstats->filled |= + BIT(NL80211_TID_STATS_TX_MSDU_RETRIES); + tidstats->tx_msdu_retries = sta->tx_msdu_retries[i]; + } + + if (!(tidstats->filled & + BIT(NL80211_TID_STATS_TX_MSDU_FAILED)) && + local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) { + tidstats->filled |= + BIT(NL80211_TID_STATS_TX_MSDU_FAILED); + tidstats->tx_msdu_failed = sta->tx_msdu_failed[i]; + } + } + if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH sinfo->filled |= BIT(NL80211_STA_INFO_LLID) | diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 4f052bb..925e68f 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -346,6 +346,14 @@ struct ieee80211_tx_latency_stat { * @cipher_scheme: optional cipher scheme for this station * @last_tdls_pkt_time: holds the time in jiffies of last TDLS pkt ACKed * @reserved_tid: reserved TID (if any, otherwise IEEE80211_TID_UNRESERVED) + * @tx_msdu: MSDUs transmitted to this station, using IEEE80211_NUM_TID + * entry for non-QoS frames + * @tx_msdu_retries: MSDU retries for transmissions to to this station, + * using IEEE80211_NUM_TID entry for non-QoS frames + * @tx_msdu_failed: MSDU failures for transmissions to to this station, + * using IEEE80211_NUM_TID entry for non-QoS frames + * @rx_msdu: MSDUs received from this station, using IEEE80211_NUM_TID + * entry for non-QoS frames */ struct sta_info { /* General information, mostly static */ @@ -416,6 +424,10 @@ struct sta_info { u32 last_rx_rate_vht_flag; u8 last_rx_rate_vht_nss; u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1]; + u64 tx_msdu[IEEE80211_NUM_TIDS + 1]; + u64 tx_msdu_retries[IEEE80211_NUM_TIDS + 1]; + u64 tx_msdu_failed[IEEE80211_NUM_TIDS + 1]; + u64 rx_msdu[IEEE80211_NUM_TIDS + 1]; /* * Aggregation information, locked with lock. diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 7d4e930..788707f 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -730,6 +730,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) struct ieee80211_bar *bar; int rtap_len; int shift = 0; + int tid = IEEE80211_NUM_TIDS;; rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count); @@ -773,7 +774,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) && (ieee80211_is_data_qos(fc))) { - u16 tid, ssn; + u16 ssn; u8 *qc; qc = ieee80211_get_qos_ctl(hdr); @@ -782,10 +783,14 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) & IEEE80211_SCTL_SEQ); ieee80211_send_bar(&sta->sdata->vif, hdr->addr1, tid, ssn); + } else if (ieee80211_is_data_qos(fc)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + + tid = qc[0] & 0xf; } if (!acked && ieee80211_is_back_req(fc)) { - u16 tid, control; + u16 control; /* * BAR failed, store the last SSN and retry sending @@ -813,6 +818,12 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) if (!acked) sta->tx_retry_failed++; sta->tx_retry_count += retry_count; + + if (ieee80211_is_data_present(fc)) { + if (!acked) + sta->tx_msdu_failed[tid]++; + sta->tx_msdu_retries[tid] += retry_count; + } } rate_control_tx_status(local, sband, sta, skb); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 058686a..da7f352 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -815,6 +815,8 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) /* for pure STA mode without beacons, we can do it */ hdr->seq_ctrl = cpu_to_le16(tx->sdata->sequence_number); tx->sdata->sequence_number += 0x10; + if (tx->sta) + tx->sta->tx_msdu[IEEE80211_NUM_TIDS]++; return TX_CONTINUE; } @@ -831,6 +833,7 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) qc = ieee80211_get_qos_ctl(hdr); tid = *qc & IEEE80211_QOS_CTL_TID_MASK; seq = &tx->sta->tid_seq[tid]; + tx->sta->tx_msdu[tid]++; hdr->seq_ctrl = cpu_to_le16(*seq); -- cgit v0.10.2 From cbfc35b90f3b4853d1eb9fcb82e99531d6a1c629 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 5 Jan 2015 19:42:25 -0500 Subject: drm/radeon: fix VM flush on cayman/aruba (v3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to wait for the GPUVM flush to complete. There was some confusion as to how this mechanism was supposed to work. The operation is not atomic. For GPU initiated invalidations you need to read back a VM register to introduce enough latency for the update to complete. v2: drop gart changes v3: just read back rather than polling Reviewed-by: Christian König Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index 360de9f..aea48c8 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -2516,6 +2516,16 @@ void cayman_vm_flush(struct radeon_device *rdev, struct radeon_ring *ring, radeon_ring_write(ring, PACKET0(VM_INVALIDATE_REQUEST, 0)); radeon_ring_write(ring, 1 << vm_id); + /* wait for the invalidate to complete */ + radeon_ring_write(ring, PACKET3(PACKET3_WAIT_REG_MEM, 5)); + radeon_ring_write(ring, (WAIT_REG_MEM_FUNCTION(0) | /* always */ + WAIT_REG_MEM_ENGINE(0))); /* me */ + radeon_ring_write(ring, VM_INVALIDATE_REQUEST >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); /* ref */ + radeon_ring_write(ring, 0); /* mask */ + radeon_ring_write(ring, 0x20); /* poll interval */ + /* sync PFP to ME, otherwise we might get invalid PFP reads */ radeon_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0)); radeon_ring_write(ring, 0x0); diff --git a/drivers/gpu/drm/radeon/ni_dma.c b/drivers/gpu/drm/radeon/ni_dma.c index 50f8861..4be2bb7 100644 --- a/drivers/gpu/drm/radeon/ni_dma.c +++ b/drivers/gpu/drm/radeon/ni_dma.c @@ -463,5 +463,11 @@ void cayman_dma_vm_flush(struct radeon_device *rdev, struct radeon_ring *ring, radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_SRBM_WRITE, 0, 0, 0)); radeon_ring_write(ring, (0xf << 16) | (VM_INVALIDATE_REQUEST >> 2)); radeon_ring_write(ring, 1 << vm_id); + + /* wait for invalidate to complete */ + radeon_ring_write(ring, DMA_SRBM_READ_PACKET); + radeon_ring_write(ring, (0xff << 20) | (VM_INVALIDATE_REQUEST >> 2)); + radeon_ring_write(ring, 0); /* mask */ + radeon_ring_write(ring, 0); /* value */ } diff --git a/drivers/gpu/drm/radeon/nid.h b/drivers/gpu/drm/radeon/nid.h index 2e12e4d..ad71254 100644 --- a/drivers/gpu/drm/radeon/nid.h +++ b/drivers/gpu/drm/radeon/nid.h @@ -1133,6 +1133,23 @@ #define PACKET3_MEM_SEMAPHORE 0x39 #define PACKET3_MPEG_INDEX 0x3A #define PACKET3_WAIT_REG_MEM 0x3C +#define WAIT_REG_MEM_FUNCTION(x) ((x) << 0) + /* 0 - always + * 1 - < + * 2 - <= + * 3 - == + * 4 - != + * 5 - >= + * 6 - > + */ +#define WAIT_REG_MEM_MEM_SPACE(x) ((x) << 4) + /* 0 - reg + * 1 - mem + */ +#define WAIT_REG_MEM_ENGINE(x) ((x) << 8) + /* 0 - me + * 1 - pfp + */ #define PACKET3_MEM_WRITE 0x3D #define PACKET3_PFP_SYNC_ME 0x42 #define PACKET3_SURFACE_SYNC 0x43 @@ -1272,6 +1289,13 @@ (1 << 21) | \ (((n) & 0xFFFFF) << 0)) +#define DMA_SRBM_POLL_PACKET ((9 << 28) | \ + (1 << 27) | \ + (1 << 26)) + +#define DMA_SRBM_READ_PACKET ((9 << 28) | \ + (1 << 27)) + /* async DMA Packet types */ #define DMA_PACKET_WRITE 0x2 #define DMA_PACKET_COPY 0x3 -- cgit v0.10.2 From d474ea7e52cbaaae22711d857949ba6018562c29 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 5 Jan 2015 19:54:50 -0500 Subject: drm/radeon: fix VM flush on SI (v3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to wait for the GPUVM flush to complete. There was some confusion as to how this mechanism was supposed to work. The operation is not atomic. For GPU initiated invalidations you need to read back a VM register to introduce enough latency for the update to complete. v2: drop gart changes v3: just read back rather than polling Reviewed-by: Christian König Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 60df444..5d89b87 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -5057,6 +5057,16 @@ void si_vm_flush(struct radeon_device *rdev, struct radeon_ring *ring, radeon_ring_write(ring, 0); radeon_ring_write(ring, 1 << vm_id); + /* wait for the invalidate to complete */ + radeon_ring_write(ring, PACKET3(PACKET3_WAIT_REG_MEM, 5)); + radeon_ring_write(ring, (WAIT_REG_MEM_FUNCTION(0) | /* always */ + WAIT_REG_MEM_ENGINE(0))); /* me */ + radeon_ring_write(ring, VM_INVALIDATE_REQUEST >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); /* ref */ + radeon_ring_write(ring, 0); /* mask */ + radeon_ring_write(ring, 0x20); /* poll interval */ + /* sync PFP to ME, otherwise we might get invalid PFP reads */ radeon_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0)); radeon_ring_write(ring, 0x0); diff --git a/drivers/gpu/drm/radeon/si_dma.c b/drivers/gpu/drm/radeon/si_dma.c index f5cc777..aa7b872 100644 --- a/drivers/gpu/drm/radeon/si_dma.c +++ b/drivers/gpu/drm/radeon/si_dma.c @@ -206,6 +206,14 @@ void si_dma_vm_flush(struct radeon_device *rdev, struct radeon_ring *ring, radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_SRBM_WRITE, 0, 0, 0, 0)); radeon_ring_write(ring, (0xf << 16) | (VM_INVALIDATE_REQUEST >> 2)); radeon_ring_write(ring, 1 << vm_id); + + /* wait for invalidate to complete */ + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_POLL_REG_MEM, 0, 0, 0, 0)); + radeon_ring_write(ring, VM_INVALIDATE_REQUEST); + radeon_ring_write(ring, 0xff << 16); /* retry */ + radeon_ring_write(ring, 1 << vm_id); /* mask */ + radeon_ring_write(ring, 0); /* value */ + radeon_ring_write(ring, (0 << 28) | 0x20); /* func(always) | poll interval */ } /** diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h index 4069be89..8499924 100644 --- a/drivers/gpu/drm/radeon/sid.h +++ b/drivers/gpu/drm/radeon/sid.h @@ -1632,6 +1632,23 @@ #define PACKET3_MPEG_INDEX 0x3A #define PACKET3_COPY_DW 0x3B #define PACKET3_WAIT_REG_MEM 0x3C +#define WAIT_REG_MEM_FUNCTION(x) ((x) << 0) + /* 0 - always + * 1 - < + * 2 - <= + * 3 - == + * 4 - != + * 5 - >= + * 6 - > + */ +#define WAIT_REG_MEM_MEM_SPACE(x) ((x) << 4) + /* 0 - reg + * 1 - mem + */ +#define WAIT_REG_MEM_ENGINE(x) ((x) << 8) + /* 0 - me + * 1 - pfp + */ #define PACKET3_MEM_WRITE 0x3D #define PACKET3_COPY_DATA 0x40 #define PACKET3_CP_DMA 0x41 @@ -1835,6 +1852,7 @@ #define DMA_PACKET_TRAP 0x7 #define DMA_PACKET_SRBM_WRITE 0x9 #define DMA_PACKET_CONSTANT_FILL 0xd +#define DMA_PACKET_POLL_REG_MEM 0xe #define DMA_PACKET_NOP 0xf #define VCE_STATUS 0x20004 -- cgit v0.10.2 From 3a01fd367e09ebf05d75a000407364e7ebe2b678 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 5 Jan 2015 19:59:47 -0500 Subject: drm/radeon: fix VM flush on CIK (v3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to wait for the GPUVM flush to complete. There was some confusion as to how this mechanism was supposed to work. The operation is not atomic. For GPU initiated invalidations you need to read back a VM register to introduce enough latency for the update to complete. v2: drop gart changes v3: just read back rather than polling Reviewed-by: Christian König Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 6dcde37..64fdae5 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -6033,6 +6033,17 @@ void cik_vm_flush(struct radeon_device *rdev, struct radeon_ring *ring, radeon_ring_write(ring, 0); radeon_ring_write(ring, 1 << vm_id); + /* wait for the invalidate to complete */ + radeon_ring_write(ring, PACKET3(PACKET3_WAIT_REG_MEM, 5)); + radeon_ring_write(ring, (WAIT_REG_MEM_OPERATION(0) | /* wait */ + WAIT_REG_MEM_FUNCTION(0) | /* always */ + WAIT_REG_MEM_ENGINE(0))); /* me */ + radeon_ring_write(ring, VM_INVALIDATE_REQUEST >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); /* ref */ + radeon_ring_write(ring, 0); /* mask */ + radeon_ring_write(ring, 0x20); /* poll interval */ + /* compute doesn't have PFP */ if (usepfp) { /* sync PFP to ME, otherwise we might get invalid PFP reads */ diff --git a/drivers/gpu/drm/radeon/cik_sdma.c b/drivers/gpu/drm/radeon/cik_sdma.c index dde5c7e..a0133c7 100644 --- a/drivers/gpu/drm/radeon/cik_sdma.c +++ b/drivers/gpu/drm/radeon/cik_sdma.c @@ -903,6 +903,9 @@ void cik_sdma_vm_pad_ib(struct radeon_ib *ib) void cik_dma_vm_flush(struct radeon_device *rdev, struct radeon_ring *ring, unsigned vm_id, uint64_t pd_addr) { + u32 extra_bits = (SDMA_POLL_REG_MEM_EXTRA_OP(0) | + SDMA_POLL_REG_MEM_EXTRA_FUNC(0)); /* always */ + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000)); if (vm_id < 8) { radeon_ring_write(ring, (VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (vm_id << 2)) >> 2); @@ -943,5 +946,12 @@ void cik_dma_vm_flush(struct radeon_device *rdev, struct radeon_ring *ring, radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000)); radeon_ring_write(ring, VM_INVALIDATE_REQUEST >> 2); radeon_ring_write(ring, 1 << vm_id); + + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_POLL_REG_MEM, 0, extra_bits)); + radeon_ring_write(ring, VM_INVALIDATE_REQUEST >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); /* reference */ + radeon_ring_write(ring, 0); /* mask */ + radeon_ring_write(ring, (0xfff << 16) | 10); /* retry count, poll interval */ } -- cgit v0.10.2 From 8dfe58b2063811b415626060316672741049d4d4 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Thu, 8 Jan 2015 16:46:16 +0200 Subject: drm/amdkfd: Fix sparse warning (different address space) Signed-off-by: Oded Gabbay diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 9c8961d..30c8fda 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -280,7 +280,7 @@ static int create_compute_queue_nocpsch(struct device_queue_manager *dqm, q->queue); retval = mqd->load_mqd(mqd, q->mqd, q->pipe, - q->queue, q->properties.write_ptr); + q->queue, (uint32_t __user *) q->properties.write_ptr); if (retval != 0) { deallocate_hqd(dqm, q); mqd->uninit_mqd(mqd, q->mqd, q->mqd_mem_obj); -- cgit v0.10.2 From bc6efeeeb5a2fa968532b9d666e8b7f823b1940f Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Thu, 8 Jan 2015 11:30:10 +0100 Subject: ieee802154: 6lowpan: fix Makefile entry Since commit ea81ac2e7050798109356150ea16e71622a5c329 ("ieee802154: create 6lowpan sub-directory") we have a subdirectory for the ieee802154 6lowpan implementation. This commit also moves the Kconfig entry inside of net/ieee802154/6lowpan/ and forgot to rename the Makefile entry from obj-$(CONFIG_IEEE802154_6LOWPAN) to obj-y and handle the obj-$(CONFIG_IEEE802154_6LOWPAN) inside the created 6lowpan directory. This will occur that the ieee802154_6lowpan can't be build. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/6lowpan/Makefile b/net/ieee802154/6lowpan/Makefile index 2f1b75a..6bfb270 100644 --- a/net/ieee802154/6lowpan/Makefile +++ b/net/ieee802154/6lowpan/Makefile @@ -1,3 +1,3 @@ -obj-y += ieee802154_6lowpan.o +obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o ieee802154_6lowpan-y := core.o rx.o reassembly.o tx.o diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile index 3653397..05dab29 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile @@ -1,6 +1,6 @@ obj-$(CONFIG_IEEE802154) += ieee802154.o obj-$(CONFIG_IEEE802154_SOCKET) += ieee802154_socket.o -obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan/ +obj-y += 6lowpan/ ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \ header_ops.o sysfs.o nl802154.o -- cgit v0.10.2 From e61f7d1c3c07a7e51036b0796749edb00deff845 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Thu, 8 Jan 2015 10:34:27 -0500 Subject: libata: Whitelist SSDs that are known to properly return zeroes after TRIM As defined, the DRAT (Deterministic Read After Trim) and RZAT (Return Zero After Trim) flags in the ATA Command Set are unreliable in the sense that they only define what happens if the device successfully executed the DSM TRIM command. TRIM is only advisory, however, and the device is free to silently ignore all or parts of the request. In practice this renders the DRAT and RZAT flags completely useless and because the results are unpredictable we decided to disable discard in MD for 3.18 to avoid the risk of data corruption. Hardware vendors in the real world obviously need better guarantees than what the standards bodies provide. Unfortuntely those guarantees are encoded in product requirements documents rather than somewhere we can key off of them programatically. So we are compelled to disabling discard_zeroes_data for all devices unless we explicitly have data to support whitelisting them. This patch whitelists SSDs from a few of the main vendors. None of the whitelists are based on written guarantees. They are purely based on empirical evidence collected from internal and external users that have tested or qualified these drives in RAID deployments. The whitelist is only meant as a starting point and is by no means comprehensive: - All intel SSD models except for 510 - Micron M5?0/M600 - Samsung SSDs - Seagate SSDs Signed-off-by: Martin K. Petersen Reviewed-by: Christoph Hellwig Signed-off-by: Tejun Heo diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 5c84fb5..23c2ae0 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4233,10 +4233,33 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "PIONEER DVD-RW DVR-216D", NULL, ATA_HORKAGE_NOSETXFER }, /* devices that don't properly handle queued TRIM commands */ - { "Micron_M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, - { "Crucial_CT???M500SSD*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, - { "Micron_M550*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, - { "Crucial_CT*M550SSD*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, + { "Micron_M[56]*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | + ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "Crucial_CT*SSD*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, + + /* + * As defined, the DRAT (Deterministic Read After Trim) and RZAT + * (Return Zero After Trim) flags in the ATA Command Set are + * unreliable in the sense that they only define what happens if + * the device successfully executed the DSM TRIM command. TRIM + * is only advisory, however, and the device is free to silently + * ignore all or parts of the request. + * + * Whitelist drives that are known to reliably return zeroes + * after TRIM. + */ + + /* + * The intel 510 drive has buggy DRAT/RZAT. Explicitly exclude + * that model before whitelisting all other intel SSDs. + */ + { "INTEL*SSDSC2MH*", NULL, 0, }, + + { "INTEL*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "SSD*INTEL*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "Samsung*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "SAMSUNG*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "ST[1248][0248]0[FH]*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, /* * Some WD SATA-I drives spin up and down erratically when the link diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index e364e86..6abd17a 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -2532,13 +2532,15 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf) rbuf[15] = lowest_aligned; if (ata_id_has_trim(args->id)) { - rbuf[14] |= 0x80; /* TPE */ + rbuf[14] |= 0x80; /* LBPME */ - if (ata_id_has_zero_after_trim(args->id)) - rbuf[14] |= 0x40; /* TPRZ */ + if (ata_id_has_zero_after_trim(args->id) && + dev->horkage & ATA_HORKAGE_ZERO_AFTER_TRIM) { + ata_dev_info(dev, "Enabling discard_zeroes_data\n"); + rbuf[14] |= 0x40; /* LBPRZ */ + } } } - return 0; } diff --git a/include/linux/libata.h b/include/linux/libata.h index 2d18241..f2b440e 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -422,6 +422,7 @@ enum { ATA_HORKAGE_NO_NCQ_TRIM = (1 << 19), /* don't use queued TRIM */ ATA_HORKAGE_NOLPM = (1 << 20), /* don't use LPM */ ATA_HORKAGE_WD_BROKEN_LPM = (1 << 21), /* some WDs have broken LPM */ + ATA_HORKAGE_ZERO_AFTER_TRIM = (1 << 22),/* guarantees zero after trim */ /* DMA mask for user DMA control: User visible values; DO NOT renumber */ -- cgit v0.10.2 From ddaca25aa4dade234163290f6a9e091e7f57b36a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 8 Jan 2015 12:23:05 +0100 Subject: ASoC: Export snd_soc_runtime_set_dai_fmt() Export snd_soc_runtime_set_dai_fmt() so it can be used in modules. Fixes: ce64c8b9cf5b ("ASoC: Add helper function for changing the DAI link format") Reported-by: kbuild test robot Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 80ea358..d342ee2 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1491,6 +1491,7 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, return 0; } +EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt); static int snd_soc_instantiate_card(struct snd_soc_card *card) { -- cgit v0.10.2 From a6b3db2c837a2db359bf7f846346680883dbb032 Mon Sep 17 00:00:00 2001 From: Vaishali Thakkar Date: Thu, 8 Jan 2015 08:59:39 +0530 Subject: ASoC: wm5102: Use put_unaligned_be16 This patch introduces the use of function put_unaligned_be16. This is done using Coccinelle and semantic patch used is as follows: @a@ typedef u16, __be16, uint16_t; {u16,__be16,uint16_t} e16; identifier tmp; expression ptr; expression y,e; type T; @@ - tmp = cpu_to_be16(y); <+... when != tmp ( - memcpy(ptr, (T)&tmp, \(2\|sizeof(u16)\|sizeof(__be16)\|sizeof(uint16_t)\|sizeof(e16)\)); + put_unaligned_be16(y,ptr); | - memcpy(ptr, (T)&tmp, ...); + put_unaligned_be16(y,ptr); ) ...+> ? tmp = e @@ type T; identifier a.tmp; @@ - T tmp; ...when != tmp Signed-off-by: Vaishali Thakkar Acked-by: Charles Keepax -- Changes Since v2: Make the patch compatible for current code sound/soc/codecs/wm5102.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index f439ae0..7dbac8a 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -28,6 +28,7 @@ #include #include +#include #include "arizona.h" #include "wm5102.h" @@ -617,11 +618,10 @@ static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct arizona *arizona = dev_get_drvdata(codec->dev->parent); - uint16_t data; mutex_lock(&arizona->dac_comp_lock); - data = cpu_to_be16(arizona->dac_comp_coeff); - memcpy(ucontrol->value.bytes.data, &data, sizeof(data)); + put_unaligned_be16(arizona->dac_comp_coeff, + ucontrol->value.bytes.data); mutex_unlock(&arizona->dac_comp_lock); return 0; -- cgit v0.10.2 From 83b0302d347a49f951e904184afe57ac3723476e Mon Sep 17 00:00:00 2001 From: Ashay Jaiswal Date: Thu, 8 Jan 2015 18:54:25 +0530 Subject: regulator: core: fix race condition in regulator_put() The regulator framework maintains a list of consumer regulators for a regulator device and protects it from concurrent access using the regulator device's mutex lock. In the case of regulator_put() the consumer is removed and regulator device's parameters are updated without holding the regulator device's mutex. This would lead to a race condition between the regulator_put() and any function which traverses the consumer list or modifies regulator device's parameters. Fix this race condition by holding the regulator device's mutex in case of regulator_put. Signed-off-by: Ashay Jaiswal Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index e225711..9c48fb3 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1488,7 +1488,7 @@ struct regulator *regulator_get_optional(struct device *dev, const char *id) } EXPORT_SYMBOL_GPL(regulator_get_optional); -/* Locks held by regulator_put() */ +/* regulator_list_mutex lock held by regulator_put() */ static void _regulator_put(struct regulator *regulator) { struct regulator_dev *rdev; @@ -1503,12 +1503,14 @@ static void _regulator_put(struct regulator *regulator) /* remove any sysfs entries */ if (regulator->dev) sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name); + mutex_lock(&rdev->mutex); kfree(regulator->supply_name); list_del(®ulator->list); kfree(regulator); rdev->open_count--; rdev->exclusive = 0; + mutex_unlock(&rdev->mutex); module_put(rdev->owner); } -- cgit v0.10.2 From ad26aa6c60974acf3228ed0ade97ba5793093dbe Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Thu, 8 Jan 2015 11:04:07 +0900 Subject: regulator: s2mps11: Fix wrong calculation of register offset This patch adds missing registers('BUCK7_SW' & 'LDO29_CTRL'). Since BUCK7 has 1 more register (BUCK7_SW) than others, register offset should be added one more for which has bigger address than BUCK7 registers. Fixes: 76b9840b24ae04(regulator: s2mps11: Add support S2MPS13 regulator device) Signed-off-by: Jonghwa Lee Signed-off-by: Chanwoo Choi Signed-off-by: Mark Brown Cc: diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index c1444c3..13ca20e 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -405,6 +405,40 @@ static struct regulator_ops s2mps14_reg_ops; .enable_mask = S2MPS14_ENABLE_MASK \ } +#define regulator_desc_s2mps13_buck7(num, min, step, min_sel) { \ + .name = "BUCK"#num, \ + .id = S2MPS13_BUCK##num, \ + .ops = &s2mps14_reg_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = min, \ + .uV_step = step, \ + .linear_min_sel = min_sel, \ + .n_voltages = S2MPS14_BUCK_N_VOLTAGES, \ + .ramp_delay = S2MPS13_BUCK_RAMP_DELAY, \ + .vsel_reg = S2MPS13_REG_B1OUT + (num) * 2 - 1, \ + .vsel_mask = S2MPS14_BUCK_VSEL_MASK, \ + .enable_reg = S2MPS13_REG_B1CTRL + (num - 1) * 2, \ + .enable_mask = S2MPS14_ENABLE_MASK \ +} + +#define regulator_desc_s2mps13_buck8_10(num, min, step, min_sel) { \ + .name = "BUCK"#num, \ + .id = S2MPS13_BUCK##num, \ + .ops = &s2mps14_reg_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = min, \ + .uV_step = step, \ + .linear_min_sel = min_sel, \ + .n_voltages = S2MPS14_BUCK_N_VOLTAGES, \ + .ramp_delay = S2MPS13_BUCK_RAMP_DELAY, \ + .vsel_reg = S2MPS13_REG_B1OUT + (num) * 2 - 1, \ + .vsel_mask = S2MPS14_BUCK_VSEL_MASK, \ + .enable_reg = S2MPS13_REG_B1CTRL + (num) * 2 - 1, \ + .enable_mask = S2MPS14_ENABLE_MASK \ +} + static const struct regulator_desc s2mps13_regulators[] = { regulator_desc_s2mps13_ldo(1, MIN_800_MV, STEP_12_5_MV, 0x00), regulator_desc_s2mps13_ldo(2, MIN_1400_MV, STEP_50_MV, 0x0C), @@ -452,10 +486,10 @@ static const struct regulator_desc s2mps13_regulators[] = { regulator_desc_s2mps13_buck(4, MIN_500_MV, STEP_6_25_MV, 0x10), regulator_desc_s2mps13_buck(5, MIN_500_MV, STEP_6_25_MV, 0x10), regulator_desc_s2mps13_buck(6, MIN_500_MV, STEP_6_25_MV, 0x10), - regulator_desc_s2mps13_buck(7, MIN_500_MV, STEP_6_25_MV, 0x10), - regulator_desc_s2mps13_buck(8, MIN_1000_MV, STEP_12_5_MV, 0x20), - regulator_desc_s2mps13_buck(9, MIN_1000_MV, STEP_12_5_MV, 0x20), - regulator_desc_s2mps13_buck(10, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck7(7, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck8_10(8, MIN_1000_MV, STEP_12_5_MV, 0x20), + regulator_desc_s2mps13_buck8_10(9, MIN_1000_MV, STEP_12_5_MV, 0x20), + regulator_desc_s2mps13_buck8_10(10, MIN_500_MV, STEP_6_25_MV, 0x10), }; static int s2mps14_regulator_enable(struct regulator_dev *rdev) diff --git a/include/linux/mfd/samsung/s2mps13.h b/include/linux/mfd/samsung/s2mps13.h index ce5dda8..b1fd675 100644 --- a/include/linux/mfd/samsung/s2mps13.h +++ b/include/linux/mfd/samsung/s2mps13.h @@ -59,6 +59,7 @@ enum s2mps13_reg { S2MPS13_REG_B6CTRL, S2MPS13_REG_B6OUT, S2MPS13_REG_B7CTRL, + S2MPS13_REG_B7SW, S2MPS13_REG_B7OUT, S2MPS13_REG_B8CTRL, S2MPS13_REG_B8OUT, @@ -102,6 +103,7 @@ enum s2mps13_reg { S2MPS13_REG_L26CTRL, S2MPS13_REG_L27CTRL, S2MPS13_REG_L28CTRL, + S2MPS13_REG_L29CTRL, S2MPS13_REG_L30CTRL, S2MPS13_REG_L31CTRL, S2MPS13_REG_L32CTRL, -- cgit v0.10.2 From 64aa5f5843ab12455f6984928058a267f385a82c Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 7 Jan 2015 19:45:40 -0200 Subject: ASoC: fsl_ssi: Fix irq error check Commit 2ffa531078037a0 ("ASoC: fsl_ssi: Fix module unbound") changed the way to retrieve the irq number from irq_of_parse_and_map() to platform_get_irq(), but missed to updated the irq error check accordingly. We should test for negative irq number and propagate it in the case of error. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index a65f17d..059496e 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1362,9 +1362,9 @@ static int fsl_ssi_probe(struct platform_device *pdev) } ssi_private->irq = platform_get_irq(pdev, 0); - if (!ssi_private->irq) { + if (ssi_private->irq < 0) { dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); - return -ENXIO; + return ssi_private->irq; } /* Are the RX and the TX clocks locked? */ -- cgit v0.10.2 From 69067f9d52aa325baa0d113c1f35eb98fe486bfc Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Thu, 8 Jan 2015 20:12:52 +0800 Subject: ASoC: Intel: Always enable DRAM block for FW dump The first 512 bytes of data DRAM memory is used for FW dump, and this first data SRAM block should be never power gated (always on), here always enable the block(DSRAM[0]) for D0 stage. Signed-off-by: Jie Yang Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-haswell-dsp.c b/sound/soc/intel/sst-haswell-dsp.c index 57039b0..c42ffae 100644 --- a/sound/soc/intel/sst-haswell-dsp.c +++ b/sound/soc/intel/sst-haswell-dsp.c @@ -306,7 +306,7 @@ static void hsw_reset(struct sst_dsp *sst) static int hsw_set_dsp_D0(struct sst_dsp *sst) { int tries = 10; - u32 reg; + u32 reg, fw_dump_bit; /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */ reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2); @@ -368,7 +368,9 @@ finish: can't be accessed, please enable each block before accessing. */ reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0); reg |= SST_VDRTCL0_DSRAMPGE_MASK | SST_VDRTCL0_ISRAMPGE_MASK; - writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0); + /* for D0, always enable the block(DSRAM[0]) used for FW dump */ + fw_dump_bit = 1 << SST_VDRTCL0_DSRAMPGE_SHIFT; + writel(reg & ~fw_dump_bit, sst->addr.pci_cfg + SST_VDRTCTL0); /* disable DMA finish function for SSP0 & SSP1 */ @@ -491,6 +493,7 @@ static const struct sst_sram_shift sram_shift[] = { {SST_DEV_ID_LYNX_POINT, 6, 16}, /* lp */ {SST_DEV_ID_WILDCAT_POINT, 2, 12}, /* wpt */ }; + static u32 hsw_block_get_bit(struct sst_mem_block *block) { u32 bit = 0, shift = 0, index; @@ -587,7 +590,9 @@ static int hsw_block_disable(struct sst_mem_block *block) val = readl(sst->addr.pci_cfg + SST_VDRTCTL0); bit = hsw_block_get_bit(block); - writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0); + /* don't disable DSRAM[0], keep it always enable for FW dump*/ + if (bit != (1 << SST_VDRTCL0_DSRAMPGE_SHIFT)) + writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0); /* wait 18 DSP clock ticks */ udelay(10); @@ -612,7 +617,7 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata) const struct sst_adsp_memregion *region; struct device *dev; int ret = -ENODEV, i, j, region_count; - u32 offset, size; + u32 offset, size, fw_dump_bit; dev = sst->dma_dev; @@ -669,9 +674,11 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata) } } + /* always enable the block(DSRAM[0]) used for FW dump */ + fw_dump_bit = 1 << SST_VDRTCL0_DSRAMPGE_SHIFT; /* set default power gating control, enable power gating control for all blocks. that is, can't be accessed, please enable each block before accessing. */ - writel(0xffffffff, sst->addr.pci_cfg + SST_VDRTCTL0); + writel(0xffffffff & ~fw_dump_bit, sst->addr.pci_cfg + SST_VDRTCTL0); return 0; } -- cgit v0.10.2 From 98b9c1d2ce59d98d5921bbdca5a6d7fe40b4a384 Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Thu, 8 Jan 2015 14:32:20 +0800 Subject: ASoC: Intel: Add stream direction for pcm-module map A DAI may have 2 streams(playback/capture) and different modules may be needed for them respectively, so we need add a stream direction here, the combination(dai_id + stream) can tell us which module we really need here. Signed-off-by: Jie Yang Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 13f156b..5448f79 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -78,7 +78,6 @@ static const u32 volume_map[] = { #define HSW_PCM_DAI_ID_OFFLOAD0 1 #define HSW_PCM_DAI_ID_OFFLOAD1 2 #define HSW_PCM_DAI_ID_LOOPBACK 3 -#define HSW_PCM_DAI_ID_CAPTURE 4 static const struct snd_pcm_hardware hsw_pcm_hardware = { @@ -99,6 +98,7 @@ static const struct snd_pcm_hardware hsw_pcm_hardware = { struct hsw_pcm_module_map { int dai_id; + int stream; enum sst_hsw_module_id mod_id; }; @@ -687,11 +687,11 @@ static struct snd_pcm_ops hsw_pcm_ops = { /* static mappings between PCMs and modules - may be dynamic in future */ static struct hsw_pcm_module_map mod_map[] = { - {HSW_PCM_DAI_ID_SYSTEM, SST_HSW_MODULE_PCM_SYSTEM}, - {HSW_PCM_DAI_ID_OFFLOAD0, SST_HSW_MODULE_PCM}, - {HSW_PCM_DAI_ID_OFFLOAD1, SST_HSW_MODULE_PCM}, - {HSW_PCM_DAI_ID_LOOPBACK, SST_HSW_MODULE_PCM_REFERENCE}, - {HSW_PCM_DAI_ID_CAPTURE, SST_HSW_MODULE_PCM_CAPTURE}, + {HSW_PCM_DAI_ID_SYSTEM, 0, SST_HSW_MODULE_PCM_SYSTEM}, + {HSW_PCM_DAI_ID_OFFLOAD0, 0, SST_HSW_MODULE_PCM}, + {HSW_PCM_DAI_ID_OFFLOAD1, 0, SST_HSW_MODULE_PCM}, + {HSW_PCM_DAI_ID_LOOPBACK, 1, SST_HSW_MODULE_PCM_REFERENCE}, + {HSW_PCM_DAI_ID_SYSTEM, 1, SST_HSW_MODULE_PCM_CAPTURE}, }; static int hsw_pcm_create_modules(struct hsw_priv_data *pdata) @@ -1075,7 +1075,7 @@ static void hsw_pcm_complete(struct device *dev) return; } - for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) { + for (i = 0; i < ARRAY_SIZE(mod_map); i++) { pcm_data = &pdata->pcm[i]; if (!pcm_data->substream) @@ -1109,7 +1109,7 @@ static int hsw_pcm_prepare(struct device *dev) if (pdata->pm_state == HSW_PM_STATE_D3) return 0; /* suspend all active streams */ - for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) { + for (i = 0; i < ARRAY_SIZE(mod_map); i++) { pcm_data = &pdata->pcm[i]; if (!pcm_data->substream) @@ -1128,7 +1128,7 @@ static int hsw_pcm_prepare(struct device *dev) sst_hsw_dsp_runtime_suspend(hsw); /* preserve persistent memory */ - for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) { + for (i = 0; i < ARRAY_SIZE(mod_map); i++) { pcm_data = &pdata->pcm[i]; if (!pcm_data->substream) -- cgit v0.10.2 From 0c1232503ae5e792a5bef182c64daeb5cf52498d Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 7 Jan 2015 13:44:32 -0200 Subject: ASoC: fsl_ssi: Make error message concise Currently the error message uses 'np->full_name' which leads to a very verbose log as: fsl-ssi-dai 202c000.ssi: no irq for node /soc/aips-bus@02000000/spba-bus@02000000/ssi@0202c000 We can have a concise log by using pdev->name instead: fsl-ssi-dai 202c000.ssi: no irq for node 202c000.ssi Signed-off-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index a65f17d..fa19507 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1363,7 +1363,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) ssi_private->irq = platform_get_irq(pdev, 0); if (!ssi_private->irq) { - dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); + dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); return -ENXIO; } -- cgit v0.10.2 From f03038e570728036cf6784a435e11ae1d24a5cf0 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 7 Jan 2015 13:44:33 -0200 Subject: ASoC: fsl_asrc: Make error message concise Currently the error message uses 'np->full_name' which leads to a very verbose log that contains the full path of the node. We can have a concise log by using pdev->name instead. Signed-off-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 026a801..633ffff 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -837,7 +837,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); + dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); return irq; } -- cgit v0.10.2 From 0954237f22d808df4918ced2995ff758358881c0 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 7 Jan 2015 13:44:34 -0200 Subject: ASoC: fsl_sai: Make error message concise Currently the error message uses 'np->full_name' which leads to a very verbose log that contains the full path of the node. We can have a concise log by using pdev->name instead. Signed-off-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 032d2d3..ec79c3d 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -612,7 +612,7 @@ static int fsl_sai_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); + dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); return irq; } -- cgit v0.10.2 From da2d45249af55f4fe7186b5aca76e8cb8e7f8a1a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 7 Jan 2015 13:44:35 -0200 Subject: ASoC: fsl_esai: Make error message concise Currently the error message uses 'np->full_name' which leads to a very verbose log that contains the full path of the node. We can have a concise log by using pdev->name instead. Signed-off-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 1c08ab1..5c75971 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -774,7 +774,7 @@ static int fsl_esai_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); + dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); return irq; } -- cgit v0.10.2 From b968d83f09d8a4af1c178f504bc0414bbb37651d Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 7 Jan 2015 13:44:36 -0200 Subject: ASoC: fsl_spdif: Make error message concise Currently the error message uses 'np->full_name' which leads to a very verbose log that contains the full path of the node. We can have a concise log by using pdev->name instead. Signed-off-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index af042942..735e2ee 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1198,7 +1198,7 @@ static int fsl_spdif_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); + dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); return irq; } -- cgit v0.10.2 From 277880a356c10adcb43604f6ab9ec9b2d64db8b2 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Thu, 8 Jan 2015 10:31:06 +0800 Subject: ASoC: rt5677: Add the MICBIAS VDD setting in the platform data The patch adds the MICBIAS VDD setting in the platform data. It can be set to 1V8 or 3V3 in the MICBIAS VDD. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown diff --git a/include/sound/rt5677.h b/include/sound/rt5677.h index d9eb7d8..a620704 100644 --- a/include/sound/rt5677.h +++ b/include/sound/rt5677.h @@ -37,6 +37,9 @@ struct rt5677_platform_data { OFF, GPIO4, GPIO5 and GPIO6 respectively */ unsigned int jd2_gpio; unsigned int jd3_gpio; + + /* Set MICBIAS1 VDD 1v8 or 3v3 */ + bool micbias1_vdd_3v3; }; #endif diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index cf39fe6..3b32c3e 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -4912,6 +4912,11 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, RT5677_GPIO5_DIR_OUT); } + if (rt5677->pdata.micbias1_vdd_3v3) + regmap_update_bits(rt5677->regmap, RT5677_MICBIAS, + RT5677_MICBIAS1_CTRL_VDD_MASK, + RT5677_MICBIAS1_CTRL_VDD_3_3V); + rt5677_init_gpio(i2c); rt5677_init_irq(i2c); -- cgit v0.10.2 From 57b7068de5d0cca8ac6e21085b843c1bbd49d3f4 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Fri, 26 Dec 2014 20:19:38 +0300 Subject: ASoC: add xtensa xtfpga I2S interface and platform XTFPGA boards provides an audio subsystem that consists of TI CDCE706 clock synthesizer, I2S transmitter and TLV320AIC23 audio codec. I2S transmitter has MMIO-based interface that resembles that of the OpenCores I2S transmitter. I2S transmitter is always a master on I2S bus. There's no specialized audio DMA, sample data are transferred to I2S transmitter FIFO by CPU through memory-mapped queue interface. Signed-off-by: Max Filippov Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/cdns,xtfpga-i2s.txt b/Documentation/devicetree/bindings/sound/cdns,xtfpga-i2s.txt new file mode 100644 index 0000000..befd125 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cdns,xtfpga-i2s.txt @@ -0,0 +1,18 @@ +Bindings for I2S controller built into xtfpga Xtensa bitstreams. + +Required properties: +- compatible: shall be "cdns,xtfpga-i2s". +- reg: memory region (address and length) with device registers. +- interrupts: interrupt for the device. +- clocks: phandle to the clk used as master clock. I2S bus clock + is derived from it. + +Examples: + + i2s0: xtfpga-i2s@0d080000 { + #sound-dai-cells = <0>; + compatible = "cdns,xtfpga-i2s"; + reg = <0x0d080000 0x40>; + interrupts = <2 1>; + clocks = <&cdce706 4>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index ddb9ac8..f1eb40f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10628,6 +10628,7 @@ M: Max Filippov L: linux-xtensa@linux-xtensa.org S: Maintained F: drivers/spi/spi-xtensa-xtfpga.c +F: sound/soc/xtensa/xtfpga-i2s.c YAM DRIVER FOR AX.25 M: Jean-Paul Roubelat diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 7d5d644..dcc79aa 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -55,6 +55,7 @@ source "sound/soc/spear/Kconfig" source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" source "sound/soc/ux500/Kconfig" +source "sound/soc/xtensa/Kconfig" # Supported codecs source "sound/soc/codecs/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 865e090..5b3c8f6 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -36,3 +36,4 @@ obj-$(CONFIG_SND_SOC) += spear/ obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += txx9/ obj-$(CONFIG_SND_SOC) += ux500/ +obj-$(CONFIG_SND_SOC) += xtensa/ diff --git a/sound/soc/xtensa/Kconfig b/sound/soc/xtensa/Kconfig new file mode 100644 index 0000000..c201beb --- /dev/null +++ b/sound/soc/xtensa/Kconfig @@ -0,0 +1,7 @@ +config SND_SOC_XTFPGA_I2S + tristate "XTFPGA I2S master" + select REGMAP_MMIO + help + Say Y or M if you want to add support for codecs attached to the + I2S interface on XTFPGA daughter board. You will also need to select + the drivers for the rest of XTFPGA audio subsystem. diff --git a/sound/soc/xtensa/Makefile b/sound/soc/xtensa/Makefile new file mode 100644 index 0000000..15efbf9 --- /dev/null +++ b/sound/soc/xtensa/Makefile @@ -0,0 +1,3 @@ +snd-soc-xtfpga-i2s-objs := xtfpga-i2s.o + +obj-$(CONFIG_SND_SOC_XTFPGA_I2S) += snd-soc-xtfpga-i2s.o diff --git a/sound/soc/xtensa/xtfpga-i2s.c b/sound/soc/xtensa/xtfpga-i2s.c new file mode 100644 index 0000000..1cfb19e --- /dev/null +++ b/sound/soc/xtensa/xtfpga-i2s.c @@ -0,0 +1,675 @@ +/* + * Xtfpga I2S controller driver + * + * Copyright (c) 2014 Cadence Design Systems 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 +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "xtfpga-i2s" + +#define XTFPGA_I2S_VERSION 0x00 +#define XTFPGA_I2S_CONFIG 0x04 +#define XTFPGA_I2S_INT_MASK 0x08 +#define XTFPGA_I2S_INT_STATUS 0x0c +#define XTFPGA_I2S_CHAN0_DATA 0x10 +#define XTFPGA_I2S_CHAN1_DATA 0x14 +#define XTFPGA_I2S_CHAN2_DATA 0x18 +#define XTFPGA_I2S_CHAN3_DATA 0x1c + +#define XTFPGA_I2S_CONFIG_TX_ENABLE 0x1 +#define XTFPGA_I2S_CONFIG_INT_ENABLE 0x2 +#define XTFPGA_I2S_CONFIG_LEFT 0x4 +#define XTFPGA_I2S_CONFIG_RATIO_BASE 8 +#define XTFPGA_I2S_CONFIG_RATIO_MASK 0x0000ff00 +#define XTFPGA_I2S_CONFIG_RES_BASE 16 +#define XTFPGA_I2S_CONFIG_RES_MASK 0x003f0000 +#define XTFPGA_I2S_CONFIG_LEVEL_BASE 24 +#define XTFPGA_I2S_CONFIG_LEVEL_MASK 0x0f000000 +#define XTFPGA_I2S_CONFIG_CHANNEL_BASE 28 + +#define XTFPGA_I2S_INT_UNDERRUN 0x1 +#define XTFPGA_I2S_INT_LEVEL 0x2 +#define XTFPGA_I2S_INT_VALID 0x3 + +#define XTFPGA_I2S_FIFO_SIZE 8192 + +/* + * I2S controller operation: + * + * Enabling TX: output 1 period of zeros (starting with left channel) + * and then queued data. + * + * Level status and interrupt: whenever FIFO level is below FIFO trigger, + * level status is 1 and an IRQ is asserted (if enabled). + * + * Underrun status and interrupt: whenever FIFO is empty, underrun status + * is 1 and an IRQ is asserted (if enabled). + */ +struct xtfpga_i2s { + struct device *dev; + struct clk *clk; + struct regmap *regmap; + void __iomem *regs; + + /* current playback substream. NULL if not playing. + * + * Access to that field is synchronized between the interrupt handler + * and userspace through RCU. + * + * Interrupt handler (threaded part) does PIO on substream data in RCU + * read-side critical section. Trigger callback sets and clears the + * pointer when the playback is started and stopped with + * rcu_assign_pointer. When userspace is about to free the playback + * stream in the pcm_close callback it synchronizes with the interrupt + * handler by means of synchronize_rcu call. + */ + struct snd_pcm_substream *tx_substream; + unsigned (*tx_fn)(struct xtfpga_i2s *i2s, + struct snd_pcm_runtime *runtime, + unsigned tx_ptr); + unsigned tx_ptr; /* next frame index in the sample buffer */ + + /* current fifo level estimate. + * Doesn't have to be perfectly accurate, but must be not less than + * the actual FIFO level in order to avoid stall on push attempt. + */ + unsigned tx_fifo_level; + + /* FIFO level at which level interrupt occurs */ + unsigned tx_fifo_low; + + /* maximal FIFO level */ + unsigned tx_fifo_high; +}; + +static bool xtfpga_i2s_wr_reg(struct device *dev, unsigned int reg) +{ + return reg >= XTFPGA_I2S_CONFIG; +} + +static bool xtfpga_i2s_rd_reg(struct device *dev, unsigned int reg) +{ + return reg < XTFPGA_I2S_CHAN0_DATA; +} + +static bool xtfpga_i2s_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == XTFPGA_I2S_INT_STATUS; +} + +static const struct regmap_config xtfpga_i2s_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = XTFPGA_I2S_CHAN3_DATA, + .writeable_reg = xtfpga_i2s_wr_reg, + .readable_reg = xtfpga_i2s_rd_reg, + .volatile_reg = xtfpga_i2s_volatile_reg, + .cache_type = REGCACHE_FLAT, +}; + +/* Generate functions that do PIO from TX DMA area to FIFO for all supported + * stream formats. + * Functions will be called xtfpga_pcm_tx_x, e.g. + * xtfpga_pcm_tx_2x16 for 16-bit stereo. + * + * FIFO consists of 32-bit words, one word per channel, always 2 channels. + * If I2S interface is configured with smaller sample resolution, only + * the LSB of each word is used. + */ +#define xtfpga_pcm_tx_fn(channels, sample_bits) \ +static unsigned xtfpga_pcm_tx_##channels##x##sample_bits( \ + struct xtfpga_i2s *i2s, struct snd_pcm_runtime *runtime, \ + unsigned tx_ptr) \ +{ \ + const u##sample_bits (*p)[channels] = \ + (void *)runtime->dma_area; \ +\ + for (; i2s->tx_fifo_level < i2s->tx_fifo_high; \ + i2s->tx_fifo_level += 2) { \ + iowrite32(p[tx_ptr][0], \ + i2s->regs + XTFPGA_I2S_CHAN0_DATA); \ + iowrite32(p[tx_ptr][channels - 1], \ + i2s->regs + XTFPGA_I2S_CHAN0_DATA); \ + if (++tx_ptr >= runtime->buffer_size) \ + tx_ptr = 0; \ + } \ + return tx_ptr; \ +} + +xtfpga_pcm_tx_fn(1, 16) +xtfpga_pcm_tx_fn(2, 16) +xtfpga_pcm_tx_fn(1, 32) +xtfpga_pcm_tx_fn(2, 32) + +#undef xtfpga_pcm_tx_fn + +static bool xtfpga_pcm_push_tx(struct xtfpga_i2s *i2s) +{ + struct snd_pcm_substream *tx_substream; + bool tx_active; + + rcu_read_lock(); + tx_substream = rcu_dereference(i2s->tx_substream); + tx_active = tx_substream && snd_pcm_running(tx_substream); + if (tx_active) { + unsigned tx_ptr = ACCESS_ONCE(i2s->tx_ptr); + unsigned new_tx_ptr = i2s->tx_fn(i2s, tx_substream->runtime, + tx_ptr); + + cmpxchg(&i2s->tx_ptr, tx_ptr, new_tx_ptr); + } + rcu_read_unlock(); + + return tx_active; +} + +static void xtfpga_pcm_refill_fifo(struct xtfpga_i2s *i2s) +{ + unsigned int_status; + unsigned i; + + regmap_read(i2s->regmap, XTFPGA_I2S_INT_STATUS, + &int_status); + + for (i = 0; i < 2; ++i) { + bool tx_active = xtfpga_pcm_push_tx(i2s); + + regmap_write(i2s->regmap, XTFPGA_I2S_INT_STATUS, + XTFPGA_I2S_INT_VALID); + if (tx_active) + regmap_read(i2s->regmap, XTFPGA_I2S_INT_STATUS, + &int_status); + + if (!tx_active || + !(int_status & XTFPGA_I2S_INT_LEVEL)) + break; + + /* After the push the level IRQ is still asserted, + * means FIFO level is below tx_fifo_low. Estimate + * it as tx_fifo_low. + */ + i2s->tx_fifo_level = i2s->tx_fifo_low; + } + + if (!(int_status & XTFPGA_I2S_INT_LEVEL)) + regmap_write(i2s->regmap, XTFPGA_I2S_INT_MASK, + XTFPGA_I2S_INT_VALID); + else if (!(int_status & XTFPGA_I2S_INT_UNDERRUN)) + regmap_write(i2s->regmap, XTFPGA_I2S_INT_MASK, + XTFPGA_I2S_INT_UNDERRUN); + + if (!(int_status & XTFPGA_I2S_INT_UNDERRUN)) + regmap_update_bits(i2s->regmap, XTFPGA_I2S_CONFIG, + XTFPGA_I2S_CONFIG_INT_ENABLE | + XTFPGA_I2S_CONFIG_TX_ENABLE, + XTFPGA_I2S_CONFIG_INT_ENABLE | + XTFPGA_I2S_CONFIG_TX_ENABLE); + else + regmap_update_bits(i2s->regmap, XTFPGA_I2S_CONFIG, + XTFPGA_I2S_CONFIG_INT_ENABLE | + XTFPGA_I2S_CONFIG_TX_ENABLE, 0); +} + +static irqreturn_t xtfpga_i2s_threaded_irq_handler(int irq, void *dev_id) +{ + struct xtfpga_i2s *i2s = dev_id; + struct snd_pcm_substream *tx_substream; + unsigned config, int_status, int_mask; + + regmap_read(i2s->regmap, XTFPGA_I2S_CONFIG, &config); + regmap_read(i2s->regmap, XTFPGA_I2S_INT_MASK, &int_mask); + regmap_read(i2s->regmap, XTFPGA_I2S_INT_STATUS, &int_status); + + if (!(config & XTFPGA_I2S_CONFIG_INT_ENABLE) || + !(int_status & int_mask & XTFPGA_I2S_INT_VALID)) + return IRQ_NONE; + + /* Update FIFO level estimate in accordance with interrupt status + * register. + */ + if (int_status & XTFPGA_I2S_INT_UNDERRUN) { + i2s->tx_fifo_level = 0; + regmap_update_bits(i2s->regmap, XTFPGA_I2S_CONFIG, + XTFPGA_I2S_CONFIG_TX_ENABLE, 0); + } else { + /* The FIFO isn't empty, but is below tx_fifo_low. Estimate + * it as tx_fifo_low. + */ + i2s->tx_fifo_level = i2s->tx_fifo_low; + } + + rcu_read_lock(); + tx_substream = rcu_dereference(i2s->tx_substream); + + if (tx_substream && snd_pcm_running(tx_substream)) { + snd_pcm_period_elapsed(tx_substream); + if (int_status & XTFPGA_I2S_INT_UNDERRUN) + dev_dbg_ratelimited(i2s->dev, "%s: underrun\n", + __func__); + } + rcu_read_unlock(); + + /* Refill FIFO, update allowed IRQ reasons, enable IRQ if FIFO is + * not empty. + */ + xtfpga_pcm_refill_fifo(i2s); + + return IRQ_HANDLED; +} + +static int xtfpga_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct xtfpga_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_set_dma_data(dai, substream, i2s); + return 0; +} + +static int xtfpga_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct xtfpga_i2s *i2s = snd_soc_dai_get_drvdata(dai); + unsigned srate = params_rate(params); + unsigned channels = params_channels(params); + unsigned period_size = params_period_size(params); + unsigned sample_size = snd_pcm_format_width(params_format(params)); + unsigned freq, ratio, level; + int err; + + regmap_update_bits(i2s->regmap, XTFPGA_I2S_CONFIG, + XTFPGA_I2S_CONFIG_RES_MASK, + sample_size << XTFPGA_I2S_CONFIG_RES_BASE); + + freq = 256 * srate; + err = clk_set_rate(i2s->clk, freq); + if (err < 0) + return err; + + /* ratio field of the config register controls MCLK->I2S clock + * derivation: I2S clock = MCLK / (2 * (ratio + 2)). + * + * So with MCLK = 256 * sample rate ratio is 0 for 32 bit stereo + * and 2 for 16 bit stereo. + */ + ratio = (freq - (srate * sample_size * 8)) / + (srate * sample_size * 4); + + regmap_update_bits(i2s->regmap, XTFPGA_I2S_CONFIG, + XTFPGA_I2S_CONFIG_RATIO_MASK, + ratio << XTFPGA_I2S_CONFIG_RATIO_BASE); + + i2s->tx_fifo_low = XTFPGA_I2S_FIFO_SIZE / 2; + + /* period_size * 2: FIFO always gets 2 samples per frame */ + for (level = 1; + i2s->tx_fifo_low / 2 >= period_size * 2 && + level < (XTFPGA_I2S_CONFIG_LEVEL_MASK >> + XTFPGA_I2S_CONFIG_LEVEL_BASE); ++level) + i2s->tx_fifo_low /= 2; + + i2s->tx_fifo_high = 2 * i2s->tx_fifo_low; + + regmap_update_bits(i2s->regmap, XTFPGA_I2S_CONFIG, + XTFPGA_I2S_CONFIG_LEVEL_MASK, + level << XTFPGA_I2S_CONFIG_LEVEL_BASE); + + dev_dbg(i2s->dev, + "%s srate: %u, channels: %u, sample_size: %u, period_size: %u\n", + __func__, srate, channels, sample_size, period_size); + dev_dbg(i2s->dev, "%s freq: %u, ratio: %u, level: %u\n", + __func__, freq, ratio, level); + + return 0; +} + +static int xtfpga_i2s_set_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) + return -EINVAL; + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) + return -EINVAL; + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) + return -EINVAL; + + return 0; +} + +/* PCM */ + +static const struct snd_pcm_hardware xtfpga_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 2, + .period_bytes_min = 2, + .period_bytes_max = XTFPGA_I2S_FIFO_SIZE / 2 * 8, + .periods_min = 2, + .periods_max = XTFPGA_I2S_FIFO_SIZE * 8 / 2, + .buffer_bytes_max = XTFPGA_I2S_FIFO_SIZE * 8, + .fifo_size = 16, +}; + +static int xtfpga_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + void *p; + + snd_soc_set_runtime_hwparams(substream, &xtfpga_pcm_hardware); + p = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + runtime->private_data = p; + + return 0; +} + +static int xtfpga_pcm_close(struct snd_pcm_substream *substream) +{ + synchronize_rcu(); + return 0; +} + +static int xtfpga_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + int ret; + struct snd_pcm_runtime *runtime = substream->runtime; + struct xtfpga_i2s *i2s = runtime->private_data; + unsigned channels = params_channels(hw_params); + + switch (channels) { + case 1: + case 2: + break; + + default: + return -EINVAL; + + } + + switch (params_format(hw_params)) { + case SNDRV_PCM_FORMAT_S16_LE: + i2s->tx_fn = (channels == 1) ? + xtfpga_pcm_tx_1x16 : + xtfpga_pcm_tx_2x16; + break; + + case SNDRV_PCM_FORMAT_S32_LE: + i2s->tx_fn = (channels == 1) ? + xtfpga_pcm_tx_1x32 : + xtfpga_pcm_tx_2x32; + break; + + default: + return -EINVAL; + } + + ret = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + return ret; +} + +static int xtfpga_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct xtfpga_i2s *i2s = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ACCESS_ONCE(i2s->tx_ptr) = 0; + rcu_assign_pointer(i2s->tx_substream, substream); + xtfpga_pcm_refill_fifo(i2s); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + rcu_assign_pointer(i2s->tx_substream, NULL); + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} + +static snd_pcm_uframes_t xtfpga_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct xtfpga_i2s *i2s = runtime->private_data; + snd_pcm_uframes_t pos = ACCESS_ONCE(i2s->tx_ptr); + + return pos < runtime->buffer_size ? pos : 0; +} + +static int xtfpga_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + size_t size = xtfpga_pcm_hardware.buffer_bytes_max; + + return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, + SNDRV_DMA_TYPE_DEV, + card->dev, size, size); +} + +static void xtfpga_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static const struct snd_pcm_ops xtfpga_pcm_ops = { + .open = xtfpga_pcm_open, + .close = xtfpga_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = xtfpga_pcm_hw_params, + .trigger = xtfpga_pcm_trigger, + .pointer = xtfpga_pcm_pointer, +}; + +static const struct snd_soc_platform_driver xtfpga_soc_platform = { + .pcm_new = xtfpga_pcm_new, + .pcm_free = xtfpga_pcm_free, + .ops = &xtfpga_pcm_ops, +}; + +static const struct snd_soc_component_driver xtfpga_i2s_component = { + .name = DRV_NAME, +}; + +static const struct snd_soc_dai_ops xtfpga_i2s_dai_ops = { + .startup = xtfpga_i2s_startup, + .hw_params = xtfpga_i2s_hw_params, + .set_fmt = xtfpga_i2s_set_fmt, +}; + +static struct snd_soc_dai_driver xtfpga_i2s_dai[] = { + { + .name = "xtfpga-i2s", + .id = 0, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &xtfpga_i2s_dai_ops, + }, +}; + +static int xtfpga_i2s_runtime_suspend(struct device *dev) +{ + struct xtfpga_i2s *i2s = dev_get_drvdata(dev); + + clk_disable_unprepare(i2s->clk); + return 0; +} + +static int xtfpga_i2s_runtime_resume(struct device *dev) +{ + struct xtfpga_i2s *i2s = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(i2s->clk); + if (ret) { + dev_err(dev, "clk_prepare_enable failed: %d\n", ret); + return ret; + } + return 0; +} + +static int xtfpga_i2s_probe(struct platform_device *pdev) +{ + struct xtfpga_i2s *i2s; + struct resource *mem; + int err, irq; + + i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); + if (!i2s) { + err = -ENOMEM; + goto err; + } + platform_set_drvdata(pdev, i2s); + i2s->dev = &pdev->dev; + dev_dbg(&pdev->dev, "dev: %p, i2s: %p\n", &pdev->dev, i2s); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + i2s->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(i2s->regs)) { + err = PTR_ERR(i2s->regs); + goto err; + } + + i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->regs, + &xtfpga_i2s_regmap_config); + if (IS_ERR(i2s->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + err = PTR_ERR(i2s->regmap); + goto err; + } + + i2s->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(i2s->clk)) { + dev_err(&pdev->dev, "couldn't get clock\n"); + err = PTR_ERR(i2s->clk); + goto err; + } + + regmap_write(i2s->regmap, XTFPGA_I2S_CONFIG, + (0x1 << XTFPGA_I2S_CONFIG_CHANNEL_BASE)); + regmap_write(i2s->regmap, XTFPGA_I2S_INT_STATUS, XTFPGA_I2S_INT_VALID); + regmap_write(i2s->regmap, XTFPGA_I2S_INT_MASK, XTFPGA_I2S_INT_UNDERRUN); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "No IRQ resource\n"); + err = irq; + goto err; + } + err = devm_request_threaded_irq(&pdev->dev, irq, NULL, + xtfpga_i2s_threaded_irq_handler, + IRQF_SHARED | IRQF_ONESHOT, + pdev->name, i2s); + if (err < 0) { + dev_err(&pdev->dev, "request_irq failed\n"); + goto err; + } + + err = snd_soc_register_platform(&pdev->dev, &xtfpga_soc_platform); + if (err < 0) { + dev_err(&pdev->dev, "couldn't register platform\n"); + goto err; + } + err = devm_snd_soc_register_component(&pdev->dev, + &xtfpga_i2s_component, + xtfpga_i2s_dai, + ARRAY_SIZE(xtfpga_i2s_dai)); + if (err < 0) { + dev_err(&pdev->dev, "couldn't register component\n"); + goto err_unregister_platform; + } + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + err = xtfpga_i2s_runtime_resume(&pdev->dev); + if (err) + goto err_pm_disable; + } + return 0; + +err_pm_disable: + pm_runtime_disable(&pdev->dev); +err_unregister_platform: + snd_soc_unregister_platform(&pdev->dev); +err: + dev_err(&pdev->dev, "%s: err = %d\n", __func__, err); + return err; +} + +static int xtfpga_i2s_remove(struct platform_device *pdev) +{ + struct xtfpga_i2s *i2s = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_platform(&pdev->dev); + if (i2s->regmap && !IS_ERR(i2s->regmap)) { + regmap_write(i2s->regmap, XTFPGA_I2S_CONFIG, 0); + regmap_write(i2s->regmap, XTFPGA_I2S_INT_MASK, 0); + regmap_write(i2s->regmap, XTFPGA_I2S_INT_STATUS, + XTFPGA_I2S_INT_VALID); + } + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + xtfpga_i2s_runtime_suspend(&pdev->dev); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id xtfpga_i2s_of_match[] = { + { .compatible = "cdns,xtfpga-i2s", }, + {}, +}; +MODULE_DEVICE_TABLE(of, xtfpga_i2s_of_match); +#endif + +static const struct dev_pm_ops xtfpga_i2s_pm_ops = { + SET_RUNTIME_PM_OPS(xtfpga_i2s_runtime_suspend, + xtfpga_i2s_runtime_resume, NULL) +}; + +static struct platform_driver xtfpga_i2s_driver = { + .probe = xtfpga_i2s_probe, + .remove = xtfpga_i2s_remove, + .driver = { + .name = "xtfpga-i2s", + .of_match_table = of_match_ptr(xtfpga_i2s_of_match), + .pm = &xtfpga_i2s_pm_ops, + }, +}; + +module_platform_driver(xtfpga_i2s_driver); + +MODULE_AUTHOR("Max Filippov "); +MODULE_DESCRIPTION("xtfpga I2S controller driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 1b3de223385d6bf2ab9bf2e9e80aebb26fedd426 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 12:48:41 +0100 Subject: regulator: Copy config passed during registration Copy the 'regulator_config' structure passed to regulator_register() function so the driver could safely modify it after parsing init data. The driver may want to change the config as a result of specific init data parsed by regulator core (e.g. when core handled parsing device tree). Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index e225711..c13b557 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3581,20 +3581,21 @@ static void rdev_init_debugfs(struct regulator_dev *rdev) */ struct regulator_dev * regulator_register(const struct regulator_desc *regulator_desc, - const struct regulator_config *config) + const struct regulator_config *cfg) { const struct regulation_constraints *constraints = NULL; const struct regulator_init_data *init_data; + struct regulator_config *config = NULL; static atomic_t regulator_no = ATOMIC_INIT(0); struct regulator_dev *rdev; struct device *dev; int ret, i; const char *supply = NULL; - if (regulator_desc == NULL || config == NULL) + if (regulator_desc == NULL || cfg == NULL) return ERR_PTR(-EINVAL); - dev = config->dev; + dev = cfg->dev; WARN_ON(!dev); if (regulator_desc->name == NULL || regulator_desc->ops == NULL) @@ -3624,6 +3625,16 @@ regulator_register(const struct regulator_desc *regulator_desc, if (rdev == NULL) return ERR_PTR(-ENOMEM); + /* + * Duplicate the config so the driver could override it after + * parsing init data. + */ + config = kmemdup(cfg, sizeof(*cfg), GFP_KERNEL); + if (config == NULL) { + kfree(rdev); + return ERR_PTR(-ENOMEM); + } + init_data = regulator_of_get_init_data(dev, regulator_desc, &rdev->dev.of_node); if (!init_data) { @@ -3752,6 +3763,7 @@ add_dev: rdev_init_debugfs(rdev); out: mutex_unlock(®ulator_list_mutex); + kfree(config); return rdev; unset_supplies: -- cgit v0.10.2 From 3307e9025d29105ecc5fa1144508715cdddba195 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 12:48:43 +0100 Subject: regulator: max77686: Add GPIO control Add enable control over GPIO for regulators supporting this: LDO20, LDO21, LDO22, buck8 and buck9. This is needed for proper (and full) configuration of the Maxim 77686 PMIC without creating redundant 'regulator-fixed' entries. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index 10d2062..15fb141 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,11 @@ #define MAX77686_DVS_UVSTEP 12500 /* + * Value for configuring buck[89] and LDO{20,21,22} as GPIO control. + * It is the same as 'off' for other regulators. + */ +#define MAX77686_GPIO_CONTROL 0x0 +/* * Values used for configuring LDOs and bucks. * Forcing low power mode: LDO1, 3-5, 9, 13, 17-26 */ @@ -82,6 +88,8 @@ enum max77686_ramp_rate { }; struct max77686_data { + u64 gpio_enabled:MAX77686_REGULATORS; + /* Array indexed by regulator id */ unsigned int opmode[MAX77686_REGULATORS]; }; @@ -100,6 +108,26 @@ static unsigned int max77686_get_opmode_shift(int id) } } +/* + * When regulator is configured for GPIO control then it + * replaces "normal" mode. Any change from low power mode to normal + * should actually change to GPIO control. + * Map normal mode to proper value for such regulators. + */ +static unsigned int max77686_map_normal_mode(struct max77686_data *max77686, + int id) +{ + switch (id) { + case MAX77686_BUCK8: + case MAX77686_BUCK9: + case MAX77686_LDO20 ... MAX77686_LDO22: + if (max77686->gpio_enabled & (1 << id)) + return MAX77686_GPIO_CONTROL; + } + + return MAX77686_NORMAL; +} + /* Some BUCKs and LDOs supports Normal[ON/OFF] mode during suspend */ static int max77686_set_suspend_disable(struct regulator_dev *rdev) { @@ -136,7 +164,7 @@ static int max77686_set_suspend_mode(struct regulator_dev *rdev, val = MAX77686_LDO_LOWPOWER_PWRREQ; break; case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */ - val = MAX77686_NORMAL; + val = max77686_map_normal_mode(max77686, id); break; default: pr_warn("%s: regulator_suspend_mode : 0x%x not supported\n", @@ -160,7 +188,7 @@ static int max77686_ldo_set_suspend_mode(struct regulator_dev *rdev, { unsigned int val; struct max77686_data *max77686 = rdev_get_drvdata(rdev); - int ret; + int ret, id = rdev_get_id(rdev); switch (mode) { case REGULATOR_MODE_STANDBY: /* switch off */ @@ -170,7 +198,7 @@ static int max77686_ldo_set_suspend_mode(struct regulator_dev *rdev, val = MAX77686_LDO_LOWPOWER_PWRREQ; break; case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */ - val = MAX77686_NORMAL; + val = max77686_map_normal_mode(max77686, id); break; default: pr_warn("%s: regulator_suspend_mode : 0x%x not supported\n", @@ -184,7 +212,7 @@ static int max77686_ldo_set_suspend_mode(struct regulator_dev *rdev, if (ret) return ret; - max77686->opmode[rdev_get_id(rdev)] = val; + max77686->opmode[id] = val; return 0; } @@ -197,7 +225,7 @@ static int max77686_enable(struct regulator_dev *rdev) shift = max77686_get_opmode_shift(id); if (max77686->opmode[id] == MAX77686_OFF_PWRREQ) - max77686->opmode[id] = MAX77686_NORMAL; + max77686->opmode[id] = max77686_map_normal_mode(max77686, id); return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, rdev->desc->enable_mask, @@ -229,6 +257,36 @@ static int max77686_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) MAX77686_RAMP_RATE_MASK, ramp_value << 6); } +static int max77686_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct max77686_data *max77686 = config->driver_data; + + switch (desc->id) { + case MAX77686_BUCK8: + case MAX77686_BUCK9: + case MAX77686_LDO20 ... MAX77686_LDO22: + config->ena_gpio = of_get_named_gpio(np, + "maxim,ena-gpios", 0); + config->ena_gpio_flags = GPIOF_OUT_INIT_HIGH; + config->ena_gpio_initialized = true; + break; + default: + return 0; + } + + if (gpio_is_valid(config->ena_gpio)) { + max77686->gpio_enabled |= (1 << desc->id); + + return regmap_update_bits(config->regmap, desc->enable_reg, + desc->enable_mask, + MAX77686_GPIO_CONTROL); + } + + return 0; +} + static struct regulator_ops max77686_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, @@ -283,6 +341,7 @@ static struct regulator_ops max77686_buck_dvs_ops = { .name = "LDO"#num, \ .of_match = of_match_ptr("LDO"#num), \ .regulators_node = of_match_ptr("voltage-regulators"), \ + .of_parse_cb = max77686_of_parse_cb, \ .id = MAX77686_LDO##num, \ .ops = &max77686_ops, \ .type = REGULATOR_VOLTAGE, \ @@ -355,6 +414,7 @@ static struct regulator_ops max77686_buck_dvs_ops = { .name = "BUCK"#num, \ .of_match = of_match_ptr("BUCK"#num), \ .regulators_node = of_match_ptr("voltage-regulators"), \ + .of_parse_cb = max77686_of_parse_cb, \ .id = MAX77686_BUCK##num, \ .ops = &max77686_ops, \ .type = REGULATOR_VOLTAGE, \ -- cgit v0.10.2 From bfa21a0dfe6915dc85953b5d40ea9dae5fdf205f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 12:48:42 +0100 Subject: regulator: Allow parsing custom properties when using simplified DT parsing When drivers use simplified DT parsing method (they provide 'regulator_desc.of_match') they still may want to parse custom properties for some of the regulators. For example some of the regulators support GPIO enable control. Add a driver-supplied callback for such case. This way the regulator core parses common bindings offloading a lot of code from drivers and still custom properties may be used. The callback, called for each parsed regulator, may modify the 'regulator_config' initially passed to regulator_register(). Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index c13b557..5fae8ca 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3635,7 +3635,7 @@ regulator_register(const struct regulator_desc *regulator_desc, return ERR_PTR(-ENOMEM); } - init_data = regulator_of_get_init_data(dev, regulator_desc, + init_data = regulator_of_get_init_data(dev, regulator_desc, config, &rdev->dev.of_node); if (!init_data) { init_data = config->init_data; diff --git a/drivers/regulator/internal.h b/drivers/regulator/internal.h index 80ba2a3..c74ac87 100644 --- a/drivers/regulator/internal.h +++ b/drivers/regulator/internal.h @@ -38,11 +38,13 @@ struct regulator { #ifdef CONFIG_OF struct regulator_init_data *regulator_of_get_init_data(struct device *dev, const struct regulator_desc *desc, + struct regulator_config *config, struct device_node **node); #else static inline struct regulator_init_data * regulator_of_get_init_data(struct device *dev, const struct regulator_desc *desc, + struct regulator_config *config, struct device_node **node) { return NULL; diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 91eaaf0..24e812c 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -270,6 +270,7 @@ EXPORT_SYMBOL_GPL(of_regulator_match); struct regulator_init_data *regulator_of_get_init_data(struct device *dev, const struct regulator_desc *desc, + struct regulator_config *config, struct device_node **node) { struct device_node *search, *child; @@ -307,6 +308,16 @@ struct regulator_init_data *regulator_of_get_init_data(struct device *dev, break; } + if (desc->of_parse_cb) { + if (desc->of_parse_cb(child, desc, config)) { + dev_err(dev, + "driver callback failed to parse DT for regulator %s\n", + child->name); + init_data = NULL; + break; + } + } + of_node_get(child); *node = child; break; diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 5f1e9ca..d4ad5b5 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -21,6 +21,7 @@ struct regmap; struct regulator_dev; +struct regulator_config; struct regulator_init_data; struct regulator_enable_gpio; @@ -205,6 +206,15 @@ enum regulator_type { * @supply_name: Identifying the regulator supply * @of_match: Name used to identify regulator in DT. * @regulators_node: Name of node containing regulator definitions in DT. + * @of_parse_cb: Optional callback called only if of_match is present. + * Will be called for each regulator parsed from DT, during + * init_data parsing. + * The regulator_config passed as argument to the callback will + * be a copy of config passed to regulator_register, valid only + * for this particular call. Callback may freely change the + * config but it cannot store it for later usage. + * Callback should return 0 on success or negative ERRNO + * indicating failure. * @id: Numerical identifier for the regulator. * @ops: Regulator operations table. * @irq: Interrupt number for the regulator. @@ -251,6 +261,9 @@ struct regulator_desc { const char *supply_name; const char *of_match; const char *regulators_node; + int (*of_parse_cb)(struct device_node *, + const struct regulator_desc *, + struct regulator_config *); int id; bool continuous_voltage_range; unsigned n_voltages; -- cgit v0.10.2 From 64d3d25c9b1bc06f3bd323d3b769dbdbcb616462 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 12:48:44 +0100 Subject: regulator: max77686: Document gpio properties Document usage of maxim,ena-gpios properties which turn on external/GPIO control over regulator. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/mfd/max77686.txt b/Documentation/devicetree/bindings/mfd/max77686.txt index 75fdfaf..e39f0bc 100644 --- a/Documentation/devicetree/bindings/mfd/max77686.txt +++ b/Documentation/devicetree/bindings/mfd/max77686.txt @@ -39,6 +39,12 @@ to get matched with their hardware counterparts as follow: -BUCKn : 1-4. Use standard regulator bindings for it ('regulator-off-in-suspend'). + LDO20, LDO21, LDO22, BUCK8 and BUCK9 can be configured to GPIO enable + control. To turn this feature on this property must be added to the regulator + sub-node: + - maxim,ena-gpios : one GPIO specifier enable control (the gpio + flags are actually ignored and always + ACTIVE_HIGH is used) Example: @@ -65,4 +71,12 @@ Example: regulator-always-on; regulator-boot-on; }; + + buck9_reg { + regulator-compatible = "BUCK9"; + regulator-name = "CAM_ISP_CORE_1.2V"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1200000>; + maxim,ena-gpios = <&gpm0 3 GPIO_ACTIVE_HIGH>; + }; } -- cgit v0.10.2 From 4369a69ec6ab86821352bd753c68af5880f87956 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 8 Jan 2015 10:46:33 -0500 Subject: drm/radeon: add a dpm quirk list Disable dpm on certain problematic boards rather than disabling dpm for the entire chip family since most boards work fine. https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1386534 https://bugzilla.kernel.org/show_bug.cgi?id=83731 Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 32522cc..f7da8fe 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1287,8 +1287,39 @@ dpm_failed: return ret; } +struct radeon_dpm_quirk { + u32 chip_vendor; + u32 chip_device; + u32 subsys_vendor; + u32 subsys_device; +}; + +/* cards with dpm stability problems */ +static struct radeon_dpm_quirk radeon_dpm_quirk_list[] = { + /* TURKS - https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1386534 */ + { PCI_VENDOR_ID_ATI, 0x6759, 0x1682, 0x3195 }, + /* TURKS - https://bugzilla.kernel.org/show_bug.cgi?id=83731 */ + { PCI_VENDOR_ID_ATI, 0x6840, 0x1179, 0xfb81 }, + { 0, 0, 0, 0 }, +}; + int radeon_pm_init(struct radeon_device *rdev) { + struct radeon_dpm_quirk *p = radeon_dpm_quirk_list; + bool disable_dpm = false; + + /* Apply dpm quirks */ + while (p && p->chip_device != 0) { + if (rdev->pdev->vendor == p->chip_vendor && + rdev->pdev->device == p->chip_device && + rdev->pdev->subsystem_vendor == p->subsys_vendor && + rdev->pdev->subsystem_device == p->subsys_device) { + disable_dpm = true; + break; + } + ++p; + } + /* enable dpm on rv6xx+ */ switch (rdev->family) { case CHIP_RV610: @@ -1344,6 +1375,8 @@ int radeon_pm_init(struct radeon_device *rdev) (!(rdev->flags & RADEON_IS_IGP)) && (!rdev->smc_fw)) rdev->pm.pm_method = PM_METHOD_PROFILE; + else if (disable_dpm && (radeon_dpm == -1)) + rdev->pm.pm_method = PM_METHOD_PROFILE; else if (radeon_dpm == 0) rdev->pm.pm_method = PM_METHOD_PROFILE; else -- cgit v0.10.2 From 810aa0918b2b032684c8cad13f73d6ba37ad11c0 Mon Sep 17 00:00:00 2001 From: Sam hung Date: Thu, 8 Jan 2015 13:22:43 -0800 Subject: Input: elantech - support new ICs types for version 4 This change allows the driver to recognize newer Elantech touchpads. Cc: stable@vger.kernel.org Signed-off-by: Yi ju Hong Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index f2b9780..77ecf6d 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1520,6 +1520,8 @@ static int elantech_set_properties(struct elantech_data *etd) case 7: case 8: case 9: + case 10: + case 13: etd->hw_version = 4; break; default: -- cgit v0.10.2 From 5ab17145708e6ad4582b0372fb3a171be3379293 Mon Sep 17 00:00:00 2001 From: Anshul Garg Date: Thu, 8 Jan 2015 13:41:24 -0800 Subject: Input: small tweak to autorepeat handling If a device does not support autorepeat or does not emit any key events we should not be scanning all events in a packet to decide if we should start or stop autorepeat function. Signed-off-by: Anshul Garg Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/input.c b/drivers/input/input.c index 213e3a1..26199ab 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -152,12 +152,14 @@ static void input_pass_values(struct input_dev *dev, add_input_randomness(vals->type, vals->code, vals->value); /* trigger auto repeat for key events */ - for (v = vals; v != vals + count; v++) { - if (v->type == EV_KEY && v->value != 2) { - if (v->value) - input_start_autorepeat(dev, v->code); - else - input_stop_autorepeat(dev); + if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) { + for (v = vals; v != vals + count; v++) { + if (v->type == EV_KEY && v->value != 2) { + if (v->value) + input_start_autorepeat(dev, v->code); + else + input_stop_autorepeat(dev); + } } } } -- cgit v0.10.2 From 2c50ad340c246b7f58f2d916006afe2d85d60698 Mon Sep 17 00:00:00 2001 From: Anshul Garg Date: Thu, 8 Jan 2015 13:47:37 -0800 Subject: Input: do not try to filter out events if handler is not a filter If given input handler is not a filter there is no point is iterating list of events in a packet to see if some of them need to be filtered out. Signed-off-by: Anshul Garg Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/input.c b/drivers/input/input.c index 26199ab..cc357f1 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -100,23 +100,24 @@ static unsigned int input_to_handler(struct input_handle *handle, struct input_value *end = vals; struct input_value *v; - for (v = vals; v != vals + count; v++) { - if (handler->filter && - handler->filter(handle, v->type, v->code, v->value)) - continue; - if (end != v) - *end = *v; - end++; + if (handler->filter) { + for (v = vals; v != vals + count; v++) { + if (handler->filter(handle, v->type, v->code, v->value)) + continue; + if (end != v) + *end = *v; + end++; + } + count = end - vals; } - count = end - vals; if (!count) return 0; if (handler->events) handler->events(handle, vals, count); else if (handler->event) - for (v = vals; v != end; v++) + for (v = vals; v != vals + count; v++) handler->event(handle, v->type, v->code, v->value); return count; @@ -143,8 +144,11 @@ static void input_pass_values(struct input_dev *dev, count = input_to_handler(handle, vals, count); } else { list_for_each_entry_rcu(handle, &dev->h_list, d_node) - if (handle->open) + if (handle->open) { count = input_to_handler(handle, vals, count); + if (!count) + break; + } } rcu_read_unlock(); -- cgit v0.10.2 From 5b6c26a9f6437c4467f5d7cbf0313a7ae2be86b1 Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Mon, 29 Dec 2014 11:20:54 -0800 Subject: Input: add driver for AXP20x Power Enable Key This change adds support for the Power Enable Key found on MFD AXP202 and AXP209. Besides the basic support for the button, the driver adds two entries in sysfs to configure the time delay for power on/off. Signed-off-by: Carlo Caione Acked-by: Dmitry Torokhov [wens@csie.org: made axp20x_pek_remove() static; removed driver owner field; fixed path for sysfs entries] Signed-off-by: Chen-Yu Tsai Signed-off-by: Dmitry Torokhov diff --git a/Documentation/ABI/testing/sysfs-driver-input-axp-pek b/Documentation/ABI/testing/sysfs-driver-input-axp-pek new file mode 100644 index 0000000..a5e671b --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-input-axp-pek @@ -0,0 +1,11 @@ +What: /sys/class/input/input(x)/device/startup +Date: March 2014 +Contact: Carlo Caione +Description: Startup time in us. Board is powered on if the button is pressed + for more than + +What: /sys/class/input/input(x)/device/shutdown +Date: March 2014 +Contact: Carlo Caione +Description: Shutdown time in us. Board is powered off if the button is pressed + for more than diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 9591917..9e610c4 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -426,6 +426,17 @@ config INPUT_TPS65218_PWRBUTTON To compile this driver as a module, choose M here. The module will be called tps65218-pwrbutton. +config INPUT_AXP20X_PEK + tristate "X-Powers AXP20X power button driver" + depends on MFD_AXP20X + help + Say Y here if you want to enable power key reporting via the + AXP20X PMIC. + + To compile this driver as a module, choose M here. The module will + be called axp20x-pek. + + config INPUT_TWL4030_PWRBUTTON tristate "TWL4030 Power button Driver" depends on TWL4030_CORE diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 9a1083f..f8d9ea7 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o +obj-$(CONFIG_INPUT_AXP20X_PEK) += axp20x-pek.o obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c new file mode 100644 index 0000000..8dbd097 --- /dev/null +++ b/drivers/input/misc/axp20x-pek.c @@ -0,0 +1,282 @@ +/* + * axp20x power button driver. + * + * Copyright (C) 2013 Carlo Caione + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AXP20X_PEK_STARTUP_MASK (0xc0) +#define AXP20X_PEK_SHUTDOWN_MASK (0x03) + +struct axp20x_pek { + struct axp20x_dev *axp20x; + struct input_dev *input; + int irq_dbr; + int irq_dbf; +}; + +struct axp20x_time { + unsigned int time; + unsigned int idx; +}; + +static const struct axp20x_time startup_time[] = { + { .time = 128, .idx = 0 }, + { .time = 1000, .idx = 2 }, + { .time = 3000, .idx = 1 }, + { .time = 2000, .idx = 3 }, +}; + +static const struct axp20x_time shutdown_time[] = { + { .time = 4000, .idx = 0 }, + { .time = 6000, .idx = 1 }, + { .time = 8000, .idx = 2 }, + { .time = 10000, .idx = 3 }, +}; + +struct axp20x_pek_ext_attr { + const struct axp20x_time *p_time; + unsigned int mask; +}; + +static struct axp20x_pek_ext_attr axp20x_pek_startup_ext_attr = { + .p_time = startup_time, + .mask = AXP20X_PEK_STARTUP_MASK, +}; + +static struct axp20x_pek_ext_attr axp20x_pek_shutdown_ext_attr = { + .p_time = shutdown_time, + .mask = AXP20X_PEK_SHUTDOWN_MASK, +}; + +static struct axp20x_pek_ext_attr *get_axp_ext_attr(struct device_attribute *attr) +{ + return container_of(attr, struct dev_ext_attribute, attr)->var; +} + +static ssize_t axp20x_show_ext_attr(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); + struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr); + unsigned int val; + int ret, i; + + ret = regmap_read(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY, &val); + if (ret != 0) + return ret; + + val &= axp20x_ea->mask; + val >>= ffs(axp20x_ea->mask) - 1; + + for (i = 0; i < 4; i++) + if (val == axp20x_ea->p_time[i].idx) + val = axp20x_ea->p_time[i].time; + + return sprintf(buf, "%u\n", val); +} + +static ssize_t axp20x_store_ext_attr(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); + struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr); + char val_str[20]; + size_t len; + int ret, i; + unsigned int val, idx = 0; + unsigned int best_err = UINT_MAX; + + val_str[sizeof(val_str) - 1] = '\0'; + strncpy(val_str, buf, sizeof(val_str) - 1); + len = strlen(val_str); + + if (len && val_str[len - 1] == '\n') + val_str[len - 1] = '\0'; + + ret = kstrtouint(val_str, 10, &val); + if (ret) + return ret; + + for (i = 3; i >= 0; i--) { + unsigned int err; + + err = abs(axp20x_ea->p_time[i].time - val); + if (err < best_err) { + best_err = err; + idx = axp20x_ea->p_time[i].idx; + } + + if (!err) + break; + } + + idx <<= ffs(axp20x_ea->mask) - 1; + ret = regmap_update_bits(axp20x_pek->axp20x->regmap, + AXP20X_PEK_KEY, + axp20x_ea->mask, idx); + if (ret != 0) + return -EINVAL; + return count; +} + +static struct dev_ext_attribute axp20x_dev_attr_startup = { + .attr = __ATTR(startup, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr), + .var = &axp20x_pek_startup_ext_attr +}; + +static struct dev_ext_attribute axp20x_dev_attr_shutdown = { + .attr = __ATTR(shutdown, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr), + .var = &axp20x_pek_shutdown_ext_attr +}; + +static irqreturn_t axp20x_pek_irq(int irq, void *pwr) +{ + struct input_dev *idev = pwr; + struct axp20x_pek *axp20x_pek = input_get_drvdata(idev); + + if (irq == axp20x_pek->irq_dbr) + input_report_key(idev, KEY_POWER, true); + else if (irq == axp20x_pek->irq_dbf) + input_report_key(idev, KEY_POWER, false); + + input_sync(idev); + + return IRQ_HANDLED; +} + +static int axp20x_pek_probe(struct platform_device *pdev) +{ + struct axp20x_pek *axp20x_pek; + struct axp20x_dev *axp20x; + struct input_dev *idev; + int error; + + axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek), + GFP_KERNEL); + if (!axp20x_pek) + return -ENOMEM; + + axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent); + axp20x = axp20x_pek->axp20x; + + axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR"); + if (axp20x_pek->irq_dbr < 0) { + dev_err(&pdev->dev, "No IRQ for PEK_DBR, error=%d\n", + axp20x_pek->irq_dbr); + return axp20x_pek->irq_dbr; + } + axp20x_pek->irq_dbr = regmap_irq_get_virq(axp20x->regmap_irqc, + axp20x_pek->irq_dbr); + + axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF"); + if (axp20x_pek->irq_dbf < 0) { + dev_err(&pdev->dev, "No IRQ for PEK_DBF, error=%d\n", + axp20x_pek->irq_dbf); + return axp20x_pek->irq_dbf; + } + axp20x_pek->irq_dbf = regmap_irq_get_virq(axp20x->regmap_irqc, + axp20x_pek->irq_dbf); + + axp20x_pek->input = devm_input_allocate_device(&pdev->dev); + if (!axp20x_pek->input) + return -ENOMEM; + + idev = axp20x_pek->input; + + idev->name = "axp20x-pek"; + idev->phys = "m1kbd/input2"; + idev->dev.parent = &pdev->dev; + + input_set_capability(idev, EV_KEY, KEY_POWER); + + input_set_drvdata(idev, axp20x_pek); + + error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbr, + axp20x_pek_irq, 0, + "axp20x-pek-dbr", idev); + if (error < 0) { + dev_err(axp20x->dev, "Failed to request dbr IRQ#%d: %d\n", + axp20x_pek->irq_dbr, error); + + return error; + } + + error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbf, + axp20x_pek_irq, 0, + "axp20x-pek-dbf", idev); + if (error < 0) { + dev_err(axp20x->dev, "Failed to request dbf IRQ#%d: %d\n", + axp20x_pek->irq_dbf, error); + return error; + } + + error = device_create_file(&pdev->dev, &axp20x_dev_attr_startup.attr); + if (error) + return error; + + error = device_create_file(&pdev->dev, &axp20x_dev_attr_shutdown.attr); + if (error) + goto clear_startup_attr; + + error = input_register_device(idev); + if (error) { + dev_err(axp20x->dev, "Can't register input device: %d\n", + error); + goto clear_attr; + } + + platform_set_drvdata(pdev, axp20x_pek); + + return 0; + +clear_attr: + device_remove_file(&pdev->dev, &axp20x_dev_attr_shutdown.attr); + +clear_startup_attr: + device_remove_file(&pdev->dev, &axp20x_dev_attr_startup.attr); + + return error; +} + +static int axp20x_pek_remove(struct platform_device *pdev) +{ + device_remove_file(&pdev->dev, &axp20x_dev_attr_shutdown.attr); + device_remove_file(&pdev->dev, &axp20x_dev_attr_startup.attr); + + return 0; +} + +static struct platform_driver axp20x_pek_driver = { + .probe = axp20x_pek_probe, + .remove = axp20x_pek_remove, + .driver = { + .name = "axp20x-pek", + }, +}; +module_platform_driver(axp20x_pek_driver); + +MODULE_DESCRIPTION("axp20x Power Button"); +MODULE_AUTHOR("Carlo Caione "); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From b388de88308fde104fb6520dbf9e7b342fd5761a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 29 Dec 2014 15:13:06 -0800 Subject: Input: axp20x-pek - switch over to using attribute group Instead of registering device attributes individually let's use attribute groups and also devm_* infrastructure to ease cleanup. Tested-by: Chen-Yu Tsai Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c index 8dbd097..f1c8447 100644 --- a/drivers/input/misc/axp20x-pek.c +++ b/drivers/input/misc/axp20x-pek.c @@ -138,17 +138,28 @@ static ssize_t axp20x_store_ext_attr(struct device *dev, axp20x_ea->mask, idx); if (ret != 0) return -EINVAL; + return count; } static struct dev_ext_attribute axp20x_dev_attr_startup = { .attr = __ATTR(startup, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr), - .var = &axp20x_pek_startup_ext_attr + .var = &axp20x_pek_startup_ext_attr, }; static struct dev_ext_attribute axp20x_dev_attr_shutdown = { .attr = __ATTR(shutdown, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr), - .var = &axp20x_pek_shutdown_ext_attr + .var = &axp20x_pek_shutdown_ext_attr, +}; + +static struct attribute *axp20x_attributes[] = { + &axp20x_dev_attr_startup.attr.attr, + &axp20x_dev_attr_shutdown.attr.attr, + NULL, +}; + +static const struct attribute_group axp20x_attribute_group = { + .attrs = axp20x_attributes, }; static irqreturn_t axp20x_pek_irq(int irq, void *pwr) @@ -166,6 +177,13 @@ static irqreturn_t axp20x_pek_irq(int irq, void *pwr) return IRQ_HANDLED; } +static void axp20x_remove_sysfs_group(void *_data) +{ + struct device *dev = _data; + + sysfs_remove_group(&dev->kobj, &axp20x_attribute_group); +} + static int axp20x_pek_probe(struct platform_device *pdev) { struct axp20x_pek *axp20x_pek; @@ -214,12 +232,11 @@ static int axp20x_pek_probe(struct platform_device *pdev) input_set_drvdata(idev, axp20x_pek); error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbr, - axp20x_pek_irq, 0, - "axp20x-pek-dbr", idev); + axp20x_pek_irq, 0, + "axp20x-pek-dbr", idev); if (error < 0) { dev_err(axp20x->dev, "Failed to request dbr IRQ#%d: %d\n", axp20x_pek->irq_dbr, error); - return error; } @@ -232,45 +249,36 @@ static int axp20x_pek_probe(struct platform_device *pdev) return error; } - error = device_create_file(&pdev->dev, &axp20x_dev_attr_startup.attr); - if (error) + error = sysfs_create_group(&pdev->dev.kobj, &axp20x_attribute_group); + if (error) { + dev_err(axp20x->dev, "Failed to create sysfs attributes: %d\n", + error); return error; + } - error = device_create_file(&pdev->dev, &axp20x_dev_attr_shutdown.attr); - if (error) - goto clear_startup_attr; + error = devm_add_action(&pdev->dev, + axp20x_remove_sysfs_group, &pdev->dev); + if (error) { + axp20x_remove_sysfs_group(&pdev->dev); + dev_err(&pdev->dev, "Failed to add sysfs cleanup action: %d\n", + error); + return error; + } error = input_register_device(idev); if (error) { dev_err(axp20x->dev, "Can't register input device: %d\n", error); - goto clear_attr; + return error; } platform_set_drvdata(pdev, axp20x_pek); return 0; - -clear_attr: - device_remove_file(&pdev->dev, &axp20x_dev_attr_shutdown.attr); - -clear_startup_attr: - device_remove_file(&pdev->dev, &axp20x_dev_attr_startup.attr); - - return error; -} - -static int axp20x_pek_remove(struct platform_device *pdev) -{ - device_remove_file(&pdev->dev, &axp20x_dev_attr_shutdown.attr); - device_remove_file(&pdev->dev, &axp20x_dev_attr_startup.attr); - - return 0; } static struct platform_driver axp20x_pek_driver = { .probe = axp20x_pek_probe, - .remove = axp20x_pek_remove, .driver = { .name = "axp20x-pek", }, -- cgit v0.10.2 From 9333caeaeae4f831054e0e127a6ed3948b604d3e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 8 Jan 2015 14:53:23 -0800 Subject: Input: I8042 - add Acer Aspire 7738 to the nomux list When KBC is in active multiplexing mode the touchpad on this laptop does not work. Reported-by: Bilal Koc Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 97cdc58..764857b 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -415,6 +415,13 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { }, }, { + /* Acer Aspire 7738 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7738"), + }, + }, + { /* Gericom Bellagio */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Gericom"), -- cgit v0.10.2 From 2155355fda502e75cd942db101fbb08e1a826ba8 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Fri, 9 Jan 2015 10:45:13 +1100 Subject: xfs: move xfs_fs.h to libxfs Ioctl API definitions are shared with userspace, so move the header file that defines them all to libxfs along with all the other code shared with userspace. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Signed-off-by: Dave Chinner diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h new file mode 100644 index 0000000..18dc721 --- /dev/null +++ b/fs/xfs/libxfs/xfs_fs.h @@ -0,0 +1,576 @@ +/* + * Copyright (c) 1995-2005 Silicon Graphics, Inc. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __XFS_FS_H__ +#define __XFS_FS_H__ + +/* + * SGI's XFS filesystem's major stuff (constants, structures) + */ + +/* + * Direct I/O attribute record used with XFS_IOC_DIOINFO + * d_miniosz is the min xfer size, xfer size multiple and file seek offset + * alignment. + */ +#ifndef HAVE_DIOATTR +struct dioattr { + __u32 d_mem; /* data buffer memory alignment */ + __u32 d_miniosz; /* min xfer size */ + __u32 d_maxiosz; /* max xfer size */ +}; +#endif + +/* + * Structure for XFS_IOC_FSGETXATTR[A] and XFS_IOC_FSSETXATTR. + */ +#ifndef HAVE_FSXATTR +struct fsxattr { + __u32 fsx_xflags; /* xflags field value (get/set) */ + __u32 fsx_extsize; /* extsize field value (get/set)*/ + __u32 fsx_nextents; /* nextents field value (get) */ + __u32 fsx_projid; /* project identifier (get/set) */ + unsigned char fsx_pad[12]; +}; +#endif + +/* + * Flags for the bs_xflags/fsx_xflags field + * There should be a one-to-one correspondence between these flags and the + * XFS_DIFLAG_s. + */ +#define XFS_XFLAG_REALTIME 0x00000001 /* data in realtime volume */ +#define XFS_XFLAG_PREALLOC 0x00000002 /* preallocated file extents */ +#define XFS_XFLAG_IMMUTABLE 0x00000008 /* file cannot be modified */ +#define XFS_XFLAG_APPEND 0x00000010 /* all writes append */ +#define XFS_XFLAG_SYNC 0x00000020 /* all writes synchronous */ +#define XFS_XFLAG_NOATIME 0x00000040 /* do not update access time */ +#define XFS_XFLAG_NODUMP 0x00000080 /* do not include in backups */ +#define XFS_XFLAG_RTINHERIT 0x00000100 /* create with rt bit set */ +#define XFS_XFLAG_PROJINHERIT 0x00000200 /* create with parents projid */ +#define XFS_XFLAG_NOSYMLINKS 0x00000400 /* disallow symlink creation */ +#define XFS_XFLAG_EXTSIZE 0x00000800 /* extent size allocator hint */ +#define XFS_XFLAG_EXTSZINHERIT 0x00001000 /* inherit inode extent size */ +#define XFS_XFLAG_NODEFRAG 0x00002000 /* do not defragment */ +#define XFS_XFLAG_FILESTREAM 0x00004000 /* use filestream allocator */ +#define XFS_XFLAG_HASATTR 0x80000000 /* no DIFLAG for this */ + +/* + * Structure for XFS_IOC_GETBMAP. + * On input, fill in bmv_offset and bmv_length of the first structure + * to indicate the area of interest in the file, and bmv_entries with + * the number of array elements given back. The first structure is + * updated on return to give the offset and length for the next call. + */ +#ifndef HAVE_GETBMAP +struct getbmap { + __s64 bmv_offset; /* file offset of segment in blocks */ + __s64 bmv_block; /* starting block (64-bit daddr_t) */ + __s64 bmv_length; /* length of segment, blocks */ + __s32 bmv_count; /* # of entries in array incl. 1st */ + __s32 bmv_entries; /* # of entries filled in (output) */ +}; +#endif + +/* + * Structure for XFS_IOC_GETBMAPX. Fields bmv_offset through bmv_entries + * are used exactly as in the getbmap structure. The getbmapx structure + * has additional bmv_iflags and bmv_oflags fields. The bmv_iflags field + * is only used for the first structure. It contains input flags + * specifying XFS_IOC_GETBMAPX actions. The bmv_oflags field is filled + * in by the XFS_IOC_GETBMAPX command for each returned structure after + * the first. + */ +#ifndef HAVE_GETBMAPX +struct getbmapx { + __s64 bmv_offset; /* file offset of segment in blocks */ + __s64 bmv_block; /* starting block (64-bit daddr_t) */ + __s64 bmv_length; /* length of segment, blocks */ + __s32 bmv_count; /* # of entries in array incl. 1st */ + __s32 bmv_entries; /* # of entries filled in (output). */ + __s32 bmv_iflags; /* input flags (1st structure) */ + __s32 bmv_oflags; /* output flags (after 1st structure)*/ + __s32 bmv_unused1; /* future use */ + __s32 bmv_unused2; /* future use */ +}; +#endif + +/* bmv_iflags values - set by XFS_IOC_GETBMAPX caller. */ +#define BMV_IF_ATTRFORK 0x1 /* return attr fork rather than data */ +#define BMV_IF_NO_DMAPI_READ 0x2 /* Do not generate DMAPI read event */ +#define BMV_IF_PREALLOC 0x4 /* rtn status BMV_OF_PREALLOC if req */ +#define BMV_IF_DELALLOC 0x8 /* rtn status BMV_OF_DELALLOC if req */ +#define BMV_IF_NO_HOLES 0x10 /* Do not return holes */ +#define BMV_IF_VALID \ + (BMV_IF_ATTRFORK|BMV_IF_NO_DMAPI_READ|BMV_IF_PREALLOC| \ + BMV_IF_DELALLOC|BMV_IF_NO_HOLES) + +/* bmv_oflags values - returned for each non-header segment */ +#define BMV_OF_PREALLOC 0x1 /* segment = unwritten pre-allocation */ +#define BMV_OF_DELALLOC 0x2 /* segment = delayed allocation */ +#define BMV_OF_LAST 0x4 /* segment is the last in the file */ + +/* + * Structure for XFS_IOC_FSSETDM. + * For use by backup and restore programs to set the XFS on-disk inode + * fields di_dmevmask and di_dmstate. These must be set to exactly and + * only values previously obtained via xfs_bulkstat! (Specifically the + * xfs_bstat_t fields bs_dmevmask and bs_dmstate.) + */ +#ifndef HAVE_FSDMIDATA +struct fsdmidata { + __u32 fsd_dmevmask; /* corresponds to di_dmevmask */ + __u16 fsd_padding; + __u16 fsd_dmstate; /* corresponds to di_dmstate */ +}; +#endif + +/* + * File segment locking set data type for 64 bit access. + * Also used for all the RESV/FREE interfaces. + */ +typedef struct xfs_flock64 { + __s16 l_type; + __s16 l_whence; + __s64 l_start; + __s64 l_len; /* len == 0 means until end of file */ + __s32 l_sysid; + __u32 l_pid; + __s32 l_pad[4]; /* reserve area */ +} xfs_flock64_t; + +/* + * Output for XFS_IOC_FSGEOMETRY_V1 + */ +typedef struct xfs_fsop_geom_v1 { + __u32 blocksize; /* filesystem (data) block size */ + __u32 rtextsize; /* realtime extent size */ + __u32 agblocks; /* fsblocks in an AG */ + __u32 agcount; /* number of allocation groups */ + __u32 logblocks; /* fsblocks in the log */ + __u32 sectsize; /* (data) sector size, bytes */ + __u32 inodesize; /* inode size in bytes */ + __u32 imaxpct; /* max allowed inode space(%) */ + __u64 datablocks; /* fsblocks in data subvolume */ + __u64 rtblocks; /* fsblocks in realtime subvol */ + __u64 rtextents; /* rt extents in realtime subvol*/ + __u64 logstart; /* starting fsblock of the log */ + unsigned char uuid[16]; /* unique id of the filesystem */ + __u32 sunit; /* stripe unit, fsblocks */ + __u32 swidth; /* stripe width, fsblocks */ + __s32 version; /* structure version */ + __u32 flags; /* superblock version flags */ + __u32 logsectsize; /* log sector size, bytes */ + __u32 rtsectsize; /* realtime sector size, bytes */ + __u32 dirblocksize; /* directory block size, bytes */ +} xfs_fsop_geom_v1_t; + +/* + * Output for XFS_IOC_FSGEOMETRY + */ +typedef struct xfs_fsop_geom { + __u32 blocksize; /* filesystem (data) block size */ + __u32 rtextsize; /* realtime extent size */ + __u32 agblocks; /* fsblocks in an AG */ + __u32 agcount; /* number of allocation groups */ + __u32 logblocks; /* fsblocks in the log */ + __u32 sectsize; /* (data) sector size, bytes */ + __u32 inodesize; /* inode size in bytes */ + __u32 imaxpct; /* max allowed inode space(%) */ + __u64 datablocks; /* fsblocks in data subvolume */ + __u64 rtblocks; /* fsblocks in realtime subvol */ + __u64 rtextents; /* rt extents in realtime subvol*/ + __u64 logstart; /* starting fsblock of the log */ + unsigned char uuid[16]; /* unique id of the filesystem */ + __u32 sunit; /* stripe unit, fsblocks */ + __u32 swidth; /* stripe width, fsblocks */ + __s32 version; /* structure version */ + __u32 flags; /* superblock version flags */ + __u32 logsectsize; /* log sector size, bytes */ + __u32 rtsectsize; /* realtime sector size, bytes */ + __u32 dirblocksize; /* directory block size, bytes */ + __u32 logsunit; /* log stripe unit, bytes */ +} xfs_fsop_geom_t; + +/* Output for XFS_FS_COUNTS */ +typedef struct xfs_fsop_counts { + __u64 freedata; /* free data section blocks */ + __u64 freertx; /* free rt extents */ + __u64 freeino; /* free inodes */ + __u64 allocino; /* total allocated inodes */ +} xfs_fsop_counts_t; + +/* Input/Output for XFS_GET_RESBLKS and XFS_SET_RESBLKS */ +typedef struct xfs_fsop_resblks { + __u64 resblks; + __u64 resblks_avail; +} xfs_fsop_resblks_t; + +#define XFS_FSOP_GEOM_VERSION 0 + +#define XFS_FSOP_GEOM_FLAGS_ATTR 0x0001 /* attributes in use */ +#define XFS_FSOP_GEOM_FLAGS_NLINK 0x0002 /* 32-bit nlink values */ +#define XFS_FSOP_GEOM_FLAGS_QUOTA 0x0004 /* quotas enabled */ +#define XFS_FSOP_GEOM_FLAGS_IALIGN 0x0008 /* inode alignment */ +#define XFS_FSOP_GEOM_FLAGS_DALIGN 0x0010 /* large data alignment */ +#define XFS_FSOP_GEOM_FLAGS_SHARED 0x0020 /* read-only shared */ +#define XFS_FSOP_GEOM_FLAGS_EXTFLG 0x0040 /* special extent flag */ +#define XFS_FSOP_GEOM_FLAGS_DIRV2 0x0080 /* directory version 2 */ +#define XFS_FSOP_GEOM_FLAGS_LOGV2 0x0100 /* log format version 2 */ +#define XFS_FSOP_GEOM_FLAGS_SECTOR 0x0200 /* sector sizes >1BB */ +#define XFS_FSOP_GEOM_FLAGS_ATTR2 0x0400 /* inline attributes rework */ +#define XFS_FSOP_GEOM_FLAGS_PROJID32 0x0800 /* 32-bit project IDs */ +#define XFS_FSOP_GEOM_FLAGS_DIRV2CI 0x1000 /* ASCII only CI names */ +#define XFS_FSOP_GEOM_FLAGS_LAZYSB 0x4000 /* lazy superblock counters */ +#define XFS_FSOP_GEOM_FLAGS_V5SB 0x8000 /* version 5 superblock */ +#define XFS_FSOP_GEOM_FLAGS_FTYPE 0x10000 /* inode directory types */ +#define XFS_FSOP_GEOM_FLAGS_FINOBT 0x20000 /* free inode btree */ + +/* + * Minimum and maximum sizes need for growth checks. + * + * Block counts are in units of filesystem blocks, not basic blocks. + */ +#define XFS_MIN_AG_BLOCKS 64 +#define XFS_MIN_LOG_BLOCKS 512ULL +#define XFS_MAX_LOG_BLOCKS (1024 * 1024ULL) +#define XFS_MIN_LOG_BYTES (10 * 1024 * 1024ULL) + +/* keep the maximum size under 2^31 by a small amount */ +#define XFS_MAX_LOG_BYTES \ + ((2 * 1024 * 1024 * 1024ULL) - XFS_MIN_LOG_BYTES) + +/* Used for sanity checks on superblock */ +#define XFS_MAX_DBLOCKS(s) ((xfs_rfsblock_t)(s)->sb_agcount * (s)->sb_agblocks) +#define XFS_MIN_DBLOCKS(s) ((xfs_rfsblock_t)((s)->sb_agcount - 1) * \ + (s)->sb_agblocks + XFS_MIN_AG_BLOCKS) + +/* + * Structures for XFS_IOC_FSGROWFSDATA, XFS_IOC_FSGROWFSLOG & XFS_IOC_FSGROWFSRT + */ +typedef struct xfs_growfs_data { + __u64 newblocks; /* new data subvol size, fsblocks */ + __u32 imaxpct; /* new inode space percentage limit */ +} xfs_growfs_data_t; + +typedef struct xfs_growfs_log { + __u32 newblocks; /* new log size, fsblocks */ + __u32 isint; /* 1 if new log is internal */ +} xfs_growfs_log_t; + +typedef struct xfs_growfs_rt { + __u64 newblocks; /* new realtime size, fsblocks */ + __u32 extsize; /* new realtime extent size, fsblocks */ +} xfs_growfs_rt_t; + + +/* + * Structures returned from ioctl XFS_IOC_FSBULKSTAT & XFS_IOC_FSBULKSTAT_SINGLE + */ +typedef struct xfs_bstime { + time_t tv_sec; /* seconds */ + __s32 tv_nsec; /* and nanoseconds */ +} xfs_bstime_t; + +typedef struct xfs_bstat { + __u64 bs_ino; /* inode number */ + __u16 bs_mode; /* type and mode */ + __u16 bs_nlink; /* number of links */ + __u32 bs_uid; /* user id */ + __u32 bs_gid; /* group id */ + __u32 bs_rdev; /* device value */ + __s32 bs_blksize; /* block size */ + __s64 bs_size; /* file size */ + xfs_bstime_t bs_atime; /* access time */ + xfs_bstime_t bs_mtime; /* modify time */ + xfs_bstime_t bs_ctime; /* inode change time */ + int64_t bs_blocks; /* number of blocks */ + __u32 bs_xflags; /* extended flags */ + __s32 bs_extsize; /* extent size */ + __s32 bs_extents; /* number of extents */ + __u32 bs_gen; /* generation count */ + __u16 bs_projid_lo; /* lower part of project id */ +#define bs_projid bs_projid_lo /* (previously just bs_projid) */ + __u16 bs_forkoff; /* inode fork offset in bytes */ + __u16 bs_projid_hi; /* higher part of project id */ + unsigned char bs_pad[10]; /* pad space, unused */ + __u32 bs_dmevmask; /* DMIG event mask */ + __u16 bs_dmstate; /* DMIG state info */ + __u16 bs_aextents; /* attribute number of extents */ +} xfs_bstat_t; + +/* + * Project quota id helpers (previously projid was 16bit only + * and using two 16bit values to hold new 32bit projid was choosen + * to retain compatibility with "old" filesystems). + */ +static inline __uint32_t +bstat_get_projid(struct xfs_bstat *bs) +{ + return (__uint32_t)bs->bs_projid_hi << 16 | bs->bs_projid_lo; +} + +/* + * The user-level BulkStat Request interface structure. + */ +typedef struct xfs_fsop_bulkreq { + __u64 __user *lastip; /* last inode # pointer */ + __s32 icount; /* count of entries in buffer */ + void __user *ubuffer;/* user buffer for inode desc. */ + __s32 __user *ocount; /* output count pointer */ +} xfs_fsop_bulkreq_t; + + +/* + * Structures returned from xfs_inumbers routine (XFS_IOC_FSINUMBERS). + */ +typedef struct xfs_inogrp { + __u64 xi_startino; /* starting inode number */ + __s32 xi_alloccount; /* # bits set in allocmask */ + __u64 xi_allocmask; /* mask of allocated inodes */ +} xfs_inogrp_t; + + +/* + * Error injection. + */ +typedef struct xfs_error_injection { + __s32 fd; + __s32 errtag; +} xfs_error_injection_t; + + +/* + * Speculative preallocation trimming. + */ +#define XFS_EOFBLOCKS_VERSION 1 +struct xfs_fs_eofblocks { + __u32 eof_version; + __u32 eof_flags; + uid_t eof_uid; + gid_t eof_gid; + prid_t eof_prid; + __u32 pad32; + __u64 eof_min_file_size; + __u64 pad64[12]; +}; + +/* eof_flags values */ +#define XFS_EOF_FLAGS_SYNC (1 << 0) /* sync/wait mode scan */ +#define XFS_EOF_FLAGS_UID (1 << 1) /* filter by uid */ +#define XFS_EOF_FLAGS_GID (1 << 2) /* filter by gid */ +#define XFS_EOF_FLAGS_PRID (1 << 3) /* filter by project id */ +#define XFS_EOF_FLAGS_MINFILESIZE (1 << 4) /* filter by min file size */ +#define XFS_EOF_FLAGS_UNION (1 << 5) /* union filter algorithm; + * kernel only, not included in + * valid mask */ +#define XFS_EOF_FLAGS_VALID \ + (XFS_EOF_FLAGS_SYNC | \ + XFS_EOF_FLAGS_UID | \ + XFS_EOF_FLAGS_GID | \ + XFS_EOF_FLAGS_PRID | \ + XFS_EOF_FLAGS_MINFILESIZE) + + +/* + * The user-level Handle Request interface structure. + */ +typedef struct xfs_fsop_handlereq { + __u32 fd; /* fd for FD_TO_HANDLE */ + void __user *path; /* user pathname */ + __u32 oflags; /* open flags */ + void __user *ihandle;/* user supplied handle */ + __u32 ihandlen; /* user supplied length */ + void __user *ohandle;/* user buffer for handle */ + __u32 __user *ohandlen;/* user buffer length */ +} xfs_fsop_handlereq_t; + +/* + * Compound structures for passing args through Handle Request interfaces + * xfs_fssetdm_by_handle, xfs_attrlist_by_handle, xfs_attrmulti_by_handle + * - ioctls: XFS_IOC_FSSETDM_BY_HANDLE, XFS_IOC_ATTRLIST_BY_HANDLE, and + * XFS_IOC_ATTRMULTI_BY_HANDLE + */ + +typedef struct xfs_fsop_setdm_handlereq { + struct xfs_fsop_handlereq hreq; /* handle information */ + struct fsdmidata __user *data; /* DMAPI data */ +} xfs_fsop_setdm_handlereq_t; + +typedef struct xfs_attrlist_cursor { + __u32 opaque[4]; +} xfs_attrlist_cursor_t; + +typedef struct xfs_fsop_attrlist_handlereq { + struct xfs_fsop_handlereq hreq; /* handle interface structure */ + struct xfs_attrlist_cursor pos; /* opaque cookie, list offset */ + __u32 flags; /* which namespace to use */ + __u32 buflen; /* length of buffer supplied */ + void __user *buffer; /* returned names */ +} xfs_fsop_attrlist_handlereq_t; + +typedef struct xfs_attr_multiop { + __u32 am_opcode; +#define ATTR_OP_GET 1 /* return the indicated attr's value */ +#define ATTR_OP_SET 2 /* set/create the indicated attr/value pair */ +#define ATTR_OP_REMOVE 3 /* remove the indicated attr */ + __s32 am_error; + void __user *am_attrname; + void __user *am_attrvalue; + __u32 am_length; + __u32 am_flags; +} xfs_attr_multiop_t; + +typedef struct xfs_fsop_attrmulti_handlereq { + struct xfs_fsop_handlereq hreq; /* handle interface structure */ + __u32 opcount;/* count of following multiop */ + struct xfs_attr_multiop __user *ops; /* attr_multi data */ +} xfs_fsop_attrmulti_handlereq_t; + +/* + * per machine unique filesystem identifier types. + */ +typedef struct { __u32 val[2]; } xfs_fsid_t; /* file system id type */ + +typedef struct xfs_fid { + __u16 fid_len; /* length of remainder */ + __u16 fid_pad; + __u32 fid_gen; /* generation number */ + __u64 fid_ino; /* 64 bits inode number */ +} xfs_fid_t; + +typedef struct xfs_handle { + union { + __s64 align; /* force alignment of ha_fid */ + xfs_fsid_t _ha_fsid; /* unique file system identifier */ + } ha_u; + xfs_fid_t ha_fid; /* file system specific file ID */ +} xfs_handle_t; +#define ha_fsid ha_u._ha_fsid + +#define XFS_HSIZE(handle) (((char *) &(handle).ha_fid.fid_pad \ + - (char *) &(handle)) \ + + (handle).ha_fid.fid_len) + +/* + * Structure passed to XFS_IOC_SWAPEXT + */ +typedef struct xfs_swapext +{ + __int64_t sx_version; /* version */ +#define XFS_SX_VERSION 0 + __int64_t sx_fdtarget; /* fd of target file */ + __int64_t sx_fdtmp; /* fd of tmp file */ + xfs_off_t sx_offset; /* offset into file */ + xfs_off_t sx_length; /* leng from offset */ + char sx_pad[16]; /* pad space, unused */ + xfs_bstat_t sx_stat; /* stat of target b4 copy */ +} xfs_swapext_t; + +/* + * Flags for going down operation + */ +#define XFS_FSOP_GOING_FLAGS_DEFAULT 0x0 /* going down */ +#define XFS_FSOP_GOING_FLAGS_LOGFLUSH 0x1 /* flush log but not data */ +#define XFS_FSOP_GOING_FLAGS_NOLOGFLUSH 0x2 /* don't flush log nor data */ + +/* + * ioctl commands that are used by Linux filesystems + */ +#define XFS_IOC_GETXFLAGS FS_IOC_GETFLAGS +#define XFS_IOC_SETXFLAGS FS_IOC_SETFLAGS +#define XFS_IOC_GETVERSION FS_IOC_GETVERSION + +/* + * ioctl commands that replace IRIX fcntl()'s + * For 'documentation' purposed more than anything else, + * the "cmd #" field reflects the IRIX fcntl number. + */ +#define XFS_IOC_ALLOCSP _IOW ('X', 10, struct xfs_flock64) +#define XFS_IOC_FREESP _IOW ('X', 11, struct xfs_flock64) +#define XFS_IOC_DIOINFO _IOR ('X', 30, struct dioattr) +#define XFS_IOC_FSGETXATTR _IOR ('X', 31, struct fsxattr) +#define XFS_IOC_FSSETXATTR _IOW ('X', 32, struct fsxattr) +#define XFS_IOC_ALLOCSP64 _IOW ('X', 36, struct xfs_flock64) +#define XFS_IOC_FREESP64 _IOW ('X', 37, struct xfs_flock64) +#define XFS_IOC_GETBMAP _IOWR('X', 38, struct getbmap) +#define XFS_IOC_FSSETDM _IOW ('X', 39, struct fsdmidata) +#define XFS_IOC_RESVSP _IOW ('X', 40, struct xfs_flock64) +#define XFS_IOC_UNRESVSP _IOW ('X', 41, struct xfs_flock64) +#define XFS_IOC_RESVSP64 _IOW ('X', 42, struct xfs_flock64) +#define XFS_IOC_UNRESVSP64 _IOW ('X', 43, struct xfs_flock64) +#define XFS_IOC_GETBMAPA _IOWR('X', 44, struct getbmap) +#define XFS_IOC_FSGETXATTRA _IOR ('X', 45, struct fsxattr) +/* XFS_IOC_SETBIOSIZE ---- deprecated 46 */ +/* XFS_IOC_GETBIOSIZE ---- deprecated 47 */ +#define XFS_IOC_GETBMAPX _IOWR('X', 56, struct getbmap) +#define XFS_IOC_ZERO_RANGE _IOW ('X', 57, struct xfs_flock64) +#define XFS_IOC_FREE_EOFBLOCKS _IOR ('X', 58, struct xfs_fs_eofblocks) + +/* + * ioctl commands that replace IRIX syssgi()'s + */ +#define XFS_IOC_FSGEOMETRY_V1 _IOR ('X', 100, struct xfs_fsop_geom_v1) +#define XFS_IOC_FSBULKSTAT _IOWR('X', 101, struct xfs_fsop_bulkreq) +#define XFS_IOC_FSBULKSTAT_SINGLE _IOWR('X', 102, struct xfs_fsop_bulkreq) +#define XFS_IOC_FSINUMBERS _IOWR('X', 103, struct xfs_fsop_bulkreq) +#define XFS_IOC_PATH_TO_FSHANDLE _IOWR('X', 104, struct xfs_fsop_handlereq) +#define XFS_IOC_PATH_TO_HANDLE _IOWR('X', 105, struct xfs_fsop_handlereq) +#define XFS_IOC_FD_TO_HANDLE _IOWR('X', 106, struct xfs_fsop_handlereq) +#define XFS_IOC_OPEN_BY_HANDLE _IOWR('X', 107, struct xfs_fsop_handlereq) +#define XFS_IOC_READLINK_BY_HANDLE _IOWR('X', 108, struct xfs_fsop_handlereq) +#define XFS_IOC_SWAPEXT _IOWR('X', 109, struct xfs_swapext) +#define XFS_IOC_FSGROWFSDATA _IOW ('X', 110, struct xfs_growfs_data) +#define XFS_IOC_FSGROWFSLOG _IOW ('X', 111, struct xfs_growfs_log) +#define XFS_IOC_FSGROWFSRT _IOW ('X', 112, struct xfs_growfs_rt) +#define XFS_IOC_FSCOUNTS _IOR ('X', 113, struct xfs_fsop_counts) +#define XFS_IOC_SET_RESBLKS _IOWR('X', 114, struct xfs_fsop_resblks) +#define XFS_IOC_GET_RESBLKS _IOR ('X', 115, struct xfs_fsop_resblks) +#define XFS_IOC_ERROR_INJECTION _IOW ('X', 116, struct xfs_error_injection) +#define XFS_IOC_ERROR_CLEARALL _IOW ('X', 117, struct xfs_error_injection) +/* XFS_IOC_ATTRCTL_BY_HANDLE -- deprecated 118 */ + +/* XFS_IOC_FREEZE -- FIFREEZE 119 */ +/* XFS_IOC_THAW -- FITHAW 120 */ +#ifndef FIFREEZE +#define XFS_IOC_FREEZE _IOWR('X', 119, int) +#define XFS_IOC_THAW _IOWR('X', 120, int) +#endif + +#define XFS_IOC_FSSETDM_BY_HANDLE _IOW ('X', 121, struct xfs_fsop_setdm_handlereq) +#define XFS_IOC_ATTRLIST_BY_HANDLE _IOW ('X', 122, struct xfs_fsop_attrlist_handlereq) +#define XFS_IOC_ATTRMULTI_BY_HANDLE _IOW ('X', 123, struct xfs_fsop_attrmulti_handlereq) +#define XFS_IOC_FSGEOMETRY _IOR ('X', 124, struct xfs_fsop_geom) +#define XFS_IOC_GOINGDOWN _IOR ('X', 125, __uint32_t) +/* XFS_IOC_GETFSUUID ---------- deprecated 140 */ + + +#ifndef HAVE_BBMACROS +/* + * Block I/O parameterization. A basic block (BB) is the lowest size of + * filesystem allocation, and must equal 512. Length units given to bio + * routines are in BB's. + */ +#define BBSHIFT 9 +#define BBSIZE (1<> BBSHIFT) +#define BTOBBT(bytes) ((__u64)(bytes) >> BBSHIFT) +#define BBTOB(bbs) ((bbs) << BBSHIFT) +#endif + +#endif /* __XFS_FS_H__ */ diff --git a/fs/xfs/xfs_fs.h b/fs/xfs/xfs_fs.h deleted file mode 100644 index 18dc721..0000000 --- a/fs/xfs/xfs_fs.h +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Copyright (c) 1995-2005 Silicon Graphics, Inc. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __XFS_FS_H__ -#define __XFS_FS_H__ - -/* - * SGI's XFS filesystem's major stuff (constants, structures) - */ - -/* - * Direct I/O attribute record used with XFS_IOC_DIOINFO - * d_miniosz is the min xfer size, xfer size multiple and file seek offset - * alignment. - */ -#ifndef HAVE_DIOATTR -struct dioattr { - __u32 d_mem; /* data buffer memory alignment */ - __u32 d_miniosz; /* min xfer size */ - __u32 d_maxiosz; /* max xfer size */ -}; -#endif - -/* - * Structure for XFS_IOC_FSGETXATTR[A] and XFS_IOC_FSSETXATTR. - */ -#ifndef HAVE_FSXATTR -struct fsxattr { - __u32 fsx_xflags; /* xflags field value (get/set) */ - __u32 fsx_extsize; /* extsize field value (get/set)*/ - __u32 fsx_nextents; /* nextents field value (get) */ - __u32 fsx_projid; /* project identifier (get/set) */ - unsigned char fsx_pad[12]; -}; -#endif - -/* - * Flags for the bs_xflags/fsx_xflags field - * There should be a one-to-one correspondence between these flags and the - * XFS_DIFLAG_s. - */ -#define XFS_XFLAG_REALTIME 0x00000001 /* data in realtime volume */ -#define XFS_XFLAG_PREALLOC 0x00000002 /* preallocated file extents */ -#define XFS_XFLAG_IMMUTABLE 0x00000008 /* file cannot be modified */ -#define XFS_XFLAG_APPEND 0x00000010 /* all writes append */ -#define XFS_XFLAG_SYNC 0x00000020 /* all writes synchronous */ -#define XFS_XFLAG_NOATIME 0x00000040 /* do not update access time */ -#define XFS_XFLAG_NODUMP 0x00000080 /* do not include in backups */ -#define XFS_XFLAG_RTINHERIT 0x00000100 /* create with rt bit set */ -#define XFS_XFLAG_PROJINHERIT 0x00000200 /* create with parents projid */ -#define XFS_XFLAG_NOSYMLINKS 0x00000400 /* disallow symlink creation */ -#define XFS_XFLAG_EXTSIZE 0x00000800 /* extent size allocator hint */ -#define XFS_XFLAG_EXTSZINHERIT 0x00001000 /* inherit inode extent size */ -#define XFS_XFLAG_NODEFRAG 0x00002000 /* do not defragment */ -#define XFS_XFLAG_FILESTREAM 0x00004000 /* use filestream allocator */ -#define XFS_XFLAG_HASATTR 0x80000000 /* no DIFLAG for this */ - -/* - * Structure for XFS_IOC_GETBMAP. - * On input, fill in bmv_offset and bmv_length of the first structure - * to indicate the area of interest in the file, and bmv_entries with - * the number of array elements given back. The first structure is - * updated on return to give the offset and length for the next call. - */ -#ifndef HAVE_GETBMAP -struct getbmap { - __s64 bmv_offset; /* file offset of segment in blocks */ - __s64 bmv_block; /* starting block (64-bit daddr_t) */ - __s64 bmv_length; /* length of segment, blocks */ - __s32 bmv_count; /* # of entries in array incl. 1st */ - __s32 bmv_entries; /* # of entries filled in (output) */ -}; -#endif - -/* - * Structure for XFS_IOC_GETBMAPX. Fields bmv_offset through bmv_entries - * are used exactly as in the getbmap structure. The getbmapx structure - * has additional bmv_iflags and bmv_oflags fields. The bmv_iflags field - * is only used for the first structure. It contains input flags - * specifying XFS_IOC_GETBMAPX actions. The bmv_oflags field is filled - * in by the XFS_IOC_GETBMAPX command for each returned structure after - * the first. - */ -#ifndef HAVE_GETBMAPX -struct getbmapx { - __s64 bmv_offset; /* file offset of segment in blocks */ - __s64 bmv_block; /* starting block (64-bit daddr_t) */ - __s64 bmv_length; /* length of segment, blocks */ - __s32 bmv_count; /* # of entries in array incl. 1st */ - __s32 bmv_entries; /* # of entries filled in (output). */ - __s32 bmv_iflags; /* input flags (1st structure) */ - __s32 bmv_oflags; /* output flags (after 1st structure)*/ - __s32 bmv_unused1; /* future use */ - __s32 bmv_unused2; /* future use */ -}; -#endif - -/* bmv_iflags values - set by XFS_IOC_GETBMAPX caller. */ -#define BMV_IF_ATTRFORK 0x1 /* return attr fork rather than data */ -#define BMV_IF_NO_DMAPI_READ 0x2 /* Do not generate DMAPI read event */ -#define BMV_IF_PREALLOC 0x4 /* rtn status BMV_OF_PREALLOC if req */ -#define BMV_IF_DELALLOC 0x8 /* rtn status BMV_OF_DELALLOC if req */ -#define BMV_IF_NO_HOLES 0x10 /* Do not return holes */ -#define BMV_IF_VALID \ - (BMV_IF_ATTRFORK|BMV_IF_NO_DMAPI_READ|BMV_IF_PREALLOC| \ - BMV_IF_DELALLOC|BMV_IF_NO_HOLES) - -/* bmv_oflags values - returned for each non-header segment */ -#define BMV_OF_PREALLOC 0x1 /* segment = unwritten pre-allocation */ -#define BMV_OF_DELALLOC 0x2 /* segment = delayed allocation */ -#define BMV_OF_LAST 0x4 /* segment is the last in the file */ - -/* - * Structure for XFS_IOC_FSSETDM. - * For use by backup and restore programs to set the XFS on-disk inode - * fields di_dmevmask and di_dmstate. These must be set to exactly and - * only values previously obtained via xfs_bulkstat! (Specifically the - * xfs_bstat_t fields bs_dmevmask and bs_dmstate.) - */ -#ifndef HAVE_FSDMIDATA -struct fsdmidata { - __u32 fsd_dmevmask; /* corresponds to di_dmevmask */ - __u16 fsd_padding; - __u16 fsd_dmstate; /* corresponds to di_dmstate */ -}; -#endif - -/* - * File segment locking set data type for 64 bit access. - * Also used for all the RESV/FREE interfaces. - */ -typedef struct xfs_flock64 { - __s16 l_type; - __s16 l_whence; - __s64 l_start; - __s64 l_len; /* len == 0 means until end of file */ - __s32 l_sysid; - __u32 l_pid; - __s32 l_pad[4]; /* reserve area */ -} xfs_flock64_t; - -/* - * Output for XFS_IOC_FSGEOMETRY_V1 - */ -typedef struct xfs_fsop_geom_v1 { - __u32 blocksize; /* filesystem (data) block size */ - __u32 rtextsize; /* realtime extent size */ - __u32 agblocks; /* fsblocks in an AG */ - __u32 agcount; /* number of allocation groups */ - __u32 logblocks; /* fsblocks in the log */ - __u32 sectsize; /* (data) sector size, bytes */ - __u32 inodesize; /* inode size in bytes */ - __u32 imaxpct; /* max allowed inode space(%) */ - __u64 datablocks; /* fsblocks in data subvolume */ - __u64 rtblocks; /* fsblocks in realtime subvol */ - __u64 rtextents; /* rt extents in realtime subvol*/ - __u64 logstart; /* starting fsblock of the log */ - unsigned char uuid[16]; /* unique id of the filesystem */ - __u32 sunit; /* stripe unit, fsblocks */ - __u32 swidth; /* stripe width, fsblocks */ - __s32 version; /* structure version */ - __u32 flags; /* superblock version flags */ - __u32 logsectsize; /* log sector size, bytes */ - __u32 rtsectsize; /* realtime sector size, bytes */ - __u32 dirblocksize; /* directory block size, bytes */ -} xfs_fsop_geom_v1_t; - -/* - * Output for XFS_IOC_FSGEOMETRY - */ -typedef struct xfs_fsop_geom { - __u32 blocksize; /* filesystem (data) block size */ - __u32 rtextsize; /* realtime extent size */ - __u32 agblocks; /* fsblocks in an AG */ - __u32 agcount; /* number of allocation groups */ - __u32 logblocks; /* fsblocks in the log */ - __u32 sectsize; /* (data) sector size, bytes */ - __u32 inodesize; /* inode size in bytes */ - __u32 imaxpct; /* max allowed inode space(%) */ - __u64 datablocks; /* fsblocks in data subvolume */ - __u64 rtblocks; /* fsblocks in realtime subvol */ - __u64 rtextents; /* rt extents in realtime subvol*/ - __u64 logstart; /* starting fsblock of the log */ - unsigned char uuid[16]; /* unique id of the filesystem */ - __u32 sunit; /* stripe unit, fsblocks */ - __u32 swidth; /* stripe width, fsblocks */ - __s32 version; /* structure version */ - __u32 flags; /* superblock version flags */ - __u32 logsectsize; /* log sector size, bytes */ - __u32 rtsectsize; /* realtime sector size, bytes */ - __u32 dirblocksize; /* directory block size, bytes */ - __u32 logsunit; /* log stripe unit, bytes */ -} xfs_fsop_geom_t; - -/* Output for XFS_FS_COUNTS */ -typedef struct xfs_fsop_counts { - __u64 freedata; /* free data section blocks */ - __u64 freertx; /* free rt extents */ - __u64 freeino; /* free inodes */ - __u64 allocino; /* total allocated inodes */ -} xfs_fsop_counts_t; - -/* Input/Output for XFS_GET_RESBLKS and XFS_SET_RESBLKS */ -typedef struct xfs_fsop_resblks { - __u64 resblks; - __u64 resblks_avail; -} xfs_fsop_resblks_t; - -#define XFS_FSOP_GEOM_VERSION 0 - -#define XFS_FSOP_GEOM_FLAGS_ATTR 0x0001 /* attributes in use */ -#define XFS_FSOP_GEOM_FLAGS_NLINK 0x0002 /* 32-bit nlink values */ -#define XFS_FSOP_GEOM_FLAGS_QUOTA 0x0004 /* quotas enabled */ -#define XFS_FSOP_GEOM_FLAGS_IALIGN 0x0008 /* inode alignment */ -#define XFS_FSOP_GEOM_FLAGS_DALIGN 0x0010 /* large data alignment */ -#define XFS_FSOP_GEOM_FLAGS_SHARED 0x0020 /* read-only shared */ -#define XFS_FSOP_GEOM_FLAGS_EXTFLG 0x0040 /* special extent flag */ -#define XFS_FSOP_GEOM_FLAGS_DIRV2 0x0080 /* directory version 2 */ -#define XFS_FSOP_GEOM_FLAGS_LOGV2 0x0100 /* log format version 2 */ -#define XFS_FSOP_GEOM_FLAGS_SECTOR 0x0200 /* sector sizes >1BB */ -#define XFS_FSOP_GEOM_FLAGS_ATTR2 0x0400 /* inline attributes rework */ -#define XFS_FSOP_GEOM_FLAGS_PROJID32 0x0800 /* 32-bit project IDs */ -#define XFS_FSOP_GEOM_FLAGS_DIRV2CI 0x1000 /* ASCII only CI names */ -#define XFS_FSOP_GEOM_FLAGS_LAZYSB 0x4000 /* lazy superblock counters */ -#define XFS_FSOP_GEOM_FLAGS_V5SB 0x8000 /* version 5 superblock */ -#define XFS_FSOP_GEOM_FLAGS_FTYPE 0x10000 /* inode directory types */ -#define XFS_FSOP_GEOM_FLAGS_FINOBT 0x20000 /* free inode btree */ - -/* - * Minimum and maximum sizes need for growth checks. - * - * Block counts are in units of filesystem blocks, not basic blocks. - */ -#define XFS_MIN_AG_BLOCKS 64 -#define XFS_MIN_LOG_BLOCKS 512ULL -#define XFS_MAX_LOG_BLOCKS (1024 * 1024ULL) -#define XFS_MIN_LOG_BYTES (10 * 1024 * 1024ULL) - -/* keep the maximum size under 2^31 by a small amount */ -#define XFS_MAX_LOG_BYTES \ - ((2 * 1024 * 1024 * 1024ULL) - XFS_MIN_LOG_BYTES) - -/* Used for sanity checks on superblock */ -#define XFS_MAX_DBLOCKS(s) ((xfs_rfsblock_t)(s)->sb_agcount * (s)->sb_agblocks) -#define XFS_MIN_DBLOCKS(s) ((xfs_rfsblock_t)((s)->sb_agcount - 1) * \ - (s)->sb_agblocks + XFS_MIN_AG_BLOCKS) - -/* - * Structures for XFS_IOC_FSGROWFSDATA, XFS_IOC_FSGROWFSLOG & XFS_IOC_FSGROWFSRT - */ -typedef struct xfs_growfs_data { - __u64 newblocks; /* new data subvol size, fsblocks */ - __u32 imaxpct; /* new inode space percentage limit */ -} xfs_growfs_data_t; - -typedef struct xfs_growfs_log { - __u32 newblocks; /* new log size, fsblocks */ - __u32 isint; /* 1 if new log is internal */ -} xfs_growfs_log_t; - -typedef struct xfs_growfs_rt { - __u64 newblocks; /* new realtime size, fsblocks */ - __u32 extsize; /* new realtime extent size, fsblocks */ -} xfs_growfs_rt_t; - - -/* - * Structures returned from ioctl XFS_IOC_FSBULKSTAT & XFS_IOC_FSBULKSTAT_SINGLE - */ -typedef struct xfs_bstime { - time_t tv_sec; /* seconds */ - __s32 tv_nsec; /* and nanoseconds */ -} xfs_bstime_t; - -typedef struct xfs_bstat { - __u64 bs_ino; /* inode number */ - __u16 bs_mode; /* type and mode */ - __u16 bs_nlink; /* number of links */ - __u32 bs_uid; /* user id */ - __u32 bs_gid; /* group id */ - __u32 bs_rdev; /* device value */ - __s32 bs_blksize; /* block size */ - __s64 bs_size; /* file size */ - xfs_bstime_t bs_atime; /* access time */ - xfs_bstime_t bs_mtime; /* modify time */ - xfs_bstime_t bs_ctime; /* inode change time */ - int64_t bs_blocks; /* number of blocks */ - __u32 bs_xflags; /* extended flags */ - __s32 bs_extsize; /* extent size */ - __s32 bs_extents; /* number of extents */ - __u32 bs_gen; /* generation count */ - __u16 bs_projid_lo; /* lower part of project id */ -#define bs_projid bs_projid_lo /* (previously just bs_projid) */ - __u16 bs_forkoff; /* inode fork offset in bytes */ - __u16 bs_projid_hi; /* higher part of project id */ - unsigned char bs_pad[10]; /* pad space, unused */ - __u32 bs_dmevmask; /* DMIG event mask */ - __u16 bs_dmstate; /* DMIG state info */ - __u16 bs_aextents; /* attribute number of extents */ -} xfs_bstat_t; - -/* - * Project quota id helpers (previously projid was 16bit only - * and using two 16bit values to hold new 32bit projid was choosen - * to retain compatibility with "old" filesystems). - */ -static inline __uint32_t -bstat_get_projid(struct xfs_bstat *bs) -{ - return (__uint32_t)bs->bs_projid_hi << 16 | bs->bs_projid_lo; -} - -/* - * The user-level BulkStat Request interface structure. - */ -typedef struct xfs_fsop_bulkreq { - __u64 __user *lastip; /* last inode # pointer */ - __s32 icount; /* count of entries in buffer */ - void __user *ubuffer;/* user buffer for inode desc. */ - __s32 __user *ocount; /* output count pointer */ -} xfs_fsop_bulkreq_t; - - -/* - * Structures returned from xfs_inumbers routine (XFS_IOC_FSINUMBERS). - */ -typedef struct xfs_inogrp { - __u64 xi_startino; /* starting inode number */ - __s32 xi_alloccount; /* # bits set in allocmask */ - __u64 xi_allocmask; /* mask of allocated inodes */ -} xfs_inogrp_t; - - -/* - * Error injection. - */ -typedef struct xfs_error_injection { - __s32 fd; - __s32 errtag; -} xfs_error_injection_t; - - -/* - * Speculative preallocation trimming. - */ -#define XFS_EOFBLOCKS_VERSION 1 -struct xfs_fs_eofblocks { - __u32 eof_version; - __u32 eof_flags; - uid_t eof_uid; - gid_t eof_gid; - prid_t eof_prid; - __u32 pad32; - __u64 eof_min_file_size; - __u64 pad64[12]; -}; - -/* eof_flags values */ -#define XFS_EOF_FLAGS_SYNC (1 << 0) /* sync/wait mode scan */ -#define XFS_EOF_FLAGS_UID (1 << 1) /* filter by uid */ -#define XFS_EOF_FLAGS_GID (1 << 2) /* filter by gid */ -#define XFS_EOF_FLAGS_PRID (1 << 3) /* filter by project id */ -#define XFS_EOF_FLAGS_MINFILESIZE (1 << 4) /* filter by min file size */ -#define XFS_EOF_FLAGS_UNION (1 << 5) /* union filter algorithm; - * kernel only, not included in - * valid mask */ -#define XFS_EOF_FLAGS_VALID \ - (XFS_EOF_FLAGS_SYNC | \ - XFS_EOF_FLAGS_UID | \ - XFS_EOF_FLAGS_GID | \ - XFS_EOF_FLAGS_PRID | \ - XFS_EOF_FLAGS_MINFILESIZE) - - -/* - * The user-level Handle Request interface structure. - */ -typedef struct xfs_fsop_handlereq { - __u32 fd; /* fd for FD_TO_HANDLE */ - void __user *path; /* user pathname */ - __u32 oflags; /* open flags */ - void __user *ihandle;/* user supplied handle */ - __u32 ihandlen; /* user supplied length */ - void __user *ohandle;/* user buffer for handle */ - __u32 __user *ohandlen;/* user buffer length */ -} xfs_fsop_handlereq_t; - -/* - * Compound structures for passing args through Handle Request interfaces - * xfs_fssetdm_by_handle, xfs_attrlist_by_handle, xfs_attrmulti_by_handle - * - ioctls: XFS_IOC_FSSETDM_BY_HANDLE, XFS_IOC_ATTRLIST_BY_HANDLE, and - * XFS_IOC_ATTRMULTI_BY_HANDLE - */ - -typedef struct xfs_fsop_setdm_handlereq { - struct xfs_fsop_handlereq hreq; /* handle information */ - struct fsdmidata __user *data; /* DMAPI data */ -} xfs_fsop_setdm_handlereq_t; - -typedef struct xfs_attrlist_cursor { - __u32 opaque[4]; -} xfs_attrlist_cursor_t; - -typedef struct xfs_fsop_attrlist_handlereq { - struct xfs_fsop_handlereq hreq; /* handle interface structure */ - struct xfs_attrlist_cursor pos; /* opaque cookie, list offset */ - __u32 flags; /* which namespace to use */ - __u32 buflen; /* length of buffer supplied */ - void __user *buffer; /* returned names */ -} xfs_fsop_attrlist_handlereq_t; - -typedef struct xfs_attr_multiop { - __u32 am_opcode; -#define ATTR_OP_GET 1 /* return the indicated attr's value */ -#define ATTR_OP_SET 2 /* set/create the indicated attr/value pair */ -#define ATTR_OP_REMOVE 3 /* remove the indicated attr */ - __s32 am_error; - void __user *am_attrname; - void __user *am_attrvalue; - __u32 am_length; - __u32 am_flags; -} xfs_attr_multiop_t; - -typedef struct xfs_fsop_attrmulti_handlereq { - struct xfs_fsop_handlereq hreq; /* handle interface structure */ - __u32 opcount;/* count of following multiop */ - struct xfs_attr_multiop __user *ops; /* attr_multi data */ -} xfs_fsop_attrmulti_handlereq_t; - -/* - * per machine unique filesystem identifier types. - */ -typedef struct { __u32 val[2]; } xfs_fsid_t; /* file system id type */ - -typedef struct xfs_fid { - __u16 fid_len; /* length of remainder */ - __u16 fid_pad; - __u32 fid_gen; /* generation number */ - __u64 fid_ino; /* 64 bits inode number */ -} xfs_fid_t; - -typedef struct xfs_handle { - union { - __s64 align; /* force alignment of ha_fid */ - xfs_fsid_t _ha_fsid; /* unique file system identifier */ - } ha_u; - xfs_fid_t ha_fid; /* file system specific file ID */ -} xfs_handle_t; -#define ha_fsid ha_u._ha_fsid - -#define XFS_HSIZE(handle) (((char *) &(handle).ha_fid.fid_pad \ - - (char *) &(handle)) \ - + (handle).ha_fid.fid_len) - -/* - * Structure passed to XFS_IOC_SWAPEXT - */ -typedef struct xfs_swapext -{ - __int64_t sx_version; /* version */ -#define XFS_SX_VERSION 0 - __int64_t sx_fdtarget; /* fd of target file */ - __int64_t sx_fdtmp; /* fd of tmp file */ - xfs_off_t sx_offset; /* offset into file */ - xfs_off_t sx_length; /* leng from offset */ - char sx_pad[16]; /* pad space, unused */ - xfs_bstat_t sx_stat; /* stat of target b4 copy */ -} xfs_swapext_t; - -/* - * Flags for going down operation - */ -#define XFS_FSOP_GOING_FLAGS_DEFAULT 0x0 /* going down */ -#define XFS_FSOP_GOING_FLAGS_LOGFLUSH 0x1 /* flush log but not data */ -#define XFS_FSOP_GOING_FLAGS_NOLOGFLUSH 0x2 /* don't flush log nor data */ - -/* - * ioctl commands that are used by Linux filesystems - */ -#define XFS_IOC_GETXFLAGS FS_IOC_GETFLAGS -#define XFS_IOC_SETXFLAGS FS_IOC_SETFLAGS -#define XFS_IOC_GETVERSION FS_IOC_GETVERSION - -/* - * ioctl commands that replace IRIX fcntl()'s - * For 'documentation' purposed more than anything else, - * the "cmd #" field reflects the IRIX fcntl number. - */ -#define XFS_IOC_ALLOCSP _IOW ('X', 10, struct xfs_flock64) -#define XFS_IOC_FREESP _IOW ('X', 11, struct xfs_flock64) -#define XFS_IOC_DIOINFO _IOR ('X', 30, struct dioattr) -#define XFS_IOC_FSGETXATTR _IOR ('X', 31, struct fsxattr) -#define XFS_IOC_FSSETXATTR _IOW ('X', 32, struct fsxattr) -#define XFS_IOC_ALLOCSP64 _IOW ('X', 36, struct xfs_flock64) -#define XFS_IOC_FREESP64 _IOW ('X', 37, struct xfs_flock64) -#define XFS_IOC_GETBMAP _IOWR('X', 38, struct getbmap) -#define XFS_IOC_FSSETDM _IOW ('X', 39, struct fsdmidata) -#define XFS_IOC_RESVSP _IOW ('X', 40, struct xfs_flock64) -#define XFS_IOC_UNRESVSP _IOW ('X', 41, struct xfs_flock64) -#define XFS_IOC_RESVSP64 _IOW ('X', 42, struct xfs_flock64) -#define XFS_IOC_UNRESVSP64 _IOW ('X', 43, struct xfs_flock64) -#define XFS_IOC_GETBMAPA _IOWR('X', 44, struct getbmap) -#define XFS_IOC_FSGETXATTRA _IOR ('X', 45, struct fsxattr) -/* XFS_IOC_SETBIOSIZE ---- deprecated 46 */ -/* XFS_IOC_GETBIOSIZE ---- deprecated 47 */ -#define XFS_IOC_GETBMAPX _IOWR('X', 56, struct getbmap) -#define XFS_IOC_ZERO_RANGE _IOW ('X', 57, struct xfs_flock64) -#define XFS_IOC_FREE_EOFBLOCKS _IOR ('X', 58, struct xfs_fs_eofblocks) - -/* - * ioctl commands that replace IRIX syssgi()'s - */ -#define XFS_IOC_FSGEOMETRY_V1 _IOR ('X', 100, struct xfs_fsop_geom_v1) -#define XFS_IOC_FSBULKSTAT _IOWR('X', 101, struct xfs_fsop_bulkreq) -#define XFS_IOC_FSBULKSTAT_SINGLE _IOWR('X', 102, struct xfs_fsop_bulkreq) -#define XFS_IOC_FSINUMBERS _IOWR('X', 103, struct xfs_fsop_bulkreq) -#define XFS_IOC_PATH_TO_FSHANDLE _IOWR('X', 104, struct xfs_fsop_handlereq) -#define XFS_IOC_PATH_TO_HANDLE _IOWR('X', 105, struct xfs_fsop_handlereq) -#define XFS_IOC_FD_TO_HANDLE _IOWR('X', 106, struct xfs_fsop_handlereq) -#define XFS_IOC_OPEN_BY_HANDLE _IOWR('X', 107, struct xfs_fsop_handlereq) -#define XFS_IOC_READLINK_BY_HANDLE _IOWR('X', 108, struct xfs_fsop_handlereq) -#define XFS_IOC_SWAPEXT _IOWR('X', 109, struct xfs_swapext) -#define XFS_IOC_FSGROWFSDATA _IOW ('X', 110, struct xfs_growfs_data) -#define XFS_IOC_FSGROWFSLOG _IOW ('X', 111, struct xfs_growfs_log) -#define XFS_IOC_FSGROWFSRT _IOW ('X', 112, struct xfs_growfs_rt) -#define XFS_IOC_FSCOUNTS _IOR ('X', 113, struct xfs_fsop_counts) -#define XFS_IOC_SET_RESBLKS _IOWR('X', 114, struct xfs_fsop_resblks) -#define XFS_IOC_GET_RESBLKS _IOR ('X', 115, struct xfs_fsop_resblks) -#define XFS_IOC_ERROR_INJECTION _IOW ('X', 116, struct xfs_error_injection) -#define XFS_IOC_ERROR_CLEARALL _IOW ('X', 117, struct xfs_error_injection) -/* XFS_IOC_ATTRCTL_BY_HANDLE -- deprecated 118 */ - -/* XFS_IOC_FREEZE -- FIFREEZE 119 */ -/* XFS_IOC_THAW -- FITHAW 120 */ -#ifndef FIFREEZE -#define XFS_IOC_FREEZE _IOWR('X', 119, int) -#define XFS_IOC_THAW _IOWR('X', 120, int) -#endif - -#define XFS_IOC_FSSETDM_BY_HANDLE _IOW ('X', 121, struct xfs_fsop_setdm_handlereq) -#define XFS_IOC_ATTRLIST_BY_HANDLE _IOW ('X', 122, struct xfs_fsop_attrlist_handlereq) -#define XFS_IOC_ATTRMULTI_BY_HANDLE _IOW ('X', 123, struct xfs_fsop_attrmulti_handlereq) -#define XFS_IOC_FSGEOMETRY _IOR ('X', 124, struct xfs_fsop_geom) -#define XFS_IOC_GOINGDOWN _IOR ('X', 125, __uint32_t) -/* XFS_IOC_GETFSUUID ---------- deprecated 140 */ - - -#ifndef HAVE_BBMACROS -/* - * Block I/O parameterization. A basic block (BB) is the lowest size of - * filesystem allocation, and must equal 512. Length units given to bio - * routines are in BB's. - */ -#define BBSHIFT 9 -#define BBSIZE (1<> BBSHIFT) -#define BTOBBT(bytes) ((__u64)(bytes) >> BBSHIFT) -#define BBTOB(bbs) ((bbs) << BBSHIFT) -#endif - -#endif /* __XFS_FS_H__ */ -- cgit v0.10.2 From 5ebdc213ac02877e23fe7594d4b92b120488aac9 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Fri, 9 Jan 2015 10:46:31 +1100 Subject: xfs: move xfs_types.h to libxfs The types used by the core XFS code are common between kernel and userspace. xfs_types.h is duplicated in both kernel and userspace, so move it to libxfs along with all the other shared code. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Signed-off-by: Dave Chinner diff --git a/fs/xfs/libxfs/xfs_types.h b/fs/xfs/libxfs/xfs_types.h new file mode 100644 index 0000000..b79dc66 --- /dev/null +++ b/fs/xfs/libxfs/xfs_types.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2000-2005 Silicon Graphics, Inc. + * 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. + * + * This program is distributed in the hope that it would 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 the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __XFS_TYPES_H__ +#define __XFS_TYPES_H__ + +typedef __uint32_t prid_t; /* project ID */ + +typedef __uint32_t xfs_agblock_t; /* blockno in alloc. group */ +typedef __uint32_t xfs_agino_t; /* inode # within allocation grp */ +typedef __uint32_t xfs_extlen_t; /* extent length in blocks */ +typedef __uint32_t xfs_agnumber_t; /* allocation group number */ +typedef __int32_t xfs_extnum_t; /* # of extents in a file */ +typedef __int16_t xfs_aextnum_t; /* # extents in an attribute fork */ +typedef __int64_t xfs_fsize_t; /* bytes in a file */ +typedef __uint64_t xfs_ufsize_t; /* unsigned bytes in a file */ + +typedef __int32_t xfs_suminfo_t; /* type of bitmap summary info */ +typedef __int32_t xfs_rtword_t; /* word type for bitmap manipulations */ + +typedef __int64_t xfs_lsn_t; /* log sequence number */ +typedef __int32_t xfs_tid_t; /* transaction identifier */ + +typedef __uint32_t xfs_dablk_t; /* dir/attr block number (in file) */ +typedef __uint32_t xfs_dahash_t; /* dir/attr hash value */ + +typedef __uint64_t xfs_fsblock_t; /* blockno in filesystem (agno|agbno) */ +typedef __uint64_t xfs_rfsblock_t; /* blockno in filesystem (raw) */ +typedef __uint64_t xfs_rtblock_t; /* extent (block) in realtime area */ +typedef __uint64_t xfs_fileoff_t; /* block number in a file */ +typedef __uint64_t xfs_filblks_t; /* number of blocks in a file */ + +typedef __int64_t xfs_srtblock_t; /* signed version of xfs_rtblock_t */ +typedef __int64_t xfs_sfiloff_t; /* signed block number in a file */ + +/* + * Null values for the types. + */ +#define NULLFSBLOCK ((xfs_fsblock_t)-1) +#define NULLRFSBLOCK ((xfs_rfsblock_t)-1) +#define NULLRTBLOCK ((xfs_rtblock_t)-1) +#define NULLFILEOFF ((xfs_fileoff_t)-1) + +#define NULLAGBLOCK ((xfs_agblock_t)-1) +#define NULLAGNUMBER ((xfs_agnumber_t)-1) +#define NULLEXTNUM ((xfs_extnum_t)-1) + +#define NULLCOMMITLSN ((xfs_lsn_t)-1) + +#define NULLFSINO ((xfs_ino_t)-1) +#define NULLAGINO ((xfs_agino_t)-1) + +/* + * Max values for extlen, extnum, aextnum. + */ +#define MAXEXTLEN ((xfs_extlen_t)0x001fffff) /* 21 bits */ +#define MAXEXTNUM ((xfs_extnum_t)0x7fffffff) /* signed int */ +#define MAXAEXTNUM ((xfs_aextnum_t)0x7fff) /* signed short */ + +/* + * Minimum and maximum blocksize and sectorsize. + * The blocksize upper limit is pretty much arbitrary. + * The sectorsize upper limit is due to sizeof(sb_sectsize). + */ +#define XFS_MIN_BLOCKSIZE_LOG 9 /* i.e. 512 bytes */ +#define XFS_MAX_BLOCKSIZE_LOG 16 /* i.e. 65536 bytes */ +#define XFS_MIN_BLOCKSIZE (1 << XFS_MIN_BLOCKSIZE_LOG) +#define XFS_MAX_BLOCKSIZE (1 << XFS_MAX_BLOCKSIZE_LOG) +#define XFS_MIN_SECTORSIZE_LOG 9 /* i.e. 512 bytes */ +#define XFS_MAX_SECTORSIZE_LOG 15 /* i.e. 32768 bytes */ +#define XFS_MIN_SECTORSIZE (1 << XFS_MIN_SECTORSIZE_LOG) +#define XFS_MAX_SECTORSIZE (1 << XFS_MAX_SECTORSIZE_LOG) + +/* + * Inode fork identifiers. + */ +#define XFS_DATA_FORK 0 +#define XFS_ATTR_FORK 1 + +/* + * Min numbers of data/attr fork btree root pointers. + */ +#define MINDBTPTRS 3 +#define MINABTPTRS 2 + +/* + * MAXNAMELEN is the length (including the terminating null) of + * the longest permissible file (component) name. + */ +#define MAXNAMELEN 256 + +typedef enum { + XFS_LOOKUP_EQi, XFS_LOOKUP_LEi, XFS_LOOKUP_GEi +} xfs_lookup_t; + +typedef enum { + XFS_BTNUM_BNOi, XFS_BTNUM_CNTi, XFS_BTNUM_BMAPi, XFS_BTNUM_INOi, + XFS_BTNUM_FINOi, XFS_BTNUM_MAX +} xfs_btnum_t; + +struct xfs_name { + const unsigned char *name; + int len; + int type; +}; + +/* + * uid_t and gid_t are hard-coded to 32 bits in the inode. + * Hence, an 'id' in a dquot is 32 bits.. + */ +typedef __uint32_t xfs_dqid_t; + +/* + * Constants for bit manipulations. + */ +#define XFS_NBBYLOG 3 /* log2(NBBY) */ +#define XFS_WORDLOG 2 /* log2(sizeof(xfs_rtword_t)) */ +#define XFS_NBWORDLOG (XFS_NBBYLOG + XFS_WORDLOG) +#define XFS_NBWORD (1 << XFS_NBWORDLOG) +#define XFS_WORDMASK ((1 << XFS_WORDLOG) - 1) + + +#endif /* __XFS_TYPES_H__ */ diff --git a/fs/xfs/xfs_types.h b/fs/xfs/xfs_types.h deleted file mode 100644 index b79dc66..0000000 --- a/fs/xfs/xfs_types.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2000-2005 Silicon Graphics, Inc. - * 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. - * - * This program is distributed in the hope that it would 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 the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __XFS_TYPES_H__ -#define __XFS_TYPES_H__ - -typedef __uint32_t prid_t; /* project ID */ - -typedef __uint32_t xfs_agblock_t; /* blockno in alloc. group */ -typedef __uint32_t xfs_agino_t; /* inode # within allocation grp */ -typedef __uint32_t xfs_extlen_t; /* extent length in blocks */ -typedef __uint32_t xfs_agnumber_t; /* allocation group number */ -typedef __int32_t xfs_extnum_t; /* # of extents in a file */ -typedef __int16_t xfs_aextnum_t; /* # extents in an attribute fork */ -typedef __int64_t xfs_fsize_t; /* bytes in a file */ -typedef __uint64_t xfs_ufsize_t; /* unsigned bytes in a file */ - -typedef __int32_t xfs_suminfo_t; /* type of bitmap summary info */ -typedef __int32_t xfs_rtword_t; /* word type for bitmap manipulations */ - -typedef __int64_t xfs_lsn_t; /* log sequence number */ -typedef __int32_t xfs_tid_t; /* transaction identifier */ - -typedef __uint32_t xfs_dablk_t; /* dir/attr block number (in file) */ -typedef __uint32_t xfs_dahash_t; /* dir/attr hash value */ - -typedef __uint64_t xfs_fsblock_t; /* blockno in filesystem (agno|agbno) */ -typedef __uint64_t xfs_rfsblock_t; /* blockno in filesystem (raw) */ -typedef __uint64_t xfs_rtblock_t; /* extent (block) in realtime area */ -typedef __uint64_t xfs_fileoff_t; /* block number in a file */ -typedef __uint64_t xfs_filblks_t; /* number of blocks in a file */ - -typedef __int64_t xfs_srtblock_t; /* signed version of xfs_rtblock_t */ -typedef __int64_t xfs_sfiloff_t; /* signed block number in a file */ - -/* - * Null values for the types. - */ -#define NULLFSBLOCK ((xfs_fsblock_t)-1) -#define NULLRFSBLOCK ((xfs_rfsblock_t)-1) -#define NULLRTBLOCK ((xfs_rtblock_t)-1) -#define NULLFILEOFF ((xfs_fileoff_t)-1) - -#define NULLAGBLOCK ((xfs_agblock_t)-1) -#define NULLAGNUMBER ((xfs_agnumber_t)-1) -#define NULLEXTNUM ((xfs_extnum_t)-1) - -#define NULLCOMMITLSN ((xfs_lsn_t)-1) - -#define NULLFSINO ((xfs_ino_t)-1) -#define NULLAGINO ((xfs_agino_t)-1) - -/* - * Max values for extlen, extnum, aextnum. - */ -#define MAXEXTLEN ((xfs_extlen_t)0x001fffff) /* 21 bits */ -#define MAXEXTNUM ((xfs_extnum_t)0x7fffffff) /* signed int */ -#define MAXAEXTNUM ((xfs_aextnum_t)0x7fff) /* signed short */ - -/* - * Minimum and maximum blocksize and sectorsize. - * The blocksize upper limit is pretty much arbitrary. - * The sectorsize upper limit is due to sizeof(sb_sectsize). - */ -#define XFS_MIN_BLOCKSIZE_LOG 9 /* i.e. 512 bytes */ -#define XFS_MAX_BLOCKSIZE_LOG 16 /* i.e. 65536 bytes */ -#define XFS_MIN_BLOCKSIZE (1 << XFS_MIN_BLOCKSIZE_LOG) -#define XFS_MAX_BLOCKSIZE (1 << XFS_MAX_BLOCKSIZE_LOG) -#define XFS_MIN_SECTORSIZE_LOG 9 /* i.e. 512 bytes */ -#define XFS_MAX_SECTORSIZE_LOG 15 /* i.e. 32768 bytes */ -#define XFS_MIN_SECTORSIZE (1 << XFS_MIN_SECTORSIZE_LOG) -#define XFS_MAX_SECTORSIZE (1 << XFS_MAX_SECTORSIZE_LOG) - -/* - * Inode fork identifiers. - */ -#define XFS_DATA_FORK 0 -#define XFS_ATTR_FORK 1 - -/* - * Min numbers of data/attr fork btree root pointers. - */ -#define MINDBTPTRS 3 -#define MINABTPTRS 2 - -/* - * MAXNAMELEN is the length (including the terminating null) of - * the longest permissible file (component) name. - */ -#define MAXNAMELEN 256 - -typedef enum { - XFS_LOOKUP_EQi, XFS_LOOKUP_LEi, XFS_LOOKUP_GEi -} xfs_lookup_t; - -typedef enum { - XFS_BTNUM_BNOi, XFS_BTNUM_CNTi, XFS_BTNUM_BMAPi, XFS_BTNUM_INOi, - XFS_BTNUM_FINOi, XFS_BTNUM_MAX -} xfs_btnum_t; - -struct xfs_name { - const unsigned char *name; - int len; - int type; -}; - -/* - * uid_t and gid_t are hard-coded to 32 bits in the inode. - * Hence, an 'id' in a dquot is 32 bits.. - */ -typedef __uint32_t xfs_dqid_t; - -/* - * Constants for bit manipulations. - */ -#define XFS_NBBYLOG 3 /* log2(NBBY) */ -#define XFS_WORDLOG 2 /* log2(sizeof(xfs_rtword_t)) */ -#define XFS_NBWORDLOG (XFS_NBBYLOG + XFS_WORDLOG) -#define XFS_NBWORD (1 << XFS_NBWORDLOG) -#define XFS_WORDMASK ((1 << XFS_WORDLOG) - 1) - - -#endif /* __XFS_TYPES_H__ */ -- cgit v0.10.2 From 9799b438ce21662fa173ffc0b30d93567a71dfa0 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Fri, 9 Jan 2015 10:46:49 +1100 Subject: xfs: move struct xfs_bmalloca to libxfs It no long is used for stack splits, so strip the kernel workqueue bits from it and push it back into libxfs/xfs_bmap.h so that it can be shared with the userspace code. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Signed-off-by: Dave Chinner diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h index 44db6db..856f53c 100644 --- a/fs/xfs/libxfs/xfs_bmap.h +++ b/fs/xfs/libxfs/xfs_bmap.h @@ -28,6 +28,37 @@ struct xfs_trans; extern kmem_zone_t *xfs_bmap_free_item_zone; /* + * Argument structure for xfs_bmap_alloc. + */ +struct xfs_bmalloca { + xfs_fsblock_t *firstblock; /* i/o first block allocated */ + struct xfs_bmap_free *flist; /* bmap freelist */ + struct xfs_trans *tp; /* transaction pointer */ + struct xfs_inode *ip; /* incore inode pointer */ + struct xfs_bmbt_irec prev; /* extent before the new one */ + struct xfs_bmbt_irec got; /* extent after, or delayed */ + + xfs_fileoff_t offset; /* offset in file filling in */ + xfs_extlen_t length; /* i/o length asked/allocated */ + xfs_fsblock_t blkno; /* starting block of new extent */ + + struct xfs_btree_cur *cur; /* btree cursor */ + xfs_extnum_t idx; /* current extent index */ + int nallocs;/* number of extents alloc'd */ + int logflags;/* flags for transaction logging */ + + xfs_extlen_t total; /* total blocks needed for xaction */ + xfs_extlen_t minlen; /* minimum allocation size (blocks) */ + xfs_extlen_t minleft; /* amount must be left after alloc */ + bool eof; /* set if allocating past last extent */ + bool wasdel; /* replacing a delayed allocation */ + bool userdata;/* set if is user data */ + bool aeof; /* allocated space at eof */ + bool conv; /* overwriting unwritten extents */ + int flags; +}; + +/* * List of extents to be free "later". * The list is kept sorted on xbf_startblock. */ diff --git a/fs/xfs/xfs_bmap_util.h b/fs/xfs/xfs_bmap_util.h index 2fdb72d..1fce0f3 100644 --- a/fs/xfs/xfs_bmap_util.h +++ b/fs/xfs/xfs_bmap_util.h @@ -26,40 +26,7 @@ struct xfs_ifork; struct xfs_inode; struct xfs_mount; struct xfs_trans; - -/* - * Argument structure for xfs_bmap_alloc. - */ -struct xfs_bmalloca { - xfs_fsblock_t *firstblock; /* i/o first block allocated */ - struct xfs_bmap_free *flist; /* bmap freelist */ - struct xfs_trans *tp; /* transaction pointer */ - struct xfs_inode *ip; /* incore inode pointer */ - struct xfs_bmbt_irec prev; /* extent before the new one */ - struct xfs_bmbt_irec got; /* extent after, or delayed */ - - xfs_fileoff_t offset; /* offset in file filling in */ - xfs_extlen_t length; /* i/o length asked/allocated */ - xfs_fsblock_t blkno; /* starting block of new extent */ - - struct xfs_btree_cur *cur; /* btree cursor */ - xfs_extnum_t idx; /* current extent index */ - int nallocs;/* number of extents alloc'd */ - int logflags;/* flags for transaction logging */ - - xfs_extlen_t total; /* total blocks needed for xaction */ - xfs_extlen_t minlen; /* minimum allocation size (blocks) */ - xfs_extlen_t minleft; /* amount must be left after alloc */ - bool eof; /* set if allocating past last extent */ - bool wasdel; /* replacing a delayed allocation */ - bool userdata;/* set if is user data */ - bool aeof; /* allocated space at eof */ - bool conv; /* overwriting unwritten extents */ - int flags; - struct completion *done; - struct work_struct work; - int result; -}; +struct xfs_bmalloca; int xfs_bmap_finish(struct xfs_trans **tp, struct xfs_bmap_free *flist, int *committed); -- cgit v0.10.2 From aa5d95c1b57792119804c587b9a468019219d7e0 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Fri, 9 Jan 2015 10:47:14 +1100 Subject: xfs: move xfs_bmap_finish prototype This function is used libxfs code, but is implemented separately in userspace. Move the function prototype to xfs_bmap.h so that the prototype is shared even if the implementations aren't. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Signed-off-by: Dave Chinner diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h index 856f53c..b9d8a49 100644 --- a/fs/xfs/libxfs/xfs_bmap.h +++ b/fs/xfs/libxfs/xfs_bmap.h @@ -180,6 +180,8 @@ void xfs_bmap_local_to_extents_empty(struct xfs_inode *ip, int whichfork); void xfs_bmap_add_free(xfs_fsblock_t bno, xfs_filblks_t len, struct xfs_bmap_free *flist, struct xfs_mount *mp); void xfs_bmap_cancel(struct xfs_bmap_free *flist); +int xfs_bmap_finish(struct xfs_trans **tp, struct xfs_bmap_free *flist, + int *committed); void xfs_bmap_compute_maxlevels(struct xfs_mount *mp, int whichfork); int xfs_bmap_first_unused(struct xfs_trans *tp, struct xfs_inode *ip, xfs_extlen_t len, xfs_fileoff_t *unused, int whichfork); diff --git a/fs/xfs/xfs_bmap_util.h b/fs/xfs/xfs_bmap_util.h index 1fce0f3..736429a 100644 --- a/fs/xfs/xfs_bmap_util.h +++ b/fs/xfs/xfs_bmap_util.h @@ -28,8 +28,6 @@ struct xfs_mount; struct xfs_trans; struct xfs_bmalloca; -int xfs_bmap_finish(struct xfs_trans **tp, struct xfs_bmap_free *flist, - int *committed); int xfs_bmap_rtalloc(struct xfs_bmalloca *ap); int xfs_bmap_eof(struct xfs_inode *ip, xfs_fileoff_t endoff, int whichfork, int *eof); -- cgit v0.10.2 From 64af7a6ea5a4c7e12ae79415250d054424b7e0c2 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Fri, 9 Jan 2015 10:47:43 +1100 Subject: xfs: remove deprecated sysctls xfsbufd_centisecs and age_buffer_centisecs were due for removal in 3.14. We forgot to do that - it's now well past time to remove these deprecated, unused sysctls. Signed-off-by: Dave Chinner Reviewed-by: Eric Sandeen Signed-off-by: Dave Chinner diff --git a/Documentation/filesystems/xfs.txt b/Documentation/filesystems/xfs.txt index 5be51fd..7077103 100644 --- a/Documentation/filesystems/xfs.txt +++ b/Documentation/filesystems/xfs.txt @@ -348,16 +348,13 @@ The following sysctls are available for the XFS filesystem: Deprecated Sysctls ================== - fs.xfs.xfsbufd_centisecs (Min: 50 Default: 100 Max: 3000) - Dirty metadata is now tracked by the log subsystem and - flushing is driven by log space and idling demands. The - xfsbufd no longer exists, so this syctl does nothing. +None at present. - Due for removal in 3.14. - fs.xfs.age_buffer_centisecs (Min: 100 Default: 1500 Max: 720000) - Dirty metadata is now tracked by the log subsystem and - flushing is driven by log space and idling demands. The - xfsbufd no longer exists, so this syctl does nothing. +Removed Sysctls +=============== - Due for removal in 3.14. + Name Removed + ---- ------- + fs.xfs.xfsbufd_centisec v3.20 + fs.xfs.age_buffer_centisecs v3.20 diff --git a/fs/xfs/xfs_sysctl.c b/fs/xfs/xfs_sysctl.c index 1743b9f..a0c8067 100644 --- a/fs/xfs/xfs_sysctl.c +++ b/fs/xfs/xfs_sysctl.c @@ -149,24 +149,6 @@ static struct ctl_table xfs_table[] = { .extra2 = &xfs_params.inherit_noatim.max }, { - .procname = "xfsbufd_centisecs", - .data = &xfs_params.xfs_buf_timer.val, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &xfs_params.xfs_buf_timer.min, - .extra2 = &xfs_params.xfs_buf_timer.max - }, - { - .procname = "age_buffer_centisecs", - .data = &xfs_params.xfs_buf_age.val, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &xfs_params.xfs_buf_age.min, - .extra2 = &xfs_params.xfs_buf_age.max - }, - { .procname = "inherit_nosymlinks", .data = &xfs_params.inherit_nosym.val, .maxlen = sizeof(int), -- cgit v0.10.2 From d32057fc84c141af22ddf07b58e52570e52369cd Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 9 Jan 2015 10:48:12 +1100 Subject: xfs: pass a 64-bit count argument to xfs_iomap_write_unwritten The code is already ready for it, and the pnfs layout commit code expects to be able to pass a larger than 32-bit argument. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index c980e2a..ccb1dd0 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -802,7 +802,7 @@ int xfs_iomap_write_unwritten( xfs_inode_t *ip, xfs_off_t offset, - size_t count) + xfs_off_t count) { xfs_mount_t *mp = ip->i_mount; xfs_fileoff_t offset_fsb; diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h index 411fbb8..8688e66 100644 --- a/fs/xfs/xfs_iomap.h +++ b/fs/xfs/xfs_iomap.h @@ -27,6 +27,6 @@ int xfs_iomap_write_delay(struct xfs_inode *, xfs_off_t, size_t, struct xfs_bmbt_irec *); int xfs_iomap_write_allocate(struct xfs_inode *, xfs_off_t, struct xfs_bmbt_irec *); -int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, size_t); +int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, xfs_off_t); #endif /* __XFS_IOMAP_H__*/ -- cgit v0.10.2 From 43fd1fce9643586e0995ee5d11fb40641575348a Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Fri, 9 Jan 2015 10:48:58 +1100 Subject: xfs: fix implicit bool to int conversion try_wait_for_completion returns bool so the wrapper function xfs_dqflock_nowait should probably also return bool and not int. Signed-off-by: Nicholas Mc Guire Reviewed-by: Brian Foster Signed-off-by: Dave Chinner diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index c24c67e..2f536f3 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -86,7 +86,7 @@ static inline void xfs_dqflock(xfs_dquot_t *dqp) wait_for_completion(&dqp->q_flush); } -static inline int xfs_dqflock_nowait(xfs_dquot_t *dqp) +static inline bool xfs_dqflock_nowait(xfs_dquot_t *dqp) { return try_wait_for_completion(&dqp->q_flush); } -- cgit v0.10.2 From 3b50d9029809b60a5081d90c282aa04d438d3ea1 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Tue, 6 Jan 2015 15:45:31 -0800 Subject: ipv6: fix redefinition of in6_pktinfo and ip6_mtuinfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both netinet/in.h and linux/ipv6.h define these two structs, if we include both of them, we got: /usr/include/linux/ipv6.h:19:8: error: redefinition of ‘struct in6_pktinfo’ struct in6_pktinfo { ^ In file included from /usr/include/arpa/inet.h:22:0, from txtimestamp.c:33: /usr/include/netinet/in.h:524:8: note: originally defined here struct in6_pktinfo ^ In file included from txtimestamp.c:40:0: /usr/include/linux/ipv6.h:24:8: error: redefinition of ‘struct ip6_mtuinfo’ struct ip6_mtuinfo { ^ In file included from /usr/include/arpa/inet.h:22:0, from txtimestamp.c:33: /usr/include/netinet/in.h:531:8: note: originally defined here struct ip6_mtuinfo ^ So similarly to what we did for in6_addr, we need to sync with libc header on their definitions. Signed-off-by: Cong Wang Signed-off-by: David S. Miller diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index e863d08..b9b1b7d 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -15,16 +15,19 @@ * *under construction* */ - +#if __UAPI_DEF_IN6_PKTINFO struct in6_pktinfo { struct in6_addr ipi6_addr; int ipi6_ifindex; }; +#endif +#if __UAPI_DEF_IP6_MTUINFO struct ip6_mtuinfo { struct sockaddr_in6 ip6m_addr; __u32 ip6m_mtu; }; +#endif struct in6_ifreq { struct in6_addr ifr6_addr; diff --git a/include/uapi/linux/libc-compat.h b/include/uapi/linux/libc-compat.h index e28807a..fa673e9 100644 --- a/include/uapi/linux/libc-compat.h +++ b/include/uapi/linux/libc-compat.h @@ -70,6 +70,8 @@ #define __UAPI_DEF_IPV6_MREQ 0 #define __UAPI_DEF_IPPROTO_V6 0 #define __UAPI_DEF_IPV6_OPTIONS 0 +#define __UAPI_DEF_IN6_PKTINFO 0 +#define __UAPI_DEF_IP6_MTUINFO 0 #else @@ -84,6 +86,8 @@ #define __UAPI_DEF_IPV6_MREQ 1 #define __UAPI_DEF_IPPROTO_V6 1 #define __UAPI_DEF_IPV6_OPTIONS 1 +#define __UAPI_DEF_IN6_PKTINFO 1 +#define __UAPI_DEF_IP6_MTUINFO 1 #endif /* _NETINET_IN_H */ @@ -106,6 +110,8 @@ #define __UAPI_DEF_IPV6_MREQ 1 #define __UAPI_DEF_IPPROTO_V6 1 #define __UAPI_DEF_IPV6_OPTIONS 1 +#define __UAPI_DEF_IN6_PKTINFO 1 +#define __UAPI_DEF_IP6_MTUINFO 1 /* Definitions for xattr.h */ #define __UAPI_DEF_XATTR 1 -- cgit v0.10.2 From cd91cc5bdddf50f5c775fe5baeef7891b31d8741 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Tue, 6 Jan 2015 15:45:32 -0800 Subject: doc: fix the compile error of txtimestamp.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vinson reported: HOSTCC Documentation/networking/timestamping/txtimestamp Documentation/networking/timestamping/txtimestamp.c:64:8: error: redefinition of ‘struct in6_pktinfo’ struct in6_pktinfo { ^ In file included from /usr/include/arpa/inet.h:23:0, from Documentation/networking/timestamping/txtimestamp.c:33: /usr/include/netinet/in.h:456:8: note: originally defined here struct in6_pktinfo ^ After we sync with libc header, we don't need this ugly hack any more. Reported-by: Vinson Lee Signed-off-by: Cong Wang Signed-off-by: David S. Miller diff --git a/Documentation/networking/timestamping/txtimestamp.c b/Documentation/networking/timestamping/txtimestamp.c index 876f71c..8778e68 100644 --- a/Documentation/networking/timestamping/txtimestamp.c +++ b/Documentation/networking/timestamping/txtimestamp.c @@ -59,14 +59,6 @@ #include #include -/* ugly hack to work around netinet/in.h and linux/ipv6.h conflicts */ -#ifndef in6_pktinfo -struct in6_pktinfo { - struct in6_addr ipi6_addr; - int ipi6_ifindex; -}; -#endif - /* command line parameters */ static int cfg_proto = SOCK_STREAM; static int cfg_ipproto = IPPROTO_TCP; -- cgit v0.10.2 From 49aa284ffe64c4c13d63d315aa374e0f4bdd4b7c Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 7 Jan 2015 08:48:00 +0530 Subject: cxgb4: Add support for devlog Add support for device log entry in debugfs Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index 5ab5c31..73b1f3a 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -290,11 +290,19 @@ enum chip_type { T5_LAST_REV = T5_A1, }; +struct devlog_params { + u32 memtype; /* which memory (EDC0, EDC1, MC) */ + u32 start; /* start of log in firmware memory */ + u32 size; /* size of log */ +}; + struct adapter_params { struct sge_params sge; struct tp_params tp; struct vpd_params vpd; struct pci_params pci; + struct devlog_params devlog; + enum pcie_memwin drv_memwin; unsigned int sf_size; /* serial flash size in bytes */ unsigned int sf_nsec; /* # of flash sectors */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index c98a350..1d3d1c5 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -43,6 +43,203 @@ #include "cxgb4_debugfs.h" #include "l2t.h" +/* Firmware Device Log dump. + */ +static const char * const devlog_level_strings[] = { + [FW_DEVLOG_LEVEL_EMERG] = "EMERG", + [FW_DEVLOG_LEVEL_CRIT] = "CRIT", + [FW_DEVLOG_LEVEL_ERR] = "ERR", + [FW_DEVLOG_LEVEL_NOTICE] = "NOTICE", + [FW_DEVLOG_LEVEL_INFO] = "INFO", + [FW_DEVLOG_LEVEL_DEBUG] = "DEBUG" +}; + +static const char * const devlog_facility_strings[] = { + [FW_DEVLOG_FACILITY_CORE] = "CORE", + [FW_DEVLOG_FACILITY_SCHED] = "SCHED", + [FW_DEVLOG_FACILITY_TIMER] = "TIMER", + [FW_DEVLOG_FACILITY_RES] = "RES", + [FW_DEVLOG_FACILITY_HW] = "HW", + [FW_DEVLOG_FACILITY_FLR] = "FLR", + [FW_DEVLOG_FACILITY_DMAQ] = "DMAQ", + [FW_DEVLOG_FACILITY_PHY] = "PHY", + [FW_DEVLOG_FACILITY_MAC] = "MAC", + [FW_DEVLOG_FACILITY_PORT] = "PORT", + [FW_DEVLOG_FACILITY_VI] = "VI", + [FW_DEVLOG_FACILITY_FILTER] = "FILTER", + [FW_DEVLOG_FACILITY_ACL] = "ACL", + [FW_DEVLOG_FACILITY_TM] = "TM", + [FW_DEVLOG_FACILITY_QFC] = "QFC", + [FW_DEVLOG_FACILITY_DCB] = "DCB", + [FW_DEVLOG_FACILITY_ETH] = "ETH", + [FW_DEVLOG_FACILITY_OFLD] = "OFLD", + [FW_DEVLOG_FACILITY_RI] = "RI", + [FW_DEVLOG_FACILITY_ISCSI] = "ISCSI", + [FW_DEVLOG_FACILITY_FCOE] = "FCOE", + [FW_DEVLOG_FACILITY_FOISCSI] = "FOISCSI", + [FW_DEVLOG_FACILITY_FOFCOE] = "FOFCOE" +}; + +/* Information gathered by Device Log Open routine for the display routine. + */ +struct devlog_info { + unsigned int nentries; /* number of entries in log[] */ + unsigned int first; /* first [temporal] entry in log[] */ + struct fw_devlog_e log[0]; /* Firmware Device Log */ +}; + +/* Dump a Firmaware Device Log entry. + */ +static int devlog_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN) + seq_printf(seq, "%10s %15s %8s %8s %s\n", + "Seq#", "Tstamp", "Level", "Facility", "Message"); + else { + struct devlog_info *dinfo = seq->private; + int fidx = (uintptr_t)v - 2; + unsigned long index; + struct fw_devlog_e *e; + + /* Get a pointer to the log entry to display. Skip unused log + * entries. + */ + index = dinfo->first + fidx; + if (index >= dinfo->nentries) + index -= dinfo->nentries; + e = &dinfo->log[index]; + if (e->timestamp == 0) + return 0; + + /* Print the message. This depends on the firmware using + * exactly the same formating strings as the kernel so we may + * eventually have to put a format interpreter in here ... + */ + seq_printf(seq, "%10d %15llu %8s %8s ", + e->seqno, e->timestamp, + (e->level < ARRAY_SIZE(devlog_level_strings) + ? devlog_level_strings[e->level] + : "UNKNOWN"), + (e->facility < ARRAY_SIZE(devlog_facility_strings) + ? devlog_facility_strings[e->facility] + : "UNKNOWN")); + seq_printf(seq, e->fmt, e->params[0], e->params[1], + e->params[2], e->params[3], e->params[4], + e->params[5], e->params[6], e->params[7]); + } + return 0; +} + +/* Sequential File Operations for Device Log. + */ +static inline void *devlog_get_idx(struct devlog_info *dinfo, loff_t pos) +{ + if (pos > dinfo->nentries) + return NULL; + + return (void *)(uintptr_t)(pos + 1); +} + +static void *devlog_start(struct seq_file *seq, loff_t *pos) +{ + struct devlog_info *dinfo = seq->private; + + return (*pos + ? devlog_get_idx(dinfo, *pos) + : SEQ_START_TOKEN); +} + +static void *devlog_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct devlog_info *dinfo = seq->private; + + (*pos)++; + return devlog_get_idx(dinfo, *pos); +} + +static void devlog_stop(struct seq_file *seq, void *v) +{ +} + +static const struct seq_operations devlog_seq_ops = { + .start = devlog_start, + .next = devlog_next, + .stop = devlog_stop, + .show = devlog_show +}; + +/* Set up for reading the firmware's device log. We read the entire log here + * and then display it incrementally in devlog_show(). + */ +static int devlog_open(struct inode *inode, struct file *file) +{ + struct adapter *adap = inode->i_private; + struct devlog_params *dparams = &adap->params.devlog; + struct devlog_info *dinfo; + unsigned int index; + u32 fseqno; + int ret; + + /* If we don't know where the log is we can't do anything. + */ + if (dparams->start == 0) + return -ENXIO; + + /* Allocate the space to read in the firmware's device log and set up + * for the iterated call to our display function. + */ + dinfo = __seq_open_private(file, &devlog_seq_ops, + sizeof(*dinfo) + dparams->size); + if (!dinfo) + return -ENOMEM; + + /* Record the basic log buffer information and read in the raw log. + */ + dinfo->nentries = (dparams->size / sizeof(struct fw_devlog_e)); + dinfo->first = 0; + spin_lock(&adap->win0_lock); + ret = t4_memory_rw(adap, adap->params.drv_memwin, dparams->memtype, + dparams->start, dparams->size, (__be32 *)dinfo->log, + T4_MEMORY_READ); + spin_unlock(&adap->win0_lock); + if (ret) { + seq_release_private(inode, file); + return ret; + } + + /* Translate log multi-byte integral elements into host native format + * and determine where the first entry in the log is. + */ + for (fseqno = ~((u32)0), index = 0; index < dinfo->nentries; index++) { + struct fw_devlog_e *e = &dinfo->log[index]; + int i; + __u32 seqno; + + if (e->timestamp == 0) + continue; + + e->timestamp = (__force __be64)be64_to_cpu(e->timestamp); + seqno = be32_to_cpu(e->seqno); + for (i = 0; i < 8; i++) + e->params[i] = + (__force __be32)be32_to_cpu(e->params[i]); + + if (seqno < fseqno) { + fseqno = seqno; + dinfo->first = index; + } + } + return 0; +} + +static const struct file_operations devlog_fops = { + .owner = THIS_MODULE, + .open = devlog_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private +}; + static ssize_t mem_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -121,6 +318,7 @@ int t4_setup_debugfs(struct adapter *adap) u32 size; static struct t4_debugfs_entry t4_debugfs_files[] = { + { "devlog", &devlog_fops, S_IRUSR, 0 }, { "l2t", &t4_l2t_fops, S_IRUSR, 0}, }; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 04e675b..6de41cc 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -5541,6 +5541,8 @@ static int adap_init0(struct adapter *adap) enum dev_state state; u32 params[7], val[7]; struct fw_caps_config_cmd caps_cmd; + struct fw_devlog_cmd devlog_cmd; + u32 devlog_meminfo; int reset = 1; /* Contact FW, advertising Master capability */ @@ -5621,6 +5623,30 @@ static int adap_init0(struct adapter *adap) if (ret < 0) goto bye; + /* Read firmware device log parameters. We really need to find a way + * to get these parameters initialized with some default values (which + * are likely to be correct) for the case where we either don't + * attache to the firmware or it's crashed when we probe the adapter. + * That way we'll still be able to perform early firmware startup + * debugging ... If the request to get the Firmware's Device Log + * parameters fails, we'll live so we don't make that a fatal error. + */ + memset(&devlog_cmd, 0, sizeof(devlog_cmd)); + devlog_cmd.op_to_write = htonl(FW_CMD_OP_V(FW_DEVLOG_CMD) | + FW_CMD_REQUEST_F | FW_CMD_READ_F); + devlog_cmd.retval_len16 = htonl(FW_LEN16(devlog_cmd)); + ret = t4_wr_mbox(adap, adap->mbox, &devlog_cmd, sizeof(devlog_cmd), + &devlog_cmd); + if (ret == 0) { + devlog_meminfo = + ntohl(devlog_cmd.memtype_devlog_memaddr16_devlog); + adap->params.devlog.memtype = + FW_DEVLOG_CMD_MEMTYPE_DEVLOG_G(devlog_meminfo); + adap->params.devlog.start = + FW_DEVLOG_CMD_MEMADDR16_DEVLOG_G(devlog_meminfo) << 4; + adap->params.devlog.size = ntohl(devlog_cmd.memsize_devlog); + } + /* * Find out what ports are available to us. Note that we need to do * this before calling adap_init0_no_config() since it needs nports diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h index c19a90e..5e5eee6 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h @@ -110,6 +110,18 @@ enum { SGE_INGPADBOUNDARY_SHIFT = 5,/* ingress queue pad boundary */ }; +/* PCI-e memory window access */ +enum pcie_memwin { + MEMWIN_NIC = 0, + MEMWIN_RSVD1 = 1, + MEMWIN_RSVD2 = 2, + MEMWIN_RDMA = 3, + MEMWIN_RSVD4 = 4, + MEMWIN_FOISCSI = 5, + MEMWIN_CSIOSTOR = 6, + MEMWIN_RSVD7 = 7, +}; + struct sge_qstat { /* data written to SGE queue status entries */ __be32 qid; __be16 cidx; diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h index 7c0aec8..de82833 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h @@ -673,6 +673,7 @@ enum fw_cmd_opcodes { FW_RSS_IND_TBL_CMD = 0x20, FW_RSS_GLB_CONFIG_CMD = 0x22, FW_RSS_VI_CONFIG_CMD = 0x23, + FW_DEVLOG_CMD = 0x25, FW_CLIP_CMD = 0x28, FW_LASTC2E_CMD = 0x40, FW_ERROR_CMD = 0x80, @@ -3038,4 +3039,84 @@ enum fw_hdr_flags { FW_HDR_FLAGS_RESET_HALT = 0x00000001, }; +/* length of the formatting string */ +#define FW_DEVLOG_FMT_LEN 192 + +/* maximum number of the formatting string parameters */ +#define FW_DEVLOG_FMT_PARAMS_NUM 8 + +/* priority levels */ +enum fw_devlog_level { + FW_DEVLOG_LEVEL_EMERG = 0x0, + FW_DEVLOG_LEVEL_CRIT = 0x1, + FW_DEVLOG_LEVEL_ERR = 0x2, + FW_DEVLOG_LEVEL_NOTICE = 0x3, + FW_DEVLOG_LEVEL_INFO = 0x4, + FW_DEVLOG_LEVEL_DEBUG = 0x5, + FW_DEVLOG_LEVEL_MAX = 0x5, +}; + +/* facilities that may send a log message */ +enum fw_devlog_facility { + FW_DEVLOG_FACILITY_CORE = 0x00, + FW_DEVLOG_FACILITY_CF = 0x01, + FW_DEVLOG_FACILITY_SCHED = 0x02, + FW_DEVLOG_FACILITY_TIMER = 0x04, + FW_DEVLOG_FACILITY_RES = 0x06, + FW_DEVLOG_FACILITY_HW = 0x08, + FW_DEVLOG_FACILITY_FLR = 0x10, + FW_DEVLOG_FACILITY_DMAQ = 0x12, + FW_DEVLOG_FACILITY_PHY = 0x14, + FW_DEVLOG_FACILITY_MAC = 0x16, + FW_DEVLOG_FACILITY_PORT = 0x18, + FW_DEVLOG_FACILITY_VI = 0x1A, + FW_DEVLOG_FACILITY_FILTER = 0x1C, + FW_DEVLOG_FACILITY_ACL = 0x1E, + FW_DEVLOG_FACILITY_TM = 0x20, + FW_DEVLOG_FACILITY_QFC = 0x22, + FW_DEVLOG_FACILITY_DCB = 0x24, + FW_DEVLOG_FACILITY_ETH = 0x26, + FW_DEVLOG_FACILITY_OFLD = 0x28, + FW_DEVLOG_FACILITY_RI = 0x2A, + FW_DEVLOG_FACILITY_ISCSI = 0x2C, + FW_DEVLOG_FACILITY_FCOE = 0x2E, + FW_DEVLOG_FACILITY_FOISCSI = 0x30, + FW_DEVLOG_FACILITY_FOFCOE = 0x32, + FW_DEVLOG_FACILITY_MAX = 0x32, +}; + +/* log message format */ +struct fw_devlog_e { + __be64 timestamp; + __be32 seqno; + __be16 reserved1; + __u8 level; + __u8 facility; + __u8 fmt[FW_DEVLOG_FMT_LEN]; + __be32 params[FW_DEVLOG_FMT_PARAMS_NUM]; + __be32 reserved3[4]; +}; + +struct fw_devlog_cmd { + __be32 op_to_write; + __be32 retval_len16; + __u8 level; + __u8 r2[7]; + __be32 memtype_devlog_memaddr16_devlog; + __be32 memsize_devlog; + __be32 r3[2]; +}; + +#define FW_DEVLOG_CMD_MEMTYPE_DEVLOG_S 28 +#define FW_DEVLOG_CMD_MEMTYPE_DEVLOG_M 0xf +#define FW_DEVLOG_CMD_MEMTYPE_DEVLOG_G(x) \ + (((x) >> FW_DEVLOG_CMD_MEMTYPE_DEVLOG_S) & \ + FW_DEVLOG_CMD_MEMTYPE_DEVLOG_M) + +#define FW_DEVLOG_CMD_MEMADDR16_DEVLOG_S 0 +#define FW_DEVLOG_CMD_MEMADDR16_DEVLOG_M 0xfffffff +#define FW_DEVLOG_CMD_MEMADDR16_DEVLOG_G(x) \ + (((x) >> FW_DEVLOG_CMD_MEMADDR16_DEVLOG_S) & \ + FW_DEVLOG_CMD_MEMADDR16_DEVLOG_M) + #endif /* _T4FW_INTERFACE_H_ */ -- cgit v0.10.2 From f1ff24aa95f5612c7744d1ff9b8394a9e09bc6c9 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 7 Jan 2015 08:48:01 +0530 Subject: cxgb4: Add support for cim_la entry in debugfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CIM LA captures the embedded processor’s internal state. Optionally, it can also trace the flow of data in and out of the embedded processor. Therefore, the CIM LA output contains detailed information of what code the embedded processor executed prior to the CIM LA capture. Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index 73b1f3a..46cd506 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -304,6 +304,8 @@ struct adapter_params { struct devlog_params devlog; enum pcie_memwin drv_memwin; + unsigned int cim_la_size; + unsigned int sf_size; /* serial flash size in bytes */ unsigned int sf_nsec; /* # of flash sectors */ unsigned int sf_fw_start; /* start of FW image in flash */ @@ -1034,6 +1036,11 @@ int t4_mc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *parity); int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *parity); +int t4_cim_read(struct adapter *adap, unsigned int addr, unsigned int n, + unsigned int *valp); +int t4_cim_write(struct adapter *adap, unsigned int addr, unsigned int n, + const unsigned int *valp); +int t4_cim_read_la(struct adapter *adap, u32 *la_buf, unsigned int *wrptr); const char *t4_get_port_type_description(enum fw_port_type port_type); void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p); void t4_read_mtu_tbl(struct adapter *adap, u16 *mtus, u8 *mtu_log); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index 1d3d1c5..0f7b23f 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -43,8 +43,132 @@ #include "cxgb4_debugfs.h" #include "l2t.h" -/* Firmware Device Log dump. - */ +/* generic seq_file support for showing a table of size rows x width. */ +static void *seq_tab_get_idx(struct seq_tab *tb, loff_t pos) +{ + pos -= tb->skip_first; + return pos >= tb->rows ? NULL : &tb->data[pos * tb->width]; +} + +static void *seq_tab_start(struct seq_file *seq, loff_t *pos) +{ + struct seq_tab *tb = seq->private; + + if (tb->skip_first && *pos == 0) + return SEQ_START_TOKEN; + + return seq_tab_get_idx(tb, *pos); +} + +static void *seq_tab_next(struct seq_file *seq, void *v, loff_t *pos) +{ + v = seq_tab_get_idx(seq->private, *pos + 1); + if (v) + ++*pos; + return v; +} + +static void seq_tab_stop(struct seq_file *seq, void *v) +{ +} + +static int seq_tab_show(struct seq_file *seq, void *v) +{ + const struct seq_tab *tb = seq->private; + + return tb->show(seq, v, ((char *)v - tb->data) / tb->width); +} + +static const struct seq_operations seq_tab_ops = { + .start = seq_tab_start, + .next = seq_tab_next, + .stop = seq_tab_stop, + .show = seq_tab_show +}; + +struct seq_tab *seq_open_tab(struct file *f, unsigned int rows, + unsigned int width, unsigned int have_header, + int (*show)(struct seq_file *seq, void *v, int i)) +{ + struct seq_tab *p; + + p = __seq_open_private(f, &seq_tab_ops, sizeof(*p) + rows * width); + if (p) { + p->show = show; + p->rows = rows; + p->width = width; + p->skip_first = have_header != 0; + } + return p; +} + +static int cim_la_show(struct seq_file *seq, void *v, int idx) +{ + if (v == SEQ_START_TOKEN) + seq_puts(seq, "Status Data PC LS0Stat LS0Addr " + " LS0Data\n"); + else { + const u32 *p = v; + + seq_printf(seq, + " %02x %x%07x %x%07x %08x %08x %08x%08x%08x%08x\n", + (p[0] >> 4) & 0xff, p[0] & 0xf, p[1] >> 4, + p[1] & 0xf, p[2] >> 4, p[2] & 0xf, p[3], p[4], p[5], + p[6], p[7]); + } + return 0; +} + +static int cim_la_show_3in1(struct seq_file *seq, void *v, int idx) +{ + if (v == SEQ_START_TOKEN) { + seq_puts(seq, "Status Data PC\n"); + } else { + const u32 *p = v; + + seq_printf(seq, " %02x %08x %08x\n", p[5] & 0xff, p[6], + p[7]); + seq_printf(seq, " %02x %02x%06x %02x%06x\n", + (p[3] >> 8) & 0xff, p[3] & 0xff, p[4] >> 8, + p[4] & 0xff, p[5] >> 8); + seq_printf(seq, " %02x %x%07x %x%07x\n", (p[0] >> 4) & 0xff, + p[0] & 0xf, p[1] >> 4, p[1] & 0xf, p[2] >> 4); + } + return 0; +} + +static int cim_la_open(struct inode *inode, struct file *file) +{ + int ret; + unsigned int cfg; + struct seq_tab *p; + struct adapter *adap = inode->i_private; + + ret = t4_cim_read(adap, UP_UP_DBG_LA_CFG_A, 1, &cfg); + if (ret) + return ret; + + p = seq_open_tab(file, adap->params.cim_la_size / 8, 8 * sizeof(u32), 1, + cfg & UPDBGLACAPTPCONLY_F ? + cim_la_show_3in1 : cim_la_show); + if (!p) + return -ENOMEM; + + ret = t4_cim_read_la(adap, (u32 *)p->data, NULL); + if (ret) + seq_release_private(inode, file); + return ret; +} + +static const struct file_operations cim_la_fops = { + .owner = THIS_MODULE, + .open = cim_la_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private +}; + +/* Firmware Device Log dump. */ static const char * const devlog_level_strings[] = { [FW_DEVLOG_LEVEL_EMERG] = "EMERG", [FW_DEVLOG_LEVEL_CRIT] = "CRIT", @@ -318,6 +442,7 @@ int t4_setup_debugfs(struct adapter *adap) u32 size; static struct t4_debugfs_entry t4_debugfs_files[] = { + { "cim_la", &cim_la_fops, S_IRUSR, 0 }, { "devlog", &devlog_fops, S_IRUSR, 0 }, { "l2t", &t4_l2t_fops, S_IRUSR, 0}, }; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.h index a3d8867..70fcbc9 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.h @@ -44,6 +44,18 @@ struct t4_debugfs_entry { unsigned char data; }; +struct seq_tab { + int (*show)(struct seq_file *seq, void *v, int idx); + unsigned int rows; /* # of entries */ + unsigned char width; /* size in bytes of each entry */ + unsigned char skip_first; /* whether the first line is a header */ + char data[0]; /* the table data */ +}; + +struct seq_tab *seq_open_tab(struct file *f, unsigned int rows, + unsigned int width, unsigned int have_header, + int (*show)(struct seq_file *seq, void *v, int i)); + int t4_setup_debugfs(struct adapter *adap); void add_debugfs_files(struct adapter *adap, struct t4_debugfs_entry *files, diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 3776279..1e30554 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -4031,6 +4031,7 @@ int t4_prep_adapter(struct adapter *adapter) return -EINVAL; } + adapter->params.cim_la_size = CIMLA_SIZE; init_cong_ctrl(adapter->params.a_wnd, adapter->params.b_wnd); /* @@ -4323,3 +4324,122 @@ int t4_port_init(struct adapter *adap, int mbox, int pf, int vf) } return 0; } + +/** + * t4_cim_read - read a block from CIM internal address space + * @adap: the adapter + * @addr: the start address within the CIM address space + * @n: number of words to read + * @valp: where to store the result + * + * Reads a block of 4-byte words from the CIM intenal address space. + */ +int t4_cim_read(struct adapter *adap, unsigned int addr, unsigned int n, + unsigned int *valp) +{ + int ret = 0; + + if (t4_read_reg(adap, CIM_HOST_ACC_CTRL_A) & HOSTBUSY_F) + return -EBUSY; + + for ( ; !ret && n--; addr += 4) { + t4_write_reg(adap, CIM_HOST_ACC_CTRL_A, addr); + ret = t4_wait_op_done(adap, CIM_HOST_ACC_CTRL_A, HOSTBUSY_F, + 0, 5, 2); + if (!ret) + *valp++ = t4_read_reg(adap, CIM_HOST_ACC_DATA_A); + } + return ret; +} + +/** + * t4_cim_write - write a block into CIM internal address space + * @adap: the adapter + * @addr: the start address within the CIM address space + * @n: number of words to write + * @valp: set of values to write + * + * Writes a block of 4-byte words into the CIM intenal address space. + */ +int t4_cim_write(struct adapter *adap, unsigned int addr, unsigned int n, + const unsigned int *valp) +{ + int ret = 0; + + if (t4_read_reg(adap, CIM_HOST_ACC_CTRL_A) & HOSTBUSY_F) + return -EBUSY; + + for ( ; !ret && n--; addr += 4) { + t4_write_reg(adap, CIM_HOST_ACC_DATA_A, *valp++); + t4_write_reg(adap, CIM_HOST_ACC_CTRL_A, addr | HOSTWRITE_F); + ret = t4_wait_op_done(adap, CIM_HOST_ACC_CTRL_A, HOSTBUSY_F, + 0, 5, 2); + } + return ret; +} + +static int t4_cim_write1(struct adapter *adap, unsigned int addr, + unsigned int val) +{ + return t4_cim_write(adap, addr, 1, &val); +} + +/** + * t4_cim_read_la - read CIM LA capture buffer + * @adap: the adapter + * @la_buf: where to store the LA data + * @wrptr: the HW write pointer within the capture buffer + * + * Reads the contents of the CIM LA buffer with the most recent entry at + * the end of the returned data and with the entry at @wrptr first. + * We try to leave the LA in the running state we find it in. + */ +int t4_cim_read_la(struct adapter *adap, u32 *la_buf, unsigned int *wrptr) +{ + int i, ret; + unsigned int cfg, val, idx; + + ret = t4_cim_read(adap, UP_UP_DBG_LA_CFG_A, 1, &cfg); + if (ret) + return ret; + + if (cfg & UPDBGLAEN_F) { /* LA is running, freeze it */ + ret = t4_cim_write1(adap, UP_UP_DBG_LA_CFG_A, 0); + if (ret) + return ret; + } + + ret = t4_cim_read(adap, UP_UP_DBG_LA_CFG_A, 1, &val); + if (ret) + goto restart; + + idx = UPDBGLAWRPTR_G(val); + if (wrptr) + *wrptr = idx; + + for (i = 0; i < adap->params.cim_la_size; i++) { + ret = t4_cim_write1(adap, UP_UP_DBG_LA_CFG_A, + UPDBGLARDPTR_V(idx) | UPDBGLARDEN_F); + if (ret) + break; + ret = t4_cim_read(adap, UP_UP_DBG_LA_CFG_A, 1, &val); + if (ret) + break; + if (val & UPDBGLARDEN_F) { + ret = -ETIMEDOUT; + break; + } + ret = t4_cim_read(adap, UP_UP_DBG_LA_DATA_A, 1, &la_buf[i]); + if (ret) + break; + idx = (idx + 1) & UPDBGLARDPTR_M; + } +restart: + if (cfg & UPDBGLAEN_F) { + int r = t4_cim_write1(adap, UP_UP_DBG_LA_CFG_A, + cfg & ~UPDBGLARDEN_F); + if (!ret) + ret = r; + } + return ret; +} diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h index 5e5eee6..bcc925b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h @@ -56,6 +56,10 @@ enum { }; enum { + CIMLA_SIZE = 2048, /* # of 32-bit words in CIM LA */ +}; + +enum { SF_PAGE_SIZE = 256, /* serial flash page size */ SF_SEC_SIZE = 64 * 1024, /* serial flash sector size */ }; diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index 4077227..3fe6eeb 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -2062,4 +2062,38 @@ #define PL_VF_WHOAMI_A 0x0 #define PL_VF_REVISION_A 0x8 +/* registers for module CIM */ +#define CIM_HOST_ACC_CTRL_A 0x7b50 +#define CIM_HOST_ACC_DATA_A 0x7b54 +#define UP_UP_DBG_LA_CFG_A 0x140 +#define UP_UP_DBG_LA_DATA_A 0x144 + +#define HOSTBUSY_S 17 +#define HOSTBUSY_V(x) ((x) << HOSTBUSY_S) +#define HOSTBUSY_F HOSTBUSY_V(1U) + +#define HOSTWRITE_S 16 +#define HOSTWRITE_V(x) ((x) << HOSTWRITE_S) +#define HOSTWRITE_F HOSTWRITE_V(1U) + +#define UPDBGLARDEN_S 1 +#define UPDBGLARDEN_V(x) ((x) << UPDBGLARDEN_S) +#define UPDBGLARDEN_F UPDBGLARDEN_V(1U) + +#define UPDBGLAEN_S 0 +#define UPDBGLAEN_V(x) ((x) << UPDBGLAEN_S) +#define UPDBGLAEN_F UPDBGLAEN_V(1U) + +#define UPDBGLARDPTR_S 2 +#define UPDBGLARDPTR_M 0xfffU +#define UPDBGLARDPTR_V(x) ((x) << UPDBGLARDPTR_S) + +#define UPDBGLAWRPTR_S 16 +#define UPDBGLAWRPTR_M 0xfffU +#define UPDBGLAWRPTR_G(x) (((x) >> UPDBGLAWRPTR_S) & UPDBGLAWRPTR_M) + +#define UPDBGLACAPTPCONLY_S 30 +#define UPDBGLACAPTPCONLY_V(x) ((x) << UPDBGLACAPTPCONLY_S) +#define UPDBGLACAPTPCONLY_F UPDBGLACAPTPCONLY_V(1U) + #endif /* __T4_REGS_H */ -- cgit v0.10.2 From 74b3092c45c3101bd110013ac7fb693b3c9231f3 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 7 Jan 2015 08:48:02 +0530 Subject: cxgb4: Add support for cim_qcfg entry in debugfs Adds debug log to get cim queue config Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index 46cd506..7c785b5 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -1041,6 +1041,7 @@ int t4_cim_read(struct adapter *adap, unsigned int addr, unsigned int n, int t4_cim_write(struct adapter *adap, unsigned int addr, unsigned int n, const unsigned int *valp); int t4_cim_read_la(struct adapter *adap, u32 *la_buf, unsigned int *wrptr); +void t4_read_cimq_cfg(struct adapter *adap, u16 *base, u16 *size, u16 *thres); const char *t4_get_port_type_description(enum fw_port_type port_type); void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p); void t4_read_mtu_tbl(struct adapter *adap, u16 *mtus, u8 *mtu_log); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index 0f7b23f..a8b0223 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -168,6 +168,75 @@ static const struct file_operations cim_la_fops = { .release = seq_release_private }; +static int cim_qcfg_show(struct seq_file *seq, void *v) +{ + static const char * const qname[] = { + "TP0", "TP1", "ULP", "SGE0", "SGE1", "NC-SI", + "ULP0", "ULP1", "ULP2", "ULP3", "SGE", "NC-SI", + "SGE0-RX", "SGE1-RX" + }; + + int i; + struct adapter *adap = seq->private; + u16 base[CIM_NUM_IBQ + CIM_NUM_OBQ_T5]; + u16 size[CIM_NUM_IBQ + CIM_NUM_OBQ_T5]; + u32 stat[(4 * (CIM_NUM_IBQ + CIM_NUM_OBQ_T5))]; + u16 thres[CIM_NUM_IBQ]; + u32 obq_wr_t4[2 * CIM_NUM_OBQ], *wr; + u32 obq_wr_t5[2 * CIM_NUM_OBQ_T5]; + u32 *p = stat; + int cim_num_obq = is_t4(adap->params.chip) ? + CIM_NUM_OBQ : CIM_NUM_OBQ_T5; + + i = t4_cim_read(adap, is_t4(adap->params.chip) ? UP_IBQ_0_RDADDR_A : + UP_IBQ_0_SHADOW_RDADDR_A, + ARRAY_SIZE(stat), stat); + if (!i) { + if (is_t4(adap->params.chip)) { + i = t4_cim_read(adap, UP_OBQ_0_REALADDR_A, + ARRAY_SIZE(obq_wr_t4), obq_wr_t4); + wr = obq_wr_t4; + } else { + i = t4_cim_read(adap, UP_OBQ_0_SHADOW_REALADDR_A, + ARRAY_SIZE(obq_wr_t5), obq_wr_t5); + wr = obq_wr_t5; + } + } + if (i) + return i; + + t4_read_cimq_cfg(adap, base, size, thres); + + seq_printf(seq, + " Queue Base Size Thres RdPtr WrPtr SOP EOP Avail\n"); + for (i = 0; i < CIM_NUM_IBQ; i++, p += 4) + seq_printf(seq, "%7s %5x %5u %5u %6x %4x %4u %4u %5u\n", + qname[i], base[i], size[i], thres[i], + IBQRDADDR_G(p[0]), IBQWRADDR_G(p[1]), + QUESOPCNT_G(p[3]), QUEEOPCNT_G(p[3]), + QUEREMFLITS_G(p[2]) * 16); + for ( ; i < CIM_NUM_IBQ + cim_num_obq; i++, p += 4, wr += 2) + seq_printf(seq, "%7s %5x %5u %12x %4x %4u %4u %5u\n", + qname[i], base[i], size[i], + QUERDADDR_G(p[0]) & 0x3fff, wr[0] - base[i], + QUESOPCNT_G(p[3]), QUEEOPCNT_G(p[3]), + QUEREMFLITS_G(p[2]) * 16); + return 0; +} + +static int cim_qcfg_open(struct inode *inode, struct file *file) +{ + return single_open(file, cim_qcfg_show, inode->i_private); +} + +static const struct file_operations cim_qcfg_fops = { + .owner = THIS_MODULE, + .open = cim_qcfg_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /* Firmware Device Log dump. */ static const char * const devlog_level_strings[] = { [FW_DEVLOG_LEVEL_EMERG] = "EMERG", @@ -443,6 +512,7 @@ int t4_setup_debugfs(struct adapter *adap) static struct t4_debugfs_entry t4_debugfs_files[] = { { "cim_la", &cim_la_fops, S_IRUSR, 0 }, + { "cim_qcfg", &cim_qcfg_fops, S_IRUSR, 0 }, { "devlog", &devlog_fops, S_IRUSR, 0 }, { "l2t", &t4_l2t_fops, S_IRUSR, 0}, }; diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 1e30554..734d33e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -4326,6 +4326,41 @@ int t4_port_init(struct adapter *adap, int mbox, int pf, int vf) } /** + * t4_read_cimq_cfg - read CIM queue configuration + * @adap: the adapter + * @base: holds the queue base addresses in bytes + * @size: holds the queue sizes in bytes + * @thres: holds the queue full thresholds in bytes + * + * Returns the current configuration of the CIM queues, starting with + * the IBQs, then the OBQs. + */ +void t4_read_cimq_cfg(struct adapter *adap, u16 *base, u16 *size, u16 *thres) +{ + unsigned int i, v; + int cim_num_obq = is_t4(adap->params.chip) ? + CIM_NUM_OBQ : CIM_NUM_OBQ_T5; + + for (i = 0; i < CIM_NUM_IBQ; i++) { + t4_write_reg(adap, CIM_QUEUE_CONFIG_REF_A, IBQSELECT_F | + QUENUMSELECT_V(i)); + v = t4_read_reg(adap, CIM_QUEUE_CONFIG_CTRL_A); + /* value is in 256-byte units */ + *base++ = CIMQBASE_G(v) * 256; + *size++ = CIMQSIZE_G(v) * 256; + *thres++ = QUEFULLTHRSH_G(v) * 8; /* 8-byte unit */ + } + for (i = 0; i < cim_num_obq; i++) { + t4_write_reg(adap, CIM_QUEUE_CONFIG_REF_A, OBQSELECT_F | + QUENUMSELECT_V(i)); + v = t4_read_reg(adap, CIM_QUEUE_CONFIG_CTRL_A); + /* value is in 256-byte units */ + *base++ = CIMQBASE_G(v) * 256; + *size++ = CIMQSIZE_G(v) * 256; + } +} + +/** * t4_cim_read - read a block from CIM internal address space * @adap: the adapter * @addr: the start address within the CIM address space diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h index bcc925b..f6b82da 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h @@ -56,6 +56,9 @@ enum { }; enum { + CIM_NUM_IBQ = 6, /* # of CIM IBQs */ + CIM_NUM_OBQ = 6, /* # of CIM OBQs */ + CIM_NUM_OBQ_T5 = 8, /* # of CIM OBQs for T5 adapter */ CIMLA_SIZE = 2048, /* # of 32-bit words in CIM LA */ }; diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index 3fe6eeb..a2cae0e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -2096,4 +2096,59 @@ #define UPDBGLACAPTPCONLY_V(x) ((x) << UPDBGLACAPTPCONLY_S) #define UPDBGLACAPTPCONLY_F UPDBGLACAPTPCONLY_V(1U) +#define CIM_QUEUE_CONFIG_REF_A 0x7b48 +#define CIM_QUEUE_CONFIG_CTRL_A 0x7b4c + +#define CIMQSIZE_S 24 +#define CIMQSIZE_M 0x3fU +#define CIMQSIZE_G(x) (((x) >> CIMQSIZE_S) & CIMQSIZE_M) + +#define CIMQBASE_S 16 +#define CIMQBASE_M 0x3fU +#define CIMQBASE_G(x) (((x) >> CIMQBASE_S) & CIMQBASE_M) + +#define QUEFULLTHRSH_S 0 +#define QUEFULLTHRSH_M 0x1ffU +#define QUEFULLTHRSH_G(x) (((x) >> QUEFULLTHRSH_S) & QUEFULLTHRSH_M) + +#define UP_IBQ_0_RDADDR_A 0x10 +#define UP_IBQ_0_SHADOW_RDADDR_A 0x280 +#define UP_OBQ_0_REALADDR_A 0x104 +#define UP_OBQ_0_SHADOW_REALADDR_A 0x394 + +#define IBQRDADDR_S 0 +#define IBQRDADDR_M 0x1fffU +#define IBQRDADDR_G(x) (((x) >> IBQRDADDR_S) & IBQRDADDR_M) + +#define IBQWRADDR_S 0 +#define IBQWRADDR_M 0x1fffU +#define IBQWRADDR_G(x) (((x) >> IBQWRADDR_S) & IBQWRADDR_M) + +#define QUERDADDR_S 0 +#define QUERDADDR_M 0x7fffU +#define QUERDADDR_G(x) (((x) >> QUERDADDR_S) & QUERDADDR_M) + +#define QUEREMFLITS_S 0 +#define QUEREMFLITS_M 0x7ffU +#define QUEREMFLITS_G(x) (((x) >> QUEREMFLITS_S) & QUEREMFLITS_M) + +#define QUEEOPCNT_S 16 +#define QUEEOPCNT_M 0xfffU +#define QUEEOPCNT_G(x) (((x) >> QUEEOPCNT_S) & QUEEOPCNT_M) + +#define QUESOPCNT_S 0 +#define QUESOPCNT_M 0xfffU +#define QUESOPCNT_G(x) (((x) >> QUESOPCNT_S) & QUESOPCNT_M) + +#define OBQSELECT_S 4 +#define OBQSELECT_V(x) ((x) << OBQSELECT_S) +#define OBQSELECT_F OBQSELECT_V(1U) + +#define IBQSELECT_S 3 +#define IBQSELECT_V(x) ((x) << IBQSELECT_S) +#define IBQSELECT_F IBQSELECT_V(1U) + +#define QUENUMSELECT_S 0 +#define QUENUMSELECT_V(x) ((x) << QUENUMSELECT_S) + #endif /* __T4_REGS_H */ -- cgit v0.10.2 From ef82f662ba9454e1ccc193cd9d9b8a8a09ec2a43 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 7 Jan 2015 08:48:03 +0530 Subject: cxgb4: Add support for mps_tcam debugfs Debug log to get the MPS TCAM table Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index a8b0223..e9f3489 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -433,6 +433,136 @@ static const struct file_operations devlog_fops = { .release = seq_release_private }; +static inline void tcamxy2valmask(u64 x, u64 y, u8 *addr, u64 *mask) +{ + *mask = x | y; + y = (__force u64)cpu_to_be64(y); + memcpy(addr, (char *)&y + 2, ETH_ALEN); +} + +static int mps_tcam_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN) + seq_puts(seq, "Idx Ethernet address Mask Vld Ports PF" + " VF Replication " + "P0 P1 P2 P3 ML\n"); + else { + u64 mask; + u8 addr[ETH_ALEN]; + struct adapter *adap = seq->private; + unsigned int idx = (uintptr_t)v - 2; + u64 tcamy = t4_read_reg64(adap, MPS_CLS_TCAM_Y_L(idx)); + u64 tcamx = t4_read_reg64(adap, MPS_CLS_TCAM_X_L(idx)); + u32 cls_lo = t4_read_reg(adap, MPS_CLS_SRAM_L(idx)); + u32 cls_hi = t4_read_reg(adap, MPS_CLS_SRAM_H(idx)); + u32 rplc[4] = {0, 0, 0, 0}; + + if (tcamx & tcamy) { + seq_printf(seq, "%3u -\n", idx); + goto out; + } + + if (cls_lo & REPLICATE_F) { + struct fw_ldst_cmd ldst_cmd; + int ret; + + memset(&ldst_cmd, 0, sizeof(ldst_cmd)); + ldst_cmd.op_to_addrspace = + htonl(FW_CMD_OP_V(FW_LDST_CMD) | + FW_CMD_REQUEST_F | + FW_CMD_READ_F | + FW_LDST_CMD_ADDRSPACE_V( + FW_LDST_ADDRSPC_MPS)); + ldst_cmd.cycles_to_len16 = htonl(FW_LEN16(ldst_cmd)); + ldst_cmd.u.mps.fid_ctl = + htons(FW_LDST_CMD_FID_V(FW_LDST_MPS_RPLC) | + FW_LDST_CMD_CTL_V(idx)); + ret = t4_wr_mbox(adap, adap->mbox, &ldst_cmd, + sizeof(ldst_cmd), &ldst_cmd); + if (ret) + dev_warn(adap->pdev_dev, "Can't read MPS " + "replication map for idx %d: %d\n", + idx, -ret); + else { + rplc[0] = ntohl(ldst_cmd.u.mps.rplc31_0); + rplc[1] = ntohl(ldst_cmd.u.mps.rplc63_32); + rplc[2] = ntohl(ldst_cmd.u.mps.rplc95_64); + rplc[3] = ntohl(ldst_cmd.u.mps.rplc127_96); + } + } + + tcamxy2valmask(tcamx, tcamy, addr, &mask); + seq_printf(seq, "%3u %02x:%02x:%02x:%02x:%02x:%02x %012llx" + "%3c %#x%4u%4d", + idx, addr[0], addr[1], addr[2], addr[3], addr[4], + addr[5], (unsigned long long)mask, + (cls_lo & SRAM_VLD_F) ? 'Y' : 'N', PORTMAP_G(cls_hi), + PF_G(cls_lo), + (cls_lo & VF_VALID_F) ? VF_G(cls_lo) : -1); + if (cls_lo & REPLICATE_F) + seq_printf(seq, " %08x %08x %08x %08x", + rplc[3], rplc[2], rplc[1], rplc[0]); + else + seq_printf(seq, "%36c", ' '); + seq_printf(seq, "%4u%3u%3u%3u %#x\n", + SRAM_PRIO0_G(cls_lo), SRAM_PRIO1_G(cls_lo), + SRAM_PRIO2_G(cls_lo), SRAM_PRIO3_G(cls_lo), + (cls_lo >> MULTILISTEN0_S) & 0xf); + } +out: return 0; +} + +static inline void *mps_tcam_get_idx(struct seq_file *seq, loff_t pos) +{ + struct adapter *adap = seq->private; + int max_mac_addr = is_t4(adap->params.chip) ? + NUM_MPS_CLS_SRAM_L_INSTANCES : + NUM_MPS_T5_CLS_SRAM_L_INSTANCES; + return ((pos <= max_mac_addr) ? (void *)(uintptr_t)(pos + 1) : NULL); +} + +static void *mps_tcam_start(struct seq_file *seq, loff_t *pos) +{ + return *pos ? mps_tcam_get_idx(seq, *pos) : SEQ_START_TOKEN; +} + +static void *mps_tcam_next(struct seq_file *seq, void *v, loff_t *pos) +{ + ++*pos; + return mps_tcam_get_idx(seq, *pos); +} + +static void mps_tcam_stop(struct seq_file *seq, void *v) +{ +} + +static const struct seq_operations mps_tcam_seq_ops = { + .start = mps_tcam_start, + .next = mps_tcam_next, + .stop = mps_tcam_stop, + .show = mps_tcam_show +}; + +static int mps_tcam_open(struct inode *inode, struct file *file) +{ + int res = seq_open(file, &mps_tcam_seq_ops); + + if (!res) { + struct seq_file *seq = file->private_data; + + seq->private = inode->i_private; + } + return res; +} + +static const struct file_operations mps_tcam_debugfs_fops = { + .owner = THIS_MODULE, + .open = mps_tcam_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + static ssize_t mem_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -515,6 +645,7 @@ int t4_setup_debugfs(struct adapter *adap) { "cim_qcfg", &cim_qcfg_fops, S_IRUSR, 0 }, { "devlog", &devlog_fops, S_IRUSR, 0 }, { "l2t", &t4_l2t_fops, S_IRUSR, 0}, + { "mps_tcam", &mps_tcam_debugfs_fops, S_IRUSR, 0 }, }; add_debugfs_files(adap, diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index a2cae0e..7ce55f9 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -1708,6 +1708,66 @@ #define MPS_RX_PERR_INT_CAUSE_A 0x11074 +#define MPS_CLS_TCAM_Y_L_A 0xf000 +#define MPS_CLS_TCAM_X_L_A 0xf008 + +#define MPS_CLS_TCAM_Y_L(idx) (MPS_CLS_TCAM_Y_L_A + (idx) * 16) +#define NUM_MPS_CLS_TCAM_Y_L_INSTANCES 512 + +#define MPS_CLS_TCAM_X_L(idx) (MPS_CLS_TCAM_X_L_A + (idx) * 16) +#define NUM_MPS_CLS_TCAM_X_L_INSTANCES 512 + +#define MPS_CLS_SRAM_L_A 0xe000 +#define MPS_CLS_SRAM_H_A 0xe004 + +#define MPS_CLS_SRAM_L(idx) (MPS_CLS_SRAM_L_A + (idx) * 8) +#define NUM_MPS_CLS_SRAM_L_INSTANCES 336 + +#define MPS_CLS_SRAM_H(idx) (MPS_CLS_SRAM_H_A + (idx) * 8) +#define NUM_MPS_CLS_SRAM_H_INSTANCES 336 + +#define MULTILISTEN0_S 25 + +#define REPLICATE_S 11 +#define REPLICATE_V(x) ((x) << REPLICATE_S) +#define REPLICATE_F REPLICATE_V(1U) + +#define PF_S 8 +#define PF_M 0x7U +#define PF_G(x) (((x) >> PF_S) & PF_M) + +#define VF_VALID_S 7 +#define VF_VALID_V(x) ((x) << VF_VALID_S) +#define VF_VALID_F VF_VALID_V(1U) + +#define VF_S 0 +#define VF_M 0x7fU +#define VF_G(x) (((x) >> VF_S) & VF_M) + +#define SRAM_PRIO3_S 22 +#define SRAM_PRIO3_M 0x7U +#define SRAM_PRIO3_G(x) (((x) >> SRAM_PRIO3_S) & SRAM_PRIO3_M) + +#define SRAM_PRIO2_S 19 +#define SRAM_PRIO2_M 0x7U +#define SRAM_PRIO2_G(x) (((x) >> SRAM_PRIO2_S) & SRAM_PRIO2_M) + +#define SRAM_PRIO1_S 16 +#define SRAM_PRIO1_M 0x7U +#define SRAM_PRIO1_G(x) (((x) >> SRAM_PRIO1_S) & SRAM_PRIO1_M) + +#define SRAM_PRIO0_S 13 +#define SRAM_PRIO0_M 0x7U +#define SRAM_PRIO0_G(x) (((x) >> SRAM_PRIO0_S) & SRAM_PRIO0_M) + +#define SRAM_VLD_S 12 +#define SRAM_VLD_V(x) ((x) << SRAM_VLD_S) +#define SRAM_VLD_F SRAM_VLD_V(1U) + +#define PORTMAP_S 0 +#define PORTMAP_M 0xfU +#define PORTMAP_G(x) (((x) >> PORTMAP_S) & PORTMAP_M) + #define CPL_INTR_CAUSE_A 0x19054 #define CIM_OP_MAP_PERR_S 5 -- cgit v0.10.2 From efb975a67ea7846b966080f999589de570686aa0 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Wed, 7 Jan 2015 13:41:52 +0800 Subject: rhashtable: optimize rhashtable_lookup routine Define an internal compare function and relevant compare argument, and then make use of rhashtable_lookup_compare() to lookup key in hash table, reducing duplicated code between rhashtable_lookup() and rhashtable_lookup_compare(). Signed-off-by: Ying Xue Cc: Thomas Graf Acked-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/lib/rhashtable.c b/lib/rhashtable.c index cbad192..f2fdd7a 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -612,6 +612,19 @@ restart: } EXPORT_SYMBOL_GPL(rhashtable_remove); +struct rhashtable_compare_arg { + struct rhashtable *ht; + const void *key; +}; + +static bool rhashtable_compare(void *ptr, void *arg) +{ + struct rhashtable_compare_arg *x = arg; + struct rhashtable *ht = x->ht; + + return !memcmp(ptr + ht->p.key_offset, x->key, ht->p.key_len); +} + /** * rhashtable_lookup - lookup key in hash table * @ht: hash table @@ -627,32 +640,14 @@ EXPORT_SYMBOL_GPL(rhashtable_remove); */ void *rhashtable_lookup(struct rhashtable *ht, const void *key) { - const struct bucket_table *tbl, *old_tbl; - struct rhash_head *he; - u32 hash; + struct rhashtable_compare_arg arg = { + .ht = ht, + .key = key, + }; BUG_ON(!ht->p.key_len); - rcu_read_lock(); - old_tbl = rht_dereference_rcu(ht->tbl, ht); - tbl = rht_dereference_rcu(ht->future_tbl, ht); - hash = key_hashfn(ht, key, ht->p.key_len); -restart: - rht_for_each_rcu(he, tbl, rht_bucket_index(tbl, hash)) { - if (memcmp(rht_obj(ht, he) + ht->p.key_offset, key, - ht->p.key_len)) - continue; - rcu_read_unlock(); - return rht_obj(ht, he); - } - - if (unlikely(tbl != old_tbl)) { - tbl = old_tbl; - goto restart; - } - - rcu_read_unlock(); - return NULL; + return rhashtable_lookup_compare(ht, key, &rhashtable_compare, &arg); } EXPORT_SYMBOL_GPL(rhashtable_lookup); -- cgit v0.10.2 From 54c5b7d311c8e1801f9dcce9f388a7420a25fa90 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Wed, 7 Jan 2015 13:41:53 +0800 Subject: rhashtable: introduce rhashtable_wakeup_worker helper function Introduce rhashtable_wakeup_worker() helper function to reduce duplicated code where to wake up worker. By the way, as long as the both "future_tbl" and "tbl" bucket table pointers point to the same bucket array, we should try to wake up the resizing worker thread, otherwise, it indicates the work of resizing hash table is not finished yet. However, currently we will wake up the worker thread only when the two pointers point to different bucket array. Obviously this is wrong. So, the issue is also fixed as well in the patch. Signed-off-by: Ying Xue Cc: Thomas Graf Acked-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/lib/rhashtable.c b/lib/rhashtable.c index f2fdd7a..2000685 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -492,6 +492,19 @@ static void rht_deferred_worker(struct work_struct *work) mutex_unlock(&ht->mutex); } +static void rhashtable_wakeup_worker(struct rhashtable *ht) +{ + struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); + struct bucket_table *new_tbl = rht_dereference_rcu(ht->future_tbl, ht); + size_t size = tbl->size; + + /* Only adjust the table if no resizing is currently in progress. */ + if (tbl == new_tbl && + ((ht->p.grow_decision && ht->p.grow_decision(ht, size)) || + (ht->p.shrink_decision && ht->p.shrink_decision(ht, size)))) + schedule_delayed_work(&ht->run_work, 0); +} + /** * rhashtable_insert - insert object into hash hash table * @ht: hash table @@ -532,10 +545,7 @@ void rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj) atomic_inc(&ht->nelems); - /* Only grow the table if no resizing is currently in progress. */ - if (ht->tbl != ht->future_tbl && - ht->p.grow_decision && ht->p.grow_decision(ht, tbl->size)) - schedule_delayed_work(&ht->run_work, 0); + rhashtable_wakeup_worker(ht); rcu_read_unlock(); } @@ -584,10 +594,7 @@ restart: spin_unlock_bh(lock); - if (ht->tbl != ht->future_tbl && - ht->p.shrink_decision && - ht->p.shrink_decision(ht, tbl->size)) - schedule_delayed_work(&ht->run_work, 0); + rhashtable_wakeup_worker(ht); rcu_read_unlock(); -- cgit v0.10.2 From db30485408326a6f466a843b291b23535f63eda0 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Wed, 7 Jan 2015 13:41:54 +0800 Subject: rhashtable: involve rhashtable_lookup_insert routine Involve a new function called rhashtable_lookup_insert() which makes lookup and insertion atomic under bucket lock protection, helping us avoid to introduce an extra lock when we search and insert an object into hash table. Signed-off-by: Ying Xue Signed-off-by: Thomas Graf Acked-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index de1459c7..73c913f 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -168,6 +168,7 @@ int rhashtable_shrink(struct rhashtable *ht); void *rhashtable_lookup(struct rhashtable *ht, const void *key); void *rhashtable_lookup_compare(struct rhashtable *ht, const void *key, bool (*compare)(void *, void *), void *arg); +bool rhashtable_lookup_insert(struct rhashtable *ht, struct rhash_head *obj); void rhashtable_destroy(struct rhashtable *ht); diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 2000685..4430233 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -505,8 +505,26 @@ static void rhashtable_wakeup_worker(struct rhashtable *ht) schedule_delayed_work(&ht->run_work, 0); } +static void __rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj, + struct bucket_table *tbl, u32 hash) +{ + struct rhash_head *head = rht_dereference_bucket(tbl->buckets[hash], + tbl, hash); + + if (rht_is_a_nulls(head)) + INIT_RHT_NULLS_HEAD(obj->next, ht, hash); + else + RCU_INIT_POINTER(obj->next, head); + + rcu_assign_pointer(tbl->buckets[hash], obj); + + atomic_inc(&ht->nelems); + + rhashtable_wakeup_worker(ht); +} + /** - * rhashtable_insert - insert object into hash hash table + * rhashtable_insert - insert object into hash table * @ht: hash table * @obj: pointer to hash head inside object * @@ -523,7 +541,6 @@ static void rhashtable_wakeup_worker(struct rhashtable *ht) void rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj) { struct bucket_table *tbl; - struct rhash_head *head; spinlock_t *lock; unsigned hash; @@ -534,19 +551,9 @@ void rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj) lock = bucket_lock(tbl, hash); spin_lock_bh(lock); - head = rht_dereference_bucket(tbl->buckets[hash], tbl, hash); - if (rht_is_a_nulls(head)) - INIT_RHT_NULLS_HEAD(obj->next, ht, hash); - else - RCU_INIT_POINTER(obj->next, head); - - rcu_assign_pointer(tbl->buckets[hash], obj); + __rhashtable_insert(ht, obj, tbl, hash); spin_unlock_bh(lock); - atomic_inc(&ht->nelems); - - rhashtable_wakeup_worker(ht); - rcu_read_unlock(); } EXPORT_SYMBOL_GPL(rhashtable_insert); @@ -560,7 +567,7 @@ EXPORT_SYMBOL_GPL(rhashtable_insert); * walk the bucket chain upon removal. The removal operation is thus * considerable slow if the hash table is not correctly sized. * - * Will automatically shrink the table via rhashtable_expand() if the the + * Will automatically shrink the table via rhashtable_expand() if the * shrink_decision function specified at rhashtable_init() returns true. * * The caller must ensure that no concurrent table mutations occur. It is @@ -641,7 +648,7 @@ static bool rhashtable_compare(void *ptr, void *arg) * for a entry with an identical key. The first matching entry is returned. * * This lookup function may only be used for fixed key hash table (key_len - * paramter set). It will BUG() if used inappropriately. + * parameter set). It will BUG() if used inappropriately. * * Lookups may occur in parallel with hashtable mutations and resizing. */ @@ -702,6 +709,66 @@ restart: } EXPORT_SYMBOL_GPL(rhashtable_lookup_compare); +/** + * rhashtable_lookup_insert - lookup and insert object into hash table + * @ht: hash table + * @obj: pointer to hash head inside object + * + * Locks down the bucket chain in both the old and new table if a resize + * is in progress to ensure that writers can't remove from the old table + * and can't insert to the new table during the atomic operation of search + * and insertion. Searches for duplicates in both the old and new table if + * a resize is in progress. + * + * This lookup function may only be used for fixed key hash table (key_len + * parameter set). It will BUG() if used inappropriately. + * + * It is safe to call this function from atomic context. + * + * Will trigger an automatic deferred table resizing if the size grows + * beyond the watermark indicated by grow_decision() which can be passed + * to rhashtable_init(). + */ +bool rhashtable_lookup_insert(struct rhashtable *ht, struct rhash_head *obj) +{ + struct bucket_table *new_tbl, *old_tbl; + spinlock_t *new_bucket_lock, *old_bucket_lock; + u32 new_hash, old_hash; + bool success = true; + + BUG_ON(!ht->p.key_len); + + rcu_read_lock(); + + old_tbl = rht_dereference_rcu(ht->tbl, ht); + old_hash = head_hashfn(ht, old_tbl, obj); + old_bucket_lock = bucket_lock(old_tbl, old_hash); + spin_lock_bh(old_bucket_lock); + + new_tbl = rht_dereference_rcu(ht->future_tbl, ht); + new_hash = head_hashfn(ht, new_tbl, obj); + new_bucket_lock = bucket_lock(new_tbl, new_hash); + if (unlikely(old_tbl != new_tbl)) + spin_lock_bh_nested(new_bucket_lock, RHT_LOCK_NESTED); + + if (rhashtable_lookup(ht, rht_obj(ht, obj) + ht->p.key_offset)) { + success = false; + goto exit; + } + + __rhashtable_insert(ht, obj, new_tbl, new_hash); + +exit: + if (unlikely(old_tbl != new_tbl)) + spin_unlock_bh(new_bucket_lock); + spin_unlock_bh(old_bucket_lock); + + rcu_read_unlock(); + + return success; +} +EXPORT_SYMBOL_GPL(rhashtable_lookup_insert); + static size_t rounded_hashtable_size(struct rhashtable_params *params) { return max(roundup_pow_of_two(params->nelem_hint * 4 / 3), -- cgit v0.10.2 From bd6d4db552ceb52fb19890a454836dcda59743ce Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Wed, 7 Jan 2015 13:41:55 +0800 Subject: rhashtable: future table needs to be traversed when remove an object When remove an object from hash table, we currently only traverse old bucket table to check whether the object exists. If the object is not found in it, we will try again. But in the second search loop, we still search the object from the old table instead of future table. As a result, the object may be not removed from hash table especially when resizing is currently in progress and the object is just saved in the future table. Signed-off-by: Ying Xue Cc: Thomas Graf Acked-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 4430233..1aef942 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -608,10 +608,10 @@ restart: return true; } - if (tbl != rht_dereference_rcu(ht->tbl, ht)) { + if (tbl != rht_dereference_rcu(ht->future_tbl, ht)) { spin_unlock_bh(lock); - tbl = rht_dereference_rcu(ht->tbl, ht); + tbl = rht_dereference_rcu(ht->future_tbl, ht); hash = head_hashfn(ht, tbl, obj); lock = bucket_lock(tbl, hash); -- cgit v0.10.2 From c0c09bfdc4150b3918526660768585cd477adf35 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Wed, 7 Jan 2015 13:41:56 +0800 Subject: rhashtable: avoid unnecessary wakeup for worker queue Move condition statements of verifying whether hash table size exceeds its maximum threshold or reaches its minimum threshold from resizing functions to resizing decision functions, avoiding unnecessary wakeup for worker queue thread. Signed-off-by: Ying Xue Cc: Thomas Graf Acked-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index 73c913f..326acd8 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -113,7 +113,7 @@ struct rhashtable { struct bucket_table __rcu *tbl; struct bucket_table __rcu *future_tbl; atomic_t nelems; - size_t shift; + atomic_t shift; struct rhashtable_params p; struct delayed_work run_work; struct mutex mutex; diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 1aef942..7fb474b 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -199,7 +199,8 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht, bool rht_grow_above_75(const struct rhashtable *ht, size_t new_size) { /* Expand table when exceeding 75% load */ - return atomic_read(&ht->nelems) > (new_size / 4 * 3); + return atomic_read(&ht->nelems) > (new_size / 4 * 3) && + (ht->p.max_shift && atomic_read(&ht->shift) < ht->p.max_shift); } EXPORT_SYMBOL_GPL(rht_grow_above_75); @@ -211,7 +212,8 @@ EXPORT_SYMBOL_GPL(rht_grow_above_75); bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size) { /* Shrink table beneath 30% load */ - return atomic_read(&ht->nelems) < (new_size * 3 / 10); + return atomic_read(&ht->nelems) < (new_size * 3 / 10) && + (atomic_read(&ht->shift) > ht->p.min_shift); } EXPORT_SYMBOL_GPL(rht_shrink_below_30); @@ -318,14 +320,11 @@ int rhashtable_expand(struct rhashtable *ht) ASSERT_RHT_MUTEX(ht); - if (ht->p.max_shift && ht->shift >= ht->p.max_shift) - return 0; - new_tbl = bucket_table_alloc(ht, old_tbl->size * 2); if (new_tbl == NULL) return -ENOMEM; - ht->shift++; + atomic_inc(&ht->shift); /* Make insertions go into the new, empty table right away. Deletions * and lookups will be attempted in both tables until we synchronize. @@ -421,9 +420,6 @@ int rhashtable_shrink(struct rhashtable *ht) ASSERT_RHT_MUTEX(ht); - if (ht->shift <= ht->p.min_shift) - return 0; - new_tbl = bucket_table_alloc(ht, tbl->size / 2); if (new_tbl == NULL) return -ENOMEM; @@ -462,7 +458,7 @@ int rhashtable_shrink(struct rhashtable *ht) /* Publish the new, valid hash table */ rcu_assign_pointer(ht->tbl, new_tbl); - ht->shift--; + atomic_dec(&ht->shift); /* Wait for readers. No new readers will have references to the * old hash table. @@ -851,7 +847,7 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params) if (tbl == NULL) return -ENOMEM; - ht->shift = ilog2(tbl->size); + atomic_set(&ht->shift, ilog2(tbl->size)); RCU_INIT_POINTER(ht->tbl, tbl); RCU_INIT_POINTER(ht->future_tbl, tbl); -- cgit v0.10.2 From 545a148e43bed67618cc90b66f9864fba0878890 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Wed, 7 Jan 2015 13:41:57 +0800 Subject: rhashtable: initialize atomic nelems variable Signed-off-by: Ying Xue Cc: Thomas Graf Acked-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 7fb474b..8023b55 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -847,6 +847,7 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params) if (tbl == NULL) return -ENOMEM; + atomic_set(&ht->nelems, 0); atomic_set(&ht->shift, ilog2(tbl->size)); RCU_INIT_POINTER(ht->tbl, tbl); RCU_INIT_POINTER(ht->future_tbl, tbl); -- cgit v0.10.2 From 07f6c4bc048a7a8939c68a668bf77474890794c5 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Wed, 7 Jan 2015 13:41:58 +0800 Subject: tipc: convert tipc reference table to use generic rhashtable As tipc reference table is statically allocated, its memory size requested on stack initialization stage is quite big even if the maximum port number is just restricted to 8191 currently, however, the number already becomes insufficient in practice. But if the maximum ports is allowed to its theory value - 2^32, its consumed memory size will reach a ridiculously unacceptable value. Apart from this, heavy tipc users spend a considerable amount of time in tipc_sk_get() due to the read-lock on ref_table_lock. If tipc reference table is converted with generic rhashtable, above mentioned both disadvantages would be resolved respectively: making use of the new resizable hash table can avoid locking on the lookup; smaller memory size is required at initial stage, for example, 256 hash bucket slots are requested at the beginning phase instead of allocating the entire 8191 slots in old mode. The hash table will grow if entries exceeds 75% of table size up to a total table size of 1M, and it will automatically shrink if usage falls below 30%, but the minimum table size is allowed down to 256. Also converts ref_table_lock to a separate mutex to protect hash table mutations on write side. Lastly defers the release of the socket reference using call_rcu() to allow using an RCU read-side protected call to rhashtable_lookup(). Signed-off-by: Ying Xue Acked-by: Jon Maloy Acked-by: Erik Hugne Cc: Thomas Graf Acked-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/net/tipc/Kconfig b/net/tipc/Kconfig index c890848..91c8a8e 100644 --- a/net/tipc/Kconfig +++ b/net/tipc/Kconfig @@ -20,18 +20,6 @@ menuconfig TIPC If in doubt, say N. -config TIPC_PORTS - int "Maximum number of ports in a node" - depends on TIPC - range 127 65535 - default "8191" - help - Specifies how many ports can be supported by a node. - Can range from 127 to 65535 ports; default is 8191. - - Setting this to a smaller value saves some memory, - setting it to higher allows for more ports. - config TIPC_MEDIA_IB bool "InfiniBand media type support" depends on TIPC && INFINIBAND_IPOIB diff --git a/net/tipc/config.c b/net/tipc/config.c index 876f4c6..0b3a90e 100644 --- a/net/tipc/config.c +++ b/net/tipc/config.c @@ -183,22 +183,6 @@ static struct sk_buff *cfg_set_own_addr(void) return tipc_cfg_reply_error_string("cannot change to network mode"); } -static struct sk_buff *cfg_set_max_ports(void) -{ - u32 value; - - if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) - return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); - value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); - if (value == tipc_max_ports) - return tipc_cfg_reply_none(); - if (value < 127 || value > 65535) - return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE - " (max ports must be 127-65535)"); - return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED - " (cannot change max ports while TIPC is active)"); -} - static struct sk_buff *cfg_set_netid(void) { u32 value; @@ -285,15 +269,9 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area case TIPC_CMD_SET_NODE_ADDR: rep_tlv_buf = cfg_set_own_addr(); break; - case TIPC_CMD_SET_MAX_PORTS: - rep_tlv_buf = cfg_set_max_ports(); - break; case TIPC_CMD_SET_NETID: rep_tlv_buf = cfg_set_netid(); break; - case TIPC_CMD_GET_MAX_PORTS: - rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_ports); - break; case TIPC_CMD_GET_NETID: rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_net_id); break; @@ -317,6 +295,8 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area case TIPC_CMD_SET_REMOTE_MNG: case TIPC_CMD_GET_REMOTE_MNG: case TIPC_CMD_DUMP_LOG: + case TIPC_CMD_SET_MAX_PORTS: + case TIPC_CMD_GET_MAX_PORTS: rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED " (obsolete command)"); break; diff --git a/net/tipc/core.c b/net/tipc/core.c index a5737b8..71b2ada 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -34,6 +34,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "core.h" #include "name_table.h" #include "subscr.h" @@ -47,7 +49,6 @@ int tipc_random __read_mostly; /* configurable TIPC parameters */ u32 tipc_own_addr __read_mostly; -int tipc_max_ports __read_mostly; int tipc_net_id __read_mostly; int sysctl_tipc_rmem[3] __read_mostly; /* min/default/max */ @@ -84,9 +85,9 @@ static void tipc_core_stop(void) tipc_netlink_stop(); tipc_subscr_stop(); tipc_nametbl_stop(); - tipc_sk_ref_table_stop(); tipc_socket_stop(); tipc_unregister_sysctl(); + tipc_sk_rht_destroy(); } /** @@ -98,7 +99,7 @@ static int tipc_core_start(void) get_random_bytes(&tipc_random, sizeof(tipc_random)); - err = tipc_sk_ref_table_init(tipc_max_ports, tipc_random); + err = tipc_sk_rht_init(); if (err) goto out_reftbl; @@ -138,7 +139,7 @@ out_socket: out_netlink: tipc_nametbl_stop(); out_nametbl: - tipc_sk_ref_table_stop(); + tipc_sk_rht_destroy(); out_reftbl: return err; } @@ -150,7 +151,6 @@ static int __init tipc_init(void) pr_info("Activated (version " TIPC_MOD_VER ")\n"); tipc_own_addr = 0; - tipc_max_ports = CONFIG_TIPC_PORTS; tipc_net_id = 4711; sysctl_tipc_rmem[0] = TIPC_CONN_OVERLOAD_LIMIT >> 4 << diff --git a/net/tipc/core.h b/net/tipc/core.h index 8460213..56fe422 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -37,8 +37,6 @@ #ifndef _TIPC_CORE_H #define _TIPC_CORE_H -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include #include @@ -79,7 +77,6 @@ int tipc_snprintf(char *buf, int len, const char *fmt, ...); * Global configuration variables */ extern u32 tipc_own_addr __read_mostly; -extern int tipc_max_ports __read_mostly; extern int tipc_net_id __read_mostly; extern int sysctl_tipc_rmem[3] __read_mostly; extern int sysctl_tipc_named_timeout __read_mostly; diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 4731cad..701f31b 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -34,22 +34,25 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include +#include #include "core.h" #include "name_table.h" #include "node.h" #include "link.h" -#include #include "config.h" #include "socket.h" -#define SS_LISTENING -1 /* socket is listening */ -#define SS_READY -2 /* socket is connectionless */ +#define SS_LISTENING -1 /* socket is listening */ +#define SS_READY -2 /* socket is connectionless */ -#define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */ -#define CONN_PROBING_INTERVAL 3600000 /* [ms] => 1 h */ -#define TIPC_FWD_MSG 1 -#define TIPC_CONN_OK 0 -#define TIPC_CONN_PROBING 1 +#define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */ +#define CONN_PROBING_INTERVAL 3600000 /* [ms] => 1 h */ +#define TIPC_FWD_MSG 1 +#define TIPC_CONN_OK 0 +#define TIPC_CONN_PROBING 1 +#define TIPC_MAX_PORT 0xffffffff +#define TIPC_MIN_PORT 1 /** * struct tipc_sock - TIPC socket structure @@ -59,7 +62,7 @@ * @conn_instance: TIPC instance used when connection was established * @published: non-zero if port has one or more associated names * @max_pkt: maximum packet size "hint" used when building messages sent by port - * @ref: unique reference to port in TIPC object registry + * @portid: unique port identity in TIPC socket hash table * @phdr: preformatted message header used when sending messages * @port_list: adjacent ports in TIPC's global list of ports * @publications: list of publications for port @@ -74,6 +77,8 @@ * @link_cong: non-zero if owner must sleep because of link congestion * @sent_unacked: # messages sent by socket, and not yet acked by peer * @rcv_unacked: # messages read by user, but not yet acked back to peer + * @node: hash table node + * @rcu: rcu struct for tipc_sock */ struct tipc_sock { struct sock sk; @@ -82,7 +87,7 @@ struct tipc_sock { u32 conn_instance; int published; u32 max_pkt; - u32 ref; + u32 portid; struct tipc_msg phdr; struct list_head sock_list; struct list_head publications; @@ -95,6 +100,8 @@ struct tipc_sock { bool link_cong; uint sent_unacked; uint rcv_unacked; + struct rhash_head node; + struct rcu_head rcu; }; static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb); @@ -103,16 +110,14 @@ static void tipc_write_space(struct sock *sk); static int tipc_release(struct socket *sock); static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags); static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p); -static void tipc_sk_timeout(unsigned long ref); +static void tipc_sk_timeout(unsigned long portid); static int tipc_sk_publish(struct tipc_sock *tsk, uint scope, struct tipc_name_seq const *seq); static int tipc_sk_withdraw(struct tipc_sock *tsk, uint scope, struct tipc_name_seq const *seq); -static u32 tipc_sk_ref_acquire(struct tipc_sock *tsk); -static void tipc_sk_ref_discard(u32 ref); -static struct tipc_sock *tipc_sk_get(u32 ref); -static struct tipc_sock *tipc_sk_get_next(u32 *ref); -static void tipc_sk_put(struct tipc_sock *tsk); +static struct tipc_sock *tipc_sk_lookup(u32 portid); +static int tipc_sk_insert(struct tipc_sock *tsk); +static void tipc_sk_remove(struct tipc_sock *tsk); static const struct proto_ops packet_ops; static const struct proto_ops stream_ops; @@ -174,6 +179,9 @@ static const struct nla_policy tipc_nl_sock_policy[TIPC_NLA_SOCK_MAX + 1] = { * - port reference */ +/* Protects tipc socket hash table mutations */ +static struct rhashtable tipc_sk_rht; + static u32 tsk_peer_node(struct tipc_sock *tsk) { return msg_destnode(&tsk->phdr); @@ -305,7 +313,6 @@ static int tipc_sk_create(struct net *net, struct socket *sock, struct sock *sk; struct tipc_sock *tsk; struct tipc_msg *msg; - u32 ref; /* Validate arguments */ if (unlikely(protocol != 0)) @@ -339,24 +346,22 @@ static int tipc_sk_create(struct net *net, struct socket *sock, return -ENOMEM; tsk = tipc_sk(sk); - ref = tipc_sk_ref_acquire(tsk); - if (!ref) { - pr_warn("Socket create failed; reference table exhausted\n"); - return -ENOMEM; - } tsk->max_pkt = MAX_PKT_DEFAULT; - tsk->ref = ref; INIT_LIST_HEAD(&tsk->publications); msg = &tsk->phdr; tipc_msg_init(msg, TIPC_LOW_IMPORTANCE, TIPC_NAMED_MSG, NAMED_H_SIZE, 0); - msg_set_origport(msg, ref); /* Finish initializing socket data structures */ sock->ops = ops; sock->state = state; sock_init_data(sock, sk); - k_init_timer(&tsk->timer, (Handler)tipc_sk_timeout, ref); + if (tipc_sk_insert(tsk)) { + pr_warn("Socket create failed; port numbrer exhausted\n"); + return -EINVAL; + } + msg_set_origport(msg, tsk->portid); + k_init_timer(&tsk->timer, (Handler)tipc_sk_timeout, tsk->portid); sk->sk_backlog_rcv = tipc_backlog_rcv; sk->sk_rcvbuf = sysctl_tipc_rmem[1]; sk->sk_data_ready = tipc_data_ready; @@ -442,6 +447,13 @@ int tipc_sock_accept_local(struct socket *sock, struct socket **newsock, return ret; } +static void tipc_sk_callback(struct rcu_head *head) +{ + struct tipc_sock *tsk = container_of(head, struct tipc_sock, rcu); + + sock_put(&tsk->sk); +} + /** * tipc_release - destroy a TIPC socket * @sock: socket to destroy @@ -491,7 +503,7 @@ static int tipc_release(struct socket *sock) (sock->state == SS_CONNECTED)) { sock->state = SS_DISCONNECTING; tsk->connected = 0; - tipc_node_remove_conn(dnode, tsk->ref); + tipc_node_remove_conn(dnode, tsk->portid); } if (tipc_msg_reverse(skb, &dnode, TIPC_ERR_NO_PORT)) tipc_link_xmit_skb(skb, dnode, 0); @@ -499,16 +511,16 @@ static int tipc_release(struct socket *sock) } tipc_sk_withdraw(tsk, 0, NULL); - tipc_sk_ref_discard(tsk->ref); k_cancel_timer(&tsk->timer); + tipc_sk_remove(tsk); if (tsk->connected) { skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, SHORT_H_SIZE, 0, dnode, tipc_own_addr, tsk_peer_port(tsk), - tsk->ref, TIPC_ERR_NO_PORT); + tsk->portid, TIPC_ERR_NO_PORT); if (skb) - tipc_link_xmit_skb(skb, dnode, tsk->ref); - tipc_node_remove_conn(dnode, tsk->ref); + tipc_link_xmit_skb(skb, dnode, tsk->portid); + tipc_node_remove_conn(dnode, tsk->portid); } k_term_timer(&tsk->timer); @@ -518,7 +530,8 @@ static int tipc_release(struct socket *sock) /* Reject any messages that accumulated in backlog queue */ sock->state = SS_DISCONNECTING; release_sock(sk); - sock_put(sk); + + call_rcu(&tsk->rcu, tipc_sk_callback); sock->sk = NULL; return 0; @@ -611,7 +624,7 @@ static int tipc_getname(struct socket *sock, struct sockaddr *uaddr, addr->addr.id.ref = tsk_peer_port(tsk); addr->addr.id.node = tsk_peer_node(tsk); } else { - addr->addr.id.ref = tsk->ref; + addr->addr.id.ref = tsk->portid; addr->addr.id.node = tipc_own_addr; } @@ -946,7 +959,7 @@ static int tipc_sendmsg(struct kiocb *iocb, struct socket *sock, } new_mtu: - mtu = tipc_node_get_mtu(dnode, tsk->ref); + mtu = tipc_node_get_mtu(dnode, tsk->portid); __skb_queue_head_init(&head); rc = tipc_msg_build(mhdr, m, 0, dsz, mtu, &head); if (rc < 0) @@ -955,7 +968,7 @@ new_mtu: do { skb = skb_peek(&head); TIPC_SKB_CB(skb)->wakeup_pending = tsk->link_cong; - rc = tipc_link_xmit(&head, dnode, tsk->ref); + rc = tipc_link_xmit(&head, dnode, tsk->portid); if (likely(rc >= 0)) { if (sock->state != SS_READY) sock->state = SS_CONNECTING; @@ -1028,7 +1041,7 @@ static int tipc_send_stream(struct kiocb *iocb, struct socket *sock, struct tipc_msg *mhdr = &tsk->phdr; struct sk_buff_head head; DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name); - u32 ref = tsk->ref; + u32 portid = tsk->portid; int rc = -EINVAL; long timeo; u32 dnode; @@ -1067,7 +1080,7 @@ next: goto exit; do { if (likely(!tsk_conn_cong(tsk))) { - rc = tipc_link_xmit(&head, dnode, ref); + rc = tipc_link_xmit(&head, dnode, portid); if (likely(!rc)) { tsk->sent_unacked++; sent += send; @@ -1076,7 +1089,7 @@ next: goto next; } if (rc == -EMSGSIZE) { - tsk->max_pkt = tipc_node_get_mtu(dnode, ref); + tsk->max_pkt = tipc_node_get_mtu(dnode, portid); goto next; } if (rc != -ELINKCONG) @@ -1130,8 +1143,8 @@ static void tipc_sk_finish_conn(struct tipc_sock *tsk, u32 peer_port, tsk->probing_state = TIPC_CONN_OK; tsk->connected = 1; k_start_timer(&tsk->timer, tsk->probing_interval); - tipc_node_add_conn(peer_node, tsk->ref, peer_port); - tsk->max_pkt = tipc_node_get_mtu(peer_node, tsk->ref); + tipc_node_add_conn(peer_node, tsk->portid, peer_port); + tsk->max_pkt = tipc_node_get_mtu(peer_node, tsk->portid); } /** @@ -1238,7 +1251,7 @@ static void tipc_sk_send_ack(struct tipc_sock *tsk, uint ack) if (!tsk->connected) return; skb = tipc_msg_create(CONN_MANAGER, CONN_ACK, INT_H_SIZE, 0, dnode, - tipc_own_addr, peer_port, tsk->ref, TIPC_OK); + tipc_own_addr, peer_port, tsk->portid, TIPC_OK); if (!skb) return; msg = buf_msg(skb); @@ -1552,7 +1565,7 @@ static int filter_connect(struct tipc_sock *tsk, struct sk_buff **buf) tsk->connected = 0; /* let timer expire on it's own */ tipc_node_remove_conn(tsk_peer_node(tsk), - tsk->ref); + tsk->portid); } retval = TIPC_OK; } @@ -1743,7 +1756,7 @@ int tipc_sk_rcv(struct sk_buff *skb) u32 dnode; /* Validate destination and message */ - tsk = tipc_sk_get(dport); + tsk = tipc_sk_lookup(dport); if (unlikely(!tsk)) { rc = tipc_msg_eval(skb, &dnode); goto exit; @@ -1763,7 +1776,7 @@ int tipc_sk_rcv(struct sk_buff *skb) rc = -TIPC_ERR_OVERLOAD; } spin_unlock_bh(&sk->sk_lock.slock); - tipc_sk_put(tsk); + sock_put(sk); if (likely(!rc)) return 0; exit: @@ -2050,20 +2063,20 @@ restart: goto restart; } if (tipc_msg_reverse(skb, &dnode, TIPC_CONN_SHUTDOWN)) - tipc_link_xmit_skb(skb, dnode, tsk->ref); - tipc_node_remove_conn(dnode, tsk->ref); + tipc_link_xmit_skb(skb, dnode, tsk->portid); + tipc_node_remove_conn(dnode, tsk->portid); } else { dnode = tsk_peer_node(tsk); skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, SHORT_H_SIZE, 0, dnode, tipc_own_addr, tsk_peer_port(tsk), - tsk->ref, TIPC_CONN_SHUTDOWN); - tipc_link_xmit_skb(skb, dnode, tsk->ref); + tsk->portid, TIPC_CONN_SHUTDOWN); + tipc_link_xmit_skb(skb, dnode, tsk->portid); } tsk->connected = 0; sock->state = SS_DISCONNECTING; - tipc_node_remove_conn(dnode, tsk->ref); + tipc_node_remove_conn(dnode, tsk->portid); /* fall through */ case SS_DISCONNECTING: @@ -2084,14 +2097,14 @@ restart: return res; } -static void tipc_sk_timeout(unsigned long ref) +static void tipc_sk_timeout(unsigned long portid) { struct tipc_sock *tsk; struct sock *sk; struct sk_buff *skb = NULL; u32 peer_port, peer_node; - tsk = tipc_sk_get(ref); + tsk = tipc_sk_lookup(portid); if (!tsk) return; @@ -2108,20 +2121,20 @@ static void tipc_sk_timeout(unsigned long ref) /* Previous probe not answered -> self abort */ skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, SHORT_H_SIZE, 0, tipc_own_addr, - peer_node, ref, peer_port, + peer_node, portid, peer_port, TIPC_ERR_NO_PORT); } else { skb = tipc_msg_create(CONN_MANAGER, CONN_PROBE, INT_H_SIZE, 0, peer_node, tipc_own_addr, - peer_port, ref, TIPC_OK); + peer_port, portid, TIPC_OK); tsk->probing_state = TIPC_CONN_PROBING; k_start_timer(&tsk->timer, tsk->probing_interval); } bh_unlock_sock(sk); if (skb) - tipc_link_xmit_skb(skb, peer_node, ref); + tipc_link_xmit_skb(skb, peer_node, portid); exit: - tipc_sk_put(tsk); + sock_put(sk); } static int tipc_sk_publish(struct tipc_sock *tsk, uint scope, @@ -2132,12 +2145,12 @@ static int tipc_sk_publish(struct tipc_sock *tsk, uint scope, if (tsk->connected) return -EINVAL; - key = tsk->ref + tsk->pub_count + 1; - if (key == tsk->ref) + key = tsk->portid + tsk->pub_count + 1; + if (key == tsk->portid) return -EADDRINUSE; publ = tipc_nametbl_publish(seq->type, seq->lower, seq->upper, - scope, tsk->ref, key); + scope, tsk->portid, key); if (unlikely(!publ)) return -EINVAL; @@ -2188,9 +2201,9 @@ static int tipc_sk_show(struct tipc_sock *tsk, char *buf, ret = tipc_snprintf(buf, len, "<%u.%u.%u:%u>:", tipc_zone(tipc_own_addr), tipc_cluster(tipc_own_addr), - tipc_node(tipc_own_addr), tsk->ref); + tipc_node(tipc_own_addr), tsk->portid); else - ret = tipc_snprintf(buf, len, "%-10u:", tsk->ref); + ret = tipc_snprintf(buf, len, "%-10u:", tsk->portid); if (tsk->connected) { u32 dport = tsk_peer_port(tsk); @@ -2224,13 +2237,15 @@ static int tipc_sk_show(struct tipc_sock *tsk, char *buf, struct sk_buff *tipc_sk_socks_show(void) { + const struct bucket_table *tbl; + struct rhash_head *pos; struct sk_buff *buf; struct tlv_desc *rep_tlv; char *pb; int pb_len; struct tipc_sock *tsk; int str_len = 0; - u32 ref = 0; + int i; buf = tipc_cfg_reply_alloc(TLV_SPACE(ULTRA_STRING_MAX_LEN)); if (!buf) @@ -2239,14 +2254,18 @@ struct sk_buff *tipc_sk_socks_show(void) pb = TLV_DATA(rep_tlv); pb_len = ULTRA_STRING_MAX_LEN; - tsk = tipc_sk_get_next(&ref); - for (; tsk; tsk = tipc_sk_get_next(&ref)) { - lock_sock(&tsk->sk); - str_len += tipc_sk_show(tsk, pb + str_len, - pb_len - str_len, 0); - release_sock(&tsk->sk); - tipc_sk_put(tsk); + rcu_read_lock(); + tbl = rht_dereference_rcu((&tipc_sk_rht)->tbl, &tipc_sk_rht); + for (i = 0; i < tbl->size; i++) { + rht_for_each_entry_rcu(tsk, pos, tbl, i, node) { + spin_lock_bh(&tsk->sk.sk_lock.slock); + str_len += tipc_sk_show(tsk, pb + str_len, + pb_len - str_len, 0); + spin_unlock_bh(&tsk->sk.sk_lock.slock); + } } + rcu_read_unlock(); + str_len += 1; /* for "\0" */ skb_put(buf, TLV_SPACE(str_len)); TLV_SET(rep_tlv, TIPC_TLV_ULTRA_STRING, NULL, str_len); @@ -2259,255 +2278,91 @@ struct sk_buff *tipc_sk_socks_show(void) */ void tipc_sk_reinit(void) { + const struct bucket_table *tbl; + struct rhash_head *pos; + struct tipc_sock *tsk; struct tipc_msg *msg; - u32 ref = 0; - struct tipc_sock *tsk = tipc_sk_get_next(&ref); + int i; - for (; tsk; tsk = tipc_sk_get_next(&ref)) { - lock_sock(&tsk->sk); - msg = &tsk->phdr; - msg_set_prevnode(msg, tipc_own_addr); - msg_set_orignode(msg, tipc_own_addr); - release_sock(&tsk->sk); - tipc_sk_put(tsk); + rcu_read_lock(); + tbl = rht_dereference_rcu((&tipc_sk_rht)->tbl, &tipc_sk_rht); + for (i = 0; i < tbl->size; i++) { + rht_for_each_entry_rcu(tsk, pos, tbl, i, node) { + spin_lock_bh(&tsk->sk.sk_lock.slock); + msg = &tsk->phdr; + msg_set_prevnode(msg, tipc_own_addr); + msg_set_orignode(msg, tipc_own_addr); + spin_unlock_bh(&tsk->sk.sk_lock.slock); + } } + rcu_read_unlock(); } -/** - * struct reference - TIPC socket reference entry - * @tsk: pointer to socket associated with reference entry - * @ref: reference value for socket (combines instance & array index info) - */ -struct reference { - struct tipc_sock *tsk; - u32 ref; -}; - -/** - * struct tipc_ref_table - table of TIPC socket reference entries - * @entries: pointer to array of reference entries - * @capacity: array index of first unusable entry - * @init_point: array index of first uninitialized entry - * @first_free: array index of first unused socket reference entry - * @last_free: array index of last unused socket reference entry - * @index_mask: bitmask for array index portion of reference values - * @start_mask: initial value for instance value portion of reference values - */ -struct ref_table { - struct reference *entries; - u32 capacity; - u32 init_point; - u32 first_free; - u32 last_free; - u32 index_mask; - u32 start_mask; -}; - -/* Socket reference table consists of 2**N entries. - * - * State Socket ptr Reference - * ----- ---------- --------- - * In use non-NULL XXXX|own index - * (XXXX changes each time entry is acquired) - * Free NULL YYYY|next free index - * (YYYY is one more than last used XXXX) - * Uninitialized NULL 0 - * - * Entry 0 is not used; this allows index 0 to denote the end of the free list. - * - * Note that a reference value of 0 does not necessarily indicate that an - * entry is uninitialized, since the last entry in the free list could also - * have a reference value of 0 (although this is unlikely). - */ - -static struct ref_table tipc_ref_table; - -static DEFINE_RWLOCK(ref_table_lock); - -/** - * tipc_ref_table_init - create reference table for sockets - */ -int tipc_sk_ref_table_init(u32 req_sz, u32 start) +static struct tipc_sock *tipc_sk_lookup(u32 portid) { - struct reference *table; - u32 actual_sz; - - /* account for unused entry, then round up size to a power of 2 */ - - req_sz++; - for (actual_sz = 16; actual_sz < req_sz; actual_sz <<= 1) { - /* do nothing */ - }; - - /* allocate table & mark all entries as uninitialized */ - table = vzalloc(actual_sz * sizeof(struct reference)); - if (table == NULL) - return -ENOMEM; - - tipc_ref_table.entries = table; - tipc_ref_table.capacity = req_sz; - tipc_ref_table.init_point = 1; - tipc_ref_table.first_free = 0; - tipc_ref_table.last_free = 0; - tipc_ref_table.index_mask = actual_sz - 1; - tipc_ref_table.start_mask = start & ~tipc_ref_table.index_mask; + struct tipc_sock *tsk; - return 0; -} + rcu_read_lock(); + tsk = rhashtable_lookup(&tipc_sk_rht, &portid); + if (tsk) + sock_hold(&tsk->sk); + rcu_read_unlock(); -/** - * tipc_ref_table_stop - destroy reference table for sockets - */ -void tipc_sk_ref_table_stop(void) -{ - if (!tipc_ref_table.entries) - return; - vfree(tipc_ref_table.entries); - tipc_ref_table.entries = NULL; + return tsk; } -/* tipc_ref_acquire - create reference to a socket - * - * Register an socket pointer in the reference table. - * Returns a unique reference value that is used from then on to retrieve the - * socket pointer, or to determine if the socket has been deregistered. - */ -u32 tipc_sk_ref_acquire(struct tipc_sock *tsk) +static int tipc_sk_insert(struct tipc_sock *tsk) { - u32 index; - u32 index_mask; - u32 next_plus_upper; - u32 ref = 0; - struct reference *entry; - - if (unlikely(!tsk)) { - pr_err("Attempt to acquire ref. to non-existent obj\n"); - return 0; - } - if (unlikely(!tipc_ref_table.entries)) { - pr_err("Ref. table not found in acquisition attempt\n"); - return 0; - } - - /* Take a free entry, if available; otherwise initialize a new one */ - write_lock_bh(&ref_table_lock); - index = tipc_ref_table.first_free; - entry = &tipc_ref_table.entries[index]; + u32 remaining = (TIPC_MAX_PORT - TIPC_MIN_PORT) + 1; + u32 portid = prandom_u32() % remaining + TIPC_MIN_PORT; - if (likely(index)) { - index = tipc_ref_table.first_free; - entry = &tipc_ref_table.entries[index]; - index_mask = tipc_ref_table.index_mask; - next_plus_upper = entry->ref; - tipc_ref_table.first_free = next_plus_upper & index_mask; - ref = (next_plus_upper & ~index_mask) + index; - entry->tsk = tsk; - } else if (tipc_ref_table.init_point < tipc_ref_table.capacity) { - index = tipc_ref_table.init_point++; - entry = &tipc_ref_table.entries[index]; - ref = tipc_ref_table.start_mask + index; + while (remaining--) { + portid++; + if ((portid < TIPC_MIN_PORT) || (portid > TIPC_MAX_PORT)) + portid = TIPC_MIN_PORT; + tsk->portid = portid; + sock_hold(&tsk->sk); + if (rhashtable_lookup_insert(&tipc_sk_rht, &tsk->node)) + return 0; + sock_put(&tsk->sk); } - if (ref) { - entry->ref = ref; - entry->tsk = tsk; - } - write_unlock_bh(&ref_table_lock); - return ref; + return -1; } -/* tipc_sk_ref_discard - invalidate reference to an socket - * - * Disallow future references to an socket and free up the entry for re-use. - */ -void tipc_sk_ref_discard(u32 ref) +static void tipc_sk_remove(struct tipc_sock *tsk) { - struct reference *entry; - u32 index; - u32 index_mask; - - if (unlikely(!tipc_ref_table.entries)) { - pr_err("Ref. table not found during discard attempt\n"); - return; - } - - index_mask = tipc_ref_table.index_mask; - index = ref & index_mask; - entry = &tipc_ref_table.entries[index]; - - write_lock_bh(&ref_table_lock); + struct sock *sk = &tsk->sk; - if (unlikely(!entry->tsk)) { - pr_err("Attempt to discard ref. to non-existent socket\n"); - goto exit; - } - if (unlikely(entry->ref != ref)) { - pr_err("Attempt to discard non-existent reference\n"); - goto exit; + if (rhashtable_remove(&tipc_sk_rht, &tsk->node)) { + WARN_ON(atomic_read(&sk->sk_refcnt) == 1); + __sock_put(sk); } - - /* Mark entry as unused; increment instance part of entry's - * reference to invalidate any subsequent references - */ - - entry->tsk = NULL; - entry->ref = (ref & ~index_mask) + (index_mask + 1); - - /* Append entry to free entry list */ - if (unlikely(tipc_ref_table.first_free == 0)) - tipc_ref_table.first_free = index; - else - tipc_ref_table.entries[tipc_ref_table.last_free].ref |= index; - tipc_ref_table.last_free = index; -exit: - write_unlock_bh(&ref_table_lock); } -/* tipc_sk_get - find referenced socket and return pointer to it - */ -struct tipc_sock *tipc_sk_get(u32 ref) +int tipc_sk_rht_init(void) { - struct reference *entry; - struct tipc_sock *tsk; + struct rhashtable_params rht_params = { + .nelem_hint = 192, + .head_offset = offsetof(struct tipc_sock, node), + .key_offset = offsetof(struct tipc_sock, portid), + .key_len = sizeof(u32), /* portid */ + .hashfn = jhash, + .max_shift = 20, /* 1M */ + .min_shift = 8, /* 256 */ + .grow_decision = rht_grow_above_75, + .shrink_decision = rht_shrink_below_30, + }; - if (unlikely(!tipc_ref_table.entries)) - return NULL; - read_lock_bh(&ref_table_lock); - entry = &tipc_ref_table.entries[ref & tipc_ref_table.index_mask]; - tsk = entry->tsk; - if (likely(tsk && (entry->ref == ref))) - sock_hold(&tsk->sk); - else - tsk = NULL; - read_unlock_bh(&ref_table_lock); - return tsk; + return rhashtable_init(&tipc_sk_rht, &rht_params); } -/* tipc_sk_get_next - lock & return next socket after referenced one -*/ -struct tipc_sock *tipc_sk_get_next(u32 *ref) +void tipc_sk_rht_destroy(void) { - struct reference *entry; - struct tipc_sock *tsk = NULL; - uint index = *ref & tipc_ref_table.index_mask; + /* Wait for socket readers to complete */ + synchronize_net(); - read_lock_bh(&ref_table_lock); - while (++index < tipc_ref_table.capacity) { - entry = &tipc_ref_table.entries[index]; - if (!entry->tsk) - continue; - tsk = entry->tsk; - sock_hold(&tsk->sk); - *ref = entry->ref; - break; - } - read_unlock_bh(&ref_table_lock); - return tsk; -} - -static void tipc_sk_put(struct tipc_sock *tsk) -{ - sock_put(&tsk->sk); + rhashtable_destroy(&tipc_sk_rht); } /** @@ -2829,7 +2684,7 @@ static int __tipc_nl_add_sk(struct sk_buff *skb, struct netlink_callback *cb, attrs = nla_nest_start(skb, TIPC_NLA_SOCK); if (!attrs) goto genlmsg_cancel; - if (nla_put_u32(skb, TIPC_NLA_SOCK_REF, tsk->ref)) + if (nla_put_u32(skb, TIPC_NLA_SOCK_REF, tsk->portid)) goto attr_msg_cancel; if (nla_put_u32(skb, TIPC_NLA_SOCK_ADDR, tipc_own_addr)) goto attr_msg_cancel; @@ -2859,22 +2714,29 @@ int tipc_nl_sk_dump(struct sk_buff *skb, struct netlink_callback *cb) { int err; struct tipc_sock *tsk; - u32 prev_ref = cb->args[0]; - u32 ref = prev_ref; - - tsk = tipc_sk_get_next(&ref); - for (; tsk; tsk = tipc_sk_get_next(&ref)) { - lock_sock(&tsk->sk); - err = __tipc_nl_add_sk(skb, cb, tsk); - release_sock(&tsk->sk); - tipc_sk_put(tsk); - if (err) - break; + const struct bucket_table *tbl; + struct rhash_head *pos; + u32 prev_portid = cb->args[0]; + u32 portid = prev_portid; + int i; - prev_ref = ref; + rcu_read_lock(); + tbl = rht_dereference_rcu((&tipc_sk_rht)->tbl, &tipc_sk_rht); + for (i = 0; i < tbl->size; i++) { + rht_for_each_entry_rcu(tsk, pos, tbl, i, node) { + spin_lock_bh(&tsk->sk.sk_lock.slock); + portid = tsk->portid; + err = __tipc_nl_add_sk(skb, cb, tsk); + spin_unlock_bh(&tsk->sk.sk_lock.slock); + if (err) + break; + + prev_portid = portid; + } } + rcu_read_unlock(); - cb->args[0] = prev_ref; + cb->args[0] = prev_portid; return skb->len; } @@ -2962,12 +2824,12 @@ static int __tipc_nl_list_sk_publ(struct sk_buff *skb, int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb) { int err; - u32 tsk_ref = cb->args[0]; + u32 tsk_portid = cb->args[0]; u32 last_publ = cb->args[1]; u32 done = cb->args[2]; struct tipc_sock *tsk; - if (!tsk_ref) { + if (!tsk_portid) { struct nlattr **attrs; struct nlattr *sock[TIPC_NLA_SOCK_MAX + 1]; @@ -2984,13 +2846,13 @@ int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb) if (!sock[TIPC_NLA_SOCK_REF]) return -EINVAL; - tsk_ref = nla_get_u32(sock[TIPC_NLA_SOCK_REF]); + tsk_portid = nla_get_u32(sock[TIPC_NLA_SOCK_REF]); } if (done) return 0; - tsk = tipc_sk_get(tsk_ref); + tsk = tipc_sk_lookup(tsk_portid); if (!tsk) return -EINVAL; @@ -2999,9 +2861,9 @@ int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb) if (!err) done = 1; release_sock(&tsk->sk); - tipc_sk_put(tsk); + sock_put(&tsk->sk); - cb->args[0] = tsk_ref; + cb->args[0] = tsk_portid; cb->args[1] = last_publ; cb->args[2] = done; diff --git a/net/tipc/socket.h b/net/tipc/socket.h index d340893..c7d46d06 100644 --- a/net/tipc/socket.h +++ b/net/tipc/socket.h @@ -46,8 +46,8 @@ int tipc_sk_rcv(struct sk_buff *buf); struct sk_buff *tipc_sk_socks_show(void); void tipc_sk_mcast_rcv(struct sk_buff *buf); void tipc_sk_reinit(void); -int tipc_sk_ref_table_init(u32 requested_size, u32 start); -void tipc_sk_ref_table_stop(void); +int tipc_sk_rht_init(void); +void tipc_sk_rht_destroy(void); int tipc_nl_sk_dump(struct sk_buff *skb, struct netlink_callback *cb); int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb); -- cgit v0.10.2 From 0bec3b700d106a8b0a34227b2976d1a582f1aab7 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 7 Jan 2015 10:49:49 +0100 Subject: r8169: add support for xmit_more Delay update of hw tail descriptor if we know that another skb is going to be sent. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 14a1c5c..3a28059 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -7049,6 +7049,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, u32 status, len; u32 opts[2]; int frags; + bool stop_queue; if (unlikely(!TX_FRAGS_READY_FOR(tp, skb_shinfo(skb)->nr_frags))) { netif_err(tp, drv, dev, "BUG! Tx Ring full when queue awake!\n"); @@ -7105,11 +7106,16 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, tp->cur_tx += frags + 1; - RTL_W8(TxPoll, NPQ); + stop_queue = !TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS); - mmiowb(); + if (!skb->xmit_more || stop_queue || + netif_xmit_stopped(netdev_get_tx_queue(dev, 0))) { + RTL_W8(TxPoll, NPQ); + + mmiowb(); + } - if (!TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) { + if (stop_queue) { /* Avoid wrongly optimistic queue wake-up: rtl_tx thread must * not miss a ring update when it notices a stopped queue. */ -- cgit v0.10.2 From 99b164a66b7c80be69097d19e55dd1f6a5284fd6 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 7 Jan 2015 10:39:52 -0200 Subject: Revert "ARM: imx: add FEC sleep mode callback function" i.MX platform maintainer Shawn Guo is not happy with the such commit as explained below [1]: "The GPR difference between SoCs can be encoded in device tree as well. It's pointless to repeat the same code pattern for every single platform, that need to set up GPR bits for enabling magic packet wake up, while the only difference is the register and bit offset. The platform code will become quite messy and unmaintainable if every device driver dump their GPR register setup code into platform. Sorry, but it's NACK from me." This reverts commit 456062b3ec6f5b9 ("ARM: imx: add FEC sleep mode callback function"). [1] http://www.spinics.net/lists/netdev/msg310922.html Signed-off-by: Fabio Estevam Acked-by: Shawn Guo Signed-off-by: David S. Miller diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index 2f76168..5057d61 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -31,8 +31,6 @@ #include #include #include -#include -#include #include #include #include @@ -41,35 +39,6 @@ #include "cpuidle.h" #include "hardware.h" -static struct fec_platform_data fec_pdata; - -static void imx6q_fec_sleep_enable(int enabled) -{ - struct regmap *gpr; - - gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); - if (!IS_ERR(gpr)) { - if (enabled) - regmap_update_bits(gpr, IOMUXC_GPR13, - IMX6Q_GPR13_ENET_STOP_REQ, - IMX6Q_GPR13_ENET_STOP_REQ); - - else - regmap_update_bits(gpr, IOMUXC_GPR13, - IMX6Q_GPR13_ENET_STOP_REQ, 0); - } else - pr_err("failed to find fsl,imx6q-iomux-gpr regmap\n"); -} - -static void __init imx6q_enet_plt_init(void) -{ - struct device_node *np; - - np = of_find_node_by_path("/soc/aips-bus@02100000/ethernet@02188000"); - if (np && of_get_property(np, "fsl,magic-packet", NULL)) - fec_pdata.sleep_mode_enable = imx6q_fec_sleep_enable; -} - /* For imx6q sabrelite board: set KSZ9021RN RGMII pad skew */ static int ksz9021rn_phy_fixup(struct phy_device *phydev) { @@ -292,12 +261,6 @@ static void __init imx6q_axi_init(void) } } -/* Add auxdata to pass platform data */ -static const struct of_dev_auxdata imx6q_auxdata_lookup[] __initconst = { - OF_DEV_AUXDATA("fsl,imx6q-fec", 0x02188000, NULL, &fec_pdata), - { /* sentinel */ } -}; - static void __init imx6q_init_machine(void) { struct device *parent; @@ -311,13 +274,11 @@ static void __init imx6q_init_machine(void) imx6q_enet_phy_init(); - of_platform_populate(NULL, of_default_bus_match_table, - imx6q_auxdata_lookup, parent); + of_platform_populate(NULL, of_default_bus_match_table, NULL, parent); imx_anatop_init(); cpu_is_imx6q() ? imx6q_pm_init() : imx6dl_pm_init(); imx6q_1588_init(); - imx6q_enet_plt_init(); imx6q_axi_init(); } diff --git a/arch/arm/mach-imx/mach-imx6sx.c b/arch/arm/mach-imx/mach-imx6sx.c index 747b012..7a96c65 100644 --- a/arch/arm/mach-imx/mach-imx6sx.c +++ b/arch/arm/mach-imx/mach-imx6sx.c @@ -12,62 +12,12 @@ #include #include #include -#include -#include #include #include #include "common.h" #include "cpuidle.h" -static struct fec_platform_data fec_pdata[2]; - -static void imx6sx_fec1_sleep_enable(int enabled) -{ - struct regmap *gpr; - - gpr = syscon_regmap_lookup_by_compatible("fsl,imx6sx-iomuxc-gpr"); - if (!IS_ERR(gpr)) { - if (enabled) - regmap_update_bits(gpr, IOMUXC_GPR4, - IMX6SX_GPR4_FEC_ENET1_STOP_REQ, - IMX6SX_GPR4_FEC_ENET1_STOP_REQ); - else - regmap_update_bits(gpr, IOMUXC_GPR4, - IMX6SX_GPR4_FEC_ENET1_STOP_REQ, 0); - } else - pr_err("failed to find fsl,imx6sx-iomux-gpr regmap\n"); -} - -static void imx6sx_fec2_sleep_enable(int enabled) -{ - struct regmap *gpr; - - gpr = syscon_regmap_lookup_by_compatible("fsl,imx6sx-iomuxc-gpr"); - if (!IS_ERR(gpr)) { - if (enabled) - regmap_update_bits(gpr, IOMUXC_GPR4, - IMX6SX_GPR4_FEC_ENET2_STOP_REQ, - IMX6SX_GPR4_FEC_ENET2_STOP_REQ); - else - regmap_update_bits(gpr, IOMUXC_GPR4, - IMX6SX_GPR4_FEC_ENET2_STOP_REQ, 0); - } else - pr_err("failed to find fsl,imx6sx-iomux-gpr regmap\n"); -} - -static void __init imx6sx_enet_plt_init(void) -{ - struct device_node *np; - - np = of_find_node_by_path("/soc/aips-bus@02100000/ethernet@02188000"); - if (np && of_get_property(np, "fsl,magic-packet", NULL)) - fec_pdata[0].sleep_mode_enable = imx6sx_fec1_sleep_enable; - np = of_find_node_by_path("/soc/aips-bus@02100000/ethernet@021b4000"); - if (np && of_get_property(np, "fsl,magic-packet", NULL)) - fec_pdata[1].sleep_mode_enable = imx6sx_fec2_sleep_enable; -} - static int ar8031_phy_fixup(struct phy_device *dev) { u16 val; -- cgit v0.10.2 From 3552c319493c5a4ccfd6b767a3878e472aa27984 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 7 Jan 2015 10:39:53 -0200 Subject: Revert "ARM: dts: imx6qdl: enable FEC magic-packet feature" As 456062b3ec6f ("ARM: imx: add FEC sleep mode callback function") has been reverted, also revert the dts part. This reverts commit 07b4d2dda0c00f56248 ("ARM: dts: imx6qdl: enable FEC magic-packet feature"). Signed-off-by: Fabio Estevam Signed-off-by: David S. Miller diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi index 327d362..009abd6 100644 --- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi @@ -67,7 +67,6 @@ phy-mode = "rgmii"; interrupts-extended = <&gpio1 6 IRQ_TYPE_LEVEL_HIGH>, <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>; - fsl,magic-packet; status = "okay"; }; diff --git a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi index 6bfd0bc..f1cd214 100644 --- a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi @@ -160,7 +160,6 @@ pinctrl-0 = <&pinctrl_enet>; phy-mode = "rgmii"; phy-reset-gpios = <&gpio1 25 0>; - fsl,magic-packet; status = "okay"; }; -- cgit v0.10.2 From f40e74ffa3de44f7ef4c09653b070dd115ab7fbe Mon Sep 17 00:00:00 2001 From: Praveen Madhavan Date: Wed, 7 Jan 2015 19:16:28 +0530 Subject: csiostor:firmware upgrade fix This patch fixes removes older means of upgrading Firmware using MAJOR version and adds newer interface version checking mechanism. Please apply this patch on net-next since it depends on previous commits. Signed-off-by: Praveen Madhavan Signed-off-by: David S. Miller diff --git a/drivers/scsi/csiostor/csio_hw.c b/drivers/scsi/csiostor/csio_hw.c index 6602835..b70c15f 100644 --- a/drivers/scsi/csiostor/csio_hw.c +++ b/drivers/scsi/csiostor/csio_hw.c @@ -682,43 +682,6 @@ csio_hw_get_tp_version(struct csio_hw *hw, u32 *vers) } /* - * csio_hw_check_fw_version - check if the FW is compatible with - * this driver - * @hw: HW module - * - * Checks if an adapter's FW is compatible with the driver. Returns 0 - * if there's exact match, a negative error if the version could not be - * read or there's a major/minor version mismatch/minor. - */ -static int -csio_hw_check_fw_version(struct csio_hw *hw) -{ - int ret, major, minor, micro; - - ret = csio_hw_get_fw_version(hw, &hw->fwrev); - if (!ret) - ret = csio_hw_get_tp_version(hw, &hw->tp_vers); - if (ret) - return ret; - - major = FW_HDR_FW_VER_MAJOR_G(hw->fwrev); - minor = FW_HDR_FW_VER_MINOR_G(hw->fwrev); - micro = FW_HDR_FW_VER_MICRO_G(hw->fwrev); - - if (major != FW_VERSION_MAJOR(hw)) { /* major mismatch - fail */ - csio_err(hw, "card FW has major version %u, driver wants %u\n", - major, FW_VERSION_MAJOR(hw)); - return -EINVAL; - } - - if (minor == FW_VERSION_MINOR(hw) && micro == FW_VERSION_MICRO(hw)) - return 0; /* perfect match */ - - /* Minor/micro version mismatch */ - return -EINVAL; -} - -/* * csio_hw_fw_dload - download firmware. * @hw: HW module * @fw_data: firmware image to write. @@ -1967,6 +1930,170 @@ out: return rv; } +/* Is the given firmware API compatible with the one the driver was compiled + * with? + */ +static int fw_compatible(const struct fw_hdr *hdr1, const struct fw_hdr *hdr2) +{ + + /* short circuit if it's the exact same firmware version */ + if (hdr1->chip == hdr2->chip && hdr1->fw_ver == hdr2->fw_ver) + return 1; + +#define SAME_INTF(x) (hdr1->intfver_##x == hdr2->intfver_##x) + if (hdr1->chip == hdr2->chip && SAME_INTF(nic) && SAME_INTF(vnic) && + SAME_INTF(ri) && SAME_INTF(iscsi) && SAME_INTF(fcoe)) + return 1; +#undef SAME_INTF + + return 0; +} + +/* The firmware in the filesystem is usable, but should it be installed? + * This routine explains itself in detail if it indicates the filesystem + * firmware should be installed. + */ +static int csio_should_install_fs_fw(struct csio_hw *hw, int card_fw_usable, + int k, int c) +{ + const char *reason; + + if (!card_fw_usable) { + reason = "incompatible or unusable"; + goto install; + } + + if (k > c) { + reason = "older than the version supported with this driver"; + goto install; + } + + return 0; + +install: + csio_err(hw, "firmware on card (%u.%u.%u.%u) is %s, " + "installing firmware %u.%u.%u.%u on card.\n", + FW_HDR_FW_VER_MAJOR_G(c), FW_HDR_FW_VER_MINOR_G(c), + FW_HDR_FW_VER_MICRO_G(c), FW_HDR_FW_VER_BUILD_G(c), reason, + FW_HDR_FW_VER_MAJOR_G(k), FW_HDR_FW_VER_MINOR_G(k), + FW_HDR_FW_VER_MICRO_G(k), FW_HDR_FW_VER_BUILD_G(k)); + + return 1; +} + +static struct fw_info fw_info_array[] = { + { + .chip = CHELSIO_T5, + .fs_name = FW_CFG_NAME_T5, + .fw_mod_name = FW_FNAME_T5, + .fw_hdr = { + .chip = FW_HDR_CHIP_T5, + .fw_ver = __cpu_to_be32(FW_VERSION(T5)), + .intfver_nic = FW_INTFVER(T5, NIC), + .intfver_vnic = FW_INTFVER(T5, VNIC), + .intfver_ri = FW_INTFVER(T5, RI), + .intfver_iscsi = FW_INTFVER(T5, ISCSI), + .intfver_fcoe = FW_INTFVER(T5, FCOE), + }, + } +}; + +static struct fw_info *find_fw_info(int chip) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fw_info_array); i++) { + if (fw_info_array[i].chip == chip) + return &fw_info_array[i]; + } + return NULL; +} + +int csio_hw_prep_fw(struct csio_hw *hw, struct fw_info *fw_info, + const u8 *fw_data, unsigned int fw_size, + struct fw_hdr *card_fw, enum csio_dev_state state, + int *reset) +{ + int ret, card_fw_usable, fs_fw_usable; + const struct fw_hdr *fs_fw; + const struct fw_hdr *drv_fw; + + drv_fw = &fw_info->fw_hdr; + + /* Read the header of the firmware on the card */ + ret = csio_hw_read_flash(hw, FLASH_FW_START, + sizeof(*card_fw) / sizeof(uint32_t), + (uint32_t *)card_fw, 1); + if (ret == 0) { + card_fw_usable = fw_compatible(drv_fw, (const void *)card_fw); + } else { + csio_err(hw, + "Unable to read card's firmware header: %d\n", ret); + card_fw_usable = 0; + } + + if (fw_data != NULL) { + fs_fw = (const void *)fw_data; + fs_fw_usable = fw_compatible(drv_fw, fs_fw); + } else { + fs_fw = NULL; + fs_fw_usable = 0; + } + + if (card_fw_usable && card_fw->fw_ver == drv_fw->fw_ver && + (!fs_fw_usable || fs_fw->fw_ver == drv_fw->fw_ver)) { + /* Common case: the firmware on the card is an exact match and + * the filesystem one is an exact match too, or the filesystem + * one is absent/incompatible. + */ + } else if (fs_fw_usable && state == CSIO_DEV_STATE_UNINIT && + csio_should_install_fs_fw(hw, card_fw_usable, + be32_to_cpu(fs_fw->fw_ver), + be32_to_cpu(card_fw->fw_ver))) { + ret = csio_hw_fw_upgrade(hw, hw->pfn, fw_data, + fw_size, 0); + if (ret != 0) { + csio_err(hw, + "failed to install firmware: %d\n", ret); + goto bye; + } + + /* Installed successfully, update the cached header too. */ + memcpy(card_fw, fs_fw, sizeof(*card_fw)); + card_fw_usable = 1; + *reset = 0; /* already reset as part of load_fw */ + } + + if (!card_fw_usable) { + uint32_t d, c, k; + + d = be32_to_cpu(drv_fw->fw_ver); + c = be32_to_cpu(card_fw->fw_ver); + k = fs_fw ? be32_to_cpu(fs_fw->fw_ver) : 0; + + csio_err(hw, "Cannot find a usable firmware: " + "chip state %d, " + "driver compiled with %d.%d.%d.%d, " + "card has %d.%d.%d.%d, filesystem has %d.%d.%d.%d\n", + state, + FW_HDR_FW_VER_MAJOR_G(d), FW_HDR_FW_VER_MINOR_G(d), + FW_HDR_FW_VER_MICRO_G(d), FW_HDR_FW_VER_BUILD_G(d), + FW_HDR_FW_VER_MAJOR_G(c), FW_HDR_FW_VER_MINOR_G(c), + FW_HDR_FW_VER_MICRO_G(c), FW_HDR_FW_VER_BUILD_G(c), + FW_HDR_FW_VER_MAJOR_G(k), FW_HDR_FW_VER_MINOR_G(k), + FW_HDR_FW_VER_MICRO_G(k), FW_HDR_FW_VER_BUILD_G(k)); + ret = EINVAL; + goto bye; + } + + /* We're using whatever's on the card and it's known to be good. */ + hw->fwrev = be32_to_cpu(card_fw->fw_ver); + hw->tp_vers = be32_to_cpu(card_fw->tp_microcode_ver); + +bye: + return ret; +} + /* * Returns -EINVAL if attempts to flash the firmware failed * else returns 0, @@ -1974,14 +2101,27 @@ out: * latest firmware ECANCELED is returned */ static int -csio_hw_flash_fw(struct csio_hw *hw) +csio_hw_flash_fw(struct csio_hw *hw, int *reset) { int ret = -ECANCELED; const struct firmware *fw; - const struct fw_hdr *hdr; - u32 fw_ver; + struct fw_info *fw_info; + struct fw_hdr *card_fw; struct pci_dev *pci_dev = hw->pdev; struct device *dev = &pci_dev->dev ; + const u8 *fw_data = NULL; + unsigned int fw_size = 0; + + /* This is the firmware whose headers the driver was compiled + * against + */ + fw_info = find_fw_info(CHELSIO_CHIP_VERSION(hw->chip_id)); + if (fw_info == NULL) { + csio_err(hw, + "unable to get firmware info for chip %d.\n", + CHELSIO_CHIP_VERSION(hw->chip_id)); + return -EINVAL; + } if (request_firmware(&fw, CSIO_FW_FNAME(hw), dev) < 0) { csio_err(hw, "could not find firmware image %s, err: %d\n", @@ -1989,33 +2129,25 @@ csio_hw_flash_fw(struct csio_hw *hw) return -EINVAL; } - hdr = (const struct fw_hdr *)fw->data; - fw_ver = ntohl(hdr->fw_ver); - if (FW_HDR_FW_VER_MAJOR_G(fw_ver) != FW_VERSION_MAJOR(hw)) - return -EINVAL; /* wrong major version, won't do */ - - /* - * If the flash FW is unusable or we found something newer, load it. + /* allocate memory to read the header of the firmware on the + * card */ - if (FW_HDR_FW_VER_MAJOR_G(hw->fwrev) != FW_VERSION_MAJOR(hw) || - fw_ver > hw->fwrev) { - ret = csio_hw_fw_upgrade(hw, hw->pfn, fw->data, fw->size, - /*force=*/false); - if (!ret) - csio_info(hw, - "firmware upgraded to version %pI4 from %s\n", - &hdr->fw_ver, CSIO_FW_FNAME(hw)); - else - csio_err(hw, "firmware upgrade failed! err=%d\n", ret); - } else - ret = -EINVAL; + card_fw = kmalloc(sizeof(*card_fw), GFP_KERNEL); - release_firmware(fw); + fw_data = fw->data; + fw_size = fw->size; + /* upgrade FW logic */ + ret = csio_hw_prep_fw(hw, fw_info, fw_data, fw_size, card_fw, + hw->fw_state, reset); + + /* Cleaning up */ + if (fw != NULL) + release_firmware(fw); + kfree(card_fw); return ret; } - /* * csio_hw_configure - Configure HW * @hw - HW module @@ -2071,25 +2203,18 @@ csio_hw_configure(struct csio_hw *hw) if (rv != 0) goto out; + csio_hw_get_fw_version(hw, &hw->fwrev); + csio_hw_get_tp_version(hw, &hw->tp_vers); if (csio_is_hw_master(hw) && hw->fw_state != CSIO_DEV_STATE_INIT) { - rv = csio_hw_check_fw_version(hw); - if (rv == -EINVAL) { /* Do firmware update */ - spin_unlock_irq(&hw->lock); - rv = csio_hw_flash_fw(hw); - spin_lock_irq(&hw->lock); + spin_unlock_irq(&hw->lock); + rv = csio_hw_flash_fw(hw, &reset); + spin_lock_irq(&hw->lock); + + if (rv != 0) + goto out; - if (rv == 0) { - reset = 0; - /* - * Note that the chip was reset as part of the - * firmware upgrade so we don't reset it again - * below and grab the new firmware version. - */ - rv = csio_hw_check_fw_version(hw); - } - } /* * If the firmware doesn't support Configuration * Files, use the old Driver-based, hard-wired diff --git a/drivers/scsi/csiostor/csio_hw.h b/drivers/scsi/csiostor/csio_hw.h index bd97204..1fe8fde 100644 --- a/drivers/scsi/csiostor/csio_hw.h +++ b/drivers/scsi/csiostor/csio_hw.h @@ -201,9 +201,8 @@ enum { SF_ERASE_SECTOR = 0xd8, /* erase sector */ FW_START_SEC = 8, /* first flash sector for FW */ - FW_END_SEC = 15, /* last flash sector for FW */ FW_IMG_START = FW_START_SEC * SF_SEC_SIZE, - FW_MAX_SIZE = (FW_END_SEC - FW_START_SEC + 1) * SF_SEC_SIZE, + FW_MAX_SIZE = 16 * SF_SEC_SIZE, FLASH_CFG_MAX_SIZE = 0x10000 , /* max size of the flash config file*/ FLASH_CFG_OFFSET = 0x1f0000, @@ -221,7 +220,7 @@ enum { * Location of firmware image in FLASH. */ FLASH_FW_START_SEC = 8, - FLASH_FW_NSECS = 8, + FLASH_FW_NSECS = 16, FLASH_FW_START = FLASH_START(FLASH_FW_START_SEC), FLASH_FW_MAX_SIZE = FLASH_MAX_SIZE(FLASH_FW_NSECS), diff --git a/drivers/scsi/csiostor/csio_hw_chip.h b/drivers/scsi/csiostor/csio_hw_chip.h index 70c0bdd..eec98f5 100644 --- a/drivers/scsi/csiostor/csio_hw_chip.h +++ b/drivers/scsi/csiostor/csio_hw_chip.h @@ -50,6 +50,36 @@ #define FW_CFG_NAME_T4 "cxgb4/t4-config.txt" #define FW_CFG_NAME_T5 "cxgb4/t5-config.txt" +#define T4FW_VERSION_MAJOR 0x01 +#define T4FW_VERSION_MINOR 0x0B +#define T4FW_VERSION_MICRO 0x1B +#define T4FW_VERSION_BUILD 0x00 + +#define T5FW_VERSION_MAJOR 0x01 +#define T5FW_VERSION_MINOR 0x0B +#define T5FW_VERSION_MICRO 0x1B +#define T5FW_VERSION_BUILD 0x00 + +#define CHELSIO_CHIP_CODE(version, revision) (((version) << 4) | (revision)) +#define CHELSIO_CHIP_FPGA 0x100 +#define CHELSIO_CHIP_VERSION(code) (((code) >> 12) & 0xf) +#define CHELSIO_CHIP_RELEASE(code) ((code) & 0xf) + +#define CHELSIO_T4 0x4 +#define CHELSIO_T5 0x5 + +enum chip_type { + T4_A1 = CHELSIO_CHIP_CODE(CHELSIO_T4, 1), + T4_A2 = CHELSIO_CHIP_CODE(CHELSIO_T4, 2), + T4_FIRST_REV = T4_A1, + T4_LAST_REV = T4_A2, + + T5_A0 = CHELSIO_CHIP_CODE(CHELSIO_T5, 0), + T5_A1 = CHELSIO_CHIP_CODE(CHELSIO_T5, 1), + T5_FIRST_REV = T5_A0, + T5_LAST_REV = T5_A1, +}; + /* Define static functions */ static inline int csio_is_t4(uint16_t chip) { @@ -80,10 +110,21 @@ static inline int csio_is_t5(uint16_t chip) (csio_is_t4(hw->chip_id) ? (PORT_REG(port, XGMAC_PORT_INT_CAUSE_A)) : \ (T5_PORT_REG(port, MAC_PORT_INT_CAUSE_A))) -#define FW_VERSION_MAJOR(hw) (csio_is_t4(hw->chip_id) ? 1 : 0) -#define FW_VERSION_MINOR(hw) (csio_is_t4(hw->chip_id) ? 2 : 0) -#define FW_VERSION_MICRO(hw) (csio_is_t4(hw->chip_id) ? 8 : 0) +#include "t4fw_api.h" +#define FW_VERSION(chip) ( \ + FW_HDR_FW_VER_MAJOR_G(chip##FW_VERSION_MAJOR) | \ + FW_HDR_FW_VER_MINOR_G(chip##FW_VERSION_MINOR) | \ + FW_HDR_FW_VER_MICRO_G(chip##FW_VERSION_MICRO) | \ + FW_HDR_FW_VER_BUILD_G(chip##FW_VERSION_BUILD)) +#define FW_INTFVER(chip, intf) (FW_HDR_INTFVER_##intf) + +struct fw_info { + u8 chip; + char *fs_name; + char *fw_mod_name; + struct fw_hdr fw_hdr; +}; #define CSIO_FW_FNAME(hw) \ (csio_is_t4(hw->chip_id) ? FW_FNAME_T4 : FW_FNAME_T5) -- cgit v0.10.2 From 3c01b9a896c9c592d9edc7439d5e5cf6c411d014 Mon Sep 17 00:00:00 2001 From: Kamlakant Patel Date: Mon, 1 Dec 2014 17:39:34 +0530 Subject: gpio: moxart: convert to use basic mmio gpio library This patch converts MOXART GPIO driver to use basic_mmio_gpio generic library. Signed-off-by: Kamlakant Patel Acked-by: Alexandre Courbot Tested-by: Jonas Jensen Signed-off-by: Linus Walleij diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 633ec21..e367296 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -200,6 +200,7 @@ config GPIO_F7188X config GPIO_MOXART bool "MOXART GPIO support" depends on ARCH_MOXART + select GPIO_GENERIC help Select this option to enable GPIO driver for MOXA ART SoC devices. diff --git a/drivers/gpio/gpio-moxart.c b/drivers/gpio/gpio-moxart.c index 31e2551..c3ab46e 100644 --- a/drivers/gpio/gpio-moxart.c +++ b/drivers/gpio/gpio-moxart.c @@ -23,21 +23,12 @@ #include #include #include +#include #define GPIO_DATA_OUT 0x00 #define GPIO_DATA_IN 0x04 #define GPIO_PIN_DIRECTION 0x08 -struct moxart_gpio_chip { - struct gpio_chip gpio; - void __iomem *base; -}; - -static inline struct moxart_gpio_chip *to_moxart_gpio(struct gpio_chip *chip) -{ - return container_of(chip, struct moxart_gpio_chip, gpio); -} - static int moxart_gpio_request(struct gpio_chip *chip, unsigned offset) { return pinctrl_request_gpio(offset); @@ -48,90 +39,60 @@ static void moxart_gpio_free(struct gpio_chip *chip, unsigned offset) pinctrl_free_gpio(offset); } -static void moxart_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct moxart_gpio_chip *gc = to_moxart_gpio(chip); - void __iomem *ioaddr = gc->base + GPIO_DATA_OUT; - u32 reg = readl(ioaddr); - - if (value) - reg = reg | BIT(offset); - else - reg = reg & ~BIT(offset); - - writel(reg, ioaddr); -} - static int moxart_gpio_get(struct gpio_chip *chip, unsigned offset) { - struct moxart_gpio_chip *gc = to_moxart_gpio(chip); - u32 ret = readl(gc->base + GPIO_PIN_DIRECTION); + struct bgpio_chip *bgc = to_bgpio_chip(chip); + u32 ret = bgc->read_reg(bgc->reg_dir); if (ret & BIT(offset)) - return !!(readl(gc->base + GPIO_DATA_OUT) & BIT(offset)); + return !!(bgc->read_reg(bgc->reg_set) & BIT(offset)); else - return !!(readl(gc->base + GPIO_DATA_IN) & BIT(offset)); -} - -static int moxart_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - struct moxart_gpio_chip *gc = to_moxart_gpio(chip); - void __iomem *ioaddr = gc->base + GPIO_PIN_DIRECTION; - - writel(readl(ioaddr) & ~BIT(offset), ioaddr); - return 0; -} - -static int moxart_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct moxart_gpio_chip *gc = to_moxart_gpio(chip); - void __iomem *ioaddr = gc->base + GPIO_PIN_DIRECTION; - - moxart_gpio_set(chip, offset, value); - writel(readl(ioaddr) | BIT(offset), ioaddr); - return 0; + return !!(bgc->read_reg(bgc->reg_dat) & BIT(offset)); } -static struct gpio_chip moxart_template_chip = { - .label = "moxart-gpio", - .request = moxart_gpio_request, - .free = moxart_gpio_free, - .direction_input = moxart_gpio_direction_input, - .direction_output = moxart_gpio_direction_output, - .set = moxart_gpio_set, - .get = moxart_gpio_get, - .ngpio = 32, - .owner = THIS_MODULE, -}; - static int moxart_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res; - struct moxart_gpio_chip *mgc; + struct bgpio_chip *bgc; + void __iomem *base; int ret; - mgc = devm_kzalloc(dev, sizeof(*mgc), GFP_KERNEL); - if (!mgc) + bgc = devm_kzalloc(dev, sizeof(*bgc), GFP_KERNEL); + if (!bgc) return -ENOMEM; - mgc->gpio = moxart_template_chip; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mgc->base = devm_ioremap_resource(dev, res); - if (IS_ERR(mgc->base)) - return PTR_ERR(mgc->base); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); - mgc->gpio.dev = dev; + ret = bgpio_init(bgc, dev, 4, base + GPIO_DATA_IN, + base + GPIO_DATA_OUT, NULL, + base + GPIO_PIN_DIRECTION, NULL, 0); + if (ret) { + dev_err(&pdev->dev, "bgpio_init failed\n"); + return ret; + } - ret = gpiochip_add(&mgc->gpio); + bgc->gc.label = "moxart-gpio"; + bgc->gc.request = moxart_gpio_request; + bgc->gc.free = moxart_gpio_free; + bgc->gc.get = moxart_gpio_get; + bgc->data = bgc->read_reg(bgc->reg_set); + bgc->gc.base = 0; + bgc->gc.ngpio = 32; + bgc->gc.dev = dev; + bgc->gc.owner = THIS_MODULE; + + ret = gpiochip_add(&bgc->gc); if (ret) { dev_err(dev, "%s: gpiochip_add failed\n", dev->of_node->full_name); return ret; } - return 0; + return ret; } static const struct of_device_id moxart_gpio_match[] = { -- cgit v0.10.2 From fca08f326ae0423f03b097ff54de432fe77b95d0 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Fri, 9 Jan 2015 10:19:49 +0800 Subject: ARM: probes: move all probe code to dedicate directory In discussion on LKML (https://lkml.org/lkml/2014/11/28/158), Russell King suggests to move all probe related code to arch/arm/probes. This patch does the work. Due to dependency on 'arch/arm/kernel/patch.h', this patch also moves patch.h to 'arch/arm/include/asm/patch.h', and related '#include' directives are also midified to '#include '. Following is an overview of this patch: ./arch/arm/kernel/ ./arch/arm/probes/ |-- Makefile |-- Makefile |-- probes-arm.c ==> |-- decode-arm.c |-- probes-arm.h ==> |-- decode-arm.h |-- probes-thumb.c ==> |-- decode-thumb.c |-- probes-thumb.h ==> |-- decode-thumb.h |-- probes.c ==> |-- decode.c |-- probes.h ==> |-- decode.h | |-- kprobes | | |-- Makefile |-- kprobes-arm.c ==> | |-- actions-arm.c |-- kprobes-common.c ==> | |-- actions-common.c |-- kprobes-thumb.c ==> | |-- actions-thumb.c |-- kprobes.c ==> | |-- core.c |-- kprobes.h ==> | |-- core.h |-- kprobes-test-arm.c ==> | |-- test-arm.c |-- kprobes-test.c ==> | |-- test-core.c |-- kprobes-test.h ==> | |-- test-core.h |-- kprobes-test-thumb.c ==> | `-- test-thumb.c | `-- uprobes | |-- Makefile |-- uprobes-arm.c ==> |-- actions-arm.c |-- uprobes.c ==> |-- core.c |-- uprobes.h ==> `-- core.h | `-- patch.h ==> arch/arm/include/asm/patch.h Signed-off-by: Wang Nan Acked-by: Masami Hiramatsu Signed-off-by: Jon Medhurst diff --git a/arch/arm/Makefile b/arch/arm/Makefile index c1785ee..7f99cd6 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -266,6 +266,7 @@ core-$(CONFIG_KVM_ARM_HOST) += arch/arm/kvm/ # If we have a machine-specific directory, then include it in the build. core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/ +core-y += arch/arm/probes/ core-y += arch/arm/net/ core-y += arch/arm/crypto/ core-y += arch/arm/firmware/ diff --git a/arch/arm/include/asm/patch.h b/arch/arm/include/asm/patch.h new file mode 100644 index 0000000..77e054c --- /dev/null +++ b/arch/arm/include/asm/patch.h @@ -0,0 +1,17 @@ +#ifndef _ARM_KERNEL_PATCH_H +#define _ARM_KERNEL_PATCH_H + +void patch_text(void *addr, unsigned int insn); +void __patch_text_real(void *addr, unsigned int insn, bool remap); + +static inline void __patch_text(void *addr, unsigned int insn) +{ + __patch_text_real(addr, insn, true); +} + +static inline void __patch_text_early(void *addr, unsigned int insn) +{ + __patch_text_real(addr, insn, false); +} + +#endif diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index fb2b71e..9c51a43 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -51,20 +51,8 @@ obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o insn.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o -obj-$(CONFIG_UPROBES) += probes.o probes-arm.o uprobes.o uprobes-arm.o -obj-$(CONFIG_KPROBES) += probes.o kprobes.o kprobes-common.o patch.o -ifdef CONFIG_THUMB2_KERNEL -obj-$(CONFIG_KPROBES) += kprobes-thumb.o probes-thumb.o -else -obj-$(CONFIG_KPROBES) += kprobes-arm.o probes-arm.o -endif -obj-$(CONFIG_ARM_KPROBES_TEST) += test-kprobes.o -test-kprobes-objs := kprobes-test.o -ifdef CONFIG_THUMB2_KERNEL -test-kprobes-objs += kprobes-test-thumb.o -else -test-kprobes-objs += kprobes-test-arm.o -endif +# Main staffs in KPROBES are in arch/arm/probes/ . +obj-$(CONFIG_KPROBES) += patch.o obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o obj-$(CONFIG_ARM_THUMBEE) += thumbee.o obj-$(CONFIG_KGDB) += kgdb.o patch.o diff --git a/arch/arm/kernel/jump_label.c b/arch/arm/kernel/jump_label.c index afeeb9e..d8da0759 100644 --- a/arch/arm/kernel/jump_label.c +++ b/arch/arm/kernel/jump_label.c @@ -1,8 +1,8 @@ #include #include +#include #include "insn.h" -#include "patch.h" #ifdef HAVE_JUMP_LABEL diff --git a/arch/arm/kernel/kgdb.c b/arch/arm/kernel/kgdb.c index 07db2f8..a6ad93c 100644 --- a/arch/arm/kernel/kgdb.c +++ b/arch/arm/kernel/kgdb.c @@ -14,10 +14,9 @@ #include #include +#include #include -#include "patch.h" - struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = { { "r0", 4, offsetof(struct pt_regs, ARM_r0)}, diff --git a/arch/arm/kernel/kprobes-arm.c b/arch/arm/kernel/kprobes-arm.c deleted file mode 100644 index ac300c6..0000000 --- a/arch/arm/kernel/kprobes-arm.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * arch/arm/kernel/kprobes-decode.c - * - * Copyright (C) 2006, 2007 Motorola 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. - * - * 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. - */ - -/* - * We do not have hardware single-stepping on ARM, This - * effort is further complicated by the ARM not having a - * "next PC" register. Instructions that change the PC - * can't be safely single-stepped in a MP environment, so - * we have a lot of work to do: - * - * In the prepare phase: - * *) If it is an instruction that does anything - * with the CPU mode, we reject it for a kprobe. - * (This is out of laziness rather than need. The - * instructions could be simulated.) - * - * *) Otherwise, decode the instruction rewriting its - * registers to take fixed, ordered registers and - * setting a handler for it to run the instruction. - * - * In the execution phase by an instruction's handler: - * - * *) If the PC is written to by the instruction, the - * instruction must be fully simulated in software. - * - * *) Otherwise, a modified form of the instruction is - * directly executed. Its handler calls the - * instruction in insn[0]. In insn[1] is a - * "mov pc, lr" to return. - * - * Before calling, load up the reordered registers - * from the original instruction's registers. If one - * of the original input registers is the PC, compute - * and adjust the appropriate input register. - * - * After call completes, copy the output registers to - * the original instruction's original registers. - * - * We don't use a real breakpoint instruction since that - * would have us in the kernel go from SVC mode to SVC - * mode losing the link register. Instead we use an - * undefined instruction. To simplify processing, the - * undefined instruction used for kprobes must be reserved - * exclusively for kprobes use. - * - * TODO: ifdef out some instruction decoding based on architecture. - */ - -#include -#include -#include - -#include "kprobes.h" -#include "probes-arm.h" - -#if __LINUX_ARM_ARCH__ >= 6 -#define BLX(reg) "blx "reg" \n\t" -#else -#define BLX(reg) "mov lr, pc \n\t" \ - "mov pc, "reg" \n\t" -#endif - -static void __kprobes -emulate_ldrdstrd(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc + 4; - int rt = (insn >> 12) & 0xf; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - - register unsigned long rtv asm("r0") = regs->uregs[rt]; - register unsigned long rt2v asm("r1") = regs->uregs[rt+1]; - register unsigned long rnv asm("r2") = (rn == 15) ? pc - : regs->uregs[rn]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - - __asm__ __volatile__ ( - BLX("%[fn]") - : "=r" (rtv), "=r" (rt2v), "=r" (rnv) - : "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv), - [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rt] = rtv; - regs->uregs[rt+1] = rt2v; - if (is_writeback(insn)) - regs->uregs[rn] = rnv; -} - -static void __kprobes -emulate_ldr(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc + 4; - int rt = (insn >> 12) & 0xf; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - - register unsigned long rtv asm("r0"); - register unsigned long rnv asm("r2") = (rn == 15) ? pc - : regs->uregs[rn]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - - __asm__ __volatile__ ( - BLX("%[fn]") - : "=r" (rtv), "=r" (rnv) - : "1" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - if (rt == 15) - load_write_pc(rtv, regs); - else - regs->uregs[rt] = rtv; - - if (is_writeback(insn)) - regs->uregs[rn] = rnv; -} - -static void __kprobes -emulate_str(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long rtpc = regs->ARM_pc - 4 + str_pc_offset; - unsigned long rnpc = regs->ARM_pc + 4; - int rt = (insn >> 12) & 0xf; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - - register unsigned long rtv asm("r0") = (rt == 15) ? rtpc - : regs->uregs[rt]; - register unsigned long rnv asm("r2") = (rn == 15) ? rnpc - : regs->uregs[rn]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - - __asm__ __volatile__ ( - BLX("%[fn]") - : "=r" (rnv) - : "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - if (is_writeback(insn)) - regs->uregs[rn] = rnv; -} - -static void __kprobes -emulate_rd12rn16rm0rs8_rwflags(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc + 4; - int rd = (insn >> 12) & 0xf; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - int rs = (insn >> 8) & 0xf; - - register unsigned long rdv asm("r0") = regs->uregs[rd]; - register unsigned long rnv asm("r2") = (rn == 15) ? pc - : regs->uregs[rn]; - register unsigned long rmv asm("r3") = (rm == 15) ? pc - : regs->uregs[rm]; - register unsigned long rsv asm("r1") = regs->uregs[rs]; - unsigned long cpsr = regs->ARM_cpsr; - - __asm__ __volatile__ ( - "msr cpsr_fs, %[cpsr] \n\t" - BLX("%[fn]") - "mrs %[cpsr], cpsr \n\t" - : "=r" (rdv), [cpsr] "=r" (cpsr) - : "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv), - "1" (cpsr), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - if (rd == 15) - alu_write_pc(rdv, regs); - else - regs->uregs[rd] = rdv; - regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); -} - -static void __kprobes -emulate_rd12rn16rm0_rwflags_nopc(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rd = (insn >> 12) & 0xf; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - - register unsigned long rdv asm("r0") = regs->uregs[rd]; - register unsigned long rnv asm("r2") = regs->uregs[rn]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - unsigned long cpsr = regs->ARM_cpsr; - - __asm__ __volatile__ ( - "msr cpsr_fs, %[cpsr] \n\t" - BLX("%[fn]") - "mrs %[cpsr], cpsr \n\t" - : "=r" (rdv), [cpsr] "=r" (cpsr) - : "0" (rdv), "r" (rnv), "r" (rmv), - "1" (cpsr), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rd] = rdv; - regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); -} - -static void __kprobes -emulate_rd16rn12rm0rs8_rwflags_nopc(probes_opcode_t insn, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - int rd = (insn >> 16) & 0xf; - int rn = (insn >> 12) & 0xf; - int rm = insn & 0xf; - int rs = (insn >> 8) & 0xf; - - register unsigned long rdv asm("r2") = regs->uregs[rd]; - register unsigned long rnv asm("r0") = regs->uregs[rn]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - register unsigned long rsv asm("r1") = regs->uregs[rs]; - unsigned long cpsr = regs->ARM_cpsr; - - __asm__ __volatile__ ( - "msr cpsr_fs, %[cpsr] \n\t" - BLX("%[fn]") - "mrs %[cpsr], cpsr \n\t" - : "=r" (rdv), [cpsr] "=r" (cpsr) - : "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv), - "1" (cpsr), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rd] = rdv; - regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); -} - -static void __kprobes -emulate_rd12rm0_noflags_nopc(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rd = (insn >> 12) & 0xf; - int rm = insn & 0xf; - - register unsigned long rdv asm("r0") = regs->uregs[rd]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - - __asm__ __volatile__ ( - BLX("%[fn]") - : "=r" (rdv) - : "0" (rdv), "r" (rmv), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rd] = rdv; -} - -static void __kprobes -emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(probes_opcode_t insn, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - int rdlo = (insn >> 12) & 0xf; - int rdhi = (insn >> 16) & 0xf; - int rn = insn & 0xf; - int rm = (insn >> 8) & 0xf; - - register unsigned long rdlov asm("r0") = regs->uregs[rdlo]; - register unsigned long rdhiv asm("r2") = regs->uregs[rdhi]; - register unsigned long rnv asm("r3") = regs->uregs[rn]; - register unsigned long rmv asm("r1") = regs->uregs[rm]; - unsigned long cpsr = regs->ARM_cpsr; - - __asm__ __volatile__ ( - "msr cpsr_fs, %[cpsr] \n\t" - BLX("%[fn]") - "mrs %[cpsr], cpsr \n\t" - : "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr) - : "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv), - "2" (cpsr), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rdlo] = rdlov; - regs->uregs[rdhi] = rdhiv; - regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); -} - -const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = { - [PROBES_EMULATE_NONE] = {.handler = probes_emulate_none}, - [PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop}, - [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop}, - [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop}, - [PROBES_BRANCH_IMM] = {.handler = simulate_blx1}, - [PROBES_MRS] = {.handler = simulate_mrs}, - [PROBES_BRANCH_REG] = {.handler = simulate_blx2bx}, - [PROBES_CLZ] = {.handler = emulate_rd12rm0_noflags_nopc}, - [PROBES_SATURATING_ARITHMETIC] = { - .handler = emulate_rd12rn16rm0_rwflags_nopc}, - [PROBES_MUL1] = {.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc}, - [PROBES_MUL2] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc}, - [PROBES_SWP] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, - [PROBES_LDRSTRD] = {.handler = emulate_ldrdstrd}, - [PROBES_LOAD_EXTRA] = {.handler = emulate_ldr}, - [PROBES_LOAD] = {.handler = emulate_ldr}, - [PROBES_STORE_EXTRA] = {.handler = emulate_str}, - [PROBES_STORE] = {.handler = emulate_str}, - [PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp}, - [PROBES_DATA_PROCESSING_REG] = { - .handler = emulate_rd12rn16rm0rs8_rwflags}, - [PROBES_DATA_PROCESSING_IMM] = { - .handler = emulate_rd12rn16rm0rs8_rwflags}, - [PROBES_MOV_HALFWORD] = {.handler = emulate_rd12rm0_noflags_nopc}, - [PROBES_SEV] = {.handler = probes_emulate_none}, - [PROBES_WFE] = {.handler = probes_simulate_nop}, - [PROBES_SATURATE] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, - [PROBES_REV] = {.handler = emulate_rd12rm0_noflags_nopc}, - [PROBES_MMI] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, - [PROBES_PACK] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, - [PROBES_EXTEND] = {.handler = emulate_rd12rm0_noflags_nopc}, - [PROBES_EXTEND_ADD] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, - [PROBES_MUL_ADD_LONG] = { - .handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc}, - [PROBES_MUL_ADD] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc}, - [PROBES_BITFIELD] = {.handler = emulate_rd12rm0_noflags_nopc}, - [PROBES_BRANCH] = {.handler = simulate_bbl}, - [PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm} -}; diff --git a/arch/arm/kernel/kprobes-common.c b/arch/arm/kernel/kprobes-common.c deleted file mode 100644 index 0bf5d64..0000000 --- a/arch/arm/kernel/kprobes-common.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * arch/arm/kernel/kprobes-common.c - * - * Copyright (C) 2011 Jon Medhurst . - * - * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is - * Copyright (C) 2006, 2007 Motorola 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 -#include -#include - -#include "kprobes.h" - - -static void __kprobes simulate_ldm1stm1(probes_opcode_t insn, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - int rn = (insn >> 16) & 0xf; - int lbit = insn & (1 << 20); - int wbit = insn & (1 << 21); - int ubit = insn & (1 << 23); - int pbit = insn & (1 << 24); - long *addr = (long *)regs->uregs[rn]; - int reg_bit_vector; - int reg_count; - - reg_count = 0; - reg_bit_vector = insn & 0xffff; - while (reg_bit_vector) { - reg_bit_vector &= (reg_bit_vector - 1); - ++reg_count; - } - - if (!ubit) - addr -= reg_count; - addr += (!pbit == !ubit); - - reg_bit_vector = insn & 0xffff; - while (reg_bit_vector) { - int reg = __ffs(reg_bit_vector); - reg_bit_vector &= (reg_bit_vector - 1); - if (lbit) - regs->uregs[reg] = *addr++; - else - *addr++ = regs->uregs[reg]; - } - - if (wbit) { - if (!ubit) - addr -= reg_count; - addr -= (!pbit == !ubit); - regs->uregs[rn] = (long)addr; - } -} - -static void __kprobes simulate_stm1_pc(probes_opcode_t insn, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - unsigned long addr = regs->ARM_pc - 4; - - regs->ARM_pc = (long)addr + str_pc_offset; - simulate_ldm1stm1(insn, asi, regs); - regs->ARM_pc = (long)addr + 4; -} - -static void __kprobes simulate_ldm1_pc(probes_opcode_t insn, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - simulate_ldm1stm1(insn, asi, regs); - load_write_pc(regs->ARM_pc, regs); -} - -static void __kprobes -emulate_generic_r0_12_noflags(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - register void *rregs asm("r1") = regs; - register void *rfn asm("lr") = asi->insn_fn; - - __asm__ __volatile__ ( - "stmdb sp!, {%[regs], r11} \n\t" - "ldmia %[regs], {r0-r12} \n\t" -#if __LINUX_ARM_ARCH__ >= 6 - "blx %[fn] \n\t" -#else - "str %[fn], [sp, #-4]! \n\t" - "adr lr, 1f \n\t" - "ldr pc, [sp], #4 \n\t" - "1: \n\t" -#endif - "ldr lr, [sp], #4 \n\t" /* lr = regs */ - "stmia lr, {r0-r12} \n\t" - "ldr r11, [sp], #4 \n\t" - : [regs] "=r" (rregs), [fn] "=r" (rfn) - : "0" (rregs), "1" (rfn) - : "r0", "r2", "r3", "r4", "r5", "r6", "r7", - "r8", "r9", "r10", "r12", "memory", "cc" - ); -} - -static void __kprobes -emulate_generic_r2_14_noflags(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - emulate_generic_r0_12_noflags(insn, asi, - (struct pt_regs *)(regs->uregs+2)); -} - -static void __kprobes -emulate_ldm_r3_15(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - emulate_generic_r0_12_noflags(insn, asi, - (struct pt_regs *)(regs->uregs+3)); - load_write_pc(regs->ARM_pc, regs); -} - -enum probes_insn __kprobes -kprobe_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *h) -{ - probes_insn_handler_t *handler = 0; - unsigned reglist = insn & 0xffff; - int is_ldm = insn & 0x100000; - int rn = (insn >> 16) & 0xf; - - if (rn <= 12 && (reglist & 0xe000) == 0) { - /* Instruction only uses registers in the range R0..R12 */ - handler = emulate_generic_r0_12_noflags; - - } else if (rn >= 2 && (reglist & 0x8003) == 0) { - /* Instruction only uses registers in the range R2..R14 */ - rn -= 2; - reglist >>= 2; - handler = emulate_generic_r2_14_noflags; - - } else if (rn >= 3 && (reglist & 0x0007) == 0) { - /* Instruction only uses registers in the range R3..R15 */ - if (is_ldm && (reglist & 0x8000)) { - rn -= 3; - reglist >>= 3; - handler = emulate_ldm_r3_15; - } - } - - if (handler) { - /* We can emulate the instruction in (possibly) modified form */ - asi->insn[0] = __opcode_to_mem_arm((insn & 0xfff00000) | - (rn << 16) | reglist); - asi->insn_handler = handler; - return INSN_GOOD; - } - - /* Fallback to slower simulation... */ - if (reglist & 0x8000) - handler = is_ldm ? simulate_ldm1_pc : simulate_stm1_pc; - else - handler = simulate_ldm1stm1; - asi->insn_handler = handler; - return INSN_GOOD_NO_SLOT; -} - diff --git a/arch/arm/kernel/kprobes-test-arm.c b/arch/arm/kernel/kprobes-test-arm.c deleted file mode 100644 index cb14242..0000000 --- a/arch/arm/kernel/kprobes-test-arm.c +++ /dev/null @@ -1,1346 +0,0 @@ -/* - * arch/arm/kernel/kprobes-test-arm.c - * - * Copyright (C) 2011 Jon Medhurst . - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include "kprobes-test.h" - - -#define TEST_ISA "32" - -#define TEST_ARM_TO_THUMB_INTERWORK_R(code1, reg, val, code2) \ - TESTCASE_START(code1 #reg code2) \ - TEST_ARG_REG(reg, val) \ - TEST_ARG_REG(14, 99f) \ - TEST_ARG_END("") \ - "50: nop \n\t" \ - "1: "code1 #reg code2" \n\t" \ - " bx lr \n\t" \ - ".thumb \n\t" \ - "3: adr lr, 2f \n\t" \ - " bx lr \n\t" \ - ".arm \n\t" \ - "2: nop \n\t" \ - TESTCASE_END - -#define TEST_ARM_TO_THUMB_INTERWORK_P(code1, reg, val, code2) \ - TESTCASE_START(code1 #reg code2) \ - TEST_ARG_PTR(reg, val) \ - TEST_ARG_REG(14, 99f) \ - TEST_ARG_MEM(15, 3f+1) \ - TEST_ARG_END("") \ - "50: nop \n\t" \ - "1: "code1 #reg code2" \n\t" \ - " bx lr \n\t" \ - ".thumb \n\t" \ - "3: adr lr, 2f \n\t" \ - " bx lr \n\t" \ - ".arm \n\t" \ - "2: nop \n\t" \ - TESTCASE_END - - -void kprobe_arm_test_cases(void) -{ - kprobe_test_flags = 0; - - TEST_GROUP("Data-processing (register), (register-shifted register), (immediate)") - -#define _DATA_PROCESSING_DNM(op,s,val) \ - TEST_RR( op "eq" s " r0, r",1, VAL1,", r",2, val, "") \ - TEST_RR( op "ne" s " r1, r",1, VAL1,", r",2, val, ", lsl #3") \ - TEST_RR( op "cs" s " r2, r",3, VAL1,", r",2, val, ", lsr #4") \ - TEST_RR( op "cc" s " r3, r",3, VAL1,", r",2, val, ", asr #5") \ - TEST_RR( op "mi" s " r4, r",5, VAL1,", r",2, N(val),", asr #6") \ - TEST_RR( op "pl" s " r5, r",5, VAL1,", r",2, val, ", ror #7") \ - TEST_RR( op "vs" s " r6, r",7, VAL1,", r",2, val, ", rrx") \ - TEST_R( op "vc" s " r6, r",7, VAL1,", pc, lsl #3") \ - TEST_R( op "vc" s " r6, r",7, VAL1,", sp, lsr #4") \ - TEST_R( op "vc" s " r6, pc, r",7, VAL1,", asr #5") \ - TEST_R( op "vc" s " r6, sp, r",7, VAL1,", ror #6") \ - TEST_RRR( op "hi" s " r8, r",9, VAL1,", r",14,val, ", lsl r",0, 3,"")\ - TEST_RRR( op "ls" s " r9, r",9, VAL1,", r",14,val, ", lsr r",7, 4,"")\ - TEST_RRR( op "ge" s " r10, r",11,VAL1,", r",14,val, ", asr r",7, 5,"")\ - TEST_RRR( op "lt" s " r11, r",11,VAL1,", r",14,N(val),", asr r",7, 6,"")\ - TEST_RR( op "gt" s " r12, r13" ", r",14,val, ", ror r",14,7,"")\ - TEST_RR( op "le" s " r14, r",0, val, ", r13" ", lsl r",14,8,"")\ - TEST_R( op "eq" s " r0, r",11,VAL1,", #0xf5") \ - TEST_R( op "ne" s " r11, r",0, VAL1,", #0xf5000000") \ - TEST_R( op s " r7, r",8, VAL2,", #0x000af000") \ - TEST( op s " r4, pc" ", #0x00005a00") - -#define DATA_PROCESSING_DNM(op,val) \ - _DATA_PROCESSING_DNM(op,"",val) \ - _DATA_PROCESSING_DNM(op,"s",val) - -#define DATA_PROCESSING_NM(op,val) \ - TEST_RR( op "ne r",1, VAL1,", r",2, val, "") \ - TEST_RR( op "eq r",1, VAL1,", r",2, val, ", lsl #3") \ - TEST_RR( op "cc r",3, VAL1,", r",2, val, ", lsr #4") \ - TEST_RR( op "cs r",3, VAL1,", r",2, val, ", asr #5") \ - TEST_RR( op "pl r",5, VAL1,", r",2, N(val),", asr #6") \ - TEST_RR( op "mi r",5, VAL1,", r",2, val, ", ror #7") \ - TEST_RR( op "vc r",7, VAL1,", r",2, val, ", rrx") \ - TEST_R ( op "vs r",7, VAL1,", pc, lsl #3") \ - TEST_R ( op "vs r",7, VAL1,", sp, lsr #4") \ - TEST_R( op "vs pc, r",7, VAL1,", asr #5") \ - TEST_R( op "vs sp, r",7, VAL1,", ror #6") \ - TEST_RRR( op "ls r",9, VAL1,", r",14,val, ", lsl r",0, 3,"") \ - TEST_RRR( op "hi r",9, VAL1,", r",14,val, ", lsr r",7, 4,"") \ - TEST_RRR( op "lt r",11,VAL1,", r",14,val, ", asr r",7, 5,"") \ - TEST_RRR( op "ge r",11,VAL1,", r",14,N(val),", asr r",7, 6,"") \ - TEST_RR( op "le r13" ", r",14,val, ", ror r",14,7,"") \ - TEST_RR( op "gt r",0, val, ", r13" ", lsl r",14,8,"") \ - TEST_R( op "eq r",11,VAL1,", #0xf5") \ - TEST_R( op "ne r",0, VAL1,", #0xf5000000") \ - TEST_R( op " r",8, VAL2,", #0x000af000") - -#define _DATA_PROCESSING_DM(op,s,val) \ - TEST_R( op "eq" s " r0, r",1, val, "") \ - TEST_R( op "ne" s " r1, r",1, val, ", lsl #3") \ - TEST_R( op "cs" s " r2, r",3, val, ", lsr #4") \ - TEST_R( op "cc" s " r3, r",3, val, ", asr #5") \ - TEST_R( op "mi" s " r4, r",5, N(val),", asr #6") \ - TEST_R( op "pl" s " r5, r",5, val, ", ror #7") \ - TEST_R( op "vs" s " r6, r",10,val, ", rrx") \ - TEST( op "vs" s " r7, pc, lsl #3") \ - TEST( op "vs" s " r7, sp, lsr #4") \ - TEST_RR( op "vc" s " r8, r",7, val, ", lsl r",0, 3,"") \ - TEST_RR( op "hi" s " r9, r",9, val, ", lsr r",7, 4,"") \ - TEST_RR( op "ls" s " r10, r",9, val, ", asr r",7, 5,"") \ - TEST_RR( op "ge" s " r11, r",11,N(val),", asr r",7, 6,"") \ - TEST_RR( op "lt" s " r12, r",11,val, ", ror r",14,7,"") \ - TEST_R( op "gt" s " r14, r13" ", lsl r",14,8,"") \ - TEST( op "eq" s " r0, #0xf5") \ - TEST( op "ne" s " r11, #0xf5000000") \ - TEST( op s " r7, #0x000af000") \ - TEST( op s " r4, #0x00005a00") - -#define DATA_PROCESSING_DM(op,val) \ - _DATA_PROCESSING_DM(op,"",val) \ - _DATA_PROCESSING_DM(op,"s",val) - - DATA_PROCESSING_DNM("and",0xf00f00ff) - DATA_PROCESSING_DNM("eor",0xf00f00ff) - DATA_PROCESSING_DNM("sub",VAL2) - DATA_PROCESSING_DNM("rsb",VAL2) - DATA_PROCESSING_DNM("add",VAL2) - DATA_PROCESSING_DNM("adc",VAL2) - DATA_PROCESSING_DNM("sbc",VAL2) - DATA_PROCESSING_DNM("rsc",VAL2) - DATA_PROCESSING_NM("tst",0xf00f00ff) - DATA_PROCESSING_NM("teq",0xf00f00ff) - DATA_PROCESSING_NM("cmp",VAL2) - DATA_PROCESSING_NM("cmn",VAL2) - DATA_PROCESSING_DNM("orr",0xf00f00ff) - DATA_PROCESSING_DM("mov",VAL2) - DATA_PROCESSING_DNM("bic",0xf00f00ff) - DATA_PROCESSING_DM("mvn",VAL2) - - TEST("mov ip, sp") /* This has special case emulation code */ - - TEST_SUPPORTED("mov pc, #0x1000"); - TEST_SUPPORTED("mov sp, #0x1000"); - TEST_SUPPORTED("cmp pc, #0x1000"); - TEST_SUPPORTED("cmp sp, #0x1000"); - - /* Data-processing with PC and a shift count in a register */ - TEST_UNSUPPORTED(__inst_arm(0xe15c0f1e) " @ cmp r12, r14, asl pc") - TEST_UNSUPPORTED(__inst_arm(0xe1a0cf1e) " @ mov r12, r14, asl pc") - TEST_UNSUPPORTED(__inst_arm(0xe08caf1e) " @ add r10, r12, r14, asl pc") - TEST_UNSUPPORTED(__inst_arm(0xe151021f) " @ cmp r1, pc, lsl r2") - TEST_UNSUPPORTED(__inst_arm(0xe17f0211) " @ cmn pc, r1, lsl r2") - TEST_UNSUPPORTED(__inst_arm(0xe1a0121f) " @ mov r1, pc, lsl r2") - TEST_UNSUPPORTED(__inst_arm(0xe1a0f211) " @ mov pc, r1, lsl r2") - TEST_UNSUPPORTED(__inst_arm(0xe042131f) " @ sub r1, r2, pc, lsl r3") - TEST_UNSUPPORTED(__inst_arm(0xe1cf1312) " @ bic r1, pc, r2, lsl r3") - TEST_UNSUPPORTED(__inst_arm(0xe081f312) " @ add pc, r1, r2, lsl r3") - - /* Data-processing with PC as a target and status registers updated */ - TEST_UNSUPPORTED("movs pc, r1") - TEST_UNSUPPORTED("movs pc, r1, lsl r2") - TEST_UNSUPPORTED("movs pc, #0x10000") - TEST_UNSUPPORTED("adds pc, lr, r1") - TEST_UNSUPPORTED("adds pc, lr, r1, lsl r2") - TEST_UNSUPPORTED("adds pc, lr, #4") - - /* Data-processing with SP as target */ - TEST("add sp, sp, #16") - TEST("sub sp, sp, #8") - TEST("bic sp, sp, #0x20") - TEST("orr sp, sp, #0x20") - TEST_PR( "add sp, r",10,0,", r",11,4,"") - TEST_PRR("add sp, r",10,0,", r",11,4,", asl r",12,1,"") - TEST_P( "mov sp, r",10,0,"") - TEST_PR( "mov sp, r",10,0,", asl r",12,0,"") - - /* Data-processing with PC as target */ - TEST_BF( "add pc, pc, #2f-1b-8") - TEST_BF_R ("add pc, pc, r",14,2f-1f-8,"") - TEST_BF_R ("add pc, r",14,2f-1f-8,", pc") - TEST_BF_R ("mov pc, r",0,2f,"") - TEST_BF_R ("add pc, pc, r",14,(2f-1f-8)*2,", asr #1") - TEST_BB( "sub pc, pc, #1b-2b+8") -#if __LINUX_ARM_ARCH__ == 6 && !defined(CONFIG_CPU_V7) - TEST_BB( "sub pc, pc, #1b-2b+8-2") /* UNPREDICTABLE before and after ARMv6 */ -#endif - TEST_BB_R( "sub pc, pc, r",14, 1f-2f+8,"") - TEST_BB_R( "rsb pc, r",14,1f-2f+8,", pc") - TEST_R( "add pc, pc, r",10,-2,", asl #1") -#ifdef CONFIG_THUMB2_KERNEL - TEST_ARM_TO_THUMB_INTERWORK_R("add pc, pc, r",0,3f-1f-8+1,"") - TEST_ARM_TO_THUMB_INTERWORK_R("sub pc, r",0,3f+8+1,", #8") -#endif - TEST_GROUP("Miscellaneous instructions") - - TEST("mrs r0, cpsr") - TEST("mrspl r7, cpsr") - TEST("mrs r14, cpsr") - TEST_UNSUPPORTED(__inst_arm(0xe10ff000) " @ mrs r15, cpsr") - TEST_UNSUPPORTED("mrs r0, spsr") - TEST_UNSUPPORTED("mrs lr, spsr") - - TEST_UNSUPPORTED("msr cpsr, r0") - TEST_UNSUPPORTED("msr cpsr_f, lr") - TEST_UNSUPPORTED("msr spsr, r0") - - TEST_BF_R("bx r",0,2f,"") - TEST_BB_R("bx r",7,2f,"") - TEST_BF_R("bxeq r",14,2f,"") - -#if __LINUX_ARM_ARCH__ >= 5 - TEST_R("clz r0, r",0, 0x0,"") - TEST_R("clzeq r7, r",14,0x1,"") - TEST_R("clz lr, r",7, 0xffffffff,"") - TEST( "clz r4, sp") - TEST_UNSUPPORTED(__inst_arm(0x016fff10) " @ clz pc, r0") - TEST_UNSUPPORTED(__inst_arm(0x016f0f1f) " @ clz r0, pc") - -#if __LINUX_ARM_ARCH__ >= 6 - TEST_UNSUPPORTED("bxj r0") -#endif - - TEST_BF_R("blx r",0,2f,"") - TEST_BB_R("blx r",7,2f,"") - TEST_BF_R("blxeq r",14,2f,"") - TEST_UNSUPPORTED(__inst_arm(0x0120003f) " @ blx pc") - - TEST_RR( "qadd r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "qaddvs lr, r",9, VAL2,", r",8, VAL1,"") - TEST_R( "qadd lr, r",9, VAL2,", r13") - TEST_RR( "qsub r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "qsubvs lr, r",9, VAL2,", r",8, VAL1,"") - TEST_R( "qsub lr, r",9, VAL2,", r13") - TEST_RR( "qdadd r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "qdaddvs lr, r",9, VAL2,", r",8, VAL1,"") - TEST_R( "qdadd lr, r",9, VAL2,", r13") - TEST_RR( "qdsub r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "qdsubvs lr, r",9, VAL2,", r",8, VAL1,"") - TEST_R( "qdsub lr, r",9, VAL2,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe101f050) " @ qadd pc, r0, r1") - TEST_UNSUPPORTED(__inst_arm(0xe121f050) " @ qsub pc, r0, r1") - TEST_UNSUPPORTED(__inst_arm(0xe141f050) " @ qdadd pc, r0, r1") - TEST_UNSUPPORTED(__inst_arm(0xe161f050) " @ qdsub pc, r0, r1") - TEST_UNSUPPORTED(__inst_arm(0xe16f2050) " @ qdsub r2, r0, pc") - TEST_UNSUPPORTED(__inst_arm(0xe161205f) " @ qdsub r2, pc, r1") - - TEST_UNSUPPORTED("bkpt 0xffff") - TEST_UNSUPPORTED("bkpt 0x0000") - - TEST_UNSUPPORTED(__inst_arm(0xe1600070) " @ smc #0") - - TEST_GROUP("Halfword multiply and multiply-accumulate") - - TEST_RRR( "smlabb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlabbge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "smlabb lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe10f3281) " @ smlabb pc, r1, r2, r3") - TEST_RRR( "smlatb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlatbge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "smlatb lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe10f32a1) " @ smlatb pc, r1, r2, r3") - TEST_RRR( "smlabt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlabtge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "smlabt lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe10f32c1) " @ smlabt pc, r1, r2, r3") - TEST_RRR( "smlatt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlattge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "smlatt lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe10f32e1) " @ smlatt pc, r1, r2, r3") - - TEST_RRR( "smlawb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlawbge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "smlawb lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe12f3281) " @ smlawb pc, r1, r2, r3") - TEST_RRR( "smlawt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlawtge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "smlawt lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe12f32c1) " @ smlawt pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe12032cf) " @ smlawt r0, pc, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe1203fc1) " @ smlawt r0, r1, pc, r3") - TEST_UNSUPPORTED(__inst_arm(0xe120f2c1) " @ smlawt r0, r1, r2, pc") - - TEST_RR( "smulwb r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulwbge r7, r",8, VAL3,", r",9, VAL1,"") - TEST_R( "smulwb lr, r",1, VAL2,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe12f02a1) " @ smulwb pc, r1, r2") - TEST_RR( "smulwt r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulwtge r7, r",8, VAL3,", r",9, VAL1,"") - TEST_R( "smulwt lr, r",1, VAL2,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe12f02e1) " @ smulwt pc, r1, r2") - - TEST_RRRR( "smlalbb r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlalbble r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRR( "smlalbb r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe14f1382) " @ smlalbb pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe141f382) " @ smlalbb r1, pc, r2, r3") - TEST_RRRR( "smlaltb r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlaltble r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRR( "smlaltb r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe14f13a2) " @ smlaltb pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe141f3a2) " @ smlaltb r1, pc, r2, r3") - TEST_RRRR( "smlalbt r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlalbtle r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRR( "smlalbt r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe14f13c2) " @ smlalbt pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe141f3c2) " @ smlalbt r1, pc, r2, r3") - TEST_RRRR( "smlaltt r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlalttle r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRR( "smlaltt r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe14f13e2) " @ smlalbb pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe140f3e2) " @ smlalbb r0, pc, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe14013ef) " @ smlalbb r0, r1, pc, r3") - TEST_UNSUPPORTED(__inst_arm(0xe1401fe2) " @ smlalbb r0, r1, r2, pc") - - TEST_RR( "smulbb r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulbbge r7, r",8, VAL3,", r",9, VAL1,"") - TEST_R( "smulbb lr, r",1, VAL2,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe16f0281) " @ smulbb pc, r1, r2") - TEST_RR( "smultb r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smultbge r7, r",8, VAL3,", r",9, VAL1,"") - TEST_R( "smultb lr, r",1, VAL2,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe16f02a1) " @ smultb pc, r1, r2") - TEST_RR( "smulbt r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulbtge r7, r",8, VAL3,", r",9, VAL1,"") - TEST_R( "smulbt lr, r",1, VAL2,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe16f02c1) " @ smultb pc, r1, r2") - TEST_RR( "smultt r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulttge r7, r",8, VAL3,", r",9, VAL1,"") - TEST_R( "smultt lr, r",1, VAL2,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe16f02e1) " @ smultt pc, r1, r2") - TEST_UNSUPPORTED(__inst_arm(0xe16002ef) " @ smultt r0, pc, r2") - TEST_UNSUPPORTED(__inst_arm(0xe1600fe1) " @ smultt r0, r1, pc") -#endif - - TEST_GROUP("Multiply and multiply-accumulate") - - TEST_RR( "mul r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "mulls r7, r",8, VAL2,", r",9, VAL2,"") - TEST_R( "mul lr, r",4, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe00f0291) " @ mul pc, r1, r2") - TEST_UNSUPPORTED(__inst_arm(0xe000029f) " @ mul r0, pc, r2") - TEST_UNSUPPORTED(__inst_arm(0xe0000f91) " @ mul r0, r1, pc") - TEST_RR( "muls r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "mullss r7, r",8, VAL2,", r",9, VAL2,"") - TEST_R( "muls lr, r",4, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe01f0291) " @ muls pc, r1, r2") - - TEST_RRR( "mla r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "mlahi r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "mla lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe02f3291) " @ mla pc, r1, r2, r3") - TEST_RRR( "mlas r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "mlahis r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "mlas lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe03f3291) " @ mlas pc, r1, r2, r3") - -#if __LINUX_ARM_ARCH__ >= 6 - TEST_RR( "umaal r0, r1, r",2, VAL1,", r",3, VAL2,"") - TEST_RR( "umaalls r7, r8, r",9, VAL2,", r",10, VAL1,"") - TEST_R( "umaal lr, r12, r",11,VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe041f392) " @ umaal pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe04f0392) " @ umaal r0, pc, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0500090) " @ undef") - TEST_UNSUPPORTED(__inst_arm(0xe05fff9f) " @ undef") -#endif - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_RRR( "mls r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "mlshi r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "mls lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe06f3291) " @ mls pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe060329f) " @ mls r0, pc, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0603f91) " @ mls r0, r1, pc, r3") - TEST_UNSUPPORTED(__inst_arm(0xe060f291) " @ mls r0, r1, r2, pc") -#endif - - TEST_UNSUPPORTED(__inst_arm(0xe0700090) " @ undef") - TEST_UNSUPPORTED(__inst_arm(0xe07fff9f) " @ undef") - - TEST_RR( "umull r0, r1, r",2, VAL1,", r",3, VAL2,"") - TEST_RR( "umullls r7, r8, r",9, VAL2,", r",10, VAL1,"") - TEST_R( "umull lr, r12, r",11,VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe081f392) " @ umull pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe08f1392) " @ umull r1, pc, r2, r3") - TEST_RR( "umulls r0, r1, r",2, VAL1,", r",3, VAL2,"") - TEST_RR( "umulllss r7, r8, r",9, VAL2,", r",10, VAL1,"") - TEST_R( "umulls lr, r12, r",11,VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe091f392) " @ umulls pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe09f1392) " @ umulls r1, pc, r2, r3") - - TEST_RRRR( "umlal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "umlalle r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRR( "umlal r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe0af1392) " @ umlal pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0a1f392) " @ umlal r1, pc, r2, r3") - TEST_RRRR( "umlals r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "umlalles r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRR( "umlals r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe0bf1392) " @ umlals pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0b1f392) " @ umlals r1, pc, r2, r3") - - TEST_RR( "smull r0, r1, r",2, VAL1,", r",3, VAL2,"") - TEST_RR( "smullls r7, r8, r",9, VAL2,", r",10, VAL1,"") - TEST_R( "smull lr, r12, r",11,VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe0c1f392) " @ smull pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0cf1392) " @ smull r1, pc, r2, r3") - TEST_RR( "smulls r0, r1, r",2, VAL1,", r",3, VAL2,"") - TEST_RR( "smulllss r7, r8, r",9, VAL2,", r",10, VAL1,"") - TEST_R( "smulls lr, r12, r",11,VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe0d1f392) " @ smulls pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0df1392) " @ smulls r1, pc, r2, r3") - - TEST_RRRR( "smlal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlalle r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRR( "smlal r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe0ef1392) " @ smlal pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0e1f392) " @ smlal r1, pc, r2, r3") - TEST_RRRR( "smlals r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlalles r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRR( "smlals r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe0ff1392) " @ smlals pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0f0f392) " @ smlals r0, pc, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0f0139f) " @ smlals r0, r1, pc, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0f01f92) " @ smlals r0, r1, r2, pc") - - TEST_GROUP("Synchronization primitives") - -#if __LINUX_ARM_ARCH__ < 6 - TEST_RP("swp lr, r",7,VAL2,", [r",8,0,"]") - TEST_R( "swpvs r0, r",1,VAL1,", [sp]") - TEST_RP("swp sp, r",14,VAL2,", [r",12,13*4,"]") -#else - TEST_UNSUPPORTED(__inst_arm(0xe108e097) " @ swp lr, r7, [r8]") - TEST_UNSUPPORTED(__inst_arm(0x610d0091) " @ swpvs r0, r1, [sp]") - TEST_UNSUPPORTED(__inst_arm(0xe10cd09e) " @ swp sp, r14 [r12]") -#endif - TEST_UNSUPPORTED(__inst_arm(0xe102f091) " @ swp pc, r1, [r2]") - TEST_UNSUPPORTED(__inst_arm(0xe102009f) " @ swp r0, pc, [r2]") - TEST_UNSUPPORTED(__inst_arm(0xe10f0091) " @ swp r0, r1, [pc]") -#if __LINUX_ARM_ARCH__ < 6 - TEST_RP("swpb lr, r",7,VAL2,", [r",8,0,"]") - TEST_R( "swpvsb r0, r",1,VAL1,", [sp]") -#else - TEST_UNSUPPORTED(__inst_arm(0xe148e097) " @ swpb lr, r7, [r8]") - TEST_UNSUPPORTED(__inst_arm(0x614d0091) " @ swpvsb r0, r1, [sp]") -#endif - TEST_UNSUPPORTED(__inst_arm(0xe142f091) " @ swpb pc, r1, [r2]") - - TEST_UNSUPPORTED(__inst_arm(0xe1100090)) /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe1200090)) /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe1300090)) /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe1500090)) /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe1600090)) /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe1700090)) /* Unallocated space */ -#if __LINUX_ARM_ARCH__ >= 6 - TEST_UNSUPPORTED("ldrex r2, [sp]") -#endif -#if (__LINUX_ARM_ARCH__ >= 7) || defined(CONFIG_CPU_32v6K) - TEST_UNSUPPORTED("strexd r0, r2, r3, [sp]") - TEST_UNSUPPORTED("ldrexd r2, r3, [sp]") - TEST_UNSUPPORTED("strexb r0, r2, [sp]") - TEST_UNSUPPORTED("ldrexb r2, [sp]") - TEST_UNSUPPORTED("strexh r0, r2, [sp]") - TEST_UNSUPPORTED("ldrexh r2, [sp]") -#endif - TEST_GROUP("Extra load/store instructions") - - TEST_RPR( "strh r",0, VAL1,", [r",1, 48,", -r",2, 24,"]") - TEST_RPR( "streqh r",14,VAL2,", [r",13,0, ", r",12, 48,"]") - TEST_RPR( "strh r",1, VAL1,", [r",2, 24,", r",3, 48,"]!") - TEST_RPR( "strneh r",12,VAL2,", [r",11,48,", -r",10,24,"]!") - TEST_RPR( "strh r",2, VAL1,", [r",3, 24,"], r",4, 48,"") - TEST_RPR( "strh r",10,VAL2,", [r",9, 48,"], -r",11,24,"") - TEST_UNSUPPORTED(__inst_arm(0xe1afc0ba) " @ strh r12, [pc, r10]!") - TEST_UNSUPPORTED(__inst_arm(0xe089f0bb) " @ strh pc, [r9], r11") - TEST_UNSUPPORTED(__inst_arm(0xe089a0bf) " @ strh r10, [r9], pc") - - TEST_PR( "ldrh r0, [r",0, 48,", -r",2, 24,"]") - TEST_PR( "ldrcsh r14, [r",13,0, ", r",12, 48,"]") - TEST_PR( "ldrh r1, [r",2, 24,", r",3, 48,"]!") - TEST_PR( "ldrcch r12, [r",11,48,", -r",10,24,"]!") - TEST_PR( "ldrh r2, [r",3, 24,"], r",4, 48,"") - TEST_PR( "ldrh r10, [r",9, 48,"], -r",11,24,"") - TEST_UNSUPPORTED(__inst_arm(0xe1bfc0ba) " @ ldrh r12, [pc, r10]!") - TEST_UNSUPPORTED(__inst_arm(0xe099f0bb) " @ ldrh pc, [r9], r11") - TEST_UNSUPPORTED(__inst_arm(0xe099a0bf) " @ ldrh r10, [r9], pc") - - TEST_RP( "strh r",0, VAL1,", [r",1, 24,", #-2]") - TEST_RP( "strmih r",14,VAL2,", [r",13,0, ", #2]") - TEST_RP( "strh r",1, VAL1,", [r",2, 24,", #4]!") - TEST_RP( "strplh r",12,VAL2,", [r",11,24,", #-4]!") - TEST_RP( "strh r",2, VAL1,", [r",3, 24,"], #48") - TEST_RP( "strh r",10,VAL2,", [r",9, 64,"], #-48") - TEST_UNSUPPORTED(__inst_arm(0xe1efc3b0) " @ strh r12, [pc, #48]!") - TEST_UNSUPPORTED(__inst_arm(0xe0c9f3b0) " @ strh pc, [r9], #48") - - TEST_P( "ldrh r0, [r",0, 24,", #-2]") - TEST_P( "ldrvsh r14, [r",13,0, ", #2]") - TEST_P( "ldrh r1, [r",2, 24,", #4]!") - TEST_P( "ldrvch r12, [r",11,24,", #-4]!") - TEST_P( "ldrh r2, [r",3, 24,"], #48") - TEST_P( "ldrh r10, [r",9, 64,"], #-48") - TEST( "ldrh r0, [pc, #0]") - TEST_UNSUPPORTED(__inst_arm(0xe1ffc3b0) " @ ldrh r12, [pc, #48]!") - TEST_UNSUPPORTED(__inst_arm(0xe0d9f3b0) " @ ldrh pc, [r9], #48") - - TEST_PR( "ldrsb r0, [r",0, 48,", -r",2, 24,"]") - TEST_PR( "ldrhisb r14, [r",13,0,", r",12, 48,"]") - TEST_PR( "ldrsb r1, [r",2, 24,", r",3, 48,"]!") - TEST_PR( "ldrlssb r12, [r",11,48,", -r",10,24,"]!") - TEST_PR( "ldrsb r2, [r",3, 24,"], r",4, 48,"") - TEST_PR( "ldrsb r10, [r",9, 48,"], -r",11,24,"") - TEST_UNSUPPORTED(__inst_arm(0xe1bfc0da) " @ ldrsb r12, [pc, r10]!") - TEST_UNSUPPORTED(__inst_arm(0xe099f0db) " @ ldrsb pc, [r9], r11") - - TEST_P( "ldrsb r0, [r",0, 24,", #-1]") - TEST_P( "ldrgesb r14, [r",13,0, ", #1]") - TEST_P( "ldrsb r1, [r",2, 24,", #4]!") - TEST_P( "ldrltsb r12, [r",11,24,", #-4]!") - TEST_P( "ldrsb r2, [r",3, 24,"], #48") - TEST_P( "ldrsb r10, [r",9, 64,"], #-48") - TEST( "ldrsb r0, [pc, #0]") - TEST_UNSUPPORTED(__inst_arm(0xe1ffc3d0) " @ ldrsb r12, [pc, #48]!") - TEST_UNSUPPORTED(__inst_arm(0xe0d9f3d0) " @ ldrsb pc, [r9], #48") - - TEST_PR( "ldrsh r0, [r",0, 48,", -r",2, 24,"]") - TEST_PR( "ldrgtsh r14, [r",13,0, ", r",12, 48,"]") - TEST_PR( "ldrsh r1, [r",2, 24,", r",3, 48,"]!") - TEST_PR( "ldrlesh r12, [r",11,48,", -r",10,24,"]!") - TEST_PR( "ldrsh r2, [r",3, 24,"], r",4, 48,"") - TEST_PR( "ldrsh r10, [r",9, 48,"], -r",11,24,"") - TEST_UNSUPPORTED(__inst_arm(0xe1bfc0fa) " @ ldrsh r12, [pc, r10]!") - TEST_UNSUPPORTED(__inst_arm(0xe099f0fb) " @ ldrsh pc, [r9], r11") - - TEST_P( "ldrsh r0, [r",0, 24,", #-1]") - TEST_P( "ldreqsh r14, [r",13,0 ,", #1]") - TEST_P( "ldrsh r1, [r",2, 24,", #4]!") - TEST_P( "ldrnesh r12, [r",11,24,", #-4]!") - TEST_P( "ldrsh r2, [r",3, 24,"], #48") - TEST_P( "ldrsh r10, [r",9, 64,"], #-48") - TEST( "ldrsh r0, [pc, #0]") - TEST_UNSUPPORTED(__inst_arm(0xe1ffc3f0) " @ ldrsh r12, [pc, #48]!") - TEST_UNSUPPORTED(__inst_arm(0xe0d9f3f0) " @ ldrsh pc, [r9], #48") - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_UNSUPPORTED("strht r1, [r2], r3") - TEST_UNSUPPORTED("ldrht r1, [r2], r3") - TEST_UNSUPPORTED("strht r1, [r2], #48") - TEST_UNSUPPORTED("ldrht r1, [r2], #48") - TEST_UNSUPPORTED("ldrsbt r1, [r2], r3") - TEST_UNSUPPORTED("ldrsbt r1, [r2], #48") - TEST_UNSUPPORTED("ldrsht r1, [r2], r3") - TEST_UNSUPPORTED("ldrsht r1, [r2], #48") -#endif - -#if __LINUX_ARM_ARCH__ >= 5 - TEST_RPR( "strd r",0, VAL1,", [r",1, 48,", -r",2,24,"]") - TEST_RPR( "strccd r",8, VAL2,", [r",13,0, ", r",12,48,"]") - TEST_RPR( "strd r",4, VAL1,", [r",2, 24,", r",3, 48,"]!") - TEST_RPR( "strcsd r",12,VAL2,", [r",11,48,", -r",10,24,"]!") - TEST_RPR( "strd r",2, VAL1,", [r",5, 24,"], r",4,48,"") - TEST_RPR( "strd r",10,VAL2,", [r",9, 48,"], -r",7,24,"") - TEST_UNSUPPORTED(__inst_arm(0xe1afc0fa) " @ strd r12, [pc, r10]!") - - TEST_PR( "ldrd r0, [r",0, 48,", -r",2,24,"]") - TEST_PR( "ldrmid r8, [r",13,0, ", r",12,48,"]") - TEST_PR( "ldrd r4, [r",2, 24,", r",3, 48,"]!") - TEST_PR( "ldrpld r6, [r",11,48,", -r",10,24,"]!") - TEST_PR( "ldrd r2, [r",5, 24,"], r",4,48,"") - TEST_PR( "ldrd r10, [r",9,48,"], -r",7,24,"") - TEST_UNSUPPORTED(__inst_arm(0xe1afc0da) " @ ldrd r12, [pc, r10]!") - TEST_UNSUPPORTED(__inst_arm(0xe089f0db) " @ ldrd pc, [r9], r11") - TEST_UNSUPPORTED(__inst_arm(0xe089e0db) " @ ldrd lr, [r9], r11") - TEST_UNSUPPORTED(__inst_arm(0xe089c0df) " @ ldrd r12, [r9], pc") - - TEST_RP( "strd r",0, VAL1,", [r",1, 24,", #-8]") - TEST_RP( "strvsd r",8, VAL2,", [r",13,0, ", #8]") - TEST_RP( "strd r",4, VAL1,", [r",2, 24,", #16]!") - TEST_RP( "strvcd r",12,VAL2,", [r",11,24,", #-16]!") - TEST_RP( "strd r",2, VAL1,", [r",4, 24,"], #48") - TEST_RP( "strd r",10,VAL2,", [r",9, 64,"], #-48") - TEST_UNSUPPORTED(__inst_arm(0xe1efc3f0) " @ strd r12, [pc, #48]!") - - TEST_P( "ldrd r0, [r",0, 24,", #-8]") - TEST_P( "ldrhid r8, [r",13,0, ", #8]") - TEST_P( "ldrd r4, [r",2, 24,", #16]!") - TEST_P( "ldrlsd r6, [r",11,24,", #-16]!") - TEST_P( "ldrd r2, [r",5, 24,"], #48") - TEST_P( "ldrd r10, [r",9,6,"], #-48") - TEST_UNSUPPORTED(__inst_arm(0xe1efc3d0) " @ ldrd r12, [pc, #48]!") - TEST_UNSUPPORTED(__inst_arm(0xe0c9f3d0) " @ ldrd pc, [r9], #48") - TEST_UNSUPPORTED(__inst_arm(0xe0c9e3d0) " @ ldrd lr, [r9], #48") -#endif - - TEST_GROUP("Miscellaneous") - -#if __LINUX_ARM_ARCH__ >= 7 - TEST("movw r0, #0") - TEST("movw r0, #0xffff") - TEST("movw lr, #0xffff") - TEST_UNSUPPORTED(__inst_arm(0xe300f000) " @ movw pc, #0") - TEST_R("movt r",0, VAL1,", #0") - TEST_R("movt r",0, VAL2,", #0xffff") - TEST_R("movt r",14,VAL1,", #0xffff") - TEST_UNSUPPORTED(__inst_arm(0xe340f000) " @ movt pc, #0") -#endif - - TEST_UNSUPPORTED("msr cpsr, 0x13") - TEST_UNSUPPORTED("msr cpsr_f, 0xf0000000") - TEST_UNSUPPORTED("msr spsr, 0x13") - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_SUPPORTED("yield") - TEST("sev") - TEST("nop") - TEST("wfi") - TEST_SUPPORTED("wfe") - TEST_UNSUPPORTED("dbg #0") -#endif - - TEST_GROUP("Load/store word and unsigned byte") - -#define LOAD_STORE(byte) \ - TEST_RP( "str"byte" r",0, VAL1,", [r",1, 24,", #-2]") \ - TEST_RP( "str"byte" r",14,VAL2,", [r",13,0, ", #2]") \ - TEST_RP( "str"byte" r",1, VAL1,", [r",2, 24,", #4]!") \ - TEST_RP( "str"byte" r",12,VAL2,", [r",11,24,", #-4]!") \ - TEST_RP( "str"byte" r",2, VAL1,", [r",3, 24,"], #48") \ - TEST_RP( "str"byte" r",10,VAL2,", [r",9, 64,"], #-48") \ - TEST_RPR("str"byte" r",0, VAL1,", [r",1, 48,", -r",2, 24,"]") \ - TEST_RPR("str"byte" r",14,VAL2,", [r",13,0, ", r",12, 48,"]") \ - TEST_RPR("str"byte" r",1, VAL1,", [r",2, 24,", r",3, 48,"]!") \ - TEST_RPR("str"byte" r",12,VAL2,", [r",11,48,", -r",10,24,"]!") \ - TEST_RPR("str"byte" r",2, VAL1,", [r",3, 24,"], r",4, 48,"") \ - TEST_RPR("str"byte" r",10,VAL2,", [r",9, 48,"], -r",11,24,"") \ - TEST_RPR("str"byte" r",0, VAL1,", [r",1, 24,", r",2, 32,", asl #1]")\ - TEST_RPR("str"byte" r",14,VAL2,", [r",13,0, ", r",12, 32,", lsr #2]")\ - TEST_RPR("str"byte" r",1, VAL1,", [r",2, 24,", r",3, 32,", asr #3]!")\ - TEST_RPR("str"byte" r",12,VAL2,", [r",11,24,", r",10, 4,", ror #31]!")\ - TEST_P( "ldr"byte" r0, [r",0, 24,", #-2]") \ - TEST_P( "ldr"byte" r14, [r",13,0, ", #2]") \ - TEST_P( "ldr"byte" r1, [r",2, 24,", #4]!") \ - TEST_P( "ldr"byte" r12, [r",11,24,", #-4]!") \ - TEST_P( "ldr"byte" r2, [r",3, 24,"], #48") \ - TEST_P( "ldr"byte" r10, [r",9, 64,"], #-48") \ - TEST_PR( "ldr"byte" r0, [r",0, 48,", -r",2, 24,"]") \ - TEST_PR( "ldr"byte" r14, [r",13,0, ", r",12, 48,"]") \ - TEST_PR( "ldr"byte" r1, [r",2, 24,", r",3, 48,"]!") \ - TEST_PR( "ldr"byte" r12, [r",11,48,", -r",10,24,"]!") \ - TEST_PR( "ldr"byte" r2, [r",3, 24,"], r",4, 48,"") \ - TEST_PR( "ldr"byte" r10, [r",9, 48,"], -r",11,24,"") \ - TEST_PR( "ldr"byte" r0, [r",0, 24,", r",2, 32,", asl #1]") \ - TEST_PR( "ldr"byte" r14, [r",13,0, ", r",12, 32,", lsr #2]") \ - TEST_PR( "ldr"byte" r1, [r",2, 24,", r",3, 32,", asr #3]!") \ - TEST_PR( "ldr"byte" r12, [r",11,24,", r",10, 4,", ror #31]!") \ - TEST( "ldr"byte" r0, [pc, #0]") \ - TEST_R( "ldr"byte" r12, [pc, r",14,0,"]") - - LOAD_STORE("") - TEST_P( "str pc, [r",0,0,", #15*4]") - TEST_R( "str pc, [sp, r",2,15*4,"]") - TEST_BF( "ldr pc, [sp, #15*4]") - TEST_BF_R("ldr pc, [sp, r",2,15*4,"]") - - TEST_P( "str sp, [r",0,0,", #13*4]") - TEST_R( "str sp, [sp, r",2,13*4,"]") - TEST_BF( "ldr sp, [sp, #13*4]") - TEST_BF_R("ldr sp, [sp, r",2,13*4,"]") - -#ifdef CONFIG_THUMB2_KERNEL - TEST_ARM_TO_THUMB_INTERWORK_P("ldr pc, [r",0,0,", #15*4]") -#endif - TEST_UNSUPPORTED(__inst_arm(0xe5af6008) " @ str r6, [pc, #8]!") - TEST_UNSUPPORTED(__inst_arm(0xe7af6008) " @ str r6, [pc, r8]!") - TEST_UNSUPPORTED(__inst_arm(0xe5bf6008) " @ ldr r6, [pc, #8]!") - TEST_UNSUPPORTED(__inst_arm(0xe7bf6008) " @ ldr r6, [pc, r8]!") - TEST_UNSUPPORTED(__inst_arm(0xe788600f) " @ str r6, [r8, pc]") - TEST_UNSUPPORTED(__inst_arm(0xe798600f) " @ ldr r6, [r8, pc]") - - LOAD_STORE("b") - TEST_UNSUPPORTED(__inst_arm(0xe5f7f008) " @ ldrb pc, [r7, #8]!") - TEST_UNSUPPORTED(__inst_arm(0xe7f7f008) " @ ldrb pc, [r7, r8]!") - TEST_UNSUPPORTED(__inst_arm(0xe5ef6008) " @ strb r6, [pc, #8]!") - TEST_UNSUPPORTED(__inst_arm(0xe7ef6008) " @ strb r6, [pc, r3]!") - TEST_UNSUPPORTED(__inst_arm(0xe5ff6008) " @ ldrb r6, [pc, #8]!") - TEST_UNSUPPORTED(__inst_arm(0xe7ff6008) " @ ldrb r6, [pc, r3]!") - - TEST_UNSUPPORTED("ldrt r0, [r1], #4") - TEST_UNSUPPORTED("ldrt r1, [r2], r3") - TEST_UNSUPPORTED("strt r2, [r3], #4") - TEST_UNSUPPORTED("strt r3, [r4], r5") - TEST_UNSUPPORTED("ldrbt r4, [r5], #4") - TEST_UNSUPPORTED("ldrbt r5, [r6], r7") - TEST_UNSUPPORTED("strbt r6, [r7], #4") - TEST_UNSUPPORTED("strbt r7, [r8], r9") - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_GROUP("Parallel addition and subtraction, signed") - - TEST_UNSUPPORTED(__inst_arm(0xe6000010) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe60fffff) "") /* Unallocated space */ - - TEST_RR( "sadd16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sadd16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe61cff1a) " @ sadd16 pc, r12, r10") - TEST_RR( "sasx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sasx r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe61cff3a) " @ sasx pc, r12, r10") - TEST_RR( "ssax r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "ssax r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe61cff5a) " @ ssax pc, r12, r10") - TEST_RR( "ssub16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "ssub16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe61cff7a) " @ ssub16 pc, r12, r10") - TEST_RR( "sadd8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sadd8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe61cff9a) " @ sadd8 pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe61000b0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe61fffbf) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe61000d0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe61fffdf) "") /* Unallocated space */ - TEST_RR( "ssub8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "ssub8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe61cfffa) " @ ssub8 pc, r12, r10") - - TEST_RR( "qadd16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "qadd16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe62cff1a) " @ qadd16 pc, r12, r10") - TEST_RR( "qasx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "qasx r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe62cff3a) " @ qasx pc, r12, r10") - TEST_RR( "qsax r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "qsax r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe62cff5a) " @ qsax pc, r12, r10") - TEST_RR( "qsub16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "qsub16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe62cff7a) " @ qsub16 pc, r12, r10") - TEST_RR( "qadd8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "qadd8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe62cff9a) " @ qadd8 pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe62000b0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe62fffbf) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe62000d0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe62fffdf) "") /* Unallocated space */ - TEST_RR( "qsub8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "qsub8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe62cfffa) " @ qsub8 pc, r12, r10") - - TEST_RR( "shadd16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "shadd16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe63cff1a) " @ shadd16 pc, r12, r10") - TEST_RR( "shasx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "shasx r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe63cff3a) " @ shasx pc, r12, r10") - TEST_RR( "shsax r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "shsax r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe63cff5a) " @ shsax pc, r12, r10") - TEST_RR( "shsub16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "shsub16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe63cff7a) " @ shsub16 pc, r12, r10") - TEST_RR( "shadd8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "shadd8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe63cff9a) " @ shadd8 pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe63000b0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe63fffbf) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe63000d0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe63fffdf) "") /* Unallocated space */ - TEST_RR( "shsub8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "shsub8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe63cfffa) " @ shsub8 pc, r12, r10") - - TEST_GROUP("Parallel addition and subtraction, unsigned") - - TEST_UNSUPPORTED(__inst_arm(0xe6400010) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe64fffff) "") /* Unallocated space */ - - TEST_RR( "uadd16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uadd16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe65cff1a) " @ uadd16 pc, r12, r10") - TEST_RR( "uasx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uasx r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe65cff3a) " @ uasx pc, r12, r10") - TEST_RR( "usax r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "usax r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe65cff5a) " @ usax pc, r12, r10") - TEST_RR( "usub16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "usub16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe65cff7a) " @ usub16 pc, r12, r10") - TEST_RR( "uadd8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uadd8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe65cff9a) " @ uadd8 pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe65000b0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe65fffbf) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe65000d0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe65fffdf) "") /* Unallocated space */ - TEST_RR( "usub8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "usub8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe65cfffa) " @ usub8 pc, r12, r10") - - TEST_RR( "uqadd16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uqadd16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe66cff1a) " @ uqadd16 pc, r12, r10") - TEST_RR( "uqasx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uqasx r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe66cff3a) " @ uqasx pc, r12, r10") - TEST_RR( "uqsax r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uqsax r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe66cff5a) " @ uqsax pc, r12, r10") - TEST_RR( "uqsub16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uqsub16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe66cff7a) " @ uqsub16 pc, r12, r10") - TEST_RR( "uqadd8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uqadd8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe66cff9a) " @ uqadd8 pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe66000b0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe66fffbf) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe66000d0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe66fffdf) "") /* Unallocated space */ - TEST_RR( "uqsub8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uqsub8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe66cfffa) " @ uqsub8 pc, r12, r10") - - TEST_RR( "uhadd16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uhadd16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe67cff1a) " @ uhadd16 pc, r12, r10") - TEST_RR( "uhasx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uhasx r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe67cff3a) " @ uhasx pc, r12, r10") - TEST_RR( "uhsax r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uhsax r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe67cff5a) " @ uhsax pc, r12, r10") - TEST_RR( "uhsub16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uhsub16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe67cff7a) " @ uhsub16 pc, r12, r10") - TEST_RR( "uhadd8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uhadd8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe67cff9a) " @ uhadd8 pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe67000b0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe67fffbf) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe67000d0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe67fffdf) "") /* Unallocated space */ - TEST_RR( "uhsub8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uhsub8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe67cfffa) " @ uhsub8 pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe67feffa) " @ uhsub8 r14, pc, r10") - TEST_UNSUPPORTED(__inst_arm(0xe67cefff) " @ uhsub8 r14, r12, pc") -#endif /* __LINUX_ARM_ARCH__ >= 7 */ - -#if __LINUX_ARM_ARCH__ >= 6 - TEST_GROUP("Packing, unpacking, saturation, and reversal") - - TEST_RR( "pkhbt r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "pkhbt r14,r",12, HH1,", r",10,HH2,", lsl #2") - TEST_UNSUPPORTED(__inst_arm(0xe68cf11a) " @ pkhbt pc, r12, r10, lsl #2") - TEST_RR( "pkhtb r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "pkhtb r14,r",12, HH1,", r",10,HH2,", asr #2") - TEST_UNSUPPORTED(__inst_arm(0xe68cf15a) " @ pkhtb pc, r12, r10, asr #2") - TEST_UNSUPPORTED(__inst_arm(0xe68fe15a) " @ pkhtb r14, pc, r10, asr #2") - TEST_UNSUPPORTED(__inst_arm(0xe68ce15f) " @ pkhtb r14, r12, pc, asr #2") - TEST_UNSUPPORTED(__inst_arm(0xe6900010) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe69fffdf) "") /* Unallocated space */ - - TEST_R( "ssat r0, #24, r",0, VAL1,"") - TEST_R( "ssat r14, #24, r",12, VAL2,"") - TEST_R( "ssat r0, #24, r",0, VAL1,", lsl #8") - TEST_R( "ssat r14, #24, r",12, VAL2,", asr #8") - TEST_UNSUPPORTED(__inst_arm(0xe6b7f01c) " @ ssat pc, #24, r12") - - TEST_R( "usat r0, #24, r",0, VAL1,"") - TEST_R( "usat r14, #24, r",12, VAL2,"") - TEST_R( "usat r0, #24, r",0, VAL1,", lsl #8") - TEST_R( "usat r14, #24, r",12, VAL2,", asr #8") - TEST_UNSUPPORTED(__inst_arm(0xe6f7f01c) " @ usat pc, #24, r12") - - TEST_RR( "sxtab16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sxtab16 r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "sxtb16 r8, r",7, HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe68cf47a) " @ sxtab16 pc,r12, r10, ror #8") - - TEST_RR( "sel r0, r",0, VAL1,", r",1, VAL2,"") - TEST_RR( "sel r14, r",12,VAL1,", r",10, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe68cffba) " @ sel pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe68fefba) " @ sel r14, pc, r10") - TEST_UNSUPPORTED(__inst_arm(0xe68cefbf) " @ sel r14, r12, pc") - - TEST_R( "ssat16 r0, #12, r",0, HH1,"") - TEST_R( "ssat16 r14, #12, r",12, HH2,"") - TEST_UNSUPPORTED(__inst_arm(0xe6abff3c) " @ ssat16 pc, #12, r12") - - TEST_RR( "sxtab r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sxtab r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "sxtb r8, r",7, HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe6acf47a) " @ sxtab pc,r12, r10, ror #8") - - TEST_R( "rev r0, r",0, VAL1,"") - TEST_R( "rev r14, r",12, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe6bfff3c) " @ rev pc, r12") - - TEST_RR( "sxtah r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sxtah r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "sxth r8, r",7, HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe6bcf47a) " @ sxtah pc,r12, r10, ror #8") - - TEST_R( "rev16 r0, r",0, VAL1,"") - TEST_R( "rev16 r14, r",12, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe6bfffbc) " @ rev16 pc, r12") - - TEST_RR( "uxtab16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uxtab16 r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "uxtb16 r8, r",7, HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe6ccf47a) " @ uxtab16 pc,r12, r10, ror #8") - - TEST_R( "usat16 r0, #12, r",0, HH1,"") - TEST_R( "usat16 r14, #12, r",12, HH2,"") - TEST_UNSUPPORTED(__inst_arm(0xe6ecff3c) " @ usat16 pc, #12, r12") - TEST_UNSUPPORTED(__inst_arm(0xe6ecef3f) " @ usat16 r14, #12, pc") - - TEST_RR( "uxtab r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uxtab r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "uxtb r8, r",7, HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe6ecf47a) " @ uxtab pc,r12, r10, ror #8") - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_R( "rbit r0, r",0, VAL1,"") - TEST_R( "rbit r14, r",12, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe6ffff3c) " @ rbit pc, r12") -#endif - - TEST_RR( "uxtah r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uxtah r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "uxth r8, r",7, HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe6fff077) " @ uxth pc, r7") - TEST_UNSUPPORTED(__inst_arm(0xe6ff807f) " @ uxth r8, pc") - TEST_UNSUPPORTED(__inst_arm(0xe6fcf47a) " @ uxtah pc, r12, r10, ror #8") - TEST_UNSUPPORTED(__inst_arm(0xe6fce47f) " @ uxtah r14, r12, pc, ror #8") - - TEST_R( "revsh r0, r",0, VAL1,"") - TEST_R( "revsh r14, r",12, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe6ffff3c) " @ revsh pc, r12") - TEST_UNSUPPORTED(__inst_arm(0xe6ffef3f) " @ revsh r14, pc") - - TEST_UNSUPPORTED(__inst_arm(0xe6900070) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe69fff7f) "") /* Unallocated space */ - - TEST_UNSUPPORTED(__inst_arm(0xe6d00070) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe6dfff7f) "") /* Unallocated space */ -#endif /* __LINUX_ARM_ARCH__ >= 6 */ - -#if __LINUX_ARM_ARCH__ >= 6 - TEST_GROUP("Signed multiplies") - - TEST_RRR( "smlad r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") - TEST_RRR( "smlad r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe70f8a1c) " @ smlad pc, r12, r10, r8") - TEST_RRR( "smladx r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") - TEST_RRR( "smladx r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe70f8a3c) " @ smladx pc, r12, r10, r8") - - TEST_RR( "smuad r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "smuad r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe70ffa1c) " @ smuad pc, r12, r10") - TEST_RR( "smuadx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "smuadx r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe70ffa3c) " @ smuadx pc, r12, r10") - - TEST_RRR( "smlsd r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") - TEST_RRR( "smlsd r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe70f8a5c) " @ smlsd pc, r12, r10, r8") - TEST_RRR( "smlsdx r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") - TEST_RRR( "smlsdx r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe70f8a7c) " @ smlsdx pc, r12, r10, r8") - - TEST_RR( "smusd r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "smusd r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe70ffa5c) " @ smusd pc, r12, r10") - TEST_RR( "smusdx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "smusdx r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe70ffa7c) " @ smusdx pc, r12, r10") - - TEST_RRRR( "smlald r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) - TEST_RRRR( "smlald r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) - TEST_UNSUPPORTED(__inst_arm(0xe74af819) " @ smlald pc, r10, r9, r8") - TEST_UNSUPPORTED(__inst_arm(0xe74fb819) " @ smlald r11, pc, r9, r8") - TEST_UNSUPPORTED(__inst_arm(0xe74ab81f) " @ smlald r11, r10, pc, r8") - TEST_UNSUPPORTED(__inst_arm(0xe74abf19) " @ smlald r11, r10, r9, pc") - - TEST_RRRR( "smlaldx r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) - TEST_RRRR( "smlaldx r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) - TEST_UNSUPPORTED(__inst_arm(0xe74af839) " @ smlaldx pc, r10, r9, r8") - TEST_UNSUPPORTED(__inst_arm(0xe74fb839) " @ smlaldx r11, pc, r9, r8") - - TEST_RRR( "smmla r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") - TEST_RRR( "smmla r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe75f8a1c) " @ smmla pc, r12, r10, r8") - TEST_RRR( "smmlar r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") - TEST_RRR( "smmlar r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe75f8a3c) " @ smmlar pc, r12, r10, r8") - - TEST_RR( "smmul r0, r",0, VAL1,", r",1, VAL2,"") - TEST_RR( "smmul r14, r",12,VAL2,", r",10,VAL1,"") - TEST_UNSUPPORTED(__inst_arm(0xe75ffa1c) " @ smmul pc, r12, r10") - TEST_RR( "smmulr r0, r",0, VAL1,", r",1, VAL2,"") - TEST_RR( "smmulr r14, r",12,VAL2,", r",10,VAL1,"") - TEST_UNSUPPORTED(__inst_arm(0xe75ffa3c) " @ smmulr pc, r12, r10") - - TEST_RRR( "smmls r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") - TEST_RRR( "smmls r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe75f8adc) " @ smmls pc, r12, r10, r8") - TEST_RRR( "smmlsr r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") - TEST_RRR( "smmlsr r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe75f8afc) " @ smmlsr pc, r12, r10, r8") - TEST_UNSUPPORTED(__inst_arm(0xe75e8aff) " @ smmlsr r14, pc, r10, r8") - TEST_UNSUPPORTED(__inst_arm(0xe75e8ffc) " @ smmlsr r14, r12, pc, r8") - TEST_UNSUPPORTED(__inst_arm(0xe75efafc) " @ smmlsr r14, r12, r10, pc") - - TEST_RR( "usad8 r0, r",0, VAL1,", r",1, VAL2,"") - TEST_RR( "usad8 r14, r",12,VAL2,", r",10,VAL1,"") - TEST_UNSUPPORTED(__inst_arm(0xe75ffa1c) " @ usad8 pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe75efa1f) " @ usad8 r14, pc, r10") - TEST_UNSUPPORTED(__inst_arm(0xe75eff1c) " @ usad8 r14, r12, pc") - - TEST_RRR( "usada8 r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL3,"") - TEST_RRR( "usada8 r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL3,"") - TEST_UNSUPPORTED(__inst_arm(0xe78f8a1c) " @ usada8 pc, r12, r10, r8") - TEST_UNSUPPORTED(__inst_arm(0xe78e8a1f) " @ usada8 r14, pc, r10, r8") - TEST_UNSUPPORTED(__inst_arm(0xe78e8f1c) " @ usada8 r14, r12, pc, r8") -#endif /* __LINUX_ARM_ARCH__ >= 6 */ - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_GROUP("Bit Field") - - TEST_R( "sbfx r0, r",0 , VAL1,", #0, #31") - TEST_R( "sbfxeq r14, r",12, VAL2,", #8, #16") - TEST_R( "sbfx r4, r",10, VAL1,", #16, #15") - TEST_UNSUPPORTED(__inst_arm(0xe7aff45c) " @ sbfx pc, r12, #8, #16") - - TEST_R( "ubfx r0, r",0 , VAL1,", #0, #31") - TEST_R( "ubfxcs r14, r",12, VAL2,", #8, #16") - TEST_R( "ubfx r4, r",10, VAL1,", #16, #15") - TEST_UNSUPPORTED(__inst_arm(0xe7eff45c) " @ ubfx pc, r12, #8, #16") - TEST_UNSUPPORTED(__inst_arm(0xe7efc45f) " @ ubfx r12, pc, #8, #16") - - TEST_R( "bfc r",0, VAL1,", #4, #20") - TEST_R( "bfcvs r",14,VAL2,", #4, #20") - TEST_R( "bfc r",7, VAL1,", #0, #31") - TEST_R( "bfc r",8, VAL2,", #0, #31") - TEST_UNSUPPORTED(__inst_arm(0xe7def01f) " @ bfc pc, #0, #31"); - - TEST_RR( "bfi r",0, VAL1,", r",0 , VAL2,", #0, #31") - TEST_RR( "bfipl r",12,VAL1,", r",14 , VAL2,", #4, #20") - TEST_UNSUPPORTED(__inst_arm(0xe7d7f21e) " @ bfi pc, r14, #4, #20") - - TEST_UNSUPPORTED(__inst_arm(0x07f000f0) "") /* Permanently UNDEFINED */ - TEST_UNSUPPORTED(__inst_arm(0x07ffffff) "") /* Permanently UNDEFINED */ -#endif /* __LINUX_ARM_ARCH__ >= 6 */ - - TEST_GROUP("Branch, branch with link, and block data transfer") - - TEST_P( "stmda r",0, 16*4,", {r0}") - TEST_P( "stmeqda r",4, 16*4,", {r0-r15}") - TEST_P( "stmneda r",8, 16*4,"!, {r8-r15}") - TEST_P( "stmda r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_P( "stmda r",13,0, "!, {pc}") - - TEST_P( "ldmda r",0, 16*4,", {r0}") - TEST_BF_P("ldmcsda r",4, 15*4,", {r0-r15}") - TEST_BF_P("ldmccda r",7, 15*4,"!, {r8-r15}") - TEST_P( "ldmda r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_BF_P("ldmda r",14,15*4,"!, {pc}") - - TEST_P( "stmia r",0, 16*4,", {r0}") - TEST_P( "stmmiia r",4, 16*4,", {r0-r15}") - TEST_P( "stmplia r",8, 16*4,"!, {r8-r15}") - TEST_P( "stmia r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_P( "stmia r",14,0, "!, {pc}") - - TEST_P( "ldmia r",0, 16*4,", {r0}") - TEST_BF_P("ldmvsia r",4, 0, ", {r0-r15}") - TEST_BF_P("ldmvcia r",7, 8*4, "!, {r8-r15}") - TEST_P( "ldmia r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_BF_P("ldmia r",14,15*4,"!, {pc}") - - TEST_P( "stmdb r",0, 16*4,", {r0}") - TEST_P( "stmhidb r",4, 16*4,", {r0-r15}") - TEST_P( "stmlsdb r",8, 16*4,"!, {r8-r15}") - TEST_P( "stmdb r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_P( "stmdb r",13,4, "!, {pc}") - - TEST_P( "ldmdb r",0, 16*4,", {r0}") - TEST_BF_P("ldmgedb r",4, 16*4,", {r0-r15}") - TEST_BF_P("ldmltdb r",7, 16*4,"!, {r8-r15}") - TEST_P( "ldmdb r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_BF_P("ldmdb r",14,16*4,"!, {pc}") - - TEST_P( "stmib r",0, 16*4,", {r0}") - TEST_P( "stmgtib r",4, 16*4,", {r0-r15}") - TEST_P( "stmleib r",8, 16*4,"!, {r8-r15}") - TEST_P( "stmib r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_P( "stmib r",13,-4, "!, {pc}") - - TEST_P( "ldmib r",0, 16*4,", {r0}") - TEST_BF_P("ldmeqib r",4, -4,", {r0-r15}") - TEST_BF_P("ldmneib r",7, 7*4,"!, {r8-r15}") - TEST_P( "ldmib r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_BF_P("ldmib r",14,14*4,"!, {pc}") - - TEST_P( "stmdb r",13,16*4,"!, {r3-r12,lr}") - TEST_P( "stmeqdb r",13,16*4,"!, {r3-r12}") - TEST_P( "stmnedb r",2, 16*4,", {r3-r12,lr}") - TEST_P( "stmdb r",13,16*4,"!, {r2-r12,lr}") - TEST_P( "stmdb r",0, 16*4,", {r0-r12}") - TEST_P( "stmdb r",0, 16*4,", {r0-r12,lr}") - - TEST_BF_P("ldmia r",13,5*4, "!, {r3-r12,pc}") - TEST_P( "ldmccia r",13,5*4, "!, {r3-r12}") - TEST_BF_P("ldmcsia r",2, 5*4, "!, {r3-r12,pc}") - TEST_BF_P("ldmia r",13,4*4, "!, {r2-r12,pc}") - TEST_P( "ldmia r",0, 16*4,", {r0-r12}") - TEST_P( "ldmia r",0, 16*4,", {r0-r12,lr}") - -#ifdef CONFIG_THUMB2_KERNEL - TEST_ARM_TO_THUMB_INTERWORK_P("ldmplia r",0,15*4,", {pc}") - TEST_ARM_TO_THUMB_INTERWORK_P("ldmmiia r",13,0,", {r0-r15}") -#endif - TEST_BF("b 2f") - TEST_BF("bl 2f") - TEST_BB("b 2b") - TEST_BB("bl 2b") - - TEST_BF("beq 2f") - TEST_BF("bleq 2f") - TEST_BB("bne 2b") - TEST_BB("blne 2b") - - TEST_BF("bgt 2f") - TEST_BF("blgt 2f") - TEST_BB("blt 2b") - TEST_BB("bllt 2b") - - TEST_GROUP("Supervisor Call, and coprocessor instructions") - - /* - * We can't really test these by executing them, so all - * we can do is check that probes are, or are not allowed. - * At the moment none are allowed... - */ -#define TEST_COPROCESSOR(code) TEST_UNSUPPORTED(code) - -#define COPROCESSOR_INSTRUCTIONS_ST_LD(two,cc) \ - TEST_COPROCESSOR("stc"two" 0, cr0, [r13, #4]") \ - TEST_COPROCESSOR("stc"two" 0, cr0, [r13, #-4]") \ - TEST_COPROCESSOR("stc"two" 0, cr0, [r13, #4]!") \ - TEST_COPROCESSOR("stc"two" 0, cr0, [r13, #-4]!") \ - TEST_COPROCESSOR("stc"two" 0, cr0, [r13], #4") \ - TEST_COPROCESSOR("stc"two" 0, cr0, [r13], #-4") \ - TEST_COPROCESSOR("stc"two" 0, cr0, [r13], {1}") \ - TEST_COPROCESSOR("stc"two"l 0, cr0, [r13, #4]") \ - TEST_COPROCESSOR("stc"two"l 0, cr0, [r13, #-4]") \ - TEST_COPROCESSOR("stc"two"l 0, cr0, [r13, #4]!") \ - TEST_COPROCESSOR("stc"two"l 0, cr0, [r13, #-4]!") \ - TEST_COPROCESSOR("stc"two"l 0, cr0, [r13], #4") \ - TEST_COPROCESSOR("stc"two"l 0, cr0, [r13], #-4") \ - TEST_COPROCESSOR("stc"two"l 0, cr0, [r13], {1}") \ - TEST_COPROCESSOR("ldc"two" 0, cr0, [r13, #4]") \ - TEST_COPROCESSOR("ldc"two" 0, cr0, [r13, #-4]") \ - TEST_COPROCESSOR("ldc"two" 0, cr0, [r13, #4]!") \ - TEST_COPROCESSOR("ldc"two" 0, cr0, [r13, #-4]!") \ - TEST_COPROCESSOR("ldc"two" 0, cr0, [r13], #4") \ - TEST_COPROCESSOR("ldc"two" 0, cr0, [r13], #-4") \ - TEST_COPROCESSOR("ldc"two" 0, cr0, [r13], {1}") \ - TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13, #4]") \ - TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13, #-4]") \ - TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13, #4]!") \ - TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13, #-4]!") \ - TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13], #4") \ - TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13], #-4") \ - TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13], {1}") \ - \ - TEST_COPROCESSOR( "stc"two" 0, cr0, [r15, #4]") \ - TEST_COPROCESSOR( "stc"two" 0, cr0, [r15, #-4]") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##daf0001) " @ stc"two" 0, cr0, [r15, #4]!") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##d2f0001) " @ stc"two" 0, cr0, [r15, #-4]!") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##caf0001) " @ stc"two" 0, cr0, [r15], #4") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##c2f0001) " @ stc"two" 0, cr0, [r15], #-4") \ - TEST_COPROCESSOR( "stc"two" 0, cr0, [r15], {1}") \ - TEST_COPROCESSOR( "stc"two"l 0, cr0, [r15, #4]") \ - TEST_COPROCESSOR( "stc"two"l 0, cr0, [r15, #-4]") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##def0001) " @ stc"two"l 0, cr0, [r15, #4]!") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##d6f0001) " @ stc"two"l 0, cr0, [r15, #-4]!") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##cef0001) " @ stc"two"l 0, cr0, [r15], #4") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##c6f0001) " @ stc"two"l 0, cr0, [r15], #-4") \ - TEST_COPROCESSOR( "stc"two"l 0, cr0, [r15], {1}") \ - TEST_COPROCESSOR( "ldc"two" 0, cr0, [r15, #4]") \ - TEST_COPROCESSOR( "ldc"two" 0, cr0, [r15, #-4]") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##dbf0001) " @ ldc"two" 0, cr0, [r15, #4]!") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##d3f0001) " @ ldc"two" 0, cr0, [r15, #-4]!") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##cbf0001) " @ ldc"two" 0, cr0, [r15], #4") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##c3f0001) " @ ldc"two" 0, cr0, [r15], #-4") \ - TEST_COPROCESSOR( "ldc"two" 0, cr0, [r15], {1}") \ - TEST_COPROCESSOR( "ldc"two"l 0, cr0, [r15, #4]") \ - TEST_COPROCESSOR( "ldc"two"l 0, cr0, [r15, #-4]") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##dff0001) " @ ldc"two"l 0, cr0, [r15, #4]!") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##d7f0001) " @ ldc"two"l 0, cr0, [r15, #-4]!") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##cff0001) " @ ldc"two"l 0, cr0, [r15], #4") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##c7f0001) " @ ldc"two"l 0, cr0, [r15], #-4") \ - TEST_COPROCESSOR( "ldc"two"l 0, cr0, [r15], {1}") - -#define COPROCESSOR_INSTRUCTIONS_MC_MR(two,cc) \ - \ - TEST_COPROCESSOR( "mcrr"two" 0, 15, r0, r14, cr0") \ - TEST_COPROCESSOR( "mcrr"two" 15, 0, r14, r0, cr15") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##c4f00f0) " @ mcrr"two" 0, 15, r0, r15, cr0") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##c40ff0f) " @ mcrr"two" 15, 0, r15, r0, cr15") \ - TEST_COPROCESSOR( "mrrc"two" 0, 15, r0, r14, cr0") \ - TEST_COPROCESSOR( "mrrc"two" 15, 0, r14, r0, cr15") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##c5f00f0) " @ mrrc"two" 0, 15, r0, r15, cr0") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##c50ff0f) " @ mrrc"two" 15, 0, r15, r0, cr15") \ - TEST_COPROCESSOR( "cdp"two" 15, 15, cr15, cr15, cr15, 7") \ - TEST_COPROCESSOR( "cdp"two" 0, 0, cr0, cr0, cr0, 0") \ - TEST_COPROCESSOR( "mcr"two" 15, 7, r15, cr15, cr15, 7") \ - TEST_COPROCESSOR( "mcr"two" 0, 0, r0, cr0, cr0, 0") \ - TEST_COPROCESSOR( "mrc"two" 15, 7, r15, cr15, cr15, 7") \ - TEST_COPROCESSOR( "mrc"two" 0, 0, r0, cr0, cr0, 0") - - COPROCESSOR_INSTRUCTIONS_ST_LD("",e) -#if __LINUX_ARM_ARCH__ >= 5 - COPROCESSOR_INSTRUCTIONS_MC_MR("",e) -#endif - TEST_UNSUPPORTED("svc 0") - TEST_UNSUPPORTED("svc 0xffffff") - - TEST_UNSUPPORTED("svc 0") - - TEST_GROUP("Unconditional instruction") - -#if __LINUX_ARM_ARCH__ >= 6 - TEST_UNSUPPORTED("srsda sp, 0x13") - TEST_UNSUPPORTED("srsdb sp, 0x13") - TEST_UNSUPPORTED("srsia sp, 0x13") - TEST_UNSUPPORTED("srsib sp, 0x13") - TEST_UNSUPPORTED("srsda sp!, 0x13") - TEST_UNSUPPORTED("srsdb sp!, 0x13") - TEST_UNSUPPORTED("srsia sp!, 0x13") - TEST_UNSUPPORTED("srsib sp!, 0x13") - - TEST_UNSUPPORTED("rfeda sp") - TEST_UNSUPPORTED("rfedb sp") - TEST_UNSUPPORTED("rfeia sp") - TEST_UNSUPPORTED("rfeib sp") - TEST_UNSUPPORTED("rfeda sp!") - TEST_UNSUPPORTED("rfedb sp!") - TEST_UNSUPPORTED("rfeia sp!") - TEST_UNSUPPORTED("rfeib sp!") - TEST_UNSUPPORTED(__inst_arm(0xf81d0a00) " @ rfeda pc") - TEST_UNSUPPORTED(__inst_arm(0xf91d0a00) " @ rfedb pc") - TEST_UNSUPPORTED(__inst_arm(0xf89d0a00) " @ rfeia pc") - TEST_UNSUPPORTED(__inst_arm(0xf99d0a00) " @ rfeib pc") - TEST_UNSUPPORTED(__inst_arm(0xf83d0a00) " @ rfeda pc!") - TEST_UNSUPPORTED(__inst_arm(0xf93d0a00) " @ rfedb pc!") - TEST_UNSUPPORTED(__inst_arm(0xf8bd0a00) " @ rfeia pc!") - TEST_UNSUPPORTED(__inst_arm(0xf9bd0a00) " @ rfeib pc!") -#endif /* __LINUX_ARM_ARCH__ >= 6 */ - -#if __LINUX_ARM_ARCH__ >= 6 - TEST_X( "blx __dummy_thumb_subroutine_even", - ".thumb \n\t" - ".space 4 \n\t" - ".type __dummy_thumb_subroutine_even, %%function \n\t" - "__dummy_thumb_subroutine_even: \n\t" - "mov r0, pc \n\t" - "bx lr \n\t" - ".arm \n\t" - ) - TEST( "blx __dummy_thumb_subroutine_even") - - TEST_X( "blx __dummy_thumb_subroutine_odd", - ".thumb \n\t" - ".space 2 \n\t" - ".type __dummy_thumb_subroutine_odd, %%function \n\t" - "__dummy_thumb_subroutine_odd: \n\t" - "mov r0, pc \n\t" - "bx lr \n\t" - ".arm \n\t" - ) - TEST( "blx __dummy_thumb_subroutine_odd") -#endif /* __LINUX_ARM_ARCH__ >= 6 */ - -#if __LINUX_ARM_ARCH__ >= 5 - COPROCESSOR_INSTRUCTIONS_ST_LD("2",f) -#endif -#if __LINUX_ARM_ARCH__ >= 6 - COPROCESSOR_INSTRUCTIONS_MC_MR("2",f) -#endif - - TEST_GROUP("Miscellaneous instructions, memory hints, and Advanced SIMD instructions") - -#if __LINUX_ARM_ARCH__ >= 6 - TEST_UNSUPPORTED("cps 0x13") - TEST_UNSUPPORTED("cpsie i") - TEST_UNSUPPORTED("cpsid i") - TEST_UNSUPPORTED("cpsie i,0x13") - TEST_UNSUPPORTED("cpsid i,0x13") - TEST_UNSUPPORTED("setend le") - TEST_UNSUPPORTED("setend be") -#endif - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_P("pli [r",0,0b,", #16]") - TEST( "pli [pc, #0]") - TEST_RR("pli [r",12,0b,", r",0, 16,"]") - TEST_RR("pli [r",0, 0b,", -r",12,16,", lsl #4]") -#endif - -#if __LINUX_ARM_ARCH__ >= 5 - TEST_P("pld [r",0,32,", #-16]") - TEST( "pld [pc, #0]") - TEST_PR("pld [r",7, 24, ", r",0, 16,"]") - TEST_PR("pld [r",8, 24, ", -r",12,16,", lsl #4]") -#endif - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_SUPPORTED( __inst_arm(0xf590f000) " @ pldw [r0, #0]") - TEST_SUPPORTED( __inst_arm(0xf797f000) " @ pldw [r7, r0]") - TEST_SUPPORTED( __inst_arm(0xf798f18c) " @ pldw [r8, r12, lsl #3]"); -#endif - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_UNSUPPORTED("clrex") - TEST_UNSUPPORTED("dsb") - TEST_UNSUPPORTED("dmb") - TEST_UNSUPPORTED("isb") -#endif - - verbose("\n"); -} - diff --git a/arch/arm/kernel/kprobes-test-thumb.c b/arch/arm/kernel/kprobes-test-thumb.c deleted file mode 100644 index 844dd10..0000000 --- a/arch/arm/kernel/kprobes-test-thumb.c +++ /dev/null @@ -1,1188 +0,0 @@ -/* - * arch/arm/kernel/kprobes-test-thumb.c - * - * Copyright (C) 2011 Jon Medhurst . - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include - -#include "kprobes-test.h" - - -#define TEST_ISA "16" - -#define DONT_TEST_IN_ITBLOCK(tests) \ - kprobe_test_flags |= TEST_FLAG_NO_ITBLOCK; \ - tests \ - kprobe_test_flags &= ~TEST_FLAG_NO_ITBLOCK; - -#define CONDITION_INSTRUCTIONS(cc_pos, tests) \ - kprobe_test_cc_position = cc_pos; \ - DONT_TEST_IN_ITBLOCK(tests) \ - kprobe_test_cc_position = 0; - -#define TEST_ITBLOCK(code) \ - kprobe_test_flags |= TEST_FLAG_FULL_ITBLOCK; \ - TESTCASE_START(code) \ - TEST_ARG_END("") \ - "50: nop \n\t" \ - "1: "code" \n\t" \ - " mov r1, #0x11 \n\t" \ - " mov r2, #0x22 \n\t" \ - " mov r3, #0x33 \n\t" \ - "2: nop \n\t" \ - TESTCASE_END \ - kprobe_test_flags &= ~TEST_FLAG_FULL_ITBLOCK; - -#define TEST_THUMB_TO_ARM_INTERWORK_P(code1, reg, val, code2) \ - TESTCASE_START(code1 #reg code2) \ - TEST_ARG_PTR(reg, val) \ - TEST_ARG_REG(14, 99f+1) \ - TEST_ARG_MEM(15, 3f) \ - TEST_ARG_END("") \ - " nop \n\t" /* To align 1f */ \ - "50: nop \n\t" \ - "1: "code1 #reg code2" \n\t" \ - " bx lr \n\t" \ - ".arm \n\t" \ - "3: adr lr, 2f+1 \n\t" \ - " bx lr \n\t" \ - ".thumb \n\t" \ - "2: nop \n\t" \ - TESTCASE_END - - -void kprobe_thumb16_test_cases(void) -{ - kprobe_test_flags = TEST_FLAG_NARROW_INSTR; - - TEST_GROUP("Shift (immediate), add, subtract, move, and compare") - - TEST_R( "lsls r7, r",0,VAL1,", #5") - TEST_R( "lsls r0, r",7,VAL2,", #11") - TEST_R( "lsrs r7, r",0,VAL1,", #5") - TEST_R( "lsrs r0, r",7,VAL2,", #11") - TEST_R( "asrs r7, r",0,VAL1,", #5") - TEST_R( "asrs r0, r",7,VAL2,", #11") - TEST_RR( "adds r2, r",0,VAL1,", r",7,VAL2,"") - TEST_RR( "adds r5, r",7,VAL2,", r",0,VAL2,"") - TEST_RR( "subs r2, r",0,VAL1,", r",7,VAL2,"") - TEST_RR( "subs r5, r",7,VAL2,", r",0,VAL2,"") - TEST_R( "adds r7, r",0,VAL1,", #5") - TEST_R( "adds r0, r",7,VAL2,", #2") - TEST_R( "subs r7, r",0,VAL1,", #5") - TEST_R( "subs r0, r",7,VAL2,", #2") - TEST( "movs.n r0, #0x5f") - TEST( "movs.n r7, #0xa0") - TEST_R( "cmp.n r",0,0x5e, ", #0x5f") - TEST_R( "cmp.n r",5,0x15f,", #0x5f") - TEST_R( "cmp.n r",7,0xa0, ", #0xa0") - TEST_R( "adds.n r",0,VAL1,", #0x5f") - TEST_R( "adds.n r",7,VAL2,", #0xa0") - TEST_R( "subs.n r",0,VAL1,", #0x5f") - TEST_R( "subs.n r",7,VAL2,", #0xa0") - - TEST_GROUP("16-bit Thumb data-processing instructions") - -#define DATA_PROCESSING16(op,val) \ - TEST_RR( op" r",0,VAL1,", r",7,val,"") \ - TEST_RR( op" r",7,VAL2,", r",0,val,"") - - DATA_PROCESSING16("ands",0xf00f00ff) - DATA_PROCESSING16("eors",0xf00f00ff) - DATA_PROCESSING16("lsls",11) - DATA_PROCESSING16("lsrs",11) - DATA_PROCESSING16("asrs",11) - DATA_PROCESSING16("adcs",VAL2) - DATA_PROCESSING16("sbcs",VAL2) - DATA_PROCESSING16("rors",11) - DATA_PROCESSING16("tst",0xf00f00ff) - TEST_R("rsbs r",0,VAL1,", #0") - TEST_R("rsbs r",7,VAL2,", #0") - DATA_PROCESSING16("cmp",0xf00f00ff) - DATA_PROCESSING16("cmn",0xf00f00ff) - DATA_PROCESSING16("orrs",0xf00f00ff) - DATA_PROCESSING16("muls",VAL2) - DATA_PROCESSING16("bics",0xf00f00ff) - DATA_PROCESSING16("mvns",VAL2) - - TEST_GROUP("Special data instructions and branch and exchange") - - TEST_RR( "add r",0, VAL1,", r",7,VAL2,"") - TEST_RR( "add r",3, VAL2,", r",8,VAL3,"") - TEST_RR( "add r",8, VAL3,", r",0,VAL1,"") - TEST_R( "add sp" ", r",8,-8, "") - TEST_R( "add r",14,VAL1,", pc") - TEST_BF_R("add pc" ", r",0,2f-1f-8,"") - TEST_UNSUPPORTED(__inst_thumb16(0x44ff) " @ add pc, pc") - - TEST_RR( "cmp r",3,VAL1,", r",8,VAL2,"") - TEST_RR( "cmp r",8,VAL2,", r",0,VAL1,"") - TEST_R( "cmp sp" ", r",8,-8, "") - - TEST_R( "mov r0, r",7,VAL2,"") - TEST_R( "mov r3, r",8,VAL3,"") - TEST_R( "mov r8, r",0,VAL1,"") - TEST_P( "mov sp, r",8,-8, "") - TEST( "mov lr, pc") - TEST_BF_R("mov pc, r",0,2f, "") - - TEST_BF_R("bx r",0, 2f+1,"") - TEST_BF_R("bx r",14,2f+1,"") - TESTCASE_START("bx pc") - TEST_ARG_REG(14, 99f+1) - TEST_ARG_END("") - " nop \n\t" /* To align the bx pc*/ - "50: nop \n\t" - "1: bx pc \n\t" - " bx lr \n\t" - ".arm \n\t" - " adr lr, 2f+1 \n\t" - " bx lr \n\t" - ".thumb \n\t" - "2: nop \n\t" - TESTCASE_END - - TEST_BF_R("blx r",0, 2f+1,"") - TEST_BB_R("blx r",14,2f+1,"") - TEST_UNSUPPORTED(__inst_thumb16(0x47f8) " @ blx pc") - - TEST_GROUP("Load from Literal Pool") - - TEST_X( "ldr r0, 3f", - ".align \n\t" - "3: .word "__stringify(VAL1)) - TEST_X( "ldr r7, 3f", - ".space 128 \n\t" - ".align \n\t" - "3: .word "__stringify(VAL2)) - - TEST_GROUP("16-bit Thumb Load/store instructions") - - TEST_RPR("str r",0, VAL1,", [r",1, 24,", r",2, 48,"]") - TEST_RPR("str r",7, VAL2,", [r",6, 24,", r",5, 48,"]") - TEST_RPR("strh r",0, VAL1,", [r",1, 24,", r",2, 48,"]") - TEST_RPR("strh r",7, VAL2,", [r",6, 24,", r",5, 48,"]") - TEST_RPR("strb r",0, VAL1,", [r",1, 24,", r",2, 48,"]") - TEST_RPR("strb r",7, VAL2,", [r",6, 24,", r",5, 48,"]") - TEST_PR( "ldrsb r0, [r",1, 24,", r",2, 48,"]") - TEST_PR( "ldrsb r7, [r",6, 24,", r",5, 50,"]") - TEST_PR( "ldr r0, [r",1, 24,", r",2, 48,"]") - TEST_PR( "ldr r7, [r",6, 24,", r",5, 48,"]") - TEST_PR( "ldrh r0, [r",1, 24,", r",2, 48,"]") - TEST_PR( "ldrh r7, [r",6, 24,", r",5, 50,"]") - TEST_PR( "ldrb r0, [r",1, 24,", r",2, 48,"]") - TEST_PR( "ldrb r7, [r",6, 24,", r",5, 50,"]") - TEST_PR( "ldrsh r0, [r",1, 24,", r",2, 48,"]") - TEST_PR( "ldrsh r7, [r",6, 24,", r",5, 50,"]") - - TEST_RP("str r",0, VAL1,", [r",1, 24,", #120]") - TEST_RP("str r",7, VAL2,", [r",6, 24,", #120]") - TEST_P( "ldr r0, [r",1, 24,", #120]") - TEST_P( "ldr r7, [r",6, 24,", #120]") - TEST_RP("strb r",0, VAL1,", [r",1, 24,", #30]") - TEST_RP("strb r",7, VAL2,", [r",6, 24,", #30]") - TEST_P( "ldrb r0, [r",1, 24,", #30]") - TEST_P( "ldrb r7, [r",6, 24,", #30]") - TEST_RP("strh r",0, VAL1,", [r",1, 24,", #60]") - TEST_RP("strh r",7, VAL2,", [r",6, 24,", #60]") - TEST_P( "ldrh r0, [r",1, 24,", #60]") - TEST_P( "ldrh r7, [r",6, 24,", #60]") - - TEST_R( "str r",0, VAL1,", [sp, #0]") - TEST_R( "str r",7, VAL2,", [sp, #160]") - TEST( "ldr r0, [sp, #0]") - TEST( "ldr r7, [sp, #160]") - - TEST_RP("str r",0, VAL1,", [r",0, 24,"]") - TEST_P( "ldr r0, [r",0, 24,"]") - - TEST_GROUP("Generate PC-/SP-relative address") - - TEST("add r0, pc, #4") - TEST("add r7, pc, #1020") - TEST("add r0, sp, #4") - TEST("add r7, sp, #1020") - - TEST_GROUP("Miscellaneous 16-bit instructions") - - TEST_UNSUPPORTED( "cpsie i") - TEST_UNSUPPORTED( "cpsid i") - TEST_UNSUPPORTED( "setend le") - TEST_UNSUPPORTED( "setend be") - - TEST("add sp, #"__stringify(TEST_MEMORY_SIZE)) /* Assumes TEST_MEMORY_SIZE < 0x400 */ - TEST("sub sp, #0x7f*4") - -DONT_TEST_IN_ITBLOCK( - TEST_BF_R( "cbnz r",0,0, ", 2f") - TEST_BF_R( "cbz r",2,-1,", 2f") - TEST_BF_RX( "cbnz r",4,1, ", 2f", SPACE_0x20) - TEST_BF_RX( "cbz r",7,0, ", 2f", SPACE_0x40) -) - TEST_R("sxth r0, r",7, HH1,"") - TEST_R("sxth r7, r",0, HH2,"") - TEST_R("sxtb r0, r",7, HH1,"") - TEST_R("sxtb r7, r",0, HH2,"") - TEST_R("uxth r0, r",7, HH1,"") - TEST_R("uxth r7, r",0, HH2,"") - TEST_R("uxtb r0, r",7, HH1,"") - TEST_R("uxtb r7, r",0, HH2,"") - TEST_R("rev r0, r",7, VAL1,"") - TEST_R("rev r7, r",0, VAL2,"") - TEST_R("rev16 r0, r",7, VAL1,"") - TEST_R("rev16 r7, r",0, VAL2,"") - TEST_UNSUPPORTED(__inst_thumb16(0xba80) "") - TEST_UNSUPPORTED(__inst_thumb16(0xbabf) "") - TEST_R("revsh r0, r",7, VAL1,"") - TEST_R("revsh r7, r",0, VAL2,"") - -#define TEST_POPPC(code, offset) \ - TESTCASE_START(code) \ - TEST_ARG_PTR(13, offset) \ - TEST_ARG_END("") \ - TEST_BRANCH_F(code) \ - TESTCASE_END - - TEST("push {r0}") - TEST("push {r7}") - TEST("push {r14}") - TEST("push {r0-r7,r14}") - TEST("push {r0,r2,r4,r6,r14}") - TEST("push {r1,r3,r5,r7}") - TEST("pop {r0}") - TEST("pop {r7}") - TEST("pop {r0,r2,r4,r6}") - TEST_POPPC("pop {pc}",15*4) - TEST_POPPC("pop {r0-r7,pc}",7*4) - TEST_POPPC("pop {r1,r3,r5,r7,pc}",11*4) - TEST_THUMB_TO_ARM_INTERWORK_P("pop {pc} @ ",13,15*4,"") - TEST_THUMB_TO_ARM_INTERWORK_P("pop {r0-r7,pc} @ ",13,7*4,"") - - TEST_UNSUPPORTED("bkpt.n 0") - TEST_UNSUPPORTED("bkpt.n 255") - - TEST_SUPPORTED("yield") - TEST("sev") - TEST("nop") - TEST("wfi") - TEST_SUPPORTED("wfe") - TEST_UNSUPPORTED(__inst_thumb16(0xbf50) "") /* Unassigned hints */ - TEST_UNSUPPORTED(__inst_thumb16(0xbff0) "") /* Unassigned hints */ - -#define TEST_IT(code, code2) \ - TESTCASE_START(code) \ - TEST_ARG_END("") \ - "50: nop \n\t" \ - "1: "code" \n\t" \ - " "code2" \n\t" \ - "2: nop \n\t" \ - TESTCASE_END - -DONT_TEST_IN_ITBLOCK( - TEST_IT("it eq","moveq r0,#0") - TEST_IT("it vc","movvc r0,#0") - TEST_IT("it le","movle r0,#0") - TEST_IT("ite eq","moveq r0,#0\n\t movne r1,#1") - TEST_IT("itet vc","movvc r0,#0\n\t movvs r1,#1\n\t movvc r2,#2") - TEST_IT("itete le","movle r0,#0\n\t movgt r1,#1\n\t movle r2,#2\n\t movgt r3,#3") - TEST_IT("itttt le","movle r0,#0\n\t movle r1,#1\n\t movle r2,#2\n\t movle r3,#3") - TEST_IT("iteee le","movle r0,#0\n\t movgt r1,#1\n\t movgt r2,#2\n\t movgt r3,#3") -) - - TEST_GROUP("Load and store multiple") - - TEST_P("ldmia r",4, 16*4,"!, {r0,r7}") - TEST_P("ldmia r",7, 16*4,"!, {r0-r6}") - TEST_P("stmia r",4, 16*4,"!, {r0,r7}") - TEST_P("stmia r",0, 16*4,"!, {r0-r7}") - - TEST_GROUP("Conditional branch and Supervisor Call instructions") - -CONDITION_INSTRUCTIONS(8, - TEST_BF("beq 2f") - TEST_BB("bne 2b") - TEST_BF("bgt 2f") - TEST_BB("blt 2b") -) - TEST_UNSUPPORTED(__inst_thumb16(0xde00) "") - TEST_UNSUPPORTED(__inst_thumb16(0xdeff) "") - TEST_UNSUPPORTED("svc #0x00") - TEST_UNSUPPORTED("svc #0xff") - - TEST_GROUP("Unconditional branch") - - TEST_BF( "b 2f") - TEST_BB( "b 2b") - TEST_BF_X("b 2f", SPACE_0x400) - TEST_BB_X("b 2b", SPACE_0x400) - - TEST_GROUP("Testing instructions in IT blocks") - - TEST_ITBLOCK("subs.n r0, r0") - - verbose("\n"); -} - - -void kprobe_thumb32_test_cases(void) -{ - kprobe_test_flags = 0; - - TEST_GROUP("Load/store multiple") - - TEST_UNSUPPORTED("rfedb sp") - TEST_UNSUPPORTED("rfeia sp") - TEST_UNSUPPORTED("rfedb sp!") - TEST_UNSUPPORTED("rfeia sp!") - - TEST_P( "stmia r",0, 16*4,", {r0,r8}") - TEST_P( "stmia r",4, 16*4,", {r0-r12,r14}") - TEST_P( "stmia r",7, 16*4,"!, {r8-r12,r14}") - TEST_P( "stmia r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - - TEST_P( "ldmia r",0, 16*4,", {r0,r8}") - TEST_P( "ldmia r",4, 0, ", {r0-r12,r14}") - TEST_BF_P("ldmia r",5, 8*4, "!, {r6-r12,r15}") - TEST_P( "ldmia r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_BF_P("ldmia r",14,14*4,"!, {r4,pc}") - - TEST_P( "stmdb r",0, 16*4,", {r0,r8}") - TEST_P( "stmdb r",4, 16*4,", {r0-r12,r14}") - TEST_P( "stmdb r",5, 16*4,"!, {r8-r12,r14}") - TEST_P( "stmdb r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - - TEST_P( "ldmdb r",0, 16*4,", {r0,r8}") - TEST_P( "ldmdb r",4, 16*4,", {r0-r12,r14}") - TEST_BF_P("ldmdb r",5, 16*4,"!, {r6-r12,r15}") - TEST_P( "ldmdb r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_BF_P("ldmdb r",14,16*4,"!, {r4,pc}") - - TEST_P( "stmdb r",13,16*4,"!, {r3-r12,lr}") - TEST_P( "stmdb r",13,16*4,"!, {r3-r12}") - TEST_P( "stmdb r",2, 16*4,", {r3-r12,lr}") - TEST_P( "stmdb r",13,16*4,"!, {r2-r12,lr}") - TEST_P( "stmdb r",0, 16*4,", {r0-r12}") - TEST_P( "stmdb r",0, 16*4,", {r0-r12,lr}") - - TEST_BF_P("ldmia r",13,5*4, "!, {r3-r12,pc}") - TEST_P( "ldmia r",13,5*4, "!, {r3-r12}") - TEST_BF_P("ldmia r",2, 5*4, "!, {r3-r12,pc}") - TEST_BF_P("ldmia r",13,4*4, "!, {r2-r12,pc}") - TEST_P( "ldmia r",0, 16*4,", {r0-r12}") - TEST_P( "ldmia r",0, 16*4,", {r0-r12,lr}") - - TEST_THUMB_TO_ARM_INTERWORK_P("ldmia r",0,14*4,", {r12,pc}") - TEST_THUMB_TO_ARM_INTERWORK_P("ldmia r",13,2*4,", {r0-r12,pc}") - - TEST_UNSUPPORTED(__inst_thumb32(0xe88f0101) " @ stmia pc, {r0,r8}") - TEST_UNSUPPORTED(__inst_thumb32(0xe92f5f00) " @ stmdb pc!, {r8-r12,r14}") - TEST_UNSUPPORTED(__inst_thumb32(0xe8bdc000) " @ ldmia r13!, {r14,pc}") - TEST_UNSUPPORTED(__inst_thumb32(0xe93ec000) " @ ldmdb r14!, {r14,pc}") - TEST_UNSUPPORTED(__inst_thumb32(0xe8a73f00) " @ stmia r7!, {r8-r12,sp}") - TEST_UNSUPPORTED(__inst_thumb32(0xe8a79f00) " @ stmia r7!, {r8-r12,pc}") - TEST_UNSUPPORTED(__inst_thumb32(0xe93e2010) " @ ldmdb r14!, {r4,sp}") - - TEST_GROUP("Load/store double or exclusive, table branch") - - TEST_P( "ldrd r0, r1, [r",1, 24,", #-16]") - TEST( "ldrd r12, r14, [sp, #16]") - TEST_P( "ldrd r1, r0, [r",7, 24,", #-16]!") - TEST( "ldrd r14, r12, [sp, #16]!") - TEST_P( "ldrd r1, r0, [r",7, 24,"], #16") - TEST( "ldrd r7, r8, [sp], #-16") - - TEST_X( "ldrd r12, r14, 3f", - ".align 3 \n\t" - "3: .word "__stringify(VAL1)" \n\t" - " .word "__stringify(VAL2)) - - TEST_UNSUPPORTED(__inst_thumb32(0xe9ffec04) " @ ldrd r14, r12, [pc, #16]!") - TEST_UNSUPPORTED(__inst_thumb32(0xe8ffec04) " @ ldrd r14, r12, [pc], #16") - TEST_UNSUPPORTED(__inst_thumb32(0xe9d4d800) " @ ldrd sp, r8, [r4]") - TEST_UNSUPPORTED(__inst_thumb32(0xe9d4f800) " @ ldrd pc, r8, [r4]") - TEST_UNSUPPORTED(__inst_thumb32(0xe9d47d00) " @ ldrd r7, sp, [r4]") - TEST_UNSUPPORTED(__inst_thumb32(0xe9d47f00) " @ ldrd r7, pc, [r4]") - - TEST_RRP("strd r",0, VAL1,", r",1, VAL2,", [r",1, 24,", #-16]") - TEST_RR( "strd r",12,VAL2,", r",14,VAL1,", [sp, #16]") - TEST_RRP("strd r",1, VAL1,", r",0, VAL2,", [r",7, 24,", #-16]!") - TEST_RR( "strd r",14,VAL2,", r",12,VAL1,", [sp, #16]!") - TEST_RRP("strd r",1, VAL1,", r",0, VAL2,", [r",7, 24,"], #16") - TEST_RR( "strd r",7, VAL2,", r",8, VAL1,", [sp], #-16") - TEST_UNSUPPORTED(__inst_thumb32(0xe9efec04) " @ strd r14, r12, [pc, #16]!") - TEST_UNSUPPORTED(__inst_thumb32(0xe8efec04) " @ strd r14, r12, [pc], #16") - - TEST_RX("tbb [pc, r",0, (9f-(1f+4)),"]", - "9: \n\t" - ".byte (2f-1b-4)>>1 \n\t" - ".byte (3f-1b-4)>>1 \n\t" - "3: mvn r0, r0 \n\t" - "2: nop \n\t") - - TEST_RX("tbb [pc, r",4, (9f-(1f+4)+1),"]", - "9: \n\t" - ".byte (2f-1b-4)>>1 \n\t" - ".byte (3f-1b-4)>>1 \n\t" - "3: mvn r0, r0 \n\t" - "2: nop \n\t") - - TEST_RRX("tbb [r",1,9f,", r",2,0,"]", - "9: \n\t" - ".byte (2f-1b-4)>>1 \n\t" - ".byte (3f-1b-4)>>1 \n\t" - "3: mvn r0, r0 \n\t" - "2: nop \n\t") - - TEST_RX("tbh [pc, r",7, (9f-(1f+4))>>1,"]", - "9: \n\t" - ".short (2f-1b-4)>>1 \n\t" - ".short (3f-1b-4)>>1 \n\t" - "3: mvn r0, r0 \n\t" - "2: nop \n\t") - - TEST_RX("tbh [pc, r",12, ((9f-(1f+4))>>1)+1,"]", - "9: \n\t" - ".short (2f-1b-4)>>1 \n\t" - ".short (3f-1b-4)>>1 \n\t" - "3: mvn r0, r0 \n\t" - "2: nop \n\t") - - TEST_RRX("tbh [r",1,9f, ", r",14,1,"]", - "9: \n\t" - ".short (2f-1b-4)>>1 \n\t" - ".short (3f-1b-4)>>1 \n\t" - "3: mvn r0, r0 \n\t" - "2: nop \n\t") - - TEST_UNSUPPORTED(__inst_thumb32(0xe8d1f01f) " @ tbh [r1, pc]") - TEST_UNSUPPORTED(__inst_thumb32(0xe8d1f01d) " @ tbh [r1, sp]") - TEST_UNSUPPORTED(__inst_thumb32(0xe8ddf012) " @ tbh [sp, r2]") - - TEST_UNSUPPORTED("strexb r0, r1, [r2]") - TEST_UNSUPPORTED("strexh r0, r1, [r2]") - TEST_UNSUPPORTED("strexd r0, r1, [r2]") - TEST_UNSUPPORTED("ldrexb r0, [r1]") - TEST_UNSUPPORTED("ldrexh r0, [r1]") - TEST_UNSUPPORTED("ldrexd r0, [r1]") - - TEST_GROUP("Data-processing (shifted register) and (modified immediate)") - -#define _DATA_PROCESSING32_DNM(op,s,val) \ - TEST_RR(op s".w r0, r",1, VAL1,", r",2, val, "") \ - TEST_RR(op s" r1, r",1, VAL1,", r",2, val, ", lsl #3") \ - TEST_RR(op s" r2, r",3, VAL1,", r",2, val, ", lsr #4") \ - TEST_RR(op s" r3, r",3, VAL1,", r",2, val, ", asr #5") \ - TEST_RR(op s" r4, r",5, VAL1,", r",2, N(val),", asr #6") \ - TEST_RR(op s" r5, r",5, VAL1,", r",2, val, ", ror #7") \ - TEST_RR(op s" r8, r",9, VAL1,", r",10,val, ", rrx") \ - TEST_R( op s" r0, r",11,VAL1,", #0x00010001") \ - TEST_R( op s" r11, r",0, VAL1,", #0xf5000000") \ - TEST_R( op s" r7, r",8, VAL2,", #0x000af000") - -#define DATA_PROCESSING32_DNM(op,val) \ - _DATA_PROCESSING32_DNM(op,"",val) \ - _DATA_PROCESSING32_DNM(op,"s",val) - -#define DATA_PROCESSING32_NM(op,val) \ - TEST_RR(op".w r",1, VAL1,", r",2, val, "") \ - TEST_RR(op" r",1, VAL1,", r",2, val, ", lsl #3") \ - TEST_RR(op" r",3, VAL1,", r",2, val, ", lsr #4") \ - TEST_RR(op" r",3, VAL1,", r",2, val, ", asr #5") \ - TEST_RR(op" r",5, VAL1,", r",2, N(val),", asr #6") \ - TEST_RR(op" r",5, VAL1,", r",2, val, ", ror #7") \ - TEST_RR(op" r",9, VAL1,", r",10,val, ", rrx") \ - TEST_R( op" r",11,VAL1,", #0x00010001") \ - TEST_R( op" r",0, VAL1,", #0xf5000000") \ - TEST_R( op" r",8, VAL2,", #0x000af000") - -#define _DATA_PROCESSING32_DM(op,s,val) \ - TEST_R( op s".w r0, r",14, val, "") \ - TEST_R( op s" r1, r",12, val, ", lsl #3") \ - TEST_R( op s" r2, r",11, val, ", lsr #4") \ - TEST_R( op s" r3, r",10, val, ", asr #5") \ - TEST_R( op s" r4, r",9, N(val),", asr #6") \ - TEST_R( op s" r5, r",8, val, ", ror #7") \ - TEST_R( op s" r8, r",7,val, ", rrx") \ - TEST( op s" r0, #0x00010001") \ - TEST( op s" r11, #0xf5000000") \ - TEST( op s" r7, #0x000af000") \ - TEST( op s" r4, #0x00005a00") - -#define DATA_PROCESSING32_DM(op,val) \ - _DATA_PROCESSING32_DM(op,"",val) \ - _DATA_PROCESSING32_DM(op,"s",val) - - DATA_PROCESSING32_DNM("and",0xf00f00ff) - DATA_PROCESSING32_NM("tst",0xf00f00ff) - DATA_PROCESSING32_DNM("bic",0xf00f00ff) - DATA_PROCESSING32_DNM("orr",0xf00f00ff) - DATA_PROCESSING32_DM("mov",VAL2) - DATA_PROCESSING32_DNM("orn",0xf00f00ff) - DATA_PROCESSING32_DM("mvn",VAL2) - DATA_PROCESSING32_DNM("eor",0xf00f00ff) - DATA_PROCESSING32_NM("teq",0xf00f00ff) - DATA_PROCESSING32_DNM("add",VAL2) - DATA_PROCESSING32_NM("cmn",VAL2) - DATA_PROCESSING32_DNM("adc",VAL2) - DATA_PROCESSING32_DNM("sbc",VAL2) - DATA_PROCESSING32_DNM("sub",VAL2) - DATA_PROCESSING32_NM("cmp",VAL2) - DATA_PROCESSING32_DNM("rsb",VAL2) - - TEST_RR("pkhbt r0, r",0, HH1,", r",1, HH2,"") - TEST_RR("pkhbt r14,r",12, HH1,", r",10,HH2,", lsl #2") - TEST_RR("pkhtb r0, r",0, HH1,", r",1, HH2,"") - TEST_RR("pkhtb r14,r",12, HH1,", r",10,HH2,", asr #2") - - TEST_UNSUPPORTED(__inst_thumb32(0xea170f0d) " @ tst.w r7, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xea170f0f) " @ tst.w r7, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xea1d0f07) " @ tst.w sp, r7") - TEST_UNSUPPORTED(__inst_thumb32(0xea1f0f07) " @ tst.w pc, r7") - TEST_UNSUPPORTED(__inst_thumb32(0xf01d1f08) " @ tst sp, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf01f1f08) " @ tst pc, #0x00080008") - - TEST_UNSUPPORTED(__inst_thumb32(0xea970f0d) " @ teq.w r7, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xea970f0f) " @ teq.w r7, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xea9d0f07) " @ teq.w sp, r7") - TEST_UNSUPPORTED(__inst_thumb32(0xea9f0f07) " @ teq.w pc, r7") - TEST_UNSUPPORTED(__inst_thumb32(0xf09d1f08) " @ tst sp, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf09f1f08) " @ tst pc, #0x00080008") - - TEST_UNSUPPORTED(__inst_thumb32(0xeb170f0d) " @ cmn.w r7, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xeb170f0f) " @ cmn.w r7, pc") - TEST_P("cmn.w sp, r",7,0,"") - TEST_UNSUPPORTED(__inst_thumb32(0xeb1f0f07) " @ cmn.w pc, r7") - TEST( "cmn sp, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf11f1f08) " @ cmn pc, #0x00080008") - - TEST_UNSUPPORTED(__inst_thumb32(0xebb70f0d) " @ cmp.w r7, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xebb70f0f) " @ cmp.w r7, pc") - TEST_P("cmp.w sp, r",7,0,"") - TEST_UNSUPPORTED(__inst_thumb32(0xebbf0f07) " @ cmp.w pc, r7") - TEST( "cmp sp, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf1bf1f08) " @ cmp pc, #0x00080008") - - TEST_UNSUPPORTED(__inst_thumb32(0xea5f070d) " @ movs.w r7, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xea5f070f) " @ movs.w r7, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xea5f0d07) " @ movs.w sp, r7") - TEST_UNSUPPORTED(__inst_thumb32(0xea4f0f07) " @ mov.w pc, r7") - TEST_UNSUPPORTED(__inst_thumb32(0xf04f1d08) " @ mov sp, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf04f1f08) " @ mov pc, #0x00080008") - - TEST_R("add.w r0, sp, r",1, 4,"") - TEST_R("adds r0, sp, r",1, 4,", asl #3") - TEST_R("add r0, sp, r",1, 4,", asl #4") - TEST_R("add r0, sp, r",1, 16,", ror #1") - TEST_R("add.w sp, sp, r",1, 4,"") - TEST_R("add sp, sp, r",1, 4,", asl #3") - TEST_UNSUPPORTED(__inst_thumb32(0xeb0d1d01) " @ add sp, sp, r1, asl #4") - TEST_UNSUPPORTED(__inst_thumb32(0xeb0d0d71) " @ add sp, sp, r1, ror #1") - TEST( "add.w r0, sp, #24") - TEST( "add.w sp, sp, #24") - TEST_UNSUPPORTED(__inst_thumb32(0xeb0d0f01) " @ add pc, sp, r1") - TEST_UNSUPPORTED(__inst_thumb32(0xeb0d000f) " @ add r0, sp, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xeb0d000d) " @ add r0, sp, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xeb0d0d0f) " @ add sp, sp, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xeb0d0d0d) " @ add sp, sp, sp") - - TEST_R("sub.w r0, sp, r",1, 4,"") - TEST_R("subs r0, sp, r",1, 4,", asl #3") - TEST_R("sub r0, sp, r",1, 4,", asl #4") - TEST_R("sub r0, sp, r",1, 16,", ror #1") - TEST_R("sub.w sp, sp, r",1, 4,"") - TEST_R("sub sp, sp, r",1, 4,", asl #3") - TEST_UNSUPPORTED(__inst_thumb32(0xebad1d01) " @ sub sp, sp, r1, asl #4") - TEST_UNSUPPORTED(__inst_thumb32(0xebad0d71) " @ sub sp, sp, r1, ror #1") - TEST_UNSUPPORTED(__inst_thumb32(0xebad0f01) " @ sub pc, sp, r1") - TEST( "sub.w r0, sp, #24") - TEST( "sub.w sp, sp, #24") - - TEST_UNSUPPORTED(__inst_thumb32(0xea02010f) " @ and r1, r2, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xea0f0103) " @ and r1, pc, r3") - TEST_UNSUPPORTED(__inst_thumb32(0xea020f03) " @ and pc, r2, r3") - TEST_UNSUPPORTED(__inst_thumb32(0xea02010d) " @ and r1, r2, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xea0d0103) " @ and r1, sp, r3") - TEST_UNSUPPORTED(__inst_thumb32(0xea020d03) " @ and sp, r2, r3") - TEST_UNSUPPORTED(__inst_thumb32(0xf00d1108) " @ and r1, sp, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf00f1108) " @ and r1, pc, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf0021d08) " @ and sp, r8, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf0021f08) " @ and pc, r8, #0x00080008") - - TEST_UNSUPPORTED(__inst_thumb32(0xeb02010f) " @ add r1, r2, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xeb0f0103) " @ add r1, pc, r3") - TEST_UNSUPPORTED(__inst_thumb32(0xeb020f03) " @ add pc, r2, r3") - TEST_UNSUPPORTED(__inst_thumb32(0xeb02010d) " @ add r1, r2, sp") - TEST_SUPPORTED( __inst_thumb32(0xeb0d0103) " @ add r1, sp, r3") - TEST_UNSUPPORTED(__inst_thumb32(0xeb020d03) " @ add sp, r2, r3") - TEST_SUPPORTED( __inst_thumb32(0xf10d1108) " @ add r1, sp, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf10d1f08) " @ add pc, sp, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf10f1108) " @ add r1, pc, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf1021d08) " @ add sp, r8, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf1021f08) " @ add pc, r8, #0x00080008") - - TEST_UNSUPPORTED(__inst_thumb32(0xeaa00000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xeaf00000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xeb200000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xeb800000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xebe00000) "") - - TEST_UNSUPPORTED(__inst_thumb32(0xf0a00000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xf0c00000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xf0f00000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xf1200000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xf1800000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xf1e00000) "") - - TEST_GROUP("Coprocessor instructions") - - TEST_UNSUPPORTED(__inst_thumb32(0xec000000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xeff00000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xfc000000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xfff00000) "") - - TEST_GROUP("Data-processing (plain binary immediate)") - - TEST_R("addw r0, r",1, VAL1,", #0x123") - TEST( "addw r14, sp, #0xf5a") - TEST( "addw sp, sp, #0x20") - TEST( "addw r7, pc, #0x888") - TEST_UNSUPPORTED(__inst_thumb32(0xf20f1f20) " @ addw pc, pc, #0x120") - TEST_UNSUPPORTED(__inst_thumb32(0xf20d1f20) " @ addw pc, sp, #0x120") - TEST_UNSUPPORTED(__inst_thumb32(0xf20f1d20) " @ addw sp, pc, #0x120") - TEST_UNSUPPORTED(__inst_thumb32(0xf2001d20) " @ addw sp, r0, #0x120") - - TEST_R("subw r0, r",1, VAL1,", #0x123") - TEST( "subw r14, sp, #0xf5a") - TEST( "subw sp, sp, #0x20") - TEST( "subw r7, pc, #0x888") - TEST_UNSUPPORTED(__inst_thumb32(0xf2af1f20) " @ subw pc, pc, #0x120") - TEST_UNSUPPORTED(__inst_thumb32(0xf2ad1f20) " @ subw pc, sp, #0x120") - TEST_UNSUPPORTED(__inst_thumb32(0xf2af1d20) " @ subw sp, pc, #0x120") - TEST_UNSUPPORTED(__inst_thumb32(0xf2a01d20) " @ subw sp, r0, #0x120") - - TEST("movw r0, #0") - TEST("movw r0, #0xffff") - TEST("movw lr, #0xffff") - TEST_UNSUPPORTED(__inst_thumb32(0xf2400d00) " @ movw sp, #0") - TEST_UNSUPPORTED(__inst_thumb32(0xf2400f00) " @ movw pc, #0") - - TEST_R("movt r",0, VAL1,", #0") - TEST_R("movt r",0, VAL2,", #0xffff") - TEST_R("movt r",14,VAL1,", #0xffff") - TEST_UNSUPPORTED(__inst_thumb32(0xf2c00d00) " @ movt sp, #0") - TEST_UNSUPPORTED(__inst_thumb32(0xf2c00f00) " @ movt pc, #0") - - TEST_R( "ssat r0, #24, r",0, VAL1,"") - TEST_R( "ssat r14, #24, r",12, VAL2,"") - TEST_R( "ssat r0, #24, r",0, VAL1,", lsl #8") - TEST_R( "ssat r14, #24, r",12, VAL2,", asr #8") - TEST_UNSUPPORTED(__inst_thumb32(0xf30c0d17) " @ ssat sp, #24, r12") - TEST_UNSUPPORTED(__inst_thumb32(0xf30c0f17) " @ ssat pc, #24, r12") - TEST_UNSUPPORTED(__inst_thumb32(0xf30d0c17) " @ ssat r12, #24, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xf30f0c17) " @ ssat r12, #24, pc") - - TEST_R( "usat r0, #24, r",0, VAL1,"") - TEST_R( "usat r14, #24, r",12, VAL2,"") - TEST_R( "usat r0, #24, r",0, VAL1,", lsl #8") - TEST_R( "usat r14, #24, r",12, VAL2,", asr #8") - TEST_UNSUPPORTED(__inst_thumb32(0xf38c0d17) " @ usat sp, #24, r12") - TEST_UNSUPPORTED(__inst_thumb32(0xf38c0f17) " @ usat pc, #24, r12") - TEST_UNSUPPORTED(__inst_thumb32(0xf38d0c17) " @ usat r12, #24, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xf38f0c17) " @ usat r12, #24, pc") - - TEST_R( "ssat16 r0, #12, r",0, HH1,"") - TEST_R( "ssat16 r14, #12, r",12, HH2,"") - TEST_UNSUPPORTED(__inst_thumb32(0xf32c0d0b) " @ ssat16 sp, #12, r12") - TEST_UNSUPPORTED(__inst_thumb32(0xf32c0f0b) " @ ssat16 pc, #12, r12") - TEST_UNSUPPORTED(__inst_thumb32(0xf32d0c0b) " @ ssat16 r12, #12, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xf32f0c0b) " @ ssat16 r12, #12, pc") - - TEST_R( "usat16 r0, #12, r",0, HH1,"") - TEST_R( "usat16 r14, #12, r",12, HH2,"") - TEST_UNSUPPORTED(__inst_thumb32(0xf3ac0d0b) " @ usat16 sp, #12, r12") - TEST_UNSUPPORTED(__inst_thumb32(0xf3ac0f0b) " @ usat16 pc, #12, r12") - TEST_UNSUPPORTED(__inst_thumb32(0xf3ad0c0b) " @ usat16 r12, #12, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xf3af0c0b) " @ usat16 r12, #12, pc") - - TEST_R( "sbfx r0, r",0 , VAL1,", #0, #31") - TEST_R( "sbfx r14, r",12, VAL2,", #8, #16") - TEST_R( "sbfx r4, r",10, VAL1,", #16, #15") - TEST_UNSUPPORTED(__inst_thumb32(0xf34c2d0f) " @ sbfx sp, r12, #8, #16") - TEST_UNSUPPORTED(__inst_thumb32(0xf34c2f0f) " @ sbfx pc, r12, #8, #16") - TEST_UNSUPPORTED(__inst_thumb32(0xf34d2c0f) " @ sbfx r12, sp, #8, #16") - TEST_UNSUPPORTED(__inst_thumb32(0xf34f2c0f) " @ sbfx r12, pc, #8, #16") - - TEST_R( "ubfx r0, r",0 , VAL1,", #0, #31") - TEST_R( "ubfx r14, r",12, VAL2,", #8, #16") - TEST_R( "ubfx r4, r",10, VAL1,", #16, #15") - TEST_UNSUPPORTED(__inst_thumb32(0xf3cc2d0f) " @ ubfx sp, r12, #8, #16") - TEST_UNSUPPORTED(__inst_thumb32(0xf3cc2f0f) " @ ubfx pc, r12, #8, #16") - TEST_UNSUPPORTED(__inst_thumb32(0xf3cd2c0f) " @ ubfx r12, sp, #8, #16") - TEST_UNSUPPORTED(__inst_thumb32(0xf3cf2c0f) " @ ubfx r12, pc, #8, #16") - - TEST_R( "bfc r",0, VAL1,", #4, #20") - TEST_R( "bfc r",14,VAL2,", #4, #20") - TEST_R( "bfc r",7, VAL1,", #0, #31") - TEST_R( "bfc r",8, VAL2,", #0, #31") - TEST_UNSUPPORTED(__inst_thumb32(0xf36f0d1e) " @ bfc sp, #0, #31") - TEST_UNSUPPORTED(__inst_thumb32(0xf36f0f1e) " @ bfc pc, #0, #31") - - TEST_RR( "bfi r",0, VAL1,", r",0 , VAL2,", #0, #31") - TEST_RR( "bfi r",12,VAL1,", r",14 , VAL2,", #4, #20") - TEST_UNSUPPORTED(__inst_thumb32(0xf36e1d17) " @ bfi sp, r14, #4, #20") - TEST_UNSUPPORTED(__inst_thumb32(0xf36e1f17) " @ bfi pc, r14, #4, #20") - TEST_UNSUPPORTED(__inst_thumb32(0xf36d1e17) " @ bfi r14, sp, #4, #20") - - TEST_GROUP("Branches and miscellaneous control") - -CONDITION_INSTRUCTIONS(22, - TEST_BF("beq.w 2f") - TEST_BB("bne.w 2b") - TEST_BF("bgt.w 2f") - TEST_BB("blt.w 2b") - TEST_BF_X("bpl.w 2f", SPACE_0x1000) -) - - TEST_UNSUPPORTED("msr cpsr, r0") - TEST_UNSUPPORTED("msr cpsr_f, r1") - TEST_UNSUPPORTED("msr spsr, r2") - - TEST_UNSUPPORTED("cpsie.w i") - TEST_UNSUPPORTED("cpsid.w i") - TEST_UNSUPPORTED("cps 0x13") - - TEST_SUPPORTED("yield.w") - TEST("sev.w") - TEST("nop.w") - TEST("wfi.w") - TEST_SUPPORTED("wfe.w") - TEST_UNSUPPORTED("dbg.w #0") - - TEST_UNSUPPORTED("clrex") - TEST_UNSUPPORTED("dsb") - TEST_UNSUPPORTED("dmb") - TEST_UNSUPPORTED("isb") - - TEST_UNSUPPORTED("bxj r0") - - TEST_UNSUPPORTED("subs pc, lr, #4") - - TEST("mrs r0, cpsr") - TEST("mrs r14, cpsr") - TEST_UNSUPPORTED(__inst_thumb32(0xf3ef8d00) " @ mrs sp, spsr") - TEST_UNSUPPORTED(__inst_thumb32(0xf3ef8f00) " @ mrs pc, spsr") - TEST_UNSUPPORTED("mrs r0, spsr") - TEST_UNSUPPORTED("mrs lr, spsr") - - TEST_UNSUPPORTED(__inst_thumb32(0xf7f08000) " @ smc #0") - - TEST_UNSUPPORTED(__inst_thumb32(0xf7f0a000) " @ undefeined") - - TEST_BF( "b.w 2f") - TEST_BB( "b.w 2b") - TEST_BF_X("b.w 2f", SPACE_0x1000) - - TEST_BF( "bl.w 2f") - TEST_BB( "bl.w 2b") - TEST_BB_X("bl.w 2b", SPACE_0x1000) - - TEST_X( "blx __dummy_arm_subroutine", - ".arm \n\t" - ".align \n\t" - ".type __dummy_arm_subroutine, %%function \n\t" - "__dummy_arm_subroutine: \n\t" - "mov r0, pc \n\t" - "bx lr \n\t" - ".thumb \n\t" - ) - TEST( "blx __dummy_arm_subroutine") - - TEST_GROUP("Store single data item") - -#define SINGLE_STORE(size) \ - TEST_RP( "str"size" r",0, VAL1,", [r",11,-1024,", #1024]") \ - TEST_RP( "str"size" r",14,VAL2,", [r",1, -1024,", #1080]") \ - TEST_RP( "str"size" r",0, VAL1,", [r",11,256, ", #-120]") \ - TEST_RP( "str"size" r",14,VAL2,", [r",1, 256, ", #-128]") \ - TEST_RP( "str"size" r",0, VAL1,", [r",11,24, "], #120") \ - TEST_RP( "str"size" r",14,VAL2,", [r",1, 24, "], #128") \ - TEST_RP( "str"size" r",0, VAL1,", [r",11,24, "], #-120") \ - TEST_RP( "str"size" r",14,VAL2,", [r",1, 24, "], #-128") \ - TEST_RP( "str"size" r",0, VAL1,", [r",11,24, ", #120]!") \ - TEST_RP( "str"size" r",14,VAL2,", [r",1, 24, ", #128]!") \ - TEST_RP( "str"size" r",0, VAL1,", [r",11,256, ", #-120]!") \ - TEST_RP( "str"size" r",14,VAL2,", [r",1, 256, ", #-128]!") \ - TEST_RPR("str"size".w r",0, VAL1,", [r",1, 0,", r",2, 4,"]") \ - TEST_RPR("str"size" r",14,VAL2,", [r",10,0,", r",11,4,", lsl #1]") \ - TEST_R( "str"size".w r",7, VAL1,", [sp, #24]") \ - TEST_RP( "str"size".w r",0, VAL2,", [r",0,0, "]") \ - TEST_UNSUPPORTED("str"size"t r0, [r1, #4]") - - SINGLE_STORE("b") - SINGLE_STORE("h") - SINGLE_STORE("") - - TEST("str sp, [sp]") - TEST_UNSUPPORTED(__inst_thumb32(0xf8cfe000) " @ str r14, [pc]") - TEST_UNSUPPORTED(__inst_thumb32(0xf8cef000) " @ str pc, [r14]") - - TEST_GROUP("Advanced SIMD element or structure load/store instructions") - - TEST_UNSUPPORTED(__inst_thumb32(0xf9000000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xf92fffff) "") - TEST_UNSUPPORTED(__inst_thumb32(0xf9800000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xf9efffff) "") - - TEST_GROUP("Load single data item and memory hints") - -#define SINGLE_LOAD(size) \ - TEST_P( "ldr"size" r0, [r",11,-1024, ", #1024]") \ - TEST_P( "ldr"size" r14, [r",1, -1024,", #1080]") \ - TEST_P( "ldr"size" r0, [r",11,256, ", #-120]") \ - TEST_P( "ldr"size" r14, [r",1, 256, ", #-128]") \ - TEST_P( "ldr"size" r0, [r",11,24, "], #120") \ - TEST_P( "ldr"size" r14, [r",1, 24, "], #128") \ - TEST_P( "ldr"size" r0, [r",11,24, "], #-120") \ - TEST_P( "ldr"size" r14, [r",1,24, "], #-128") \ - TEST_P( "ldr"size" r0, [r",11,24, ", #120]!") \ - TEST_P( "ldr"size" r14, [r",1, 24, ", #128]!") \ - TEST_P( "ldr"size" r0, [r",11,256, ", #-120]!") \ - TEST_P( "ldr"size" r14, [r",1, 256, ", #-128]!") \ - TEST_PR("ldr"size".w r0, [r",1, 0,", r",2, 4,"]") \ - TEST_PR("ldr"size" r14, [r",10,0,", r",11,4,", lsl #1]") \ - TEST_X( "ldr"size".w r0, 3f", \ - ".align 3 \n\t" \ - "3: .word "__stringify(VAL1)) \ - TEST_X( "ldr"size".w r14, 3f", \ - ".align 3 \n\t" \ - "3: .word "__stringify(VAL2)) \ - TEST( "ldr"size".w r7, 3b") \ - TEST( "ldr"size".w r7, [sp, #24]") \ - TEST_P( "ldr"size".w r0, [r",0,0, "]") \ - TEST_UNSUPPORTED("ldr"size"t r0, [r1, #4]") - - SINGLE_LOAD("b") - SINGLE_LOAD("sb") - SINGLE_LOAD("h") - SINGLE_LOAD("sh") - SINGLE_LOAD("") - - TEST_BF_P("ldr pc, [r",14, 15*4,"]") - TEST_P( "ldr sp, [r",14, 13*4,"]") - TEST_BF_R("ldr pc, [sp, r",14, 15*4,"]") - TEST_R( "ldr sp, [sp, r",14, 13*4,"]") - TEST_THUMB_TO_ARM_INTERWORK_P("ldr pc, [r",0,0,", #15*4]") - TEST_SUPPORTED("ldr sp, 99f") - TEST_SUPPORTED("ldr pc, 99f") - - TEST_UNSUPPORTED(__inst_thumb32(0xf854700d) " @ ldr r7, [r4, sp]") - TEST_UNSUPPORTED(__inst_thumb32(0xf854700f) " @ ldr r7, [r4, pc]") - TEST_UNSUPPORTED(__inst_thumb32(0xf814700d) " @ ldrb r7, [r4, sp]") - TEST_UNSUPPORTED(__inst_thumb32(0xf814700f) " @ ldrb r7, [r4, pc]") - TEST_UNSUPPORTED(__inst_thumb32(0xf89fd004) " @ ldrb sp, 99f") - TEST_UNSUPPORTED(__inst_thumb32(0xf814d008) " @ ldrb sp, [r4, r8]") - TEST_UNSUPPORTED(__inst_thumb32(0xf894d000) " @ ldrb sp, [r4]") - - TEST_UNSUPPORTED(__inst_thumb32(0xf8600000) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xf9ffffff) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xf9500000) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xf95fffff) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xf8000800) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xf97ffaff) "") /* Unallocated space */ - - TEST( "pli [pc, #4]") - TEST( "pli [pc, #-4]") - TEST( "pld [pc, #4]") - TEST( "pld [pc, #-4]") - - TEST_P( "pld [r",0,-1024,", #1024]") - TEST( __inst_thumb32(0xf8b0f400) " @ pldw [r0, #1024]") - TEST_P( "pli [r",4, 0b,", #1024]") - TEST_P( "pld [r",7, 120,", #-120]") - TEST( __inst_thumb32(0xf837fc78) " @ pldw [r7, #-120]") - TEST_P( "pli [r",11,120,", #-120]") - TEST( "pld [sp, #0]") - - TEST_PR("pld [r",7, 24, ", r",0, 16,"]") - TEST_PR("pld [r",8, 24, ", r",12,16,", lsl #3]") - TEST_SUPPORTED(__inst_thumb32(0xf837f000) " @ pldw [r7, r0]") - TEST_SUPPORTED(__inst_thumb32(0xf838f03c) " @ pldw [r8, r12, lsl #3]"); - TEST_RR("pli [r",12,0b,", r",0, 16,"]") - TEST_RR("pli [r",0, 0b,", r",12,16,", lsl #3]") - TEST_R( "pld [sp, r",1, 16,"]") - TEST_UNSUPPORTED(__inst_thumb32(0xf817f00d) " @pld [r7, sp]") - TEST_UNSUPPORTED(__inst_thumb32(0xf817f00f) " @pld [r7, pc]") - - TEST_GROUP("Data-processing (register)") - -#define SHIFTS32(op) \ - TEST_RR(op" r0, r",1, VAL1,", r",2, 3, "") \ - TEST_RR(op" r14, r",12,VAL2,", r",11,10,"") - - SHIFTS32("lsl") - SHIFTS32("lsls") - SHIFTS32("lsr") - SHIFTS32("lsrs") - SHIFTS32("asr") - SHIFTS32("asrs") - SHIFTS32("ror") - SHIFTS32("rors") - - TEST_UNSUPPORTED(__inst_thumb32(0xfa01ff02) " @ lsl pc, r1, r2") - TEST_UNSUPPORTED(__inst_thumb32(0xfa01fd02) " @ lsl sp, r1, r2") - TEST_UNSUPPORTED(__inst_thumb32(0xfa0ff002) " @ lsl r0, pc, r2") - TEST_UNSUPPORTED(__inst_thumb32(0xfa0df002) " @ lsl r0, sp, r2") - TEST_UNSUPPORTED(__inst_thumb32(0xfa01f00f) " @ lsl r0, r1, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xfa01f00d) " @ lsl r0, r1, sp") - - TEST_RR( "sxtah r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sxtah r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "sxth r8, r",7, HH1,"") - - TEST_UNSUPPORTED(__inst_thumb32(0xfa0fff87) " @ sxth pc, r7"); - TEST_UNSUPPORTED(__inst_thumb32(0xfa0ffd87) " @ sxth sp, r7"); - TEST_UNSUPPORTED(__inst_thumb32(0xfa0ff88f) " @ sxth r8, pc"); - TEST_UNSUPPORTED(__inst_thumb32(0xfa0ff88d) " @ sxth r8, sp"); - - TEST_RR( "uxtah r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uxtah r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "uxth r8, r",7, HH1,"") - - TEST_RR( "sxtab16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sxtab16 r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "sxtb16 r8, r",7, HH1,"") - - TEST_RR( "uxtab16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uxtab16 r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "uxtb16 r8, r",7, HH1,"") - - TEST_RR( "sxtab r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sxtab r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "sxtb r8, r",7, HH1,"") - - TEST_RR( "uxtab r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uxtab r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "uxtb r8, r",7, HH1,"") - - TEST_UNSUPPORTED(__inst_thumb32(0xfa6000f0) "") - TEST_UNSUPPORTED(__inst_thumb32(0xfa7fffff) "") - -#define PARALLEL_ADD_SUB(op) \ - TEST_RR( op"add16 r0, r",0, HH1,", r",1, HH2,"") \ - TEST_RR( op"add16 r14, r",12,HH2,", r",10,HH1,"") \ - TEST_RR( op"asx r0, r",0, HH1,", r",1, HH2,"") \ - TEST_RR( op"asx r14, r",12,HH2,", r",10,HH1,"") \ - TEST_RR( op"sax r0, r",0, HH1,", r",1, HH2,"") \ - TEST_RR( op"sax r14, r",12,HH2,", r",10,HH1,"") \ - TEST_RR( op"sub16 r0, r",0, HH1,", r",1, HH2,"") \ - TEST_RR( op"sub16 r14, r",12,HH2,", r",10,HH1,"") \ - TEST_RR( op"add8 r0, r",0, HH1,", r",1, HH2,"") \ - TEST_RR( op"add8 r14, r",12,HH2,", r",10,HH1,"") \ - TEST_RR( op"sub8 r0, r",0, HH1,", r",1, HH2,"") \ - TEST_RR( op"sub8 r14, r",12,HH2,", r",10,HH1,"") - - TEST_GROUP("Parallel addition and subtraction, signed") - - PARALLEL_ADD_SUB("s") - PARALLEL_ADD_SUB("q") - PARALLEL_ADD_SUB("sh") - - TEST_GROUP("Parallel addition and subtraction, unsigned") - - PARALLEL_ADD_SUB("u") - PARALLEL_ADD_SUB("uq") - PARALLEL_ADD_SUB("uh") - - TEST_GROUP("Miscellaneous operations") - - TEST_RR("qadd r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR("qadd lr, r",9, VAL2,", r",8, VAL1,"") - TEST_RR("qsub r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR("qsub lr, r",9, VAL2,", r",8, VAL1,"") - TEST_RR("qdadd r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR("qdadd lr, r",9, VAL2,", r",8, VAL1,"") - TEST_RR("qdsub r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR("qdsub lr, r",9, VAL2,", r",8, VAL1,"") - - TEST_R("rev.w r0, r",0, VAL1,"") - TEST_R("rev r14, r",12, VAL2,"") - TEST_R("rev16.w r0, r",0, VAL1,"") - TEST_R("rev16 r14, r",12, VAL2,"") - TEST_R("rbit r0, r",0, VAL1,"") - TEST_R("rbit r14, r",12, VAL2,"") - TEST_R("revsh.w r0, r",0, VAL1,"") - TEST_R("revsh r14, r",12, VAL2,"") - - TEST_UNSUPPORTED(__inst_thumb32(0xfa9cff8c) " @ rev pc, r12"); - TEST_UNSUPPORTED(__inst_thumb32(0xfa9cfd8c) " @ rev sp, r12"); - TEST_UNSUPPORTED(__inst_thumb32(0xfa9ffe8f) " @ rev r14, pc"); - TEST_UNSUPPORTED(__inst_thumb32(0xfa9dfe8d) " @ rev r14, sp"); - - TEST_RR("sel r0, r",0, VAL1,", r",1, VAL2,"") - TEST_RR("sel r14, r",12,VAL1,", r",10, VAL2,"") - - TEST_R("clz r0, r",0, 0x0,"") - TEST_R("clz r7, r",14,0x1,"") - TEST_R("clz lr, r",7, 0xffffffff,"") - - TEST_UNSUPPORTED(__inst_thumb32(0xfa80f030) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xfaffff7f) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xfab0f000) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xfaffff7f) "") /* Unallocated space */ - - TEST_GROUP("Multiply, multiply accumulate, and absolute difference operations") - - TEST_RR( "mul r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "mul r7, r",8, VAL2,", r",9, VAL2,"") - TEST_UNSUPPORTED(__inst_thumb32(0xfb08ff09) " @ mul pc, r8, r9") - TEST_UNSUPPORTED(__inst_thumb32(0xfb08fd09) " @ mul sp, r8, r9") - TEST_UNSUPPORTED(__inst_thumb32(0xfb0ff709) " @ mul r7, pc, r9") - TEST_UNSUPPORTED(__inst_thumb32(0xfb0df709) " @ mul r7, sp, r9") - TEST_UNSUPPORTED(__inst_thumb32(0xfb08f70f) " @ mul r7, r8, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xfb08f70d) " @ mul r7, r8, sp") - - TEST_RRR( "mla r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "mla r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_UNSUPPORTED(__inst_thumb32(0xfb08af09) " @ mla pc, r8, r9, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb08ad09) " @ mla sp, r8, r9, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb0fa709) " @ mla r7, pc, r9, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb0da709) " @ mla r7, sp, r9, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb08a70f) " @ mla r7, r8, pc, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb08a70d) " @ mla r7, r8, sp, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb08d709) " @ mla r7, r8, r9, sp"); - - TEST_RRR( "mls r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "mls r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - - TEST_RRR( "smlabb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlabb r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RRR( "smlatb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlatb r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RRR( "smlabt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlabt r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RRR( "smlatt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlatt r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "smulbb r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulbb r7, r",8, VAL3,", r",9, VAL1,"") - TEST_RR( "smultb r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smultb r7, r",8, VAL3,", r",9, VAL1,"") - TEST_RR( "smulbt r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulbt r7, r",8, VAL3,", r",9, VAL1,"") - TEST_RR( "smultt r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smultt r7, r",8, VAL3,", r",9, VAL1,"") - - TEST_RRR( "smlad r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") - TEST_RRR( "smlad r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") - TEST_RRR( "smladx r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") - TEST_RRR( "smladx r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") - TEST_RR( "smuad r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "smuad r14, r",12,HH2,", r",10,HH1,"") - TEST_RR( "smuadx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "smuadx r14, r",12,HH2,", r",10,HH1,"") - - TEST_RRR( "smlawb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlawb r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RRR( "smlawt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlawt r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "smulwb r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulwb r7, r",8, VAL3,", r",9, VAL1,"") - TEST_RR( "smulwt r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulwt r7, r",8, VAL3,", r",9, VAL1,"") - - TEST_RRR( "smlsd r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") - TEST_RRR( "smlsd r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") - TEST_RRR( "smlsdx r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") - TEST_RRR( "smlsdx r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") - TEST_RR( "smusd r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "smusd r14, r",12,HH2,", r",10,HH1,"") - TEST_RR( "smusdx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "smusdx r14, r",12,HH2,", r",10,HH1,"") - - TEST_RRR( "smmla r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") - TEST_RRR( "smmla r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") - TEST_RRR( "smmlar r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") - TEST_RRR( "smmlar r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") - TEST_RR( "smmul r0, r",0, VAL1,", r",1, VAL2,"") - TEST_RR( "smmul r14, r",12,VAL2,", r",10,VAL1,"") - TEST_RR( "smmulr r0, r",0, VAL1,", r",1, VAL2,"") - TEST_RR( "smmulr r14, r",12,VAL2,", r",10,VAL1,"") - - TEST_RRR( "smmls r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") - TEST_RRR( "smmls r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") - TEST_RRR( "smmlsr r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") - TEST_RRR( "smmlsr r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") - - TEST_RRR( "usada8 r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL3,"") - TEST_RRR( "usada8 r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL3,"") - TEST_RR( "usad8 r0, r",0, VAL1,", r",1, VAL2,"") - TEST_RR( "usad8 r14, r",12,VAL2,", r",10,VAL1,"") - - TEST_UNSUPPORTED(__inst_thumb32(0xfb00f010) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xfb0fff1f) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xfb70f010) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xfb7fff1f) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xfb700010) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xfb7fff1f) "") /* Unallocated space */ - - TEST_GROUP("Long multiply, long multiply accumulate, and divide") - - TEST_RR( "smull r0, r1, r",2, VAL1,", r",3, VAL2,"") - TEST_RR( "smull r7, r8, r",9, VAL2,", r",10, VAL1,"") - TEST_UNSUPPORTED(__inst_thumb32(0xfb89f80a) " @ smull pc, r8, r9, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb89d80a) " @ smull sp, r8, r9, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb897f0a) " @ smull r7, pc, r9, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb897d0a) " @ smull r7, sp, r9, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb8f780a) " @ smull r7, r8, pc, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb8d780a) " @ smull r7, r8, sp, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb89780f) " @ smull r7, r8, r9, pc"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb89780d) " @ smull r7, r8, r9, sp"); - - TEST_RR( "umull r0, r1, r",2, VAL1,", r",3, VAL2,"") - TEST_RR( "umull r7, r8, r",9, VAL2,", r",10, VAL1,"") - - TEST_RRRR( "smlal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlal r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - - TEST_RRRR( "smlalbb r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlalbb r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRRR( "smlalbt r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlalbt r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRRR( "smlaltb r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlaltb r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRRR( "smlaltt r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlaltt r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - - TEST_RRRR( "smlald r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) - TEST_RRRR( "smlald r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) - TEST_RRRR( "smlaldx r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) - TEST_RRRR( "smlaldx r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) - - TEST_RRRR( "smlsld r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) - TEST_RRRR( "smlsld r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) - TEST_RRRR( "smlsldx r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) - TEST_RRRR( "smlsldx r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) - - TEST_RRRR( "umlal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "umlal r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRRR( "umaal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "umaal r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - - TEST_GROUP("Coprocessor instructions") - - TEST_UNSUPPORTED(__inst_thumb32(0xfc000000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xffffffff) "") - - TEST_GROUP("Testing instructions in IT blocks") - - TEST_ITBLOCK("sub.w r0, r0") - - verbose("\n"); -} - diff --git a/arch/arm/kernel/kprobes-test.c b/arch/arm/kernel/kprobes-test.c deleted file mode 100644 index b206d77..0000000 --- a/arch/arm/kernel/kprobes-test.c +++ /dev/null @@ -1,1713 +0,0 @@ -/* - * arch/arm/kernel/kprobes-test.c - * - * Copyright (C) 2011 Jon Medhurst . - * - * 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 file contains test code for ARM kprobes. - * - * The top level function run_all_tests() executes tests for all of the - * supported instruction sets: ARM, 16-bit Thumb, and 32-bit Thumb. These tests - * fall into two categories; run_api_tests() checks basic functionality of the - * kprobes API, and run_test_cases() is a comprehensive test for kprobes - * instruction decoding and simulation. - * - * run_test_cases() first checks the kprobes decoding table for self consistency - * (using table_test()) then executes a series of test cases for each of the CPU - * instruction forms. coverage_start() and coverage_end() are used to verify - * that these test cases cover all of the possible combinations of instructions - * described by the kprobes decoding tables. - * - * The individual test cases are in kprobes-test-arm.c and kprobes-test-thumb.c - * which use the macros defined in kprobes-test.h. The rest of this - * documentation will describe the operation of the framework used by these - * test cases. - */ - -/* - * TESTING METHODOLOGY - * ------------------- - * - * The methodology used to test an ARM instruction 'test_insn' is to use - * inline assembler like: - * - * test_before: nop - * test_case: test_insn - * test_after: nop - * - * When the test case is run a kprobe is placed of each nop. The - * post-handler of the test_before probe is used to modify the saved CPU - * register context to that which we require for the test case. The - * pre-handler of the of the test_after probe saves a copy of the CPU - * register context. In this way we can execute test_insn with a specific - * register context and see the results afterwards. - * - * To actually test the kprobes instruction emulation we perform the above - * step a second time but with an additional kprobe on the test_case - * instruction itself. If the emulation is accurate then the results seen - * by the test_after probe will be identical to the first run which didn't - * have a probe on test_case. - * - * Each test case is run several times with a variety of variations in the - * flags value of stored in CPSR, and for Thumb code, different ITState. - * - * For instructions which can modify PC, a second test_after probe is used - * like this: - * - * test_before: nop - * test_case: test_insn - * test_after: nop - * b test_done - * test_after2: nop - * test_done: - * - * The test case is constructed such that test_insn branches to - * test_after2, or, if testing a conditional instruction, it may just - * continue to test_after. The probes inserted at both locations let us - * determine which happened. A similar approach is used for testing - * backwards branches... - * - * b test_before - * b test_done @ helps to cope with off by 1 branches - * test_after2: nop - * b test_done - * test_before: nop - * test_case: test_insn - * test_after: nop - * test_done: - * - * The macros used to generate the assembler instructions describe above - * are TEST_INSTRUCTION, TEST_BRANCH_F (branch forwards) and TEST_BRANCH_B - * (branch backwards). In these, the local variables numbered 1, 50, 2 and - * 99 represent: test_before, test_case, test_after2 and test_done. - * - * FRAMEWORK - * --------- - * - * Each test case is wrapped between the pair of macros TESTCASE_START and - * TESTCASE_END. As well as performing the inline assembler boilerplate, - * these call out to the kprobes_test_case_start() and - * kprobes_test_case_end() functions which drive the execution of the test - * case. The specific arguments to use for each test case are stored as - * inline data constructed using the various TEST_ARG_* macros. Putting - * this all together, a simple test case may look like: - * - * TESTCASE_START("Testing mov r0, r7") - * TEST_ARG_REG(7, 0x12345678) // Set r7=0x12345678 - * TEST_ARG_END("") - * TEST_INSTRUCTION("mov r0, r7") - * TESTCASE_END - * - * Note, in practice the single convenience macro TEST_R would be used for this - * instead. - * - * The above would expand to assembler looking something like: - * - * @ TESTCASE_START - * bl __kprobes_test_case_start - * .pushsection .rodata - * "10: - * .ascii "mov r0, r7" @ text title for test case - * .byte 0 - * .popsection - * @ start of inline data... - * .word 10b @ pointer to title in .rodata section - * - * @ TEST_ARG_REG - * .byte ARG_TYPE_REG - * .byte 7 - * .short 0 - * .word 0x1234567 - * - * @ TEST_ARG_END - * .byte ARG_TYPE_END - * .byte TEST_ISA @ flags, including ISA being tested - * .short 50f-0f @ offset of 'test_before' - * .short 2f-0f @ offset of 'test_after2' (if relevent) - * .short 99f-0f @ offset of 'test_done' - * @ start of test case code... - * 0: - * .code TEST_ISA @ switch to ISA being tested - * - * @ TEST_INSTRUCTION - * 50: nop @ location for 'test_before' probe - * 1: mov r0, r7 @ the test case instruction 'test_insn' - * nop @ location for 'test_after' probe - * - * // TESTCASE_END - * 2: - * 99: bl __kprobes_test_case_end_##TEST_ISA - * .code NONMAL_ISA - * - * When the above is execute the following happens... - * - * __kprobes_test_case_start() is an assembler wrapper which sets up space - * for a stack buffer and calls the C function kprobes_test_case_start(). - * This C function will do some initial processing of the inline data and - * setup some global state. It then inserts the test_before and test_after - * kprobes and returns a value which causes the assembler wrapper to jump - * to the start of the test case code, (local label '0'). - * - * When the test case code executes, the test_before probe will be hit and - * test_before_post_handler will call setup_test_context(). This fills the - * stack buffer and CPU registers with a test pattern and then processes - * the test case arguments. In our example there is one TEST_ARG_REG which - * indicates that R7 should be loaded with the value 0x12345678. - * - * When the test_before probe ends, the test case continues and executes - * the "mov r0, r7" instruction. It then hits the test_after probe and the - * pre-handler for this (test_after_pre_handler) will save a copy of the - * CPU register context. This should now have R0 holding the same value as - * R7. - * - * Finally we get to the call to __kprobes_test_case_end_{32,16}. This is - * an assembler wrapper which switches back to the ISA used by the test - * code and calls the C function kprobes_test_case_end(). - * - * For each run through the test case, test_case_run_count is incremented - * by one. For even runs, kprobes_test_case_end() saves a copy of the - * register and stack buffer contents from the test case just run. It then - * inserts a kprobe on the test case instruction 'test_insn' and returns a - * value to cause the test case code to be re-run. - * - * For odd numbered runs, kprobes_test_case_end() compares the register and - * stack buffer contents to those that were saved on the previous even - * numbered run (the one without the kprobe on test_insn). These should be - * the same if the kprobe instruction simulation routine is correct. - * - * The pair of test case runs is repeated with different combinations of - * flag values in CPSR and, for Thumb, different ITState. This is - * controlled by test_context_cpsr(). - * - * BUILDING TEST CASES - * ------------------- - * - * - * As an aid to building test cases, the stack buffer is initialised with - * some special values: - * - * [SP+13*4] Contains SP+120. This can be used to test instructions - * which load a value into SP. - * - * [SP+15*4] When testing branching instructions using TEST_BRANCH_{F,B}, - * this holds the target address of the branch, 'test_after2'. - * This can be used to test instructions which load a PC value - * from memory. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "kprobes.h" -#include "probes-arm.h" -#include "probes-thumb.h" -#include "kprobes-test.h" - - -#define BENCHMARKING 1 - - -/* - * Test basic API - */ - -static bool test_regs_ok; -static int test_func_instance; -static int pre_handler_called; -static int post_handler_called; -static int jprobe_func_called; -static int kretprobe_handler_called; -static int tests_failed; - -#define FUNC_ARG1 0x12345678 -#define FUNC_ARG2 0xabcdef - - -#ifndef CONFIG_THUMB2_KERNEL - -long arm_func(long r0, long r1); - -static void __used __naked __arm_kprobes_test_func(void) -{ - __asm__ __volatile__ ( - ".arm \n\t" - ".type arm_func, %%function \n\t" - "arm_func: \n\t" - "adds r0, r0, r1 \n\t" - "bx lr \n\t" - ".code "NORMAL_ISA /* Back to Thumb if necessary */ - : : : "r0", "r1", "cc" - ); -} - -#else /* CONFIG_THUMB2_KERNEL */ - -long thumb16_func(long r0, long r1); -long thumb32even_func(long r0, long r1); -long thumb32odd_func(long r0, long r1); - -static void __used __naked __thumb_kprobes_test_funcs(void) -{ - __asm__ __volatile__ ( - ".type thumb16_func, %%function \n\t" - "thumb16_func: \n\t" - "adds.n r0, r0, r1 \n\t" - "bx lr \n\t" - - ".align \n\t" - ".type thumb32even_func, %%function \n\t" - "thumb32even_func: \n\t" - "adds.w r0, r0, r1 \n\t" - "bx lr \n\t" - - ".align \n\t" - "nop.n \n\t" - ".type thumb32odd_func, %%function \n\t" - "thumb32odd_func: \n\t" - "adds.w r0, r0, r1 \n\t" - "bx lr \n\t" - - : : : "r0", "r1", "cc" - ); -} - -#endif /* CONFIG_THUMB2_KERNEL */ - - -static int call_test_func(long (*func)(long, long), bool check_test_regs) -{ - long ret; - - ++test_func_instance; - test_regs_ok = false; - - ret = (*func)(FUNC_ARG1, FUNC_ARG2); - if (ret != FUNC_ARG1 + FUNC_ARG2) { - pr_err("FAIL: call_test_func: func returned %lx\n", ret); - return false; - } - - if (check_test_regs && !test_regs_ok) { - pr_err("FAIL: test regs not OK\n"); - return false; - } - - return true; -} - -static int __kprobes pre_handler(struct kprobe *p, struct pt_regs *regs) -{ - pre_handler_called = test_func_instance; - if (regs->ARM_r0 == FUNC_ARG1 && regs->ARM_r1 == FUNC_ARG2) - test_regs_ok = true; - return 0; -} - -static void __kprobes post_handler(struct kprobe *p, struct pt_regs *regs, - unsigned long flags) -{ - post_handler_called = test_func_instance; - if (regs->ARM_r0 != FUNC_ARG1 + FUNC_ARG2 || regs->ARM_r1 != FUNC_ARG2) - test_regs_ok = false; -} - -static struct kprobe the_kprobe = { - .addr = 0, - .pre_handler = pre_handler, - .post_handler = post_handler -}; - -static int test_kprobe(long (*func)(long, long)) -{ - int ret; - - the_kprobe.addr = (kprobe_opcode_t *)func; - ret = register_kprobe(&the_kprobe); - if (ret < 0) { - pr_err("FAIL: register_kprobe failed with %d\n", ret); - return ret; - } - - ret = call_test_func(func, true); - - unregister_kprobe(&the_kprobe); - the_kprobe.flags = 0; /* Clear disable flag to allow reuse */ - - if (!ret) - return -EINVAL; - if (pre_handler_called != test_func_instance) { - pr_err("FAIL: kprobe pre_handler not called\n"); - return -EINVAL; - } - if (post_handler_called != test_func_instance) { - pr_err("FAIL: kprobe post_handler not called\n"); - return -EINVAL; - } - if (!call_test_func(func, false)) - return -EINVAL; - if (pre_handler_called == test_func_instance || - post_handler_called == test_func_instance) { - pr_err("FAIL: probe called after unregistering\n"); - return -EINVAL; - } - - return 0; -} - -static void __kprobes jprobe_func(long r0, long r1) -{ - jprobe_func_called = test_func_instance; - if (r0 == FUNC_ARG1 && r1 == FUNC_ARG2) - test_regs_ok = true; - jprobe_return(); -} - -static struct jprobe the_jprobe = { - .entry = jprobe_func, -}; - -static int test_jprobe(long (*func)(long, long)) -{ - int ret; - - the_jprobe.kp.addr = (kprobe_opcode_t *)func; - ret = register_jprobe(&the_jprobe); - if (ret < 0) { - pr_err("FAIL: register_jprobe failed with %d\n", ret); - return ret; - } - - ret = call_test_func(func, true); - - unregister_jprobe(&the_jprobe); - the_jprobe.kp.flags = 0; /* Clear disable flag to allow reuse */ - - if (!ret) - return -EINVAL; - if (jprobe_func_called != test_func_instance) { - pr_err("FAIL: jprobe handler function not called\n"); - return -EINVAL; - } - if (!call_test_func(func, false)) - return -EINVAL; - if (jprobe_func_called == test_func_instance) { - pr_err("FAIL: probe called after unregistering\n"); - return -EINVAL; - } - - return 0; -} - -static int __kprobes -kretprobe_handler(struct kretprobe_instance *ri, struct pt_regs *regs) -{ - kretprobe_handler_called = test_func_instance; - if (regs_return_value(regs) == FUNC_ARG1 + FUNC_ARG2) - test_regs_ok = true; - return 0; -} - -static struct kretprobe the_kretprobe = { - .handler = kretprobe_handler, -}; - -static int test_kretprobe(long (*func)(long, long)) -{ - int ret; - - the_kretprobe.kp.addr = (kprobe_opcode_t *)func; - ret = register_kretprobe(&the_kretprobe); - if (ret < 0) { - pr_err("FAIL: register_kretprobe failed with %d\n", ret); - return ret; - } - - ret = call_test_func(func, true); - - unregister_kretprobe(&the_kretprobe); - the_kretprobe.kp.flags = 0; /* Clear disable flag to allow reuse */ - - if (!ret) - return -EINVAL; - if (kretprobe_handler_called != test_func_instance) { - pr_err("FAIL: kretprobe handler not called\n"); - return -EINVAL; - } - if (!call_test_func(func, false)) - return -EINVAL; - if (jprobe_func_called == test_func_instance) { - pr_err("FAIL: kretprobe called after unregistering\n"); - return -EINVAL; - } - - return 0; -} - -static int run_api_tests(long (*func)(long, long)) -{ - int ret; - - pr_info(" kprobe\n"); - ret = test_kprobe(func); - if (ret < 0) - return ret; - - pr_info(" jprobe\n"); - ret = test_jprobe(func); -#if defined(CONFIG_THUMB2_KERNEL) && !defined(MODULE) - if (ret == -EINVAL) { - pr_err("FAIL: Known longtime bug with jprobe on Thumb kernels\n"); - tests_failed = ret; - ret = 0; - } -#endif - if (ret < 0) - return ret; - - pr_info(" kretprobe\n"); - ret = test_kretprobe(func); - if (ret < 0) - return ret; - - return 0; -} - - -/* - * Benchmarking - */ - -#if BENCHMARKING - -static void __naked benchmark_nop(void) -{ - __asm__ __volatile__ ( - "nop \n\t" - "bx lr" - ); -} - -#ifdef CONFIG_THUMB2_KERNEL -#define wide ".w" -#else -#define wide -#endif - -static void __naked benchmark_pushpop1(void) -{ - __asm__ __volatile__ ( - "stmdb"wide" sp!, {r3-r11,lr} \n\t" - "ldmia"wide" sp!, {r3-r11,pc}" - ); -} - -static void __naked benchmark_pushpop2(void) -{ - __asm__ __volatile__ ( - "stmdb"wide" sp!, {r0-r8,lr} \n\t" - "ldmia"wide" sp!, {r0-r8,pc}" - ); -} - -static void __naked benchmark_pushpop3(void) -{ - __asm__ __volatile__ ( - "stmdb"wide" sp!, {r4,lr} \n\t" - "ldmia"wide" sp!, {r4,pc}" - ); -} - -static void __naked benchmark_pushpop4(void) -{ - __asm__ __volatile__ ( - "stmdb"wide" sp!, {r0,lr} \n\t" - "ldmia"wide" sp!, {r0,pc}" - ); -} - - -#ifdef CONFIG_THUMB2_KERNEL - -static void __naked benchmark_pushpop_thumb(void) -{ - __asm__ __volatile__ ( - "push.n {r0-r7,lr} \n\t" - "pop.n {r0-r7,pc}" - ); -} - -#endif - -static int __kprobes -benchmark_pre_handler(struct kprobe *p, struct pt_regs *regs) -{ - return 0; -} - -static int benchmark(void(*fn)(void)) -{ - unsigned n, i, t, t0; - - for (n = 1000; ; n *= 2) { - t0 = sched_clock(); - for (i = n; i > 0; --i) - fn(); - t = sched_clock() - t0; - if (t >= 250000000) - break; /* Stop once we took more than 0.25 seconds */ - } - return t / n; /* Time for one iteration in nanoseconds */ -}; - -static int kprobe_benchmark(void(*fn)(void), unsigned offset) -{ - struct kprobe k = { - .addr = (kprobe_opcode_t *)((uintptr_t)fn + offset), - .pre_handler = benchmark_pre_handler, - }; - - int ret = register_kprobe(&k); - if (ret < 0) { - pr_err("FAIL: register_kprobe failed with %d\n", ret); - return ret; - } - - ret = benchmark(fn); - - unregister_kprobe(&k); - return ret; -}; - -struct benchmarks { - void (*fn)(void); - unsigned offset; - const char *title; -}; - -static int run_benchmarks(void) -{ - int ret; - struct benchmarks list[] = { - {&benchmark_nop, 0, "nop"}, - /* - * benchmark_pushpop{1,3} will have the optimised - * instruction emulation, whilst benchmark_pushpop{2,4} will - * be the equivalent unoptimised instructions. - */ - {&benchmark_pushpop1, 0, "stmdb sp!, {r3-r11,lr}"}, - {&benchmark_pushpop1, 4, "ldmia sp!, {r3-r11,pc}"}, - {&benchmark_pushpop2, 0, "stmdb sp!, {r0-r8,lr}"}, - {&benchmark_pushpop2, 4, "ldmia sp!, {r0-r8,pc}"}, - {&benchmark_pushpop3, 0, "stmdb sp!, {r4,lr}"}, - {&benchmark_pushpop3, 4, "ldmia sp!, {r4,pc}"}, - {&benchmark_pushpop4, 0, "stmdb sp!, {r0,lr}"}, - {&benchmark_pushpop4, 4, "ldmia sp!, {r0,pc}"}, -#ifdef CONFIG_THUMB2_KERNEL - {&benchmark_pushpop_thumb, 0, "push.n {r0-r7,lr}"}, - {&benchmark_pushpop_thumb, 2, "pop.n {r0-r7,pc}"}, -#endif - {0} - }; - - struct benchmarks *b; - for (b = list; b->fn; ++b) { - ret = kprobe_benchmark(b->fn, b->offset); - if (ret < 0) - return ret; - pr_info(" %dns for kprobe %s\n", ret, b->title); - } - - pr_info("\n"); - return 0; -} - -#endif /* BENCHMARKING */ - - -/* - * Decoding table self-consistency tests - */ - -static const int decode_struct_sizes[NUM_DECODE_TYPES] = { - [DECODE_TYPE_TABLE] = sizeof(struct decode_table), - [DECODE_TYPE_CUSTOM] = sizeof(struct decode_custom), - [DECODE_TYPE_SIMULATE] = sizeof(struct decode_simulate), - [DECODE_TYPE_EMULATE] = sizeof(struct decode_emulate), - [DECODE_TYPE_OR] = sizeof(struct decode_or), - [DECODE_TYPE_REJECT] = sizeof(struct decode_reject) -}; - -static int table_iter(const union decode_item *table, - int (*fn)(const struct decode_header *, void *), - void *args) -{ - const struct decode_header *h = (struct decode_header *)table; - int result; - - for (;;) { - enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; - - if (type == DECODE_TYPE_END) - return 0; - - result = fn(h, args); - if (result) - return result; - - h = (struct decode_header *) - ((uintptr_t)h + decode_struct_sizes[type]); - - } -} - -static int table_test_fail(const struct decode_header *h, const char* message) -{ - - pr_err("FAIL: kprobes test failure \"%s\" (mask %08x, value %08x)\n", - message, h->mask.bits, h->value.bits); - return -EINVAL; -} - -struct table_test_args { - const union decode_item *root_table; - u32 parent_mask; - u32 parent_value; -}; - -static int table_test_fn(const struct decode_header *h, void *args) -{ - struct table_test_args *a = (struct table_test_args *)args; - enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; - - if (h->value.bits & ~h->mask.bits) - return table_test_fail(h, "Match value has bits not in mask"); - - if ((h->mask.bits & a->parent_mask) != a->parent_mask) - return table_test_fail(h, "Mask has bits not in parent mask"); - - if ((h->value.bits ^ a->parent_value) & a->parent_mask) - return table_test_fail(h, "Value is inconsistent with parent"); - - if (type == DECODE_TYPE_TABLE) { - struct decode_table *d = (struct decode_table *)h; - struct table_test_args args2 = *a; - args2.parent_mask = h->mask.bits; - args2.parent_value = h->value.bits; - return table_iter(d->table.table, table_test_fn, &args2); - } - - return 0; -} - -static int table_test(const union decode_item *table) -{ - struct table_test_args args = { - .root_table = table, - .parent_mask = 0, - .parent_value = 0 - }; - return table_iter(args.root_table, table_test_fn, &args); -} - - -/* - * Decoding table test coverage analysis - * - * coverage_start() builds a coverage_table which contains a list of - * coverage_entry's to match each entry in the specified kprobes instruction - * decoding table. - * - * When test cases are run, coverage_add() is called to process each case. - * This looks up the corresponding entry in the coverage_table and sets it as - * being matched, as well as clearing the regs flag appropriate for the test. - * - * After all test cases have been run, coverage_end() is called to check that - * all entries in coverage_table have been matched and that all regs flags are - * cleared. I.e. that all possible combinations of instructions described by - * the kprobes decoding tables have had a test case executed for them. - */ - -bool coverage_fail; - -#define MAX_COVERAGE_ENTRIES 256 - -struct coverage_entry { - const struct decode_header *header; - unsigned regs; - unsigned nesting; - char matched; -}; - -struct coverage_table { - struct coverage_entry *base; - unsigned num_entries; - unsigned nesting; -}; - -struct coverage_table coverage; - -#define COVERAGE_ANY_REG (1<<0) -#define COVERAGE_SP (1<<1) -#define COVERAGE_PC (1<<2) -#define COVERAGE_PCWB (1<<3) - -static const char coverage_register_lookup[16] = { - [REG_TYPE_ANY] = COVERAGE_ANY_REG | COVERAGE_SP | COVERAGE_PC, - [REG_TYPE_SAMEAS16] = COVERAGE_ANY_REG, - [REG_TYPE_SP] = COVERAGE_SP, - [REG_TYPE_PC] = COVERAGE_PC, - [REG_TYPE_NOSP] = COVERAGE_ANY_REG | COVERAGE_SP, - [REG_TYPE_NOSPPC] = COVERAGE_ANY_REG | COVERAGE_SP | COVERAGE_PC, - [REG_TYPE_NOPC] = COVERAGE_ANY_REG | COVERAGE_PC, - [REG_TYPE_NOPCWB] = COVERAGE_ANY_REG | COVERAGE_PC | COVERAGE_PCWB, - [REG_TYPE_NOPCX] = COVERAGE_ANY_REG, - [REG_TYPE_NOSPPCX] = COVERAGE_ANY_REG | COVERAGE_SP, -}; - -unsigned coverage_start_registers(const struct decode_header *h) -{ - unsigned regs = 0; - int i; - for (i = 0; i < 20; i += 4) { - int r = (h->type_regs.bits >> (DECODE_TYPE_BITS + i)) & 0xf; - regs |= coverage_register_lookup[r] << i; - } - return regs; -} - -static int coverage_start_fn(const struct decode_header *h, void *args) -{ - struct coverage_table *coverage = (struct coverage_table *)args; - enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; - struct coverage_entry *entry = coverage->base + coverage->num_entries; - - if (coverage->num_entries == MAX_COVERAGE_ENTRIES - 1) { - pr_err("FAIL: Out of space for test coverage data"); - return -ENOMEM; - } - - ++coverage->num_entries; - - entry->header = h; - entry->regs = coverage_start_registers(h); - entry->nesting = coverage->nesting; - entry->matched = false; - - if (type == DECODE_TYPE_TABLE) { - struct decode_table *d = (struct decode_table *)h; - int ret; - ++coverage->nesting; - ret = table_iter(d->table.table, coverage_start_fn, coverage); - --coverage->nesting; - return ret; - } - - return 0; -} - -static int coverage_start(const union decode_item *table) -{ - coverage.base = kmalloc(MAX_COVERAGE_ENTRIES * - sizeof(struct coverage_entry), GFP_KERNEL); - coverage.num_entries = 0; - coverage.nesting = 0; - return table_iter(table, coverage_start_fn, &coverage); -} - -static void -coverage_add_registers(struct coverage_entry *entry, kprobe_opcode_t insn) -{ - int regs = entry->header->type_regs.bits >> DECODE_TYPE_BITS; - int i; - for (i = 0; i < 20; i += 4) { - enum decode_reg_type reg_type = (regs >> i) & 0xf; - int reg = (insn >> i) & 0xf; - int flag; - - if (!reg_type) - continue; - - if (reg == 13) - flag = COVERAGE_SP; - else if (reg == 15) - flag = COVERAGE_PC; - else - flag = COVERAGE_ANY_REG; - entry->regs &= ~(flag << i); - - switch (reg_type) { - - case REG_TYPE_NONE: - case REG_TYPE_ANY: - case REG_TYPE_SAMEAS16: - break; - - case REG_TYPE_SP: - if (reg != 13) - return; - break; - - case REG_TYPE_PC: - if (reg != 15) - return; - break; - - case REG_TYPE_NOSP: - if (reg == 13) - return; - break; - - case REG_TYPE_NOSPPC: - case REG_TYPE_NOSPPCX: - if (reg == 13 || reg == 15) - return; - break; - - case REG_TYPE_NOPCWB: - if (!is_writeback(insn)) - break; - if (reg == 15) { - entry->regs &= ~(COVERAGE_PCWB << i); - return; - } - break; - - case REG_TYPE_NOPC: - case REG_TYPE_NOPCX: - if (reg == 15) - return; - break; - } - - } -} - -static void coverage_add(kprobe_opcode_t insn) -{ - struct coverage_entry *entry = coverage.base; - struct coverage_entry *end = coverage.base + coverage.num_entries; - bool matched = false; - unsigned nesting = 0; - - for (; entry < end; ++entry) { - const struct decode_header *h = entry->header; - enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; - - if (entry->nesting > nesting) - continue; /* Skip sub-table we didn't match */ - - if (entry->nesting < nesting) - break; /* End of sub-table we were scanning */ - - if (!matched) { - if ((insn & h->mask.bits) != h->value.bits) - continue; - entry->matched = true; - } - - switch (type) { - - case DECODE_TYPE_TABLE: - ++nesting; - break; - - case DECODE_TYPE_CUSTOM: - case DECODE_TYPE_SIMULATE: - case DECODE_TYPE_EMULATE: - coverage_add_registers(entry, insn); - return; - - case DECODE_TYPE_OR: - matched = true; - break; - - case DECODE_TYPE_REJECT: - default: - return; - } - - } -} - -static void coverage_end(void) -{ - struct coverage_entry *entry = coverage.base; - struct coverage_entry *end = coverage.base + coverage.num_entries; - - for (; entry < end; ++entry) { - u32 mask = entry->header->mask.bits; - u32 value = entry->header->value.bits; - - if (entry->regs) { - pr_err("FAIL: Register test coverage missing for %08x %08x (%05x)\n", - mask, value, entry->regs); - coverage_fail = true; - } - if (!entry->matched) { - pr_err("FAIL: Test coverage entry missing for %08x %08x\n", - mask, value); - coverage_fail = true; - } - } - - kfree(coverage.base); -} - - -/* - * Framework for instruction set test cases - */ - -void __naked __kprobes_test_case_start(void) -{ - __asm__ __volatile__ ( - "stmdb sp!, {r4-r11} \n\t" - "sub sp, sp, #"__stringify(TEST_MEMORY_SIZE)"\n\t" - "bic r0, lr, #1 @ r0 = inline data \n\t" - "mov r1, sp \n\t" - "bl kprobes_test_case_start \n\t" - "bx r0 \n\t" - ); -} - -#ifndef CONFIG_THUMB2_KERNEL - -void __naked __kprobes_test_case_end_32(void) -{ - __asm__ __volatile__ ( - "mov r4, lr \n\t" - "bl kprobes_test_case_end \n\t" - "cmp r0, #0 \n\t" - "movne pc, r0 \n\t" - "mov r0, r4 \n\t" - "add sp, sp, #"__stringify(TEST_MEMORY_SIZE)"\n\t" - "ldmia sp!, {r4-r11} \n\t" - "mov pc, r0 \n\t" - ); -} - -#else /* CONFIG_THUMB2_KERNEL */ - -void __naked __kprobes_test_case_end_16(void) -{ - __asm__ __volatile__ ( - "mov r4, lr \n\t" - "bl kprobes_test_case_end \n\t" - "cmp r0, #0 \n\t" - "bxne r0 \n\t" - "mov r0, r4 \n\t" - "add sp, sp, #"__stringify(TEST_MEMORY_SIZE)"\n\t" - "ldmia sp!, {r4-r11} \n\t" - "bx r0 \n\t" - ); -} - -void __naked __kprobes_test_case_end_32(void) -{ - __asm__ __volatile__ ( - ".arm \n\t" - "orr lr, lr, #1 @ will return to Thumb code \n\t" - "ldr pc, 1f \n\t" - "1: \n\t" - ".word __kprobes_test_case_end_16 \n\t" - ); -} - -#endif - - -int kprobe_test_flags; -int kprobe_test_cc_position; - -static int test_try_count; -static int test_pass_count; -static int test_fail_count; - -static struct pt_regs initial_regs; -static struct pt_regs expected_regs; -static struct pt_regs result_regs; - -static u32 expected_memory[TEST_MEMORY_SIZE/sizeof(u32)]; - -static const char *current_title; -static struct test_arg *current_args; -static u32 *current_stack; -static uintptr_t current_branch_target; - -static uintptr_t current_code_start; -static kprobe_opcode_t current_instruction; - - -#define TEST_CASE_PASSED -1 -#define TEST_CASE_FAILED -2 - -static int test_case_run_count; -static bool test_case_is_thumb; -static int test_instance; - -/* - * We ignore the state of the imprecise abort disable flag (CPSR.A) because this - * can change randomly as the kernel doesn't take care to preserve or initialise - * this across context switches. Also, with Security Extentions, the flag may - * not be under control of the kernel; for this reason we ignore the state of - * the FIQ disable flag CPSR.F as well. - */ -#define PSR_IGNORE_BITS (PSR_A_BIT | PSR_F_BIT) - -static unsigned long test_check_cc(int cc, unsigned long cpsr) -{ - int ret = arm_check_condition(cc << 28, cpsr); - - return (ret != ARM_OPCODE_CONDTEST_FAIL); -} - -static int is_last_scenario; -static int probe_should_run; /* 0 = no, 1 = yes, -1 = unknown */ -static int memory_needs_checking; - -static unsigned long test_context_cpsr(int scenario) -{ - unsigned long cpsr; - - probe_should_run = 1; - - /* Default case is that we cycle through 16 combinations of flags */ - cpsr = (scenario & 0xf) << 28; /* N,Z,C,V flags */ - cpsr |= (scenario & 0xf) << 16; /* GE flags */ - cpsr |= (scenario & 0x1) << 27; /* Toggle Q flag */ - - if (!test_case_is_thumb) { - /* Testing ARM code */ - int cc = current_instruction >> 28; - - probe_should_run = test_check_cc(cc, cpsr) != 0; - if (scenario == 15) - is_last_scenario = true; - - } else if (kprobe_test_flags & TEST_FLAG_NO_ITBLOCK) { - /* Testing Thumb code without setting ITSTATE */ - if (kprobe_test_cc_position) { - int cc = (current_instruction >> kprobe_test_cc_position) & 0xf; - probe_should_run = test_check_cc(cc, cpsr) != 0; - } - - if (scenario == 15) - is_last_scenario = true; - - } else if (kprobe_test_flags & TEST_FLAG_FULL_ITBLOCK) { - /* Testing Thumb code with all combinations of ITSTATE */ - unsigned x = (scenario >> 4); - unsigned cond_base = x % 7; /* ITSTATE<7:5> */ - unsigned mask = x / 7 + 2; /* ITSTATE<4:0>, bits reversed */ - - if (mask > 0x1f) { - /* Finish by testing state from instruction 'itt al' */ - cond_base = 7; - mask = 0x4; - if ((scenario & 0xf) == 0xf) - is_last_scenario = true; - } - - cpsr |= cond_base << 13; /* ITSTATE<7:5> */ - cpsr |= (mask & 0x1) << 12; /* ITSTATE<4> */ - cpsr |= (mask & 0x2) << 10; /* ITSTATE<3> */ - cpsr |= (mask & 0x4) << 8; /* ITSTATE<2> */ - cpsr |= (mask & 0x8) << 23; /* ITSTATE<1> */ - cpsr |= (mask & 0x10) << 21; /* ITSTATE<0> */ - - probe_should_run = test_check_cc((cpsr >> 12) & 0xf, cpsr) != 0; - - } else { - /* Testing Thumb code with several combinations of ITSTATE */ - switch (scenario) { - case 16: /* Clear NZCV flags and 'it eq' state (false as Z=0) */ - cpsr = 0x00000800; - probe_should_run = 0; - break; - case 17: /* Set NZCV flags and 'it vc' state (false as V=1) */ - cpsr = 0xf0007800; - probe_should_run = 0; - break; - case 18: /* Clear NZCV flags and 'it ls' state (true as C=0) */ - cpsr = 0x00009800; - break; - case 19: /* Set NZCV flags and 'it cs' state (true as C=1) */ - cpsr = 0xf0002800; - is_last_scenario = true; - break; - } - } - - return cpsr; -} - -static void setup_test_context(struct pt_regs *regs) -{ - int scenario = test_case_run_count>>1; - unsigned long val; - struct test_arg *args; - int i; - - is_last_scenario = false; - memory_needs_checking = false; - - /* Initialise test memory on stack */ - val = (scenario & 1) ? VALM : ~VALM; - for (i = 0; i < TEST_MEMORY_SIZE / sizeof(current_stack[0]); ++i) - current_stack[i] = val + (i << 8); - /* Put target of branch on stack for tests which load PC from memory */ - if (current_branch_target) - current_stack[15] = current_branch_target; - /* Put a value for SP on stack for tests which load SP from memory */ - current_stack[13] = (u32)current_stack + 120; - - /* Initialise register values to their default state */ - val = (scenario & 2) ? VALR : ~VALR; - for (i = 0; i < 13; ++i) - regs->uregs[i] = val ^ (i << 8); - regs->ARM_lr = val ^ (14 << 8); - regs->ARM_cpsr &= ~(APSR_MASK | PSR_IT_MASK); - regs->ARM_cpsr |= test_context_cpsr(scenario); - - /* Perform testcase specific register setup */ - args = current_args; - for (; args[0].type != ARG_TYPE_END; ++args) - switch (args[0].type) { - case ARG_TYPE_REG: { - struct test_arg_regptr *arg = - (struct test_arg_regptr *)args; - regs->uregs[arg->reg] = arg->val; - break; - } - case ARG_TYPE_PTR: { - struct test_arg_regptr *arg = - (struct test_arg_regptr *)args; - regs->uregs[arg->reg] = - (unsigned long)current_stack + arg->val; - memory_needs_checking = true; - break; - } - case ARG_TYPE_MEM: { - struct test_arg_mem *arg = (struct test_arg_mem *)args; - current_stack[arg->index] = arg->val; - break; - } - default: - break; - } -} - -struct test_probe { - struct kprobe kprobe; - bool registered; - int hit; -}; - -static void unregister_test_probe(struct test_probe *probe) -{ - if (probe->registered) { - unregister_kprobe(&probe->kprobe); - probe->kprobe.flags = 0; /* Clear disable flag to allow reuse */ - } - probe->registered = false; -} - -static int register_test_probe(struct test_probe *probe) -{ - int ret; - - if (probe->registered) - BUG(); - - ret = register_kprobe(&probe->kprobe); - if (ret >= 0) { - probe->registered = true; - probe->hit = -1; - } - return ret; -} - -static int __kprobes -test_before_pre_handler(struct kprobe *p, struct pt_regs *regs) -{ - container_of(p, struct test_probe, kprobe)->hit = test_instance; - return 0; -} - -static void __kprobes -test_before_post_handler(struct kprobe *p, struct pt_regs *regs, - unsigned long flags) -{ - setup_test_context(regs); - initial_regs = *regs; - initial_regs.ARM_cpsr &= ~PSR_IGNORE_BITS; -} - -static int __kprobes -test_case_pre_handler(struct kprobe *p, struct pt_regs *regs) -{ - container_of(p, struct test_probe, kprobe)->hit = test_instance; - return 0; -} - -static int __kprobes -test_after_pre_handler(struct kprobe *p, struct pt_regs *regs) -{ - if (container_of(p, struct test_probe, kprobe)->hit == test_instance) - return 0; /* Already run for this test instance */ - - result_regs = *regs; - result_regs.ARM_cpsr &= ~PSR_IGNORE_BITS; - - /* Undo any changes done to SP by the test case */ - regs->ARM_sp = (unsigned long)current_stack; - - container_of(p, struct test_probe, kprobe)->hit = test_instance; - return 0; -} - -static struct test_probe test_before_probe = { - .kprobe.pre_handler = test_before_pre_handler, - .kprobe.post_handler = test_before_post_handler, -}; - -static struct test_probe test_case_probe = { - .kprobe.pre_handler = test_case_pre_handler, -}; - -static struct test_probe test_after_probe = { - .kprobe.pre_handler = test_after_pre_handler, -}; - -static struct test_probe test_after2_probe = { - .kprobe.pre_handler = test_after_pre_handler, -}; - -static void test_case_cleanup(void) -{ - unregister_test_probe(&test_before_probe); - unregister_test_probe(&test_case_probe); - unregister_test_probe(&test_after_probe); - unregister_test_probe(&test_after2_probe); -} - -static void print_registers(struct pt_regs *regs) -{ - pr_err("r0 %08lx | r1 %08lx | r2 %08lx | r3 %08lx\n", - regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3); - pr_err("r4 %08lx | r5 %08lx | r6 %08lx | r7 %08lx\n", - regs->ARM_r4, regs->ARM_r5, regs->ARM_r6, regs->ARM_r7); - pr_err("r8 %08lx | r9 %08lx | r10 %08lx | r11 %08lx\n", - regs->ARM_r8, regs->ARM_r9, regs->ARM_r10, regs->ARM_fp); - pr_err("r12 %08lx | sp %08lx | lr %08lx | pc %08lx\n", - regs->ARM_ip, regs->ARM_sp, regs->ARM_lr, regs->ARM_pc); - pr_err("cpsr %08lx\n", regs->ARM_cpsr); -} - -static void print_memory(u32 *mem, size_t size) -{ - int i; - for (i = 0; i < size / sizeof(u32); i += 4) - pr_err("%08x %08x %08x %08x\n", mem[i], mem[i+1], - mem[i+2], mem[i+3]); -} - -static size_t expected_memory_size(u32 *sp) -{ - size_t size = sizeof(expected_memory); - int offset = (uintptr_t)sp - (uintptr_t)current_stack; - if (offset > 0) - size -= offset; - return size; -} - -static void test_case_failed(const char *message) -{ - test_case_cleanup(); - - pr_err("FAIL: %s\n", message); - pr_err("FAIL: Test %s\n", current_title); - pr_err("FAIL: Scenario %d\n", test_case_run_count >> 1); -} - -static unsigned long next_instruction(unsigned long pc) -{ -#ifdef CONFIG_THUMB2_KERNEL - if ((pc & 1) && - !is_wide_instruction(__mem_to_opcode_thumb16(*(u16 *)(pc - 1)))) - return pc + 2; - else -#endif - return pc + 4; -} - -static uintptr_t __used kprobes_test_case_start(const char **title, void *stack) -{ - struct test_arg *args; - struct test_arg_end *end_arg; - unsigned long test_code; - - current_title = *title++; - args = (struct test_arg *)title; - current_args = args; - current_stack = stack; - - ++test_try_count; - - while (args->type != ARG_TYPE_END) - ++args; - end_arg = (struct test_arg_end *)args; - - test_code = (unsigned long)(args + 1); /* Code starts after args */ - - test_case_is_thumb = end_arg->flags & ARG_FLAG_THUMB; - if (test_case_is_thumb) - test_code |= 1; - - current_code_start = test_code; - - current_branch_target = 0; - if (end_arg->branch_offset != end_arg->end_offset) - current_branch_target = test_code + end_arg->branch_offset; - - test_code += end_arg->code_offset; - test_before_probe.kprobe.addr = (kprobe_opcode_t *)test_code; - - test_code = next_instruction(test_code); - test_case_probe.kprobe.addr = (kprobe_opcode_t *)test_code; - - if (test_case_is_thumb) { - u16 *p = (u16 *)(test_code & ~1); - current_instruction = __mem_to_opcode_thumb16(p[0]); - if (is_wide_instruction(current_instruction)) { - u16 instr2 = __mem_to_opcode_thumb16(p[1]); - current_instruction = __opcode_thumb32_compose(current_instruction, instr2); - } - } else { - current_instruction = __mem_to_opcode_arm(*(u32 *)test_code); - } - - if (current_title[0] == '.') - verbose("%s\n", current_title); - else - verbose("%s\t@ %0*x\n", current_title, - test_case_is_thumb ? 4 : 8, - current_instruction); - - test_code = next_instruction(test_code); - test_after_probe.kprobe.addr = (kprobe_opcode_t *)test_code; - - if (kprobe_test_flags & TEST_FLAG_NARROW_INSTR) { - if (!test_case_is_thumb || - is_wide_instruction(current_instruction)) { - test_case_failed("expected 16-bit instruction"); - goto fail; - } - } else { - if (test_case_is_thumb && - !is_wide_instruction(current_instruction)) { - test_case_failed("expected 32-bit instruction"); - goto fail; - } - } - - coverage_add(current_instruction); - - if (end_arg->flags & ARG_FLAG_UNSUPPORTED) { - if (register_test_probe(&test_case_probe) < 0) - goto pass; - test_case_failed("registered probe for unsupported instruction"); - goto fail; - } - - if (end_arg->flags & ARG_FLAG_SUPPORTED) { - if (register_test_probe(&test_case_probe) >= 0) - goto pass; - test_case_failed("couldn't register probe for supported instruction"); - goto fail; - } - - if (register_test_probe(&test_before_probe) < 0) { - test_case_failed("register test_before_probe failed"); - goto fail; - } - if (register_test_probe(&test_after_probe) < 0) { - test_case_failed("register test_after_probe failed"); - goto fail; - } - if (current_branch_target) { - test_after2_probe.kprobe.addr = - (kprobe_opcode_t *)current_branch_target; - if (register_test_probe(&test_after2_probe) < 0) { - test_case_failed("register test_after2_probe failed"); - goto fail; - } - } - - /* Start first run of test case */ - test_case_run_count = 0; - ++test_instance; - return current_code_start; -pass: - test_case_run_count = TEST_CASE_PASSED; - return (uintptr_t)test_after_probe.kprobe.addr; -fail: - test_case_run_count = TEST_CASE_FAILED; - return (uintptr_t)test_after_probe.kprobe.addr; -} - -static bool check_test_results(void) -{ - size_t mem_size = 0; - u32 *mem = 0; - - if (memcmp(&expected_regs, &result_regs, sizeof(expected_regs))) { - test_case_failed("registers differ"); - goto fail; - } - - if (memory_needs_checking) { - mem = (u32 *)result_regs.ARM_sp; - mem_size = expected_memory_size(mem); - if (memcmp(expected_memory, mem, mem_size)) { - test_case_failed("test memory differs"); - goto fail; - } - } - - return true; - -fail: - pr_err("initial_regs:\n"); - print_registers(&initial_regs); - pr_err("expected_regs:\n"); - print_registers(&expected_regs); - pr_err("result_regs:\n"); - print_registers(&result_regs); - - if (mem) { - pr_err("current_stack=%p\n", current_stack); - pr_err("expected_memory:\n"); - print_memory(expected_memory, mem_size); - pr_err("result_memory:\n"); - print_memory(mem, mem_size); - } - - return false; -} - -static uintptr_t __used kprobes_test_case_end(void) -{ - if (test_case_run_count < 0) { - if (test_case_run_count == TEST_CASE_PASSED) - /* kprobes_test_case_start did all the needed testing */ - goto pass; - else - /* kprobes_test_case_start failed */ - goto fail; - } - - if (test_before_probe.hit != test_instance) { - test_case_failed("test_before_handler not run"); - goto fail; - } - - if (test_after_probe.hit != test_instance && - test_after2_probe.hit != test_instance) { - test_case_failed("test_after_handler not run"); - goto fail; - } - - /* - * Even numbered test runs ran without a probe on the test case so - * we can gather reference results. The subsequent odd numbered run - * will have the probe inserted. - */ - if ((test_case_run_count & 1) == 0) { - /* Save results from run without probe */ - u32 *mem = (u32 *)result_regs.ARM_sp; - expected_regs = result_regs; - memcpy(expected_memory, mem, expected_memory_size(mem)); - - /* Insert probe onto test case instruction */ - if (register_test_probe(&test_case_probe) < 0) { - test_case_failed("register test_case_probe failed"); - goto fail; - } - } else { - /* Check probe ran as expected */ - if (probe_should_run == 1) { - if (test_case_probe.hit != test_instance) { - test_case_failed("test_case_handler not run"); - goto fail; - } - } else if (probe_should_run == 0) { - if (test_case_probe.hit == test_instance) { - test_case_failed("test_case_handler ran"); - goto fail; - } - } - - /* Remove probe for any subsequent reference run */ - unregister_test_probe(&test_case_probe); - - if (!check_test_results()) - goto fail; - - if (is_last_scenario) - goto pass; - } - - /* Do next test run */ - ++test_case_run_count; - ++test_instance; - return current_code_start; -fail: - ++test_fail_count; - goto end; -pass: - ++test_pass_count; -end: - test_case_cleanup(); - return 0; -} - - -/* - * Top level test functions - */ - -static int run_test_cases(void (*tests)(void), const union decode_item *table) -{ - int ret; - - pr_info(" Check decoding tables\n"); - ret = table_test(table); - if (ret) - return ret; - - pr_info(" Run test cases\n"); - ret = coverage_start(table); - if (ret) - return ret; - - tests(); - - coverage_end(); - return 0; -} - - -static int __init run_all_tests(void) -{ - int ret = 0; - - pr_info("Beginning kprobe tests...\n"); - -#ifndef CONFIG_THUMB2_KERNEL - - pr_info("Probe ARM code\n"); - ret = run_api_tests(arm_func); - if (ret) - goto out; - - pr_info("ARM instruction simulation\n"); - ret = run_test_cases(kprobe_arm_test_cases, probes_decode_arm_table); - if (ret) - goto out; - -#else /* CONFIG_THUMB2_KERNEL */ - - pr_info("Probe 16-bit Thumb code\n"); - ret = run_api_tests(thumb16_func); - if (ret) - goto out; - - pr_info("Probe 32-bit Thumb code, even halfword\n"); - ret = run_api_tests(thumb32even_func); - if (ret) - goto out; - - pr_info("Probe 32-bit Thumb code, odd halfword\n"); - ret = run_api_tests(thumb32odd_func); - if (ret) - goto out; - - pr_info("16-bit Thumb instruction simulation\n"); - ret = run_test_cases(kprobe_thumb16_test_cases, - probes_decode_thumb16_table); - if (ret) - goto out; - - pr_info("32-bit Thumb instruction simulation\n"); - ret = run_test_cases(kprobe_thumb32_test_cases, - probes_decode_thumb32_table); - if (ret) - goto out; -#endif - - pr_info("Total instruction simulation tests=%d, pass=%d fail=%d\n", - test_try_count, test_pass_count, test_fail_count); - if (test_fail_count) { - ret = -EINVAL; - goto out; - } - -#if BENCHMARKING - pr_info("Benchmarks\n"); - ret = run_benchmarks(); - if (ret) - goto out; -#endif - -#if __LINUX_ARM_ARCH__ >= 7 - /* We are able to run all test cases so coverage should be complete */ - if (coverage_fail) { - pr_err("FAIL: Test coverage checks failed\n"); - ret = -EINVAL; - goto out; - } -#endif - -out: - if (ret == 0) - ret = tests_failed; - if (ret == 0) - pr_info("Finished kprobe tests OK\n"); - else - pr_err("kprobe tests failed\n"); - - return ret; -} - - -/* - * Module setup - */ - -#ifdef MODULE - -static void __exit kprobe_test_exit(void) -{ -} - -module_init(run_all_tests) -module_exit(kprobe_test_exit) -MODULE_LICENSE("GPL"); - -#else /* !MODULE */ - -late_initcall(run_all_tests); - -#endif diff --git a/arch/arm/kernel/kprobes-test.h b/arch/arm/kernel/kprobes-test.h deleted file mode 100644 index 4430990..0000000 --- a/arch/arm/kernel/kprobes-test.h +++ /dev/null @@ -1,435 +0,0 @@ -/* - * arch/arm/kernel/kprobes-test.h - * - * Copyright (C) 2011 Jon Medhurst . - * - * 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. - */ - -#define VERBOSE 0 /* Set to '1' for more logging of test cases */ - -#ifdef CONFIG_THUMB2_KERNEL -#define NORMAL_ISA "16" -#else -#define NORMAL_ISA "32" -#endif - - -/* Flags used in kprobe_test_flags */ -#define TEST_FLAG_NO_ITBLOCK (1<<0) -#define TEST_FLAG_FULL_ITBLOCK (1<<1) -#define TEST_FLAG_NARROW_INSTR (1<<2) - -extern int kprobe_test_flags; -extern int kprobe_test_cc_position; - - -#define TEST_MEMORY_SIZE 256 - - -/* - * Test case structures. - * - * The arguments given to test cases can be one of three types. - * - * ARG_TYPE_REG - * Load a register with the given value. - * - * ARG_TYPE_PTR - * Load a register with a pointer into the stack buffer (SP + given value). - * - * ARG_TYPE_MEM - * Store the given value into the stack buffer at [SP+index]. - * - */ - -#define ARG_TYPE_END 0 -#define ARG_TYPE_REG 1 -#define ARG_TYPE_PTR 2 -#define ARG_TYPE_MEM 3 - -#define ARG_FLAG_UNSUPPORTED 0x01 -#define ARG_FLAG_SUPPORTED 0x02 -#define ARG_FLAG_THUMB 0x10 /* Must be 16 so TEST_ISA can be used */ -#define ARG_FLAG_ARM 0x20 /* Must be 32 so TEST_ISA can be used */ - -struct test_arg { - u8 type; /* ARG_TYPE_x */ - u8 _padding[7]; -}; - -struct test_arg_regptr { - u8 type; /* ARG_TYPE_REG or ARG_TYPE_PTR */ - u8 reg; - u8 _padding[2]; - u32 val; -}; - -struct test_arg_mem { - u8 type; /* ARG_TYPE_MEM */ - u8 index; - u8 _padding[2]; - u32 val; -}; - -struct test_arg_end { - u8 type; /* ARG_TYPE_END */ - u8 flags; /* ARG_FLAG_x */ - u16 code_offset; - u16 branch_offset; - u16 end_offset; -}; - - -/* - * Building blocks for test cases. - * - * Each test case is wrapped between TESTCASE_START and TESTCASE_END. - * - * To specify arguments for a test case the TEST_ARG_{REG,PTR,MEM} macros are - * used followed by a terminating TEST_ARG_END. - * - * After this, the instruction to be tested is defined with TEST_INSTRUCTION. - * Or for branches, TEST_BRANCH_B and TEST_BRANCH_F (branch forwards/backwards). - * - * Some specific test cases may make use of other custom constructs. - */ - -#if VERBOSE -#define verbose(fmt, ...) pr_info(fmt, ##__VA_ARGS__) -#else -#define verbose(fmt, ...) -#endif - -#define TEST_GROUP(title) \ - verbose("\n"); \ - verbose(title"\n"); \ - verbose("---------------------------------------------------------\n"); - -#define TESTCASE_START(title) \ - __asm__ __volatile__ ( \ - "bl __kprobes_test_case_start \n\t" \ - ".pushsection .rodata \n\t" \ - "10: \n\t" \ - /* don't use .asciz here as 'title' may be */ \ - /* multiple strings to be concatenated. */ \ - ".ascii "#title" \n\t" \ - ".byte 0 \n\t" \ - ".popsection \n\t" \ - ".word 10b \n\t" - -#define TEST_ARG_REG(reg, val) \ - ".byte "__stringify(ARG_TYPE_REG)" \n\t" \ - ".byte "#reg" \n\t" \ - ".short 0 \n\t" \ - ".word "#val" \n\t" - -#define TEST_ARG_PTR(reg, val) \ - ".byte "__stringify(ARG_TYPE_PTR)" \n\t" \ - ".byte "#reg" \n\t" \ - ".short 0 \n\t" \ - ".word "#val" \n\t" - -#define TEST_ARG_MEM(index, val) \ - ".byte "__stringify(ARG_TYPE_MEM)" \n\t" \ - ".byte "#index" \n\t" \ - ".short 0 \n\t" \ - ".word "#val" \n\t" - -#define TEST_ARG_END(flags) \ - ".byte "__stringify(ARG_TYPE_END)" \n\t" \ - ".byte "TEST_ISA flags" \n\t" \ - ".short 50f-0f \n\t" \ - ".short 2f-0f \n\t" \ - ".short 99f-0f \n\t" \ - ".code "TEST_ISA" \n\t" \ - "0: \n\t" - -#define TEST_INSTRUCTION(instruction) \ - "50: nop \n\t" \ - "1: "instruction" \n\t" \ - " nop \n\t" - -#define TEST_BRANCH_F(instruction) \ - TEST_INSTRUCTION(instruction) \ - " b 99f \n\t" \ - "2: nop \n\t" - -#define TEST_BRANCH_B(instruction) \ - " b 50f \n\t" \ - " b 99f \n\t" \ - "2: nop \n\t" \ - " b 99f \n\t" \ - TEST_INSTRUCTION(instruction) - -#define TEST_BRANCH_FX(instruction, codex) \ - TEST_INSTRUCTION(instruction) \ - " b 99f \n\t" \ - codex" \n\t" \ - " b 99f \n\t" \ - "2: nop \n\t" - -#define TEST_BRANCH_BX(instruction, codex) \ - " b 50f \n\t" \ - " b 99f \n\t" \ - "2: nop \n\t" \ - " b 99f \n\t" \ - codex" \n\t" \ - TEST_INSTRUCTION(instruction) - -#define TESTCASE_END \ - "2: \n\t" \ - "99: \n\t" \ - " bl __kprobes_test_case_end_"TEST_ISA" \n\t" \ - ".code "NORMAL_ISA" \n\t" \ - : : \ - : "r0", "r1", "r2", "r3", "ip", "lr", "memory", "cc" \ - ); - - -/* - * Macros to define test cases. - * - * Those of the form TEST_{R,P,M}* can be used to define test cases - * which take combinations of the three basic types of arguments. E.g. - * - * TEST_R One register argument - * TEST_RR Two register arguments - * TEST_RPR A register, a pointer, then a register argument - * - * For testing instructions which may branch, there are macros TEST_BF_* - * and TEST_BB_* for branching forwards and backwards. - * - * TEST_SUPPORTED and TEST_UNSUPPORTED don't cause the code to be executed, - * the just verify that a kprobe is or is not allowed on the given instruction. - */ - -#define TEST(code) \ - TESTCASE_START(code) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code) \ - TESTCASE_END - -#define TEST_UNSUPPORTED(code) \ - TESTCASE_START(code) \ - TEST_ARG_END("|"__stringify(ARG_FLAG_UNSUPPORTED)) \ - TEST_INSTRUCTION(code) \ - TESTCASE_END - -#define TEST_SUPPORTED(code) \ - TESTCASE_START(code) \ - TEST_ARG_END("|"__stringify(ARG_FLAG_SUPPORTED)) \ - TEST_INSTRUCTION(code) \ - TESTCASE_END - -#define TEST_R(code1, reg, val, code2) \ - TESTCASE_START(code1 #reg code2) \ - TEST_ARG_REG(reg, val) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg code2) \ - TESTCASE_END - -#define TEST_RR(code1, reg1, val1, code2, reg2, val2, code3) \ - TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ - TEST_ARG_REG(reg1, val1) \ - TEST_ARG_REG(reg2, val2) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3) \ - TESTCASE_END - -#define TEST_RRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ - TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ - TEST_ARG_REG(reg1, val1) \ - TEST_ARG_REG(reg2, val2) \ - TEST_ARG_REG(reg3, val3) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ - TESTCASE_END - -#define TEST_RRRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4, reg4, val4) \ - TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4 #reg4) \ - TEST_ARG_REG(reg1, val1) \ - TEST_ARG_REG(reg2, val2) \ - TEST_ARG_REG(reg3, val3) \ - TEST_ARG_REG(reg4, val4) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4 #reg4) \ - TESTCASE_END - -#define TEST_P(code1, reg1, val1, code2) \ - TESTCASE_START(code1 #reg1 code2) \ - TEST_ARG_PTR(reg1, val1) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2) \ - TESTCASE_END - -#define TEST_PR(code1, reg1, val1, code2, reg2, val2, code3) \ - TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ - TEST_ARG_PTR(reg1, val1) \ - TEST_ARG_REG(reg2, val2) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3) \ - TESTCASE_END - -#define TEST_RP(code1, reg1, val1, code2, reg2, val2, code3) \ - TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ - TEST_ARG_REG(reg1, val1) \ - TEST_ARG_PTR(reg2, val2) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3) \ - TESTCASE_END - -#define TEST_PRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ - TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ - TEST_ARG_PTR(reg1, val1) \ - TEST_ARG_REG(reg2, val2) \ - TEST_ARG_REG(reg3, val3) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ - TESTCASE_END - -#define TEST_RPR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ - TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ - TEST_ARG_REG(reg1, val1) \ - TEST_ARG_PTR(reg2, val2) \ - TEST_ARG_REG(reg3, val3) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ - TESTCASE_END - -#define TEST_RRP(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ - TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ - TEST_ARG_REG(reg1, val1) \ - TEST_ARG_REG(reg2, val2) \ - TEST_ARG_PTR(reg3, val3) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ - TESTCASE_END - -#define TEST_BF_P(code1, reg1, val1, code2) \ - TESTCASE_START(code1 #reg1 code2) \ - TEST_ARG_PTR(reg1, val1) \ - TEST_ARG_END("") \ - TEST_BRANCH_F(code1 #reg1 code2) \ - TESTCASE_END - -#define TEST_BF(code) \ - TESTCASE_START(code) \ - TEST_ARG_END("") \ - TEST_BRANCH_F(code) \ - TESTCASE_END - -#define TEST_BB(code) \ - TESTCASE_START(code) \ - TEST_ARG_END("") \ - TEST_BRANCH_B(code) \ - TESTCASE_END - -#define TEST_BF_R(code1, reg, val, code2) \ - TESTCASE_START(code1 #reg code2) \ - TEST_ARG_REG(reg, val) \ - TEST_ARG_END("") \ - TEST_BRANCH_F(code1 #reg code2) \ - TESTCASE_END - -#define TEST_BB_R(code1, reg, val, code2) \ - TESTCASE_START(code1 #reg code2) \ - TEST_ARG_REG(reg, val) \ - TEST_ARG_END("") \ - TEST_BRANCH_B(code1 #reg code2) \ - TESTCASE_END - -#define TEST_BF_RR(code1, reg1, val1, code2, reg2, val2, code3) \ - TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ - TEST_ARG_REG(reg1, val1) \ - TEST_ARG_REG(reg2, val2) \ - TEST_ARG_END("") \ - TEST_BRANCH_F(code1 #reg1 code2 #reg2 code3) \ - TESTCASE_END - -#define TEST_BF_X(code, codex) \ - TESTCASE_START(code) \ - TEST_ARG_END("") \ - TEST_BRANCH_FX(code, codex) \ - TESTCASE_END - -#define TEST_BB_X(code, codex) \ - TESTCASE_START(code) \ - TEST_ARG_END("") \ - TEST_BRANCH_BX(code, codex) \ - TESTCASE_END - -#define TEST_BF_RX(code1, reg, val, code2, codex) \ - TESTCASE_START(code1 #reg code2) \ - TEST_ARG_REG(reg, val) \ - TEST_ARG_END("") \ - TEST_BRANCH_FX(code1 #reg code2, codex) \ - TESTCASE_END - -#define TEST_X(code, codex) \ - TESTCASE_START(code) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code) \ - " b 99f \n\t" \ - " "codex" \n\t" \ - TESTCASE_END - -#define TEST_RX(code1, reg, val, code2, codex) \ - TESTCASE_START(code1 #reg code2) \ - TEST_ARG_REG(reg, val) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 __stringify(reg) code2) \ - " b 99f \n\t" \ - " "codex" \n\t" \ - TESTCASE_END - -#define TEST_RRX(code1, reg1, val1, code2, reg2, val2, code3, codex) \ - TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ - TEST_ARG_REG(reg1, val1) \ - TEST_ARG_REG(reg2, val2) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 __stringify(reg1) code2 __stringify(reg2) code3) \ - " b 99f \n\t" \ - " "codex" \n\t" \ - TESTCASE_END - - -/* - * Macros for defining space directives spread over multiple lines. - * These are required so the compiler guesses better the length of inline asm - * code and will spill the literal pool early enough to avoid generating PC - * relative loads with out of range offsets. - */ -#define TWICE(x) x x -#define SPACE_0x8 TWICE(".space 4\n\t") -#define SPACE_0x10 TWICE(SPACE_0x8) -#define SPACE_0x20 TWICE(SPACE_0x10) -#define SPACE_0x40 TWICE(SPACE_0x20) -#define SPACE_0x80 TWICE(SPACE_0x40) -#define SPACE_0x100 TWICE(SPACE_0x80) -#define SPACE_0x200 TWICE(SPACE_0x100) -#define SPACE_0x400 TWICE(SPACE_0x200) -#define SPACE_0x800 TWICE(SPACE_0x400) -#define SPACE_0x1000 TWICE(SPACE_0x800) - - -/* Various values used in test cases... */ -#define N(val) (val ^ 0xffffffff) -#define VAL1 0x12345678 -#define VAL2 N(VAL1) -#define VAL3 0xa5f801 -#define VAL4 N(VAL3) -#define VALM 0x456789ab -#define VALR 0xdeaddead -#define HH1 0x0123fecb -#define HH2 0xa9874567 - - -#ifdef CONFIG_THUMB2_KERNEL -void kprobe_thumb16_test_cases(void); -void kprobe_thumb32_test_cases(void); -#else -void kprobe_arm_test_cases(void); -#endif diff --git a/arch/arm/kernel/kprobes-thumb.c b/arch/arm/kernel/kprobes-thumb.c deleted file mode 100644 index 9495d7f..0000000 --- a/arch/arm/kernel/kprobes-thumb.c +++ /dev/null @@ -1,666 +0,0 @@ -/* - * arch/arm/kernel/kprobes-thumb.c - * - * Copyright (C) 2011 Jon Medhurst . - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include "kprobes.h" -#include "probes-thumb.h" - -/* These emulation encodings are functionally equivalent... */ -#define t32_emulate_rd8rn16rm0ra12_noflags \ - t32_emulate_rdlo12rdhi8rn16rm0_noflags - -/* t32 thumb actions */ - -static void __kprobes -t32_simulate_table_branch(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - - unsigned long rnv = (rn == 15) ? pc : regs->uregs[rn]; - unsigned long rmv = regs->uregs[rm]; - unsigned int halfwords; - - if (insn & 0x10) /* TBH */ - halfwords = ((u16 *)rnv)[rmv]; - else /* TBB */ - halfwords = ((u8 *)rnv)[rmv]; - - regs->ARM_pc = pc + 2 * halfwords; -} - -static void __kprobes -t32_simulate_mrs(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rd = (insn >> 8) & 0xf; - unsigned long mask = 0xf8ff03df; /* Mask out execution state */ - regs->uregs[rd] = regs->ARM_cpsr & mask; -} - -static void __kprobes -t32_simulate_cond_branch(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc; - - long offset = insn & 0x7ff; /* imm11 */ - offset += (insn & 0x003f0000) >> 5; /* imm6 */ - offset += (insn & 0x00002000) << 4; /* J1 */ - offset += (insn & 0x00000800) << 7; /* J2 */ - offset -= (insn & 0x04000000) >> 7; /* Apply sign bit */ - - regs->ARM_pc = pc + (offset * 2); -} - -static enum probes_insn __kprobes -t32_decode_cond_branch(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - int cc = (insn >> 22) & 0xf; - asi->insn_check_cc = probes_condition_checks[cc]; - asi->insn_handler = t32_simulate_cond_branch; - return INSN_GOOD_NO_SLOT; -} - -static void __kprobes -t32_simulate_branch(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc; - - long offset = insn & 0x7ff; /* imm11 */ - offset += (insn & 0x03ff0000) >> 5; /* imm10 */ - offset += (insn & 0x00002000) << 9; /* J1 */ - offset += (insn & 0x00000800) << 10; /* J2 */ - if (insn & 0x04000000) - offset -= 0x00800000; /* Apply sign bit */ - else - offset ^= 0x00600000; /* Invert J1 and J2 */ - - if (insn & (1 << 14)) { - /* BL or BLX */ - regs->ARM_lr = regs->ARM_pc | 1; - if (!(insn & (1 << 12))) { - /* BLX so switch to ARM mode */ - regs->ARM_cpsr &= ~PSR_T_BIT; - pc &= ~3; - } - } - - regs->ARM_pc = pc + (offset * 2); -} - -static void __kprobes -t32_simulate_ldr_literal(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long addr = regs->ARM_pc & ~3; - int rt = (insn >> 12) & 0xf; - unsigned long rtv; - - long offset = insn & 0xfff; - if (insn & 0x00800000) - addr += offset; - else - addr -= offset; - - if (insn & 0x00400000) { - /* LDR */ - rtv = *(unsigned long *)addr; - if (rt == 15) { - bx_write_pc(rtv, regs); - return; - } - } else if (insn & 0x00200000) { - /* LDRH */ - if (insn & 0x01000000) - rtv = *(s16 *)addr; - else - rtv = *(u16 *)addr; - } else { - /* LDRB */ - if (insn & 0x01000000) - rtv = *(s8 *)addr; - else - rtv = *(u8 *)addr; - } - - regs->uregs[rt] = rtv; -} - -static enum probes_insn __kprobes -t32_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - enum probes_insn ret = kprobe_decode_ldmstm(insn, asi, d); - - /* Fixup modified instruction to have halfwords in correct order...*/ - insn = __mem_to_opcode_arm(asi->insn[0]); - ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn >> 16); - ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0xffff); - - return ret; -} - -static void __kprobes -t32_emulate_ldrdstrd(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc & ~3; - int rt1 = (insn >> 12) & 0xf; - int rt2 = (insn >> 8) & 0xf; - int rn = (insn >> 16) & 0xf; - - register unsigned long rt1v asm("r0") = regs->uregs[rt1]; - register unsigned long rt2v asm("r1") = regs->uregs[rt2]; - register unsigned long rnv asm("r2") = (rn == 15) ? pc - : regs->uregs[rn]; - - __asm__ __volatile__ ( - "blx %[fn]" - : "=r" (rt1v), "=r" (rt2v), "=r" (rnv) - : "0" (rt1v), "1" (rt2v), "2" (rnv), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - if (rn != 15) - regs->uregs[rn] = rnv; /* Writeback base register */ - regs->uregs[rt1] = rt1v; - regs->uregs[rt2] = rt2v; -} - -static void __kprobes -t32_emulate_ldrstr(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rt = (insn >> 12) & 0xf; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - - register unsigned long rtv asm("r0") = regs->uregs[rt]; - register unsigned long rnv asm("r2") = regs->uregs[rn]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - - __asm__ __volatile__ ( - "blx %[fn]" - : "=r" (rtv), "=r" (rnv) - : "0" (rtv), "1" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rn] = rnv; /* Writeback base register */ - if (rt == 15) /* Can't be true for a STR as they aren't allowed */ - bx_write_pc(rtv, regs); - else - regs->uregs[rt] = rtv; -} - -static void __kprobes -t32_emulate_rd8rn16rm0_rwflags(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rd = (insn >> 8) & 0xf; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - - register unsigned long rdv asm("r1") = regs->uregs[rd]; - register unsigned long rnv asm("r2") = regs->uregs[rn]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - unsigned long cpsr = regs->ARM_cpsr; - - __asm__ __volatile__ ( - "msr cpsr_fs, %[cpsr] \n\t" - "blx %[fn] \n\t" - "mrs %[cpsr], cpsr \n\t" - : "=r" (rdv), [cpsr] "=r" (cpsr) - : "0" (rdv), "r" (rnv), "r" (rmv), - "1" (cpsr), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rd] = rdv; - regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); -} - -static void __kprobes -t32_emulate_rd8pc16_noflags(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc; - int rd = (insn >> 8) & 0xf; - - register unsigned long rdv asm("r1") = regs->uregs[rd]; - register unsigned long rnv asm("r2") = pc & ~3; - - __asm__ __volatile__ ( - "blx %[fn]" - : "=r" (rdv) - : "0" (rdv), "r" (rnv), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rd] = rdv; -} - -static void __kprobes -t32_emulate_rd8rn16_noflags(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rd = (insn >> 8) & 0xf; - int rn = (insn >> 16) & 0xf; - - register unsigned long rdv asm("r1") = regs->uregs[rd]; - register unsigned long rnv asm("r2") = regs->uregs[rn]; - - __asm__ __volatile__ ( - "blx %[fn]" - : "=r" (rdv) - : "0" (rdv), "r" (rnv), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rd] = rdv; -} - -static void __kprobes -t32_emulate_rdlo12rdhi8rn16rm0_noflags(probes_opcode_t insn, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - int rdlo = (insn >> 12) & 0xf; - int rdhi = (insn >> 8) & 0xf; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - - register unsigned long rdlov asm("r0") = regs->uregs[rdlo]; - register unsigned long rdhiv asm("r1") = regs->uregs[rdhi]; - register unsigned long rnv asm("r2") = regs->uregs[rn]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - - __asm__ __volatile__ ( - "blx %[fn]" - : "=r" (rdlov), "=r" (rdhiv) - : "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv), - [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rdlo] = rdlov; - regs->uregs[rdhi] = rdhiv; -} -/* t16 thumb actions */ - -static void __kprobes -t16_simulate_bxblx(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc + 2; - int rm = (insn >> 3) & 0xf; - unsigned long rmv = (rm == 15) ? pc : regs->uregs[rm]; - - if (insn & (1 << 7)) /* BLX ? */ - regs->ARM_lr = regs->ARM_pc | 1; - - bx_write_pc(rmv, regs); -} - -static void __kprobes -t16_simulate_ldr_literal(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long *base = (unsigned long *)((regs->ARM_pc + 2) & ~3); - long index = insn & 0xff; - int rt = (insn >> 8) & 0x7; - regs->uregs[rt] = base[index]; -} - -static void __kprobes -t16_simulate_ldrstr_sp_relative(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long* base = (unsigned long *)regs->ARM_sp; - long index = insn & 0xff; - int rt = (insn >> 8) & 0x7; - if (insn & 0x800) /* LDR */ - regs->uregs[rt] = base[index]; - else /* STR */ - base[index] = regs->uregs[rt]; -} - -static void __kprobes -t16_simulate_reladr(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long base = (insn & 0x800) ? regs->ARM_sp - : ((regs->ARM_pc + 2) & ~3); - long offset = insn & 0xff; - int rt = (insn >> 8) & 0x7; - regs->uregs[rt] = base + offset * 4; -} - -static void __kprobes -t16_simulate_add_sp_imm(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - long imm = insn & 0x7f; - if (insn & 0x80) /* SUB */ - regs->ARM_sp -= imm * 4; - else /* ADD */ - regs->ARM_sp += imm * 4; -} - -static void __kprobes -t16_simulate_cbz(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rn = insn & 0x7; - probes_opcode_t nonzero = regs->uregs[rn] ? insn : ~insn; - if (nonzero & 0x800) { - long i = insn & 0x200; - long imm5 = insn & 0xf8; - unsigned long pc = regs->ARM_pc + 2; - regs->ARM_pc = pc + (i >> 3) + (imm5 >> 2); - } -} - -static void __kprobes -t16_simulate_it(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - /* - * The 8 IT state bits are split into two parts in CPSR: - * ITSTATE<1:0> are in CPSR<26:25> - * ITSTATE<7:2> are in CPSR<15:10> - * The new IT state is in the lower byte of insn. - */ - unsigned long cpsr = regs->ARM_cpsr; - cpsr &= ~PSR_IT_MASK; - cpsr |= (insn & 0xfc) << 8; - cpsr |= (insn & 0x03) << 25; - regs->ARM_cpsr = cpsr; -} - -static void __kprobes -t16_singlestep_it(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - regs->ARM_pc += 2; - t16_simulate_it(insn, asi, regs); -} - -static enum probes_insn __kprobes -t16_decode_it(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - asi->insn_singlestep = t16_singlestep_it; - return INSN_GOOD_NO_SLOT; -} - -static void __kprobes -t16_simulate_cond_branch(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc + 2; - long offset = insn & 0x7f; - offset -= insn & 0x80; /* Apply sign bit */ - regs->ARM_pc = pc + (offset * 2); -} - -static enum probes_insn __kprobes -t16_decode_cond_branch(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - int cc = (insn >> 8) & 0xf; - asi->insn_check_cc = probes_condition_checks[cc]; - asi->insn_handler = t16_simulate_cond_branch; - return INSN_GOOD_NO_SLOT; -} - -static void __kprobes -t16_simulate_branch(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc + 2; - long offset = insn & 0x3ff; - offset -= insn & 0x400; /* Apply sign bit */ - regs->ARM_pc = pc + (offset * 2); -} - -static unsigned long __kprobes -t16_emulate_loregs(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long oldcpsr = regs->ARM_cpsr; - unsigned long newcpsr; - - __asm__ __volatile__ ( - "msr cpsr_fs, %[oldcpsr] \n\t" - "ldmia %[regs], {r0-r7} \n\t" - "blx %[fn] \n\t" - "stmia %[regs], {r0-r7} \n\t" - "mrs %[newcpsr], cpsr \n\t" - : [newcpsr] "=r" (newcpsr) - : [oldcpsr] "r" (oldcpsr), [regs] "r" (regs), - [fn] "r" (asi->insn_fn) - : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", - "lr", "memory", "cc" - ); - - return (oldcpsr & ~APSR_MASK) | (newcpsr & APSR_MASK); -} - -static void __kprobes -t16_emulate_loregs_rwflags(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - regs->ARM_cpsr = t16_emulate_loregs(insn, asi, regs); -} - -static void __kprobes -t16_emulate_loregs_noitrwflags(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long cpsr = t16_emulate_loregs(insn, asi, regs); - if (!in_it_block(cpsr)) - regs->ARM_cpsr = cpsr; -} - -static void __kprobes -t16_emulate_hiregs(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc + 2; - int rdn = (insn & 0x7) | ((insn & 0x80) >> 4); - int rm = (insn >> 3) & 0xf; - - register unsigned long rdnv asm("r1"); - register unsigned long rmv asm("r0"); - unsigned long cpsr = regs->ARM_cpsr; - - rdnv = (rdn == 15) ? pc : regs->uregs[rdn]; - rmv = (rm == 15) ? pc : regs->uregs[rm]; - - __asm__ __volatile__ ( - "msr cpsr_fs, %[cpsr] \n\t" - "blx %[fn] \n\t" - "mrs %[cpsr], cpsr \n\t" - : "=r" (rdnv), [cpsr] "=r" (cpsr) - : "0" (rdnv), "r" (rmv), "1" (cpsr), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - if (rdn == 15) - rdnv &= ~1; - - regs->uregs[rdn] = rdnv; - regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); -} - -static enum probes_insn __kprobes -t16_decode_hiregs(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - insn &= ~0x00ff; - insn |= 0x001; /* Set Rdn = R1 and Rm = R0 */ - ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn); - asi->insn_handler = t16_emulate_hiregs; - return INSN_GOOD; -} - -static void __kprobes -t16_emulate_push(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - __asm__ __volatile__ ( - "ldr r9, [%[regs], #13*4] \n\t" - "ldr r8, [%[regs], #14*4] \n\t" - "ldmia %[regs], {r0-r7} \n\t" - "blx %[fn] \n\t" - "str r9, [%[regs], #13*4] \n\t" - : - : [regs] "r" (regs), [fn] "r" (asi->insn_fn) - : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", - "lr", "memory", "cc" - ); -} - -static enum probes_insn __kprobes -t16_decode_push(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - /* - * To simulate a PUSH we use a Thumb-2 "STMDB R9!, {registers}" - * and call it with R9=SP and LR in the register list represented - * by R8. - */ - /* 1st half STMDB R9!,{} */ - ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe929); - /* 2nd half (register list) */ - ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff); - asi->insn_handler = t16_emulate_push; - return INSN_GOOD; -} - -static void __kprobes -t16_emulate_pop_nopc(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - __asm__ __volatile__ ( - "ldr r9, [%[regs], #13*4] \n\t" - "ldmia %[regs], {r0-r7} \n\t" - "blx %[fn] \n\t" - "stmia %[regs], {r0-r7} \n\t" - "str r9, [%[regs], #13*4] \n\t" - : - : [regs] "r" (regs), [fn] "r" (asi->insn_fn) - : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9", - "lr", "memory", "cc" - ); -} - -static void __kprobes -t16_emulate_pop_pc(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - register unsigned long pc asm("r8"); - - __asm__ __volatile__ ( - "ldr r9, [%[regs], #13*4] \n\t" - "ldmia %[regs], {r0-r7} \n\t" - "blx %[fn] \n\t" - "stmia %[regs], {r0-r7} \n\t" - "str r9, [%[regs], #13*4] \n\t" - : "=r" (pc) - : [regs] "r" (regs), [fn] "r" (asi->insn_fn) - : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9", - "lr", "memory", "cc" - ); - - bx_write_pc(pc, regs); -} - -static enum probes_insn __kprobes -t16_decode_pop(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - /* - * To simulate a POP we use a Thumb-2 "LDMDB R9!, {registers}" - * and call it with R9=SP and PC in the register list represented - * by R8. - */ - /* 1st half LDMIA R9!,{} */ - ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe8b9); - /* 2nd half (register list) */ - ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff); - asi->insn_handler = insn & 0x100 ? t16_emulate_pop_pc - : t16_emulate_pop_nopc; - return INSN_GOOD; -} - -const union decode_action kprobes_t16_actions[NUM_PROBES_T16_ACTIONS] = { - [PROBES_T16_ADD_SP] = {.handler = t16_simulate_add_sp_imm}, - [PROBES_T16_CBZ] = {.handler = t16_simulate_cbz}, - [PROBES_T16_SIGN_EXTEND] = {.handler = t16_emulate_loregs_rwflags}, - [PROBES_T16_PUSH] = {.decoder = t16_decode_push}, - [PROBES_T16_POP] = {.decoder = t16_decode_pop}, - [PROBES_T16_SEV] = {.handler = probes_emulate_none}, - [PROBES_T16_WFE] = {.handler = probes_simulate_nop}, - [PROBES_T16_IT] = {.decoder = t16_decode_it}, - [PROBES_T16_CMP] = {.handler = t16_emulate_loregs_rwflags}, - [PROBES_T16_ADDSUB] = {.handler = t16_emulate_loregs_noitrwflags}, - [PROBES_T16_LOGICAL] = {.handler = t16_emulate_loregs_noitrwflags}, - [PROBES_T16_LDR_LIT] = {.handler = t16_simulate_ldr_literal}, - [PROBES_T16_BLX] = {.handler = t16_simulate_bxblx}, - [PROBES_T16_HIREGOPS] = {.decoder = t16_decode_hiregs}, - [PROBES_T16_LDRHSTRH] = {.handler = t16_emulate_loregs_rwflags}, - [PROBES_T16_LDRSTR] = {.handler = t16_simulate_ldrstr_sp_relative}, - [PROBES_T16_ADR] = {.handler = t16_simulate_reladr}, - [PROBES_T16_LDMSTM] = {.handler = t16_emulate_loregs_rwflags}, - [PROBES_T16_BRANCH_COND] = {.decoder = t16_decode_cond_branch}, - [PROBES_T16_BRANCH] = {.handler = t16_simulate_branch}, -}; - -const union decode_action kprobes_t32_actions[NUM_PROBES_T32_ACTIONS] = { - [PROBES_T32_LDMSTM] = {.decoder = t32_decode_ldmstm}, - [PROBES_T32_LDRDSTRD] = {.handler = t32_emulate_ldrdstrd}, - [PROBES_T32_TABLE_BRANCH] = {.handler = t32_simulate_table_branch}, - [PROBES_T32_TST] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_MOV] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_ADDSUB] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_LOGICAL] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_CMP] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_ADDWSUBW_PC] = {.handler = t32_emulate_rd8pc16_noflags,}, - [PROBES_T32_ADDWSUBW] = {.handler = t32_emulate_rd8rn16_noflags}, - [PROBES_T32_MOVW] = {.handler = t32_emulate_rd8rn16_noflags}, - [PROBES_T32_SAT] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_BITFIELD] = {.handler = t32_emulate_rd8rn16_noflags}, - [PROBES_T32_SEV] = {.handler = probes_emulate_none}, - [PROBES_T32_WFE] = {.handler = probes_simulate_nop}, - [PROBES_T32_MRS] = {.handler = t32_simulate_mrs}, - [PROBES_T32_BRANCH_COND] = {.decoder = t32_decode_cond_branch}, - [PROBES_T32_BRANCH] = {.handler = t32_simulate_branch}, - [PROBES_T32_PLDI] = {.handler = probes_simulate_nop}, - [PROBES_T32_LDR_LIT] = {.handler = t32_simulate_ldr_literal}, - [PROBES_T32_LDRSTR] = {.handler = t32_emulate_ldrstr}, - [PROBES_T32_SIGN_EXTEND] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_MEDIA] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_REVERSE] = {.handler = t32_emulate_rd8rn16_noflags}, - [PROBES_T32_MUL_ADD] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_MUL_ADD2] = {.handler = t32_emulate_rd8rn16rm0ra12_noflags}, - [PROBES_T32_MUL_ADD_LONG] = { - .handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags}, -}; diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c deleted file mode 100644 index 6d64420..0000000 --- a/arch/arm/kernel/kprobes.c +++ /dev/null @@ -1,628 +0,0 @@ -/* - * arch/arm/kernel/kprobes.c - * - * Kprobes on ARM - * - * Abhishek Sagar - * Copyright (C) 2006, 2007 Motorola Inc. - * - * Nicolas Pitre - * Copyright (C) 2007 Marvell Ltd. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "kprobes.h" -#include "probes-arm.h" -#include "probes-thumb.h" -#include "patch.h" - -#define MIN_STACK_SIZE(addr) \ - min((unsigned long)MAX_STACK_SIZE, \ - (unsigned long)current_thread_info() + THREAD_START_SP - (addr)) - -#define flush_insns(addr, size) \ - flush_icache_range((unsigned long)(addr), \ - (unsigned long)(addr) + \ - (size)) - -/* Used as a marker in ARM_pc to note when we're in a jprobe. */ -#define JPROBE_MAGIC_ADDR 0xffffffff - -DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; -DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); - - -int __kprobes arch_prepare_kprobe(struct kprobe *p) -{ - kprobe_opcode_t insn; - kprobe_opcode_t tmp_insn[MAX_INSN_SIZE]; - unsigned long addr = (unsigned long)p->addr; - bool thumb; - kprobe_decode_insn_t *decode_insn; - const union decode_action *actions; - int is; - - if (in_exception_text(addr)) - return -EINVAL; - -#ifdef CONFIG_THUMB2_KERNEL - thumb = true; - addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */ - insn = __mem_to_opcode_thumb16(((u16 *)addr)[0]); - if (is_wide_instruction(insn)) { - u16 inst2 = __mem_to_opcode_thumb16(((u16 *)addr)[1]); - insn = __opcode_thumb32_compose(insn, inst2); - decode_insn = thumb32_probes_decode_insn; - actions = kprobes_t32_actions; - } else { - decode_insn = thumb16_probes_decode_insn; - actions = kprobes_t16_actions; - } -#else /* !CONFIG_THUMB2_KERNEL */ - thumb = false; - if (addr & 0x3) - return -EINVAL; - insn = __mem_to_opcode_arm(*p->addr); - decode_insn = arm_probes_decode_insn; - actions = kprobes_arm_actions; -#endif - - p->opcode = insn; - p->ainsn.insn = tmp_insn; - - switch ((*decode_insn)(insn, &p->ainsn, true, actions)) { - case INSN_REJECTED: /* not supported */ - return -EINVAL; - - case INSN_GOOD: /* instruction uses slot */ - p->ainsn.insn = get_insn_slot(); - if (!p->ainsn.insn) - return -ENOMEM; - for (is = 0; is < MAX_INSN_SIZE; ++is) - p->ainsn.insn[is] = tmp_insn[is]; - flush_insns(p->ainsn.insn, - sizeof(p->ainsn.insn[0]) * MAX_INSN_SIZE); - p->ainsn.insn_fn = (probes_insn_fn_t *) - ((uintptr_t)p->ainsn.insn | thumb); - break; - - case INSN_GOOD_NO_SLOT: /* instruction doesn't need insn slot */ - p->ainsn.insn = NULL; - break; - } - - return 0; -} - -void __kprobes arch_arm_kprobe(struct kprobe *p) -{ - unsigned int brkp; - void *addr; - - if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) { - /* Remove any Thumb flag */ - addr = (void *)((uintptr_t)p->addr & ~1); - - if (is_wide_instruction(p->opcode)) - brkp = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION; - else - brkp = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION; - } else { - kprobe_opcode_t insn = p->opcode; - - addr = p->addr; - brkp = KPROBE_ARM_BREAKPOINT_INSTRUCTION; - - if (insn >= 0xe0000000) - brkp |= 0xe0000000; /* Unconditional instruction */ - else - brkp |= insn & 0xf0000000; /* Copy condition from insn */ - } - - patch_text(addr, brkp); -} - -/* - * The actual disarming is done here on each CPU and synchronized using - * stop_machine. This synchronization is necessary on SMP to avoid removing - * a probe between the moment the 'Undefined Instruction' exception is raised - * and the moment the exception handler reads the faulting instruction from - * memory. It is also needed to atomically set the two half-words of a 32-bit - * Thumb breakpoint. - */ -int __kprobes __arch_disarm_kprobe(void *p) -{ - struct kprobe *kp = p; - void *addr = (void *)((uintptr_t)kp->addr & ~1); - - __patch_text(addr, kp->opcode); - - return 0; -} - -void __kprobes arch_disarm_kprobe(struct kprobe *p) -{ - stop_machine(__arch_disarm_kprobe, p, cpu_online_mask); -} - -void __kprobes arch_remove_kprobe(struct kprobe *p) -{ - if (p->ainsn.insn) { - free_insn_slot(p->ainsn.insn, 0); - p->ainsn.insn = NULL; - } -} - -static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) -{ - kcb->prev_kprobe.kp = kprobe_running(); - kcb->prev_kprobe.status = kcb->kprobe_status; -} - -static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) -{ - __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp); - kcb->kprobe_status = kcb->prev_kprobe.status; -} - -static void __kprobes set_current_kprobe(struct kprobe *p) -{ - __this_cpu_write(current_kprobe, p); -} - -static void __kprobes -singlestep_skip(struct kprobe *p, struct pt_regs *regs) -{ -#ifdef CONFIG_THUMB2_KERNEL - regs->ARM_cpsr = it_advance(regs->ARM_cpsr); - if (is_wide_instruction(p->opcode)) - regs->ARM_pc += 4; - else - regs->ARM_pc += 2; -#else - regs->ARM_pc += 4; -#endif -} - -static inline void __kprobes -singlestep(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb) -{ - p->ainsn.insn_singlestep(p->opcode, &p->ainsn, regs); -} - -/* - * Called with IRQs disabled. IRQs must remain disabled from that point - * all the way until processing this kprobe is complete. The current - * kprobes implementation cannot process more than one nested level of - * kprobe, and that level is reserved for user kprobe handlers, so we can't - * risk encountering a new kprobe in an interrupt handler. - */ -void __kprobes kprobe_handler(struct pt_regs *regs) -{ - struct kprobe *p, *cur; - struct kprobe_ctlblk *kcb; - - kcb = get_kprobe_ctlblk(); - cur = kprobe_running(); - -#ifdef CONFIG_THUMB2_KERNEL - /* - * First look for a probe which was registered using an address with - * bit 0 set, this is the usual situation for pointers to Thumb code. - * If not found, fallback to looking for one with bit 0 clear. - */ - p = get_kprobe((kprobe_opcode_t *)(regs->ARM_pc | 1)); - if (!p) - p = get_kprobe((kprobe_opcode_t *)regs->ARM_pc); - -#else /* ! CONFIG_THUMB2_KERNEL */ - p = get_kprobe((kprobe_opcode_t *)regs->ARM_pc); -#endif - - if (p) { - if (cur) { - /* Kprobe is pending, so we're recursing. */ - switch (kcb->kprobe_status) { - case KPROBE_HIT_ACTIVE: - case KPROBE_HIT_SSDONE: - /* A pre- or post-handler probe got us here. */ - kprobes_inc_nmissed_count(p); - save_previous_kprobe(kcb); - set_current_kprobe(p); - kcb->kprobe_status = KPROBE_REENTER; - singlestep(p, regs, kcb); - restore_previous_kprobe(kcb); - break; - default: - /* impossible cases */ - BUG(); - } - } else if (p->ainsn.insn_check_cc(regs->ARM_cpsr)) { - /* Probe hit and conditional execution check ok. */ - set_current_kprobe(p); - kcb->kprobe_status = KPROBE_HIT_ACTIVE; - - /* - * If we have no pre-handler or it returned 0, we - * continue with normal processing. If we have a - * pre-handler and it returned non-zero, it prepped - * for calling the break_handler below on re-entry, - * so get out doing nothing more here. - */ - if (!p->pre_handler || !p->pre_handler(p, regs)) { - kcb->kprobe_status = KPROBE_HIT_SS; - singlestep(p, regs, kcb); - if (p->post_handler) { - kcb->kprobe_status = KPROBE_HIT_SSDONE; - p->post_handler(p, regs, 0); - } - reset_current_kprobe(); - } - } else { - /* - * Probe hit but conditional execution check failed, - * so just skip the instruction and continue as if - * nothing had happened. - */ - singlestep_skip(p, regs); - } - } else if (cur) { - /* We probably hit a jprobe. Call its break handler. */ - if (cur->break_handler && cur->break_handler(cur, regs)) { - kcb->kprobe_status = KPROBE_HIT_SS; - singlestep(cur, regs, kcb); - if (cur->post_handler) { - kcb->kprobe_status = KPROBE_HIT_SSDONE; - cur->post_handler(cur, regs, 0); - } - } - reset_current_kprobe(); - } else { - /* - * The probe was removed and a race is in progress. - * There is nothing we can do about it. Let's restart - * the instruction. By the time we can restart, the - * real instruction will be there. - */ - } -} - -static int __kprobes kprobe_trap_handler(struct pt_regs *regs, unsigned int instr) -{ - unsigned long flags; - local_irq_save(flags); - kprobe_handler(regs); - local_irq_restore(flags); - return 0; -} - -int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr) -{ - struct kprobe *cur = kprobe_running(); - struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); - - switch (kcb->kprobe_status) { - case KPROBE_HIT_SS: - case KPROBE_REENTER: - /* - * We are here because the instruction being single - * stepped caused a page fault. We reset the current - * kprobe and the PC to point back to the probe address - * and allow the page fault handler to continue as a - * normal page fault. - */ - regs->ARM_pc = (long)cur->addr; - if (kcb->kprobe_status == KPROBE_REENTER) { - restore_previous_kprobe(kcb); - } else { - reset_current_kprobe(); - } - break; - - case KPROBE_HIT_ACTIVE: - case KPROBE_HIT_SSDONE: - /* - * We increment the nmissed count for accounting, - * we can also use npre/npostfault count for accounting - * these specific fault cases. - */ - kprobes_inc_nmissed_count(cur); - - /* - * We come here because instructions in the pre/post - * handler caused the page_fault, this could happen - * if handler tries to access user space by - * copy_from_user(), get_user() etc. Let the - * user-specified handler try to fix it. - */ - if (cur->fault_handler && cur->fault_handler(cur, regs, fsr)) - return 1; - break; - - default: - break; - } - - return 0; -} - -int __kprobes kprobe_exceptions_notify(struct notifier_block *self, - unsigned long val, void *data) -{ - /* - * notify_die() is currently never called on ARM, - * so this callback is currently empty. - */ - return NOTIFY_DONE; -} - -/* - * When a retprobed function returns, trampoline_handler() is called, - * calling the kretprobe's handler. We construct a struct pt_regs to - * give a view of registers r0-r11 to the user return-handler. This is - * not a complete pt_regs structure, but that should be plenty sufficient - * for kretprobe handlers which should normally be interested in r0 only - * anyway. - */ -void __naked __kprobes kretprobe_trampoline(void) -{ - __asm__ __volatile__ ( - "stmdb sp!, {r0 - r11} \n\t" - "mov r0, sp \n\t" - "bl trampoline_handler \n\t" - "mov lr, r0 \n\t" - "ldmia sp!, {r0 - r11} \n\t" -#ifdef CONFIG_THUMB2_KERNEL - "bx lr \n\t" -#else - "mov pc, lr \n\t" -#endif - : : : "memory"); -} - -/* Called from kretprobe_trampoline */ -static __used __kprobes void *trampoline_handler(struct pt_regs *regs) -{ - struct kretprobe_instance *ri = NULL; - struct hlist_head *head, empty_rp; - struct hlist_node *tmp; - unsigned long flags, orig_ret_address = 0; - unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; - - INIT_HLIST_HEAD(&empty_rp); - kretprobe_hash_lock(current, &head, &flags); - - /* - * It is possible to have multiple instances associated with a given - * task either because multiple functions in the call path have - * a return probe installed on them, and/or more than one return - * probe was registered for a target function. - * - * We can handle this because: - * - instances are always inserted at the head of the list - * - when multiple return probes are registered for the same - * function, the first instance's ret_addr will point to the - * real return address, and all the rest will point to - * kretprobe_trampoline - */ - hlist_for_each_entry_safe(ri, tmp, head, hlist) { - if (ri->task != current) - /* another task is sharing our hash bucket */ - continue; - - if (ri->rp && ri->rp->handler) { - __this_cpu_write(current_kprobe, &ri->rp->kp); - get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE; - ri->rp->handler(ri, regs); - __this_cpu_write(current_kprobe, NULL); - } - - orig_ret_address = (unsigned long)ri->ret_addr; - recycle_rp_inst(ri, &empty_rp); - - if (orig_ret_address != trampoline_address) - /* - * This is the real return address. Any other - * instances associated with this task are for - * other calls deeper on the call stack - */ - break; - } - - kretprobe_assert(ri, orig_ret_address, trampoline_address); - kretprobe_hash_unlock(current, &flags); - - hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) { - hlist_del(&ri->hlist); - kfree(ri); - } - - return (void *)orig_ret_address; -} - -void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, - struct pt_regs *regs) -{ - ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr; - - /* Replace the return addr with trampoline addr. */ - regs->ARM_lr = (unsigned long)&kretprobe_trampoline; -} - -int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) -{ - struct jprobe *jp = container_of(p, struct jprobe, kp); - struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); - long sp_addr = regs->ARM_sp; - long cpsr; - - kcb->jprobe_saved_regs = *regs; - memcpy(kcb->jprobes_stack, (void *)sp_addr, MIN_STACK_SIZE(sp_addr)); - regs->ARM_pc = (long)jp->entry; - - cpsr = regs->ARM_cpsr | PSR_I_BIT; -#ifdef CONFIG_THUMB2_KERNEL - /* Set correct Thumb state in cpsr */ - if (regs->ARM_pc & 1) - cpsr |= PSR_T_BIT; - else - cpsr &= ~PSR_T_BIT; -#endif - regs->ARM_cpsr = cpsr; - - preempt_disable(); - return 1; -} - -void __kprobes jprobe_return(void) -{ - struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); - - __asm__ __volatile__ ( - /* - * Setup an empty pt_regs. Fill SP and PC fields as - * they're needed by longjmp_break_handler. - * - * We allocate some slack between the original SP and start of - * our fabricated regs. To be precise we want to have worst case - * covered which is STMFD with all 16 regs so we allocate 2 * - * sizeof(struct_pt_regs)). - * - * This is to prevent any simulated instruction from writing - * over the regs when they are accessing the stack. - */ -#ifdef CONFIG_THUMB2_KERNEL - "sub r0, %0, %1 \n\t" - "mov sp, r0 \n\t" -#else - "sub sp, %0, %1 \n\t" -#endif - "ldr r0, ="__stringify(JPROBE_MAGIC_ADDR)"\n\t" - "str %0, [sp, %2] \n\t" - "str r0, [sp, %3] \n\t" - "mov r0, sp \n\t" - "bl kprobe_handler \n\t" - - /* - * Return to the context saved by setjmp_pre_handler - * and restored by longjmp_break_handler. - */ -#ifdef CONFIG_THUMB2_KERNEL - "ldr lr, [sp, %2] \n\t" /* lr = saved sp */ - "ldrd r0, r1, [sp, %5] \n\t" /* r0,r1 = saved lr,pc */ - "ldr r2, [sp, %4] \n\t" /* r2 = saved psr */ - "stmdb lr!, {r0, r1, r2} \n\t" /* push saved lr and */ - /* rfe context */ - "ldmia sp, {r0 - r12} \n\t" - "mov sp, lr \n\t" - "ldr lr, [sp], #4 \n\t" - "rfeia sp! \n\t" -#else - "ldr r0, [sp, %4] \n\t" - "msr cpsr_cxsf, r0 \n\t" - "ldmia sp, {r0 - pc} \n\t" -#endif - : - : "r" (kcb->jprobe_saved_regs.ARM_sp), - "I" (sizeof(struct pt_regs) * 2), - "J" (offsetof(struct pt_regs, ARM_sp)), - "J" (offsetof(struct pt_regs, ARM_pc)), - "J" (offsetof(struct pt_regs, ARM_cpsr)), - "J" (offsetof(struct pt_regs, ARM_lr)) - : "memory", "cc"); -} - -int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) -{ - struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); - long stack_addr = kcb->jprobe_saved_regs.ARM_sp; - long orig_sp = regs->ARM_sp; - struct jprobe *jp = container_of(p, struct jprobe, kp); - - if (regs->ARM_pc == JPROBE_MAGIC_ADDR) { - if (orig_sp != stack_addr) { - struct pt_regs *saved_regs = - (struct pt_regs *)kcb->jprobe_saved_regs.ARM_sp; - printk("current sp %lx does not match saved sp %lx\n", - orig_sp, stack_addr); - printk("Saved registers for jprobe %p\n", jp); - show_regs(saved_regs); - printk("Current registers\n"); - show_regs(regs); - BUG(); - } - *regs = kcb->jprobe_saved_regs; - memcpy((void *)stack_addr, kcb->jprobes_stack, - MIN_STACK_SIZE(stack_addr)); - preempt_enable_no_resched(); - return 1; - } - return 0; -} - -int __kprobes arch_trampoline_kprobe(struct kprobe *p) -{ - return 0; -} - -#ifdef CONFIG_THUMB2_KERNEL - -static struct undef_hook kprobes_thumb16_break_hook = { - .instr_mask = 0xffff, - .instr_val = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION, - .cpsr_mask = MODE_MASK, - .cpsr_val = SVC_MODE, - .fn = kprobe_trap_handler, -}; - -static struct undef_hook kprobes_thumb32_break_hook = { - .instr_mask = 0xffffffff, - .instr_val = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION, - .cpsr_mask = MODE_MASK, - .cpsr_val = SVC_MODE, - .fn = kprobe_trap_handler, -}; - -#else /* !CONFIG_THUMB2_KERNEL */ - -static struct undef_hook kprobes_arm_break_hook = { - .instr_mask = 0x0fffffff, - .instr_val = KPROBE_ARM_BREAKPOINT_INSTRUCTION, - .cpsr_mask = MODE_MASK, - .cpsr_val = SVC_MODE, - .fn = kprobe_trap_handler, -}; - -#endif /* !CONFIG_THUMB2_KERNEL */ - -int __init arch_init_kprobes() -{ - arm_probes_decode_init(); -#ifdef CONFIG_THUMB2_KERNEL - register_undef_hook(&kprobes_thumb16_break_hook); - register_undef_hook(&kprobes_thumb32_break_hook); -#else - register_undef_hook(&kprobes_arm_break_hook); -#endif - return 0; -} diff --git a/arch/arm/kernel/kprobes.h b/arch/arm/kernel/kprobes.h deleted file mode 100644 index 9a2712e..0000000 --- a/arch/arm/kernel/kprobes.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * arch/arm/kernel/kprobes.h - * - * Copyright (C) 2011 Jon Medhurst . - * - * Some contents moved here from arch/arm/include/asm/kprobes.h which is - * Copyright (C) 2006, 2007 Motorola 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. - * - * 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 _ARM_KERNEL_KPROBES_H -#define _ARM_KERNEL_KPROBES_H - -#include "probes.h" - -/* - * These undefined instructions must be unique and - * reserved solely for kprobes' use. - */ -#define KPROBE_ARM_BREAKPOINT_INSTRUCTION 0x07f001f8 -#define KPROBE_THUMB16_BREAKPOINT_INSTRUCTION 0xde18 -#define KPROBE_THUMB32_BREAKPOINT_INSTRUCTION 0xf7f0a018 - -enum probes_insn __kprobes -kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *h); - -typedef enum probes_insn (kprobe_decode_insn_t)(probes_opcode_t, - struct arch_probes_insn *, - bool, - const union decode_action *); - -#ifdef CONFIG_THUMB2_KERNEL - -extern const union decode_action kprobes_t32_actions[]; -extern const union decode_action kprobes_t16_actions[]; - -#else /* !CONFIG_THUMB2_KERNEL */ - -extern const union decode_action kprobes_arm_actions[]; - -#endif - -#endif /* _ARM_KERNEL_KPROBES_H */ diff --git a/arch/arm/kernel/patch.c b/arch/arm/kernel/patch.c index 5038960..69bda1a 100644 --- a/arch/arm/kernel/patch.c +++ b/arch/arm/kernel/patch.c @@ -8,8 +8,7 @@ #include #include #include - -#include "patch.h" +#include struct patch { void *addr; diff --git a/arch/arm/kernel/patch.h b/arch/arm/kernel/patch.h deleted file mode 100644 index 77e054c..0000000 --- a/arch/arm/kernel/patch.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _ARM_KERNEL_PATCH_H -#define _ARM_KERNEL_PATCH_H - -void patch_text(void *addr, unsigned int insn); -void __patch_text_real(void *addr, unsigned int insn, bool remap); - -static inline void __patch_text(void *addr, unsigned int insn) -{ - __patch_text_real(addr, insn, true); -} - -static inline void __patch_text_early(void *addr, unsigned int insn) -{ - __patch_text_real(addr, insn, false); -} - -#endif diff --git a/arch/arm/kernel/probes-arm.c b/arch/arm/kernel/probes-arm.c deleted file mode 100644 index 8eaef81..0000000 --- a/arch/arm/kernel/probes-arm.c +++ /dev/null @@ -1,734 +0,0 @@ -/* - * arch/arm/kernel/probes-arm.c - * - * Some code moved here from arch/arm/kernel/kprobes-arm.c - * - * Copyright (C) 2006, 2007 Motorola 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ - -#include -#include -#include -#include - -#include "probes.h" -#include "probes-arm.h" - -#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit))))) - -#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25) - -/* - * To avoid the complications of mimicing single-stepping on a - * processor without a Next-PC or a single-step mode, and to - * avoid having to deal with the side-effects of boosting, we - * simulate or emulate (almost) all ARM instructions. - * - * "Simulation" is where the instruction's behavior is duplicated in - * C code. "Emulation" is where the original instruction is rewritten - * and executed, often by altering its registers. - * - * By having all behavior of the kprobe'd instruction completed before - * returning from the kprobe_handler(), all locks (scheduler and - * interrupt) can safely be released. There is no need for secondary - * breakpoints, no race with MP or preemptable kernels, nor having to - * clean up resources counts at a later time impacting overall system - * performance. By rewriting the instruction, only the minimum registers - * need to be loaded and saved back optimizing performance. - * - * Calling the insnslot_*_rwflags version of a function doesn't hurt - * anything even when the CPSR flags aren't updated by the - * instruction. It's just a little slower in return for saving - * a little space by not having a duplicate function that doesn't - * update the flags. (The same optimization can be said for - * instructions that do or don't perform register writeback) - * Also, instructions can either read the flags, only write the - * flags, or read and write the flags. To save combinations - * rather than for sheer performance, flag functions just assume - * read and write of flags. - */ - -void __kprobes simulate_bbl(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - long iaddr = (long) regs->ARM_pc - 4; - int disp = branch_displacement(insn); - - if (insn & (1 << 24)) - regs->ARM_lr = iaddr + 4; - - regs->ARM_pc = iaddr + 8 + disp; -} - -void __kprobes simulate_blx1(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - long iaddr = (long) regs->ARM_pc - 4; - int disp = branch_displacement(insn); - - regs->ARM_lr = iaddr + 4; - regs->ARM_pc = iaddr + 8 + disp + ((insn >> 23) & 0x2); - regs->ARM_cpsr |= PSR_T_BIT; -} - -void __kprobes simulate_blx2bx(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rm = insn & 0xf; - long rmv = regs->uregs[rm]; - - if (insn & (1 << 5)) - regs->ARM_lr = (long) regs->ARM_pc; - - regs->ARM_pc = rmv & ~0x1; - regs->ARM_cpsr &= ~PSR_T_BIT; - if (rmv & 0x1) - regs->ARM_cpsr |= PSR_T_BIT; -} - -void __kprobes simulate_mrs(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rd = (insn >> 12) & 0xf; - unsigned long mask = 0xf8ff03df; /* Mask out execution state */ - regs->uregs[rd] = regs->ARM_cpsr & mask; -} - -void __kprobes simulate_mov_ipsp(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - regs->uregs[12] = regs->uregs[13]; -} - -/* - * For the instruction masking and comparisons in all the "space_*" - * functions below, Do _not_ rearrange the order of tests unless - * you're very, very sure of what you are doing. For the sake of - * efficiency, the masks for some tests sometimes assume other test - * have been done prior to them so the number of patterns to test - * for an instruction set can be as broad as possible to reduce the - * number of tests needed. - */ - -static const union decode_item arm_1111_table[] = { - /* Unconditional instructions */ - - /* memory hint 1111 0100 x001 xxxx xxxx xxxx xxxx xxxx */ - /* PLDI (immediate) 1111 0100 x101 xxxx xxxx xxxx xxxx xxxx */ - /* PLDW (immediate) 1111 0101 x001 xxxx xxxx xxxx xxxx xxxx */ - /* PLD (immediate) 1111 0101 x101 xxxx xxxx xxxx xxxx xxxx */ - DECODE_SIMULATE (0xfe300000, 0xf4100000, PROBES_PRELOAD_IMM), - - /* memory hint 1111 0110 x001 xxxx xxxx xxxx xxx0 xxxx */ - /* PLDI (register) 1111 0110 x101 xxxx xxxx xxxx xxx0 xxxx */ - /* PLDW (register) 1111 0111 x001 xxxx xxxx xxxx xxx0 xxxx */ - /* PLD (register) 1111 0111 x101 xxxx xxxx xxxx xxx0 xxxx */ - DECODE_SIMULATE (0xfe300010, 0xf6100000, PROBES_PRELOAD_REG), - - /* BLX (immediate) 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx */ - DECODE_SIMULATE (0xfe000000, 0xfa000000, PROBES_BRANCH_IMM), - - /* CPS 1111 0001 0000 xxx0 xxxx xxxx xx0x xxxx */ - /* SETEND 1111 0001 0000 0001 xxxx xxxx 0000 xxxx */ - /* SRS 1111 100x x1x0 xxxx xxxx xxxx xxxx xxxx */ - /* RFE 1111 100x x0x1 xxxx xxxx xxxx xxxx xxxx */ - - /* Coprocessor instructions... */ - /* MCRR2 1111 1100 0100 xxxx xxxx xxxx xxxx xxxx */ - /* MRRC2 1111 1100 0101 xxxx xxxx xxxx xxxx xxxx */ - /* LDC2 1111 110x xxx1 xxxx xxxx xxxx xxxx xxxx */ - /* STC2 1111 110x xxx0 xxxx xxxx xxxx xxxx xxxx */ - /* CDP2 1111 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */ - /* MCR2 1111 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */ - /* MRC2 1111 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */ - - /* Other unallocated instructions... */ - DECODE_END -}; - -static const union decode_item arm_cccc_0001_0xx0____0xxx_table[] = { - /* Miscellaneous instructions */ - - /* MRS cpsr cccc 0001 0000 xxxx xxxx xxxx 0000 xxxx */ - DECODE_SIMULATEX(0x0ff000f0, 0x01000000, PROBES_MRS, - REGS(0, NOPC, 0, 0, 0)), - - /* BX cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx */ - DECODE_SIMULATE (0x0ff000f0, 0x01200010, PROBES_BRANCH_REG), - - /* BLX (register) cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx */ - DECODE_SIMULATEX(0x0ff000f0, 0x01200030, PROBES_BRANCH_REG, - REGS(0, 0, 0, 0, NOPC)), - - /* CLZ cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx */ - DECODE_EMULATEX (0x0ff000f0, 0x01600010, PROBES_CLZ, - REGS(0, NOPC, 0, 0, NOPC)), - - /* QADD cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx */ - /* QSUB cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx */ - /* QDADD cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx */ - /* QDSUB cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx */ - DECODE_EMULATEX (0x0f9000f0, 0x01000050, PROBES_SATURATING_ARITHMETIC, - REGS(NOPC, NOPC, 0, 0, NOPC)), - - /* BXJ cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx */ - /* MSR cccc 0001 0x10 xxxx xxxx xxxx 0000 xxxx */ - /* MRS spsr cccc 0001 0100 xxxx xxxx xxxx 0000 xxxx */ - /* BKPT 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */ - /* SMC cccc 0001 0110 xxxx xxxx xxxx 0111 xxxx */ - /* And unallocated instructions... */ - DECODE_END -}; - -static const union decode_item arm_cccc_0001_0xx0____1xx0_table[] = { - /* Halfword multiply and multiply-accumulate */ - - /* SMLALxy cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx */ - DECODE_EMULATEX (0x0ff00090, 0x01400080, PROBES_MUL1, - REGS(NOPC, NOPC, NOPC, 0, NOPC)), - - /* SMULWy cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx */ - DECODE_OR (0x0ff000b0, 0x012000a0), - /* SMULxy cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx */ - DECODE_EMULATEX (0x0ff00090, 0x01600080, PROBES_MUL2, - REGS(NOPC, 0, NOPC, 0, NOPC)), - - /* SMLAxy cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx */ - DECODE_OR (0x0ff00090, 0x01000080), - /* SMLAWy cccc 0001 0010 xxxx xxxx xxxx 1x00 xxxx */ - DECODE_EMULATEX (0x0ff000b0, 0x01200080, PROBES_MUL2, - REGS(NOPC, NOPC, NOPC, 0, NOPC)), - - DECODE_END -}; - -static const union decode_item arm_cccc_0000_____1001_table[] = { - /* Multiply and multiply-accumulate */ - - /* MUL cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx */ - /* MULS cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx */ - DECODE_EMULATEX (0x0fe000f0, 0x00000090, PROBES_MUL2, - REGS(NOPC, 0, NOPC, 0, NOPC)), - - /* MLA cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx */ - /* MLAS cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx */ - DECODE_OR (0x0fe000f0, 0x00200090), - /* MLS cccc 0000 0110 xxxx xxxx xxxx 1001 xxxx */ - DECODE_EMULATEX (0x0ff000f0, 0x00600090, PROBES_MUL2, - REGS(NOPC, NOPC, NOPC, 0, NOPC)), - - /* UMAAL cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx */ - DECODE_OR (0x0ff000f0, 0x00400090), - /* UMULL cccc 0000 1000 xxxx xxxx xxxx 1001 xxxx */ - /* UMULLS cccc 0000 1001 xxxx xxxx xxxx 1001 xxxx */ - /* UMLAL cccc 0000 1010 xxxx xxxx xxxx 1001 xxxx */ - /* UMLALS cccc 0000 1011 xxxx xxxx xxxx 1001 xxxx */ - /* SMULL cccc 0000 1100 xxxx xxxx xxxx 1001 xxxx */ - /* SMULLS cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx */ - /* SMLAL cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx */ - /* SMLALS cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx */ - DECODE_EMULATEX (0x0f8000f0, 0x00800090, PROBES_MUL1, - REGS(NOPC, NOPC, NOPC, 0, NOPC)), - - DECODE_END -}; - -static const union decode_item arm_cccc_0001_____1001_table[] = { - /* Synchronization primitives */ - -#if __LINUX_ARM_ARCH__ < 6 - /* Deprecated on ARMv6 and may be UNDEFINED on v7 */ - /* SMP/SWPB cccc 0001 0x00 xxxx xxxx xxxx 1001 xxxx */ - DECODE_EMULATEX (0x0fb000f0, 0x01000090, PROBES_SWP, - REGS(NOPC, NOPC, 0, 0, NOPC)), -#endif - /* LDREX/STREX{,D,B,H} cccc 0001 1xxx xxxx xxxx xxxx 1001 xxxx */ - /* And unallocated instructions... */ - DECODE_END -}; - -static const union decode_item arm_cccc_000x_____1xx1_table[] = { - /* Extra load/store instructions */ - - /* STRHT cccc 0000 xx10 xxxx xxxx xxxx 1011 xxxx */ - /* ??? cccc 0000 xx10 xxxx xxxx xxxx 11x1 xxxx */ - /* LDRHT cccc 0000 xx11 xxxx xxxx xxxx 1011 xxxx */ - /* LDRSBT cccc 0000 xx11 xxxx xxxx xxxx 1101 xxxx */ - /* LDRSHT cccc 0000 xx11 xxxx xxxx xxxx 1111 xxxx */ - DECODE_REJECT (0x0f200090, 0x00200090), - - /* LDRD/STRD lr,pc,{... cccc 000x x0x0 xxxx 111x xxxx 1101 xxxx */ - DECODE_REJECT (0x0e10e0d0, 0x0000e0d0), - - /* LDRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1101 xxxx */ - /* STRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx */ - DECODE_EMULATEX (0x0e5000d0, 0x000000d0, PROBES_LDRSTRD, - REGS(NOPCWB, NOPCX, 0, 0, NOPC)), - - /* LDRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1101 xxxx */ - /* STRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1111 xxxx */ - DECODE_EMULATEX (0x0e5000d0, 0x004000d0, PROBES_LDRSTRD, - REGS(NOPCWB, NOPCX, 0, 0, 0)), - - /* STRH (register) cccc 000x x0x0 xxxx xxxx xxxx 1011 xxxx */ - DECODE_EMULATEX (0x0e5000f0, 0x000000b0, PROBES_STORE_EXTRA, - REGS(NOPCWB, NOPC, 0, 0, NOPC)), - - /* LDRH (register) cccc 000x x0x1 xxxx xxxx xxxx 1011 xxxx */ - /* LDRSB (register) cccc 000x x0x1 xxxx xxxx xxxx 1101 xxxx */ - /* LDRSH (register) cccc 000x x0x1 xxxx xxxx xxxx 1111 xxxx */ - DECODE_EMULATEX (0x0e500090, 0x00100090, PROBES_LOAD_EXTRA, - REGS(NOPCWB, NOPC, 0, 0, NOPC)), - - /* STRH (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1011 xxxx */ - DECODE_EMULATEX (0x0e5000f0, 0x004000b0, PROBES_STORE_EXTRA, - REGS(NOPCWB, NOPC, 0, 0, 0)), - - /* LDRH (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1011 xxxx */ - /* LDRSB (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1101 xxxx */ - /* LDRSH (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1111 xxxx */ - DECODE_EMULATEX (0x0e500090, 0x00500090, PROBES_LOAD_EXTRA, - REGS(NOPCWB, NOPC, 0, 0, 0)), - - DECODE_END -}; - -static const union decode_item arm_cccc_000x_table[] = { - /* Data-processing (register) */ - - /* S PC, ... cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx */ - DECODE_REJECT (0x0e10f000, 0x0010f000), - - /* MOV IP, SP 1110 0001 1010 0000 1100 0000 0000 1101 */ - DECODE_SIMULATE (0xffffffff, 0xe1a0c00d, PROBES_MOV_IP_SP), - - /* TST (register) cccc 0001 0001 xxxx xxxx xxxx xxx0 xxxx */ - /* TEQ (register) cccc 0001 0011 xxxx xxxx xxxx xxx0 xxxx */ - /* CMP (register) cccc 0001 0101 xxxx xxxx xxxx xxx0 xxxx */ - /* CMN (register) cccc 0001 0111 xxxx xxxx xxxx xxx0 xxxx */ - DECODE_EMULATEX (0x0f900010, 0x01100000, PROBES_DATA_PROCESSING_REG, - REGS(ANY, 0, 0, 0, ANY)), - - /* MOV (register) cccc 0001 101x xxxx xxxx xxxx xxx0 xxxx */ - /* MVN (register) cccc 0001 111x xxxx xxxx xxxx xxx0 xxxx */ - DECODE_EMULATEX (0x0fa00010, 0x01a00000, PROBES_DATA_PROCESSING_REG, - REGS(0, ANY, 0, 0, ANY)), - - /* AND (register) cccc 0000 000x xxxx xxxx xxxx xxx0 xxxx */ - /* EOR (register) cccc 0000 001x xxxx xxxx xxxx xxx0 xxxx */ - /* SUB (register) cccc 0000 010x xxxx xxxx xxxx xxx0 xxxx */ - /* RSB (register) cccc 0000 011x xxxx xxxx xxxx xxx0 xxxx */ - /* ADD (register) cccc 0000 100x xxxx xxxx xxxx xxx0 xxxx */ - /* ADC (register) cccc 0000 101x xxxx xxxx xxxx xxx0 xxxx */ - /* SBC (register) cccc 0000 110x xxxx xxxx xxxx xxx0 xxxx */ - /* RSC (register) cccc 0000 111x xxxx xxxx xxxx xxx0 xxxx */ - /* ORR (register) cccc 0001 100x xxxx xxxx xxxx xxx0 xxxx */ - /* BIC (register) cccc 0001 110x xxxx xxxx xxxx xxx0 xxxx */ - DECODE_EMULATEX (0x0e000010, 0x00000000, PROBES_DATA_PROCESSING_REG, - REGS(ANY, ANY, 0, 0, ANY)), - - /* TST (reg-shift reg) cccc 0001 0001 xxxx xxxx xxxx 0xx1 xxxx */ - /* TEQ (reg-shift reg) cccc 0001 0011 xxxx xxxx xxxx 0xx1 xxxx */ - /* CMP (reg-shift reg) cccc 0001 0101 xxxx xxxx xxxx 0xx1 xxxx */ - /* CMN (reg-shift reg) cccc 0001 0111 xxxx xxxx xxxx 0xx1 xxxx */ - DECODE_EMULATEX (0x0f900090, 0x01100010, PROBES_DATA_PROCESSING_REG, - REGS(NOPC, 0, NOPC, 0, NOPC)), - - /* MOV (reg-shift reg) cccc 0001 101x xxxx xxxx xxxx 0xx1 xxxx */ - /* MVN (reg-shift reg) cccc 0001 111x xxxx xxxx xxxx 0xx1 xxxx */ - DECODE_EMULATEX (0x0fa00090, 0x01a00010, PROBES_DATA_PROCESSING_REG, - REGS(0, NOPC, NOPC, 0, NOPC)), - - /* AND (reg-shift reg) cccc 0000 000x xxxx xxxx xxxx 0xx1 xxxx */ - /* EOR (reg-shift reg) cccc 0000 001x xxxx xxxx xxxx 0xx1 xxxx */ - /* SUB (reg-shift reg) cccc 0000 010x xxxx xxxx xxxx 0xx1 xxxx */ - /* RSB (reg-shift reg) cccc 0000 011x xxxx xxxx xxxx 0xx1 xxxx */ - /* ADD (reg-shift reg) cccc 0000 100x xxxx xxxx xxxx 0xx1 xxxx */ - /* ADC (reg-shift reg) cccc 0000 101x xxxx xxxx xxxx 0xx1 xxxx */ - /* SBC (reg-shift reg) cccc 0000 110x xxxx xxxx xxxx 0xx1 xxxx */ - /* RSC (reg-shift reg) cccc 0000 111x xxxx xxxx xxxx 0xx1 xxxx */ - /* ORR (reg-shift reg) cccc 0001 100x xxxx xxxx xxxx 0xx1 xxxx */ - /* BIC (reg-shift reg) cccc 0001 110x xxxx xxxx xxxx 0xx1 xxxx */ - DECODE_EMULATEX (0x0e000090, 0x00000010, PROBES_DATA_PROCESSING_REG, - REGS(NOPC, NOPC, NOPC, 0, NOPC)), - - DECODE_END -}; - -static const union decode_item arm_cccc_001x_table[] = { - /* Data-processing (immediate) */ - - /* MOVW cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */ - /* MOVT cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0fb00000, 0x03000000, PROBES_DATA_PROCESSING_IMM, - REGS(0, NOPC, 0, 0, 0)), - - /* YIELD cccc 0011 0010 0000 xxxx xxxx 0000 0001 */ - DECODE_OR (0x0fff00ff, 0x03200001), - /* SEV cccc 0011 0010 0000 xxxx xxxx 0000 0100 */ - DECODE_EMULATE (0x0fff00ff, 0x03200004, PROBES_EMULATE_NONE), - /* NOP cccc 0011 0010 0000 xxxx xxxx 0000 0000 */ - /* WFE cccc 0011 0010 0000 xxxx xxxx 0000 0010 */ - /* WFI cccc 0011 0010 0000 xxxx xxxx 0000 0011 */ - DECODE_SIMULATE (0x0fff00fc, 0x03200000, PROBES_SIMULATE_NOP), - /* DBG cccc 0011 0010 0000 xxxx xxxx ffff xxxx */ - /* unallocated hints cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */ - /* MSR (immediate) cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0x0fb00000, 0x03200000), - - /* S PC, ... cccc 001x xxx1 xxxx 1111 xxxx xxxx xxxx */ - DECODE_REJECT (0x0e10f000, 0x0210f000), - - /* TST (immediate) cccc 0011 0001 xxxx xxxx xxxx xxxx xxxx */ - /* TEQ (immediate) cccc 0011 0011 xxxx xxxx xxxx xxxx xxxx */ - /* CMP (immediate) cccc 0011 0101 xxxx xxxx xxxx xxxx xxxx */ - /* CMN (immediate) cccc 0011 0111 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0f900000, 0x03100000, PROBES_DATA_PROCESSING_IMM, - REGS(ANY, 0, 0, 0, 0)), - - /* MOV (immediate) cccc 0011 101x xxxx xxxx xxxx xxxx xxxx */ - /* MVN (immediate) cccc 0011 111x xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0fa00000, 0x03a00000, PROBES_DATA_PROCESSING_IMM, - REGS(0, ANY, 0, 0, 0)), - - /* AND (immediate) cccc 0010 000x xxxx xxxx xxxx xxxx xxxx */ - /* EOR (immediate) cccc 0010 001x xxxx xxxx xxxx xxxx xxxx */ - /* SUB (immediate) cccc 0010 010x xxxx xxxx xxxx xxxx xxxx */ - /* RSB (immediate) cccc 0010 011x xxxx xxxx xxxx xxxx xxxx */ - /* ADD (immediate) cccc 0010 100x xxxx xxxx xxxx xxxx xxxx */ - /* ADC (immediate) cccc 0010 101x xxxx xxxx xxxx xxxx xxxx */ - /* SBC (immediate) cccc 0010 110x xxxx xxxx xxxx xxxx xxxx */ - /* RSC (immediate) cccc 0010 111x xxxx xxxx xxxx xxxx xxxx */ - /* ORR (immediate) cccc 0011 100x xxxx xxxx xxxx xxxx xxxx */ - /* BIC (immediate) cccc 0011 110x xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0e000000, 0x02000000, PROBES_DATA_PROCESSING_IMM, - REGS(ANY, ANY, 0, 0, 0)), - - DECODE_END -}; - -static const union decode_item arm_cccc_0110_____xxx1_table[] = { - /* Media instructions */ - - /* SEL cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx */ - DECODE_EMULATEX (0x0ff000f0, 0x068000b0, PROBES_SATURATE, - REGS(NOPC, NOPC, 0, 0, NOPC)), - - /* SSAT cccc 0110 101x xxxx xxxx xxxx xx01 xxxx */ - /* USAT cccc 0110 111x xxxx xxxx xxxx xx01 xxxx */ - DECODE_OR(0x0fa00030, 0x06a00010), - /* SSAT16 cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx */ - /* USAT16 cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx */ - DECODE_EMULATEX (0x0fb000f0, 0x06a00030, PROBES_SATURATE, - REGS(0, NOPC, 0, 0, NOPC)), - - /* REV cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx */ - /* REV16 cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx */ - /* RBIT cccc 0110 1111 xxxx xxxx xxxx 0011 xxxx */ - /* REVSH cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx */ - DECODE_EMULATEX (0x0fb00070, 0x06b00030, PROBES_REV, - REGS(0, NOPC, 0, 0, NOPC)), - - /* ??? cccc 0110 0x00 xxxx xxxx xxxx xxx1 xxxx */ - DECODE_REJECT (0x0fb00010, 0x06000010), - /* ??? cccc 0110 0xxx xxxx xxxx xxxx 1011 xxxx */ - DECODE_REJECT (0x0f8000f0, 0x060000b0), - /* ??? cccc 0110 0xxx xxxx xxxx xxxx 1101 xxxx */ - DECODE_REJECT (0x0f8000f0, 0x060000d0), - /* SADD16 cccc 0110 0001 xxxx xxxx xxxx 0001 xxxx */ - /* SADDSUBX cccc 0110 0001 xxxx xxxx xxxx 0011 xxxx */ - /* SSUBADDX cccc 0110 0001 xxxx xxxx xxxx 0101 xxxx */ - /* SSUB16 cccc 0110 0001 xxxx xxxx xxxx 0111 xxxx */ - /* SADD8 cccc 0110 0001 xxxx xxxx xxxx 1001 xxxx */ - /* SSUB8 cccc 0110 0001 xxxx xxxx xxxx 1111 xxxx */ - /* QADD16 cccc 0110 0010 xxxx xxxx xxxx 0001 xxxx */ - /* QADDSUBX cccc 0110 0010 xxxx xxxx xxxx 0011 xxxx */ - /* QSUBADDX cccc 0110 0010 xxxx xxxx xxxx 0101 xxxx */ - /* QSUB16 cccc 0110 0010 xxxx xxxx xxxx 0111 xxxx */ - /* QADD8 cccc 0110 0010 xxxx xxxx xxxx 1001 xxxx */ - /* QSUB8 cccc 0110 0010 xxxx xxxx xxxx 1111 xxxx */ - /* SHADD16 cccc 0110 0011 xxxx xxxx xxxx 0001 xxxx */ - /* SHADDSUBX cccc 0110 0011 xxxx xxxx xxxx 0011 xxxx */ - /* SHSUBADDX cccc 0110 0011 xxxx xxxx xxxx 0101 xxxx */ - /* SHSUB16 cccc 0110 0011 xxxx xxxx xxxx 0111 xxxx */ - /* SHADD8 cccc 0110 0011 xxxx xxxx xxxx 1001 xxxx */ - /* SHSUB8 cccc 0110 0011 xxxx xxxx xxxx 1111 xxxx */ - /* UADD16 cccc 0110 0101 xxxx xxxx xxxx 0001 xxxx */ - /* UADDSUBX cccc 0110 0101 xxxx xxxx xxxx 0011 xxxx */ - /* USUBADDX cccc 0110 0101 xxxx xxxx xxxx 0101 xxxx */ - /* USUB16 cccc 0110 0101 xxxx xxxx xxxx 0111 xxxx */ - /* UADD8 cccc 0110 0101 xxxx xxxx xxxx 1001 xxxx */ - /* USUB8 cccc 0110 0101 xxxx xxxx xxxx 1111 xxxx */ - /* UQADD16 cccc 0110 0110 xxxx xxxx xxxx 0001 xxxx */ - /* UQADDSUBX cccc 0110 0110 xxxx xxxx xxxx 0011 xxxx */ - /* UQSUBADDX cccc 0110 0110 xxxx xxxx xxxx 0101 xxxx */ - /* UQSUB16 cccc 0110 0110 xxxx xxxx xxxx 0111 xxxx */ - /* UQADD8 cccc 0110 0110 xxxx xxxx xxxx 1001 xxxx */ - /* UQSUB8 cccc 0110 0110 xxxx xxxx xxxx 1111 xxxx */ - /* UHADD16 cccc 0110 0111 xxxx xxxx xxxx 0001 xxxx */ - /* UHADDSUBX cccc 0110 0111 xxxx xxxx xxxx 0011 xxxx */ - /* UHSUBADDX cccc 0110 0111 xxxx xxxx xxxx 0101 xxxx */ - /* UHSUB16 cccc 0110 0111 xxxx xxxx xxxx 0111 xxxx */ - /* UHADD8 cccc 0110 0111 xxxx xxxx xxxx 1001 xxxx */ - /* UHSUB8 cccc 0110 0111 xxxx xxxx xxxx 1111 xxxx */ - DECODE_EMULATEX (0x0f800010, 0x06000010, PROBES_MMI, - REGS(NOPC, NOPC, 0, 0, NOPC)), - - /* PKHBT cccc 0110 1000 xxxx xxxx xxxx x001 xxxx */ - /* PKHTB cccc 0110 1000 xxxx xxxx xxxx x101 xxxx */ - DECODE_EMULATEX (0x0ff00030, 0x06800010, PROBES_PACK, - REGS(NOPC, NOPC, 0, 0, NOPC)), - - /* ??? cccc 0110 1001 xxxx xxxx xxxx 0111 xxxx */ - /* ??? cccc 0110 1101 xxxx xxxx xxxx 0111 xxxx */ - DECODE_REJECT (0x0fb000f0, 0x06900070), - - /* SXTB16 cccc 0110 1000 1111 xxxx xxxx 0111 xxxx */ - /* SXTB cccc 0110 1010 1111 xxxx xxxx 0111 xxxx */ - /* SXTH cccc 0110 1011 1111 xxxx xxxx 0111 xxxx */ - /* UXTB16 cccc 0110 1100 1111 xxxx xxxx 0111 xxxx */ - /* UXTB cccc 0110 1110 1111 xxxx xxxx 0111 xxxx */ - /* UXTH cccc 0110 1111 1111 xxxx xxxx 0111 xxxx */ - DECODE_EMULATEX (0x0f8f00f0, 0x068f0070, PROBES_EXTEND, - REGS(0, NOPC, 0, 0, NOPC)), - - /* SXTAB16 cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx */ - /* SXTAB cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx */ - /* SXTAH cccc 0110 1011 xxxx xxxx xxxx 0111 xxxx */ - /* UXTAB16 cccc 0110 1100 xxxx xxxx xxxx 0111 xxxx */ - /* UXTAB cccc 0110 1110 xxxx xxxx xxxx 0111 xxxx */ - /* UXTAH cccc 0110 1111 xxxx xxxx xxxx 0111 xxxx */ - DECODE_EMULATEX (0x0f8000f0, 0x06800070, PROBES_EXTEND_ADD, - REGS(NOPCX, NOPC, 0, 0, NOPC)), - - DECODE_END -}; - -static const union decode_item arm_cccc_0111_____xxx1_table[] = { - /* Media instructions */ - - /* UNDEFINED cccc 0111 1111 xxxx xxxx xxxx 1111 xxxx */ - DECODE_REJECT (0x0ff000f0, 0x07f000f0), - - /* SMLALD cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx */ - /* SMLSLD cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx */ - DECODE_EMULATEX (0x0ff00090, 0x07400010, PROBES_MUL_ADD_LONG, - REGS(NOPC, NOPC, NOPC, 0, NOPC)), - - /* SMUAD cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx */ - /* SMUSD cccc 0111 0000 xxxx 1111 xxxx 01x1 xxxx */ - DECODE_OR (0x0ff0f090, 0x0700f010), - /* SMMUL cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx */ - DECODE_OR (0x0ff0f0d0, 0x0750f010), - /* USAD8 cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx */ - DECODE_EMULATEX (0x0ff0f0f0, 0x0780f010, PROBES_MUL_ADD, - REGS(NOPC, 0, NOPC, 0, NOPC)), - - /* SMLAD cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx */ - /* SMLSD cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx */ - DECODE_OR (0x0ff00090, 0x07000010), - /* SMMLA cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx */ - DECODE_OR (0x0ff000d0, 0x07500010), - /* USADA8 cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx */ - DECODE_EMULATEX (0x0ff000f0, 0x07800010, PROBES_MUL_ADD, - REGS(NOPC, NOPCX, NOPC, 0, NOPC)), - - /* SMMLS cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx */ - DECODE_EMULATEX (0x0ff000d0, 0x075000d0, PROBES_MUL_ADD, - REGS(NOPC, NOPC, NOPC, 0, NOPC)), - - /* SBFX cccc 0111 101x xxxx xxxx xxxx x101 xxxx */ - /* UBFX cccc 0111 111x xxxx xxxx xxxx x101 xxxx */ - DECODE_EMULATEX (0x0fa00070, 0x07a00050, PROBES_BITFIELD, - REGS(0, NOPC, 0, 0, NOPC)), - - /* BFC cccc 0111 110x xxxx xxxx xxxx x001 1111 */ - DECODE_EMULATEX (0x0fe0007f, 0x07c0001f, PROBES_BITFIELD, - REGS(0, NOPC, 0, 0, 0)), - - /* BFI cccc 0111 110x xxxx xxxx xxxx x001 xxxx */ - DECODE_EMULATEX (0x0fe00070, 0x07c00010, PROBES_BITFIELD, - REGS(0, NOPC, 0, 0, NOPCX)), - - DECODE_END -}; - -static const union decode_item arm_cccc_01xx_table[] = { - /* Load/store word and unsigned byte */ - - /* LDRB/STRB pc,[...] cccc 01xx x0xx xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0x0c40f000, 0x0440f000), - - /* STRT cccc 01x0 x010 xxxx xxxx xxxx xxxx xxxx */ - /* LDRT cccc 01x0 x011 xxxx xxxx xxxx xxxx xxxx */ - /* STRBT cccc 01x0 x110 xxxx xxxx xxxx xxxx xxxx */ - /* LDRBT cccc 01x0 x111 xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0x0d200000, 0x04200000), - - /* STR (immediate) cccc 010x x0x0 xxxx xxxx xxxx xxxx xxxx */ - /* STRB (immediate) cccc 010x x1x0 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0e100000, 0x04000000, PROBES_STORE, - REGS(NOPCWB, ANY, 0, 0, 0)), - - /* LDR (immediate) cccc 010x x0x1 xxxx xxxx xxxx xxxx xxxx */ - /* LDRB (immediate) cccc 010x x1x1 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0e100000, 0x04100000, PROBES_LOAD, - REGS(NOPCWB, ANY, 0, 0, 0)), - - /* STR (register) cccc 011x x0x0 xxxx xxxx xxxx xxxx xxxx */ - /* STRB (register) cccc 011x x1x0 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0e100000, 0x06000000, PROBES_STORE, - REGS(NOPCWB, ANY, 0, 0, NOPC)), - - /* LDR (register) cccc 011x x0x1 xxxx xxxx xxxx xxxx xxxx */ - /* LDRB (register) cccc 011x x1x1 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0e100000, 0x06100000, PROBES_LOAD, - REGS(NOPCWB, ANY, 0, 0, NOPC)), - - DECODE_END -}; - -static const union decode_item arm_cccc_100x_table[] = { - /* Block data transfer instructions */ - - /* LDM cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx */ - /* STM cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx */ - DECODE_CUSTOM (0x0e400000, 0x08000000, PROBES_LDMSTM), - - /* STM (user registers) cccc 100x x1x0 xxxx xxxx xxxx xxxx xxxx */ - /* LDM (user registers) cccc 100x x1x1 xxxx 0xxx xxxx xxxx xxxx */ - /* LDM (exception ret) cccc 100x x1x1 xxxx 1xxx xxxx xxxx xxxx */ - DECODE_END -}; - -const union decode_item probes_decode_arm_table[] = { - /* - * Unconditional instructions - * 1111 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xf0000000, 0xf0000000, arm_1111_table), - - /* - * Miscellaneous instructions - * cccc 0001 0xx0 xxxx xxxx xxxx 0xxx xxxx - */ - DECODE_TABLE (0x0f900080, 0x01000000, arm_cccc_0001_0xx0____0xxx_table), - - /* - * Halfword multiply and multiply-accumulate - * cccc 0001 0xx0 xxxx xxxx xxxx 1xx0 xxxx - */ - DECODE_TABLE (0x0f900090, 0x01000080, arm_cccc_0001_0xx0____1xx0_table), - - /* - * Multiply and multiply-accumulate - * cccc 0000 xxxx xxxx xxxx xxxx 1001 xxxx - */ - DECODE_TABLE (0x0f0000f0, 0x00000090, arm_cccc_0000_____1001_table), - - /* - * Synchronization primitives - * cccc 0001 xxxx xxxx xxxx xxxx 1001 xxxx - */ - DECODE_TABLE (0x0f0000f0, 0x01000090, arm_cccc_0001_____1001_table), - - /* - * Extra load/store instructions - * cccc 000x xxxx xxxx xxxx xxxx 1xx1 xxxx - */ - DECODE_TABLE (0x0e000090, 0x00000090, arm_cccc_000x_____1xx1_table), - - /* - * Data-processing (register) - * cccc 000x xxxx xxxx xxxx xxxx xxx0 xxxx - * Data-processing (register-shifted register) - * cccc 000x xxxx xxxx xxxx xxxx 0xx1 xxxx - */ - DECODE_TABLE (0x0e000000, 0x00000000, arm_cccc_000x_table), - - /* - * Data-processing (immediate) - * cccc 001x xxxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0x0e000000, 0x02000000, arm_cccc_001x_table), - - /* - * Media instructions - * cccc 011x xxxx xxxx xxxx xxxx xxx1 xxxx - */ - DECODE_TABLE (0x0f000010, 0x06000010, arm_cccc_0110_____xxx1_table), - DECODE_TABLE (0x0f000010, 0x07000010, arm_cccc_0111_____xxx1_table), - - /* - * Load/store word and unsigned byte - * cccc 01xx xxxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0x0c000000, 0x04000000, arm_cccc_01xx_table), - - /* - * Block data transfer instructions - * cccc 100x xxxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0x0e000000, 0x08000000, arm_cccc_100x_table), - - /* B cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx */ - /* BL cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx */ - DECODE_SIMULATE (0x0e000000, 0x0a000000, PROBES_BRANCH), - - /* - * Supervisor Call, and coprocessor instructions - */ - - /* MCRR cccc 1100 0100 xxxx xxxx xxxx xxxx xxxx */ - /* MRRC cccc 1100 0101 xxxx xxxx xxxx xxxx xxxx */ - /* LDC cccc 110x xxx1 xxxx xxxx xxxx xxxx xxxx */ - /* STC cccc 110x xxx0 xxxx xxxx xxxx xxxx xxxx */ - /* CDP cccc 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */ - /* MCR cccc 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */ - /* MRC cccc 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */ - /* SVC cccc 1111 xxxx xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0x0c000000, 0x0c000000), - - DECODE_END -}; -#ifdef CONFIG_ARM_KPROBES_TEST_MODULE -EXPORT_SYMBOL_GPL(probes_decode_arm_table); -#endif - -static void __kprobes arm_singlestep(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - regs->ARM_pc += 4; - asi->insn_handler(insn, asi, regs); -} - -/* Return: - * INSN_REJECTED If instruction is one not allowed to kprobe, - * INSN_GOOD If instruction is supported and uses instruction slot, - * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot. - * - * For instructions we don't want to kprobe (INSN_REJECTED return result): - * These are generally ones that modify the processor state making - * them "hard" to simulate such as switches processor modes or - * make accesses in alternate modes. Any of these could be simulated - * if the work was put into it, but low return considering they - * should also be very rare. - */ -enum probes_insn __kprobes -arm_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions) -{ - asi->insn_singlestep = arm_singlestep; - asi->insn_check_cc = probes_condition_checks[insn>>28]; - return probes_decode_insn(insn, asi, probes_decode_arm_table, false, - emulate, actions); -} diff --git a/arch/arm/kernel/probes-arm.h b/arch/arm/kernel/probes-arm.h deleted file mode 100644 index ace6572..0000000 --- a/arch/arm/kernel/probes-arm.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * arch/arm/kernel/probes-arm.h - * - * Copyright 2013 Linaro Ltd. - * Written by: David A. Long - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#ifndef _ARM_KERNEL_PROBES_ARM_H -#define _ARM_KERNEL_PROBES_ARM_H - -enum probes_arm_action { - PROBES_EMULATE_NONE, - PROBES_SIMULATE_NOP, - PROBES_PRELOAD_IMM, - PROBES_PRELOAD_REG, - PROBES_BRANCH_IMM, - PROBES_BRANCH_REG, - PROBES_MRS, - PROBES_CLZ, - PROBES_SATURATING_ARITHMETIC, - PROBES_MUL1, - PROBES_MUL2, - PROBES_SWP, - PROBES_LDRSTRD, - PROBES_LOAD, - PROBES_STORE, - PROBES_LOAD_EXTRA, - PROBES_STORE_EXTRA, - PROBES_MOV_IP_SP, - PROBES_DATA_PROCESSING_REG, - PROBES_DATA_PROCESSING_IMM, - PROBES_MOV_HALFWORD, - PROBES_SEV, - PROBES_WFE, - PROBES_SATURATE, - PROBES_REV, - PROBES_MMI, - PROBES_PACK, - PROBES_EXTEND, - PROBES_EXTEND_ADD, - PROBES_MUL_ADD_LONG, - PROBES_MUL_ADD, - PROBES_BITFIELD, - PROBES_BRANCH, - PROBES_LDMSTM, - NUM_PROBES_ARM_ACTIONS -}; - -void __kprobes simulate_bbl(probes_opcode_t opcode, - struct arch_probes_insn *asi, struct pt_regs *regs); -void __kprobes simulate_blx1(probes_opcode_t opcode, - struct arch_probes_insn *asi, struct pt_regs *regs); -void __kprobes simulate_blx2bx(probes_opcode_t opcode, - struct arch_probes_insn *asi, struct pt_regs *regs); -void __kprobes simulate_mrs(probes_opcode_t opcode, - struct arch_probes_insn *asi, struct pt_regs *regs); -void __kprobes simulate_mov_ipsp(probes_opcode_t opcode, - struct arch_probes_insn *asi, struct pt_regs *regs); - -extern const union decode_item probes_decode_arm_table[]; - -enum probes_insn arm_probes_decode_insn(probes_opcode_t, - struct arch_probes_insn *, bool emulate, - const union decode_action *actions); - -#endif diff --git a/arch/arm/kernel/probes-thumb.c b/arch/arm/kernel/probes-thumb.c deleted file mode 100644 index 4131351..0000000 --- a/arch/arm/kernel/probes-thumb.c +++ /dev/null @@ -1,882 +0,0 @@ -/* - * arch/arm/kernel/probes-thumb.c - * - * Copyright (C) 2011 Jon Medhurst . - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include - -#include "probes.h" -#include "probes-thumb.h" - - -static const union decode_item t32_table_1110_100x_x0xx[] = { - /* Load/store multiple instructions */ - - /* Rn is PC 1110 100x x0xx 1111 xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfe4f0000, 0xe80f0000), - - /* SRS 1110 1000 00x0 xxxx xxxx xxxx xxxx xxxx */ - /* RFE 1110 1000 00x1 xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xffc00000, 0xe8000000), - /* SRS 1110 1001 10x0 xxxx xxxx xxxx xxxx xxxx */ - /* RFE 1110 1001 10x1 xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xffc00000, 0xe9800000), - - /* STM Rn, {...pc} 1110 100x x0x0 xxxx 1xxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfe508000, 0xe8008000), - /* LDM Rn, {...lr,pc} 1110 100x x0x1 xxxx 11xx xxxx xxxx xxxx */ - DECODE_REJECT (0xfe50c000, 0xe810c000), - /* LDM/STM Rn, {...sp} 1110 100x x0xx xxxx xx1x xxxx xxxx xxxx */ - DECODE_REJECT (0xfe402000, 0xe8002000), - - /* STMIA 1110 1000 10x0 xxxx xxxx xxxx xxxx xxxx */ - /* LDMIA 1110 1000 10x1 xxxx xxxx xxxx xxxx xxxx */ - /* STMDB 1110 1001 00x0 xxxx xxxx xxxx xxxx xxxx */ - /* LDMDB 1110 1001 00x1 xxxx xxxx xxxx xxxx xxxx */ - DECODE_CUSTOM (0xfe400000, 0xe8000000, PROBES_T32_LDMSTM), - - DECODE_END -}; - -static const union decode_item t32_table_1110_100x_x1xx[] = { - /* Load/store dual, load/store exclusive, table branch */ - - /* STRD (immediate) 1110 1000 x110 xxxx xxxx xxxx xxxx xxxx */ - /* LDRD (immediate) 1110 1000 x111 xxxx xxxx xxxx xxxx xxxx */ - DECODE_OR (0xff600000, 0xe8600000), - /* STRD (immediate) 1110 1001 x1x0 xxxx xxxx xxxx xxxx xxxx */ - /* LDRD (immediate) 1110 1001 x1x1 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xff400000, 0xe9400000, PROBES_T32_LDRDSTRD, - REGS(NOPCWB, NOSPPC, NOSPPC, 0, 0)), - - /* TBB 1110 1000 1101 xxxx xxxx xxxx 0000 xxxx */ - /* TBH 1110 1000 1101 xxxx xxxx xxxx 0001 xxxx */ - DECODE_SIMULATEX(0xfff000e0, 0xe8d00000, PROBES_T32_TABLE_BRANCH, - REGS(NOSP, 0, 0, 0, NOSPPC)), - - /* STREX 1110 1000 0100 xxxx xxxx xxxx xxxx xxxx */ - /* LDREX 1110 1000 0101 xxxx xxxx xxxx xxxx xxxx */ - /* STREXB 1110 1000 1100 xxxx xxxx xxxx 0100 xxxx */ - /* STREXH 1110 1000 1100 xxxx xxxx xxxx 0101 xxxx */ - /* STREXD 1110 1000 1100 xxxx xxxx xxxx 0111 xxxx */ - /* LDREXB 1110 1000 1101 xxxx xxxx xxxx 0100 xxxx */ - /* LDREXH 1110 1000 1101 xxxx xxxx xxxx 0101 xxxx */ - /* LDREXD 1110 1000 1101 xxxx xxxx xxxx 0111 xxxx */ - /* And unallocated instructions... */ - DECODE_END -}; - -static const union decode_item t32_table_1110_101x[] = { - /* Data-processing (shifted register) */ - - /* TST 1110 1010 0001 xxxx xxxx 1111 xxxx xxxx */ - /* TEQ 1110 1010 1001 xxxx xxxx 1111 xxxx xxxx */ - DECODE_EMULATEX (0xff700f00, 0xea100f00, PROBES_T32_TST, - REGS(NOSPPC, 0, 0, 0, NOSPPC)), - - /* CMN 1110 1011 0001 xxxx xxxx 1111 xxxx xxxx */ - DECODE_OR (0xfff00f00, 0xeb100f00), - /* CMP 1110 1011 1011 xxxx xxxx 1111 xxxx xxxx */ - DECODE_EMULATEX (0xfff00f00, 0xebb00f00, PROBES_T32_TST, - REGS(NOPC, 0, 0, 0, NOSPPC)), - - /* MOV 1110 1010 010x 1111 xxxx xxxx xxxx xxxx */ - /* MVN 1110 1010 011x 1111 xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xffcf0000, 0xea4f0000, PROBES_T32_MOV, - REGS(0, 0, NOSPPC, 0, NOSPPC)), - - /* ??? 1110 1010 101x xxxx xxxx xxxx xxxx xxxx */ - /* ??? 1110 1010 111x xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xffa00000, 0xeaa00000), - /* ??? 1110 1011 001x xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xffe00000, 0xeb200000), - /* ??? 1110 1011 100x xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xffe00000, 0xeb800000), - /* ??? 1110 1011 111x xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xffe00000, 0xebe00000), - - /* ADD/SUB SP, SP, Rm, LSL #0..3 */ - /* 1110 1011 x0xx 1101 x000 1101 xx00 xxxx */ - DECODE_EMULATEX (0xff4f7f30, 0xeb0d0d00, PROBES_T32_ADDSUB, - REGS(SP, 0, SP, 0, NOSPPC)), - - /* ADD/SUB SP, SP, Rm, shift */ - /* 1110 1011 x0xx 1101 xxxx 1101 xxxx xxxx */ - DECODE_REJECT (0xff4f0f00, 0xeb0d0d00), - - /* ADD/SUB Rd, SP, Rm, shift */ - /* 1110 1011 x0xx 1101 xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xff4f0000, 0xeb0d0000, PROBES_T32_ADDSUB, - REGS(SP, 0, NOPC, 0, NOSPPC)), - - /* AND 1110 1010 000x xxxx xxxx xxxx xxxx xxxx */ - /* BIC 1110 1010 001x xxxx xxxx xxxx xxxx xxxx */ - /* ORR 1110 1010 010x xxxx xxxx xxxx xxxx xxxx */ - /* ORN 1110 1010 011x xxxx xxxx xxxx xxxx xxxx */ - /* EOR 1110 1010 100x xxxx xxxx xxxx xxxx xxxx */ - /* PKH 1110 1010 110x xxxx xxxx xxxx xxxx xxxx */ - /* ADD 1110 1011 000x xxxx xxxx xxxx xxxx xxxx */ - /* ADC 1110 1011 010x xxxx xxxx xxxx xxxx xxxx */ - /* SBC 1110 1011 011x xxxx xxxx xxxx xxxx xxxx */ - /* SUB 1110 1011 101x xxxx xxxx xxxx xxxx xxxx */ - /* RSB 1110 1011 110x xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfe000000, 0xea000000, PROBES_T32_LOGICAL, - REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)), - - DECODE_END -}; - -static const union decode_item t32_table_1111_0x0x___0[] = { - /* Data-processing (modified immediate) */ - - /* TST 1111 0x00 0001 xxxx 0xxx 1111 xxxx xxxx */ - /* TEQ 1111 0x00 1001 xxxx 0xxx 1111 xxxx xxxx */ - DECODE_EMULATEX (0xfb708f00, 0xf0100f00, PROBES_T32_TST, - REGS(NOSPPC, 0, 0, 0, 0)), - - /* CMN 1111 0x01 0001 xxxx 0xxx 1111 xxxx xxxx */ - DECODE_OR (0xfbf08f00, 0xf1100f00), - /* CMP 1111 0x01 1011 xxxx 0xxx 1111 xxxx xxxx */ - DECODE_EMULATEX (0xfbf08f00, 0xf1b00f00, PROBES_T32_CMP, - REGS(NOPC, 0, 0, 0, 0)), - - /* MOV 1111 0x00 010x 1111 0xxx xxxx xxxx xxxx */ - /* MVN 1111 0x00 011x 1111 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfbcf8000, 0xf04f0000, PROBES_T32_MOV, - REGS(0, 0, NOSPPC, 0, 0)), - - /* ??? 1111 0x00 101x xxxx 0xxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfbe08000, 0xf0a00000), - /* ??? 1111 0x00 110x xxxx 0xxx xxxx xxxx xxxx */ - /* ??? 1111 0x00 111x xxxx 0xxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfbc08000, 0xf0c00000), - /* ??? 1111 0x01 001x xxxx 0xxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfbe08000, 0xf1200000), - /* ??? 1111 0x01 100x xxxx 0xxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfbe08000, 0xf1800000), - /* ??? 1111 0x01 111x xxxx 0xxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfbe08000, 0xf1e00000), - - /* ADD Rd, SP, #imm 1111 0x01 000x 1101 0xxx xxxx xxxx xxxx */ - /* SUB Rd, SP, #imm 1111 0x01 101x 1101 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfb4f8000, 0xf10d0000, PROBES_T32_ADDSUB, - REGS(SP, 0, NOPC, 0, 0)), - - /* AND 1111 0x00 000x xxxx 0xxx xxxx xxxx xxxx */ - /* BIC 1111 0x00 001x xxxx 0xxx xxxx xxxx xxxx */ - /* ORR 1111 0x00 010x xxxx 0xxx xxxx xxxx xxxx */ - /* ORN 1111 0x00 011x xxxx 0xxx xxxx xxxx xxxx */ - /* EOR 1111 0x00 100x xxxx 0xxx xxxx xxxx xxxx */ - /* ADD 1111 0x01 000x xxxx 0xxx xxxx xxxx xxxx */ - /* ADC 1111 0x01 010x xxxx 0xxx xxxx xxxx xxxx */ - /* SBC 1111 0x01 011x xxxx 0xxx xxxx xxxx xxxx */ - /* SUB 1111 0x01 101x xxxx 0xxx xxxx xxxx xxxx */ - /* RSB 1111 0x01 110x xxxx 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfa008000, 0xf0000000, PROBES_T32_LOGICAL, - REGS(NOSPPC, 0, NOSPPC, 0, 0)), - - DECODE_END -}; - -static const union decode_item t32_table_1111_0x1x___0[] = { - /* Data-processing (plain binary immediate) */ - - /* ADDW Rd, PC, #imm 1111 0x10 0000 1111 0xxx xxxx xxxx xxxx */ - DECODE_OR (0xfbff8000, 0xf20f0000), - /* SUBW Rd, PC, #imm 1111 0x10 1010 1111 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfbff8000, 0xf2af0000, PROBES_T32_ADDWSUBW_PC, - REGS(PC, 0, NOSPPC, 0, 0)), - - /* ADDW SP, SP, #imm 1111 0x10 0000 1101 0xxx 1101 xxxx xxxx */ - DECODE_OR (0xfbff8f00, 0xf20d0d00), - /* SUBW SP, SP, #imm 1111 0x10 1010 1101 0xxx 1101 xxxx xxxx */ - DECODE_EMULATEX (0xfbff8f00, 0xf2ad0d00, PROBES_T32_ADDWSUBW, - REGS(SP, 0, SP, 0, 0)), - - /* ADDW 1111 0x10 0000 xxxx 0xxx xxxx xxxx xxxx */ - DECODE_OR (0xfbf08000, 0xf2000000), - /* SUBW 1111 0x10 1010 xxxx 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfbf08000, 0xf2a00000, PROBES_T32_ADDWSUBW, - REGS(NOPCX, 0, NOSPPC, 0, 0)), - - /* MOVW 1111 0x10 0100 xxxx 0xxx xxxx xxxx xxxx */ - /* MOVT 1111 0x10 1100 xxxx 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfb708000, 0xf2400000, PROBES_T32_MOVW, - REGS(0, 0, NOSPPC, 0, 0)), - - /* SSAT16 1111 0x11 0010 xxxx 0000 xxxx 00xx xxxx */ - /* SSAT 1111 0x11 00x0 xxxx 0xxx xxxx xxxx xxxx */ - /* USAT16 1111 0x11 1010 xxxx 0000 xxxx 00xx xxxx */ - /* USAT 1111 0x11 10x0 xxxx 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfb508000, 0xf3000000, PROBES_T32_SAT, - REGS(NOSPPC, 0, NOSPPC, 0, 0)), - - /* SFBX 1111 0x11 0100 xxxx 0xxx xxxx xxxx xxxx */ - /* UFBX 1111 0x11 1100 xxxx 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfb708000, 0xf3400000, PROBES_T32_BITFIELD, - REGS(NOSPPC, 0, NOSPPC, 0, 0)), - - /* BFC 1111 0x11 0110 1111 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfbff8000, 0xf36f0000, PROBES_T32_BITFIELD, - REGS(0, 0, NOSPPC, 0, 0)), - - /* BFI 1111 0x11 0110 xxxx 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfbf08000, 0xf3600000, PROBES_T32_BITFIELD, - REGS(NOSPPCX, 0, NOSPPC, 0, 0)), - - DECODE_END -}; - -static const union decode_item t32_table_1111_0xxx___1[] = { - /* Branches and miscellaneous control */ - - /* YIELD 1111 0011 1010 xxxx 10x0 x000 0000 0001 */ - DECODE_OR (0xfff0d7ff, 0xf3a08001), - /* SEV 1111 0011 1010 xxxx 10x0 x000 0000 0100 */ - DECODE_EMULATE (0xfff0d7ff, 0xf3a08004, PROBES_T32_SEV), - /* NOP 1111 0011 1010 xxxx 10x0 x000 0000 0000 */ - /* WFE 1111 0011 1010 xxxx 10x0 x000 0000 0010 */ - /* WFI 1111 0011 1010 xxxx 10x0 x000 0000 0011 */ - DECODE_SIMULATE (0xfff0d7fc, 0xf3a08000, PROBES_T32_WFE), - - /* MRS Rd, CPSR 1111 0011 1110 xxxx 10x0 xxxx xxxx xxxx */ - DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, PROBES_T32_MRS, - REGS(0, 0, NOSPPC, 0, 0)), - - /* - * Unsupported instructions - * 1111 0x11 1xxx xxxx 10x0 xxxx xxxx xxxx - * - * MSR 1111 0011 100x xxxx 10x0 xxxx xxxx xxxx - * DBG hint 1111 0011 1010 xxxx 10x0 x000 1111 xxxx - * Unallocated hints 1111 0011 1010 xxxx 10x0 x000 xxxx xxxx - * CPS 1111 0011 1010 xxxx 10x0 xxxx xxxx xxxx - * CLREX/DSB/DMB/ISB 1111 0011 1011 xxxx 10x0 xxxx xxxx xxxx - * BXJ 1111 0011 1100 xxxx 10x0 xxxx xxxx xxxx - * SUBS PC,LR,# 1111 0011 1101 xxxx 10x0 xxxx xxxx xxxx - * MRS Rd, SPSR 1111 0011 1111 xxxx 10x0 xxxx xxxx xxxx - * SMC 1111 0111 1111 xxxx 1000 xxxx xxxx xxxx - * UNDEFINED 1111 0111 1111 xxxx 1010 xxxx xxxx xxxx - * ??? 1111 0111 1xxx xxxx 1010 xxxx xxxx xxxx - */ - DECODE_REJECT (0xfb80d000, 0xf3808000), - - /* Bcc 1111 0xxx xxxx xxxx 10x0 xxxx xxxx xxxx */ - DECODE_CUSTOM (0xf800d000, 0xf0008000, PROBES_T32_BRANCH_COND), - - /* BLX 1111 0xxx xxxx xxxx 11x0 xxxx xxxx xxx0 */ - DECODE_OR (0xf800d001, 0xf000c000), - /* B 1111 0xxx xxxx xxxx 10x1 xxxx xxxx xxxx */ - /* BL 1111 0xxx xxxx xxxx 11x1 xxxx xxxx xxxx */ - DECODE_SIMULATE (0xf8009000, 0xf0009000, PROBES_T32_BRANCH), - - DECODE_END -}; - -static const union decode_item t32_table_1111_100x_x0x1__1111[] = { - /* Memory hints */ - - /* PLD (literal) 1111 1000 x001 1111 1111 xxxx xxxx xxxx */ - /* PLI (literal) 1111 1001 x001 1111 1111 xxxx xxxx xxxx */ - DECODE_SIMULATE (0xfe7ff000, 0xf81ff000, PROBES_T32_PLDI), - - /* PLD{W} (immediate) 1111 1000 10x1 xxxx 1111 xxxx xxxx xxxx */ - DECODE_OR (0xffd0f000, 0xf890f000), - /* PLD{W} (immediate) 1111 1000 00x1 xxxx 1111 1100 xxxx xxxx */ - DECODE_OR (0xffd0ff00, 0xf810fc00), - /* PLI (immediate) 1111 1001 1001 xxxx 1111 xxxx xxxx xxxx */ - DECODE_OR (0xfff0f000, 0xf990f000), - /* PLI (immediate) 1111 1001 0001 xxxx 1111 1100 xxxx xxxx */ - DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, PROBES_T32_PLDI, - REGS(NOPCX, 0, 0, 0, 0)), - - /* PLD{W} (register) 1111 1000 00x1 xxxx 1111 0000 00xx xxxx */ - DECODE_OR (0xffd0ffc0, 0xf810f000), - /* PLI (register) 1111 1001 0001 xxxx 1111 0000 00xx xxxx */ - DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, PROBES_T32_PLDI, - REGS(NOPCX, 0, 0, 0, NOSPPC)), - - /* Other unallocated instructions... */ - DECODE_END -}; - -static const union decode_item t32_table_1111_100x[] = { - /* Store/Load single data item */ - - /* ??? 1111 100x x11x xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfe600000, 0xf8600000), - - /* ??? 1111 1001 0101 xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfff00000, 0xf9500000), - - /* ??? 1111 100x 0xxx xxxx xxxx 10x0 xxxx xxxx */ - DECODE_REJECT (0xfe800d00, 0xf8000800), - - /* STRBT 1111 1000 0000 xxxx xxxx 1110 xxxx xxxx */ - /* STRHT 1111 1000 0010 xxxx xxxx 1110 xxxx xxxx */ - /* STRT 1111 1000 0100 xxxx xxxx 1110 xxxx xxxx */ - /* LDRBT 1111 1000 0001 xxxx xxxx 1110 xxxx xxxx */ - /* LDRSBT 1111 1001 0001 xxxx xxxx 1110 xxxx xxxx */ - /* LDRHT 1111 1000 0011 xxxx xxxx 1110 xxxx xxxx */ - /* LDRSHT 1111 1001 0011 xxxx xxxx 1110 xxxx xxxx */ - /* LDRT 1111 1000 0101 xxxx xxxx 1110 xxxx xxxx */ - DECODE_REJECT (0xfe800f00, 0xf8000e00), - - /* STR{,B,H} Rn,[PC...] 1111 1000 xxx0 1111 xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xff1f0000, 0xf80f0000), - - /* STR{,B,H} PC,[Rn...] 1111 1000 xxx0 xxxx 1111 xxxx xxxx xxxx */ - DECODE_REJECT (0xff10f000, 0xf800f000), - - /* LDR (literal) 1111 1000 x101 1111 xxxx xxxx xxxx xxxx */ - DECODE_SIMULATEX(0xff7f0000, 0xf85f0000, PROBES_T32_LDR_LIT, - REGS(PC, ANY, 0, 0, 0)), - - /* STR (immediate) 1111 1000 0100 xxxx xxxx 1xxx xxxx xxxx */ - /* LDR (immediate) 1111 1000 0101 xxxx xxxx 1xxx xxxx xxxx */ - DECODE_OR (0xffe00800, 0xf8400800), - /* STR (immediate) 1111 1000 1100 xxxx xxxx xxxx xxxx xxxx */ - /* LDR (immediate) 1111 1000 1101 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xffe00000, 0xf8c00000, PROBES_T32_LDRSTR, - REGS(NOPCX, ANY, 0, 0, 0)), - - /* STR (register) 1111 1000 0100 xxxx xxxx 0000 00xx xxxx */ - /* LDR (register) 1111 1000 0101 xxxx xxxx 0000 00xx xxxx */ - DECODE_EMULATEX (0xffe00fc0, 0xf8400000, PROBES_T32_LDRSTR, - REGS(NOPCX, ANY, 0, 0, NOSPPC)), - - /* LDRB (literal) 1111 1000 x001 1111 xxxx xxxx xxxx xxxx */ - /* LDRSB (literal) 1111 1001 x001 1111 xxxx xxxx xxxx xxxx */ - /* LDRH (literal) 1111 1000 x011 1111 xxxx xxxx xxxx xxxx */ - /* LDRSH (literal) 1111 1001 x011 1111 xxxx xxxx xxxx xxxx */ - DECODE_SIMULATEX(0xfe5f0000, 0xf81f0000, PROBES_T32_LDR_LIT, - REGS(PC, NOSPPCX, 0, 0, 0)), - - /* STRB (immediate) 1111 1000 0000 xxxx xxxx 1xxx xxxx xxxx */ - /* STRH (immediate) 1111 1000 0010 xxxx xxxx 1xxx xxxx xxxx */ - /* LDRB (immediate) 1111 1000 0001 xxxx xxxx 1xxx xxxx xxxx */ - /* LDRSB (immediate) 1111 1001 0001 xxxx xxxx 1xxx xxxx xxxx */ - /* LDRH (immediate) 1111 1000 0011 xxxx xxxx 1xxx xxxx xxxx */ - /* LDRSH (immediate) 1111 1001 0011 xxxx xxxx 1xxx xxxx xxxx */ - DECODE_OR (0xfec00800, 0xf8000800), - /* STRB (immediate) 1111 1000 1000 xxxx xxxx xxxx xxxx xxxx */ - /* STRH (immediate) 1111 1000 1010 xxxx xxxx xxxx xxxx xxxx */ - /* LDRB (immediate) 1111 1000 1001 xxxx xxxx xxxx xxxx xxxx */ - /* LDRSB (immediate) 1111 1001 1001 xxxx xxxx xxxx xxxx xxxx */ - /* LDRH (immediate) 1111 1000 1011 xxxx xxxx xxxx xxxx xxxx */ - /* LDRSH (immediate) 1111 1001 1011 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfec00000, 0xf8800000, PROBES_T32_LDRSTR, - REGS(NOPCX, NOSPPCX, 0, 0, 0)), - - /* STRB (register) 1111 1000 0000 xxxx xxxx 0000 00xx xxxx */ - /* STRH (register) 1111 1000 0010 xxxx xxxx 0000 00xx xxxx */ - /* LDRB (register) 1111 1000 0001 xxxx xxxx 0000 00xx xxxx */ - /* LDRSB (register) 1111 1001 0001 xxxx xxxx 0000 00xx xxxx */ - /* LDRH (register) 1111 1000 0011 xxxx xxxx 0000 00xx xxxx */ - /* LDRSH (register) 1111 1001 0011 xxxx xxxx 0000 00xx xxxx */ - DECODE_EMULATEX (0xfe800fc0, 0xf8000000, PROBES_T32_LDRSTR, - REGS(NOPCX, NOSPPCX, 0, 0, NOSPPC)), - - /* Other unallocated instructions... */ - DECODE_END -}; - -static const union decode_item t32_table_1111_1010___1111[] = { - /* Data-processing (register) */ - - /* ??? 1111 1010 011x xxxx 1111 xxxx 1xxx xxxx */ - DECODE_REJECT (0xffe0f080, 0xfa60f080), - - /* SXTH 1111 1010 0000 1111 1111 xxxx 1xxx xxxx */ - /* UXTH 1111 1010 0001 1111 1111 xxxx 1xxx xxxx */ - /* SXTB16 1111 1010 0010 1111 1111 xxxx 1xxx xxxx */ - /* UXTB16 1111 1010 0011 1111 1111 xxxx 1xxx xxxx */ - /* SXTB 1111 1010 0100 1111 1111 xxxx 1xxx xxxx */ - /* UXTB 1111 1010 0101 1111 1111 xxxx 1xxx xxxx */ - DECODE_EMULATEX (0xff8ff080, 0xfa0ff080, PROBES_T32_SIGN_EXTEND, - REGS(0, 0, NOSPPC, 0, NOSPPC)), - - - /* ??? 1111 1010 1xxx xxxx 1111 xxxx 0x11 xxxx */ - DECODE_REJECT (0xff80f0b0, 0xfa80f030), - /* ??? 1111 1010 1x11 xxxx 1111 xxxx 0xxx xxxx */ - DECODE_REJECT (0xffb0f080, 0xfab0f000), - - /* SADD16 1111 1010 1001 xxxx 1111 xxxx 0000 xxxx */ - /* SASX 1111 1010 1010 xxxx 1111 xxxx 0000 xxxx */ - /* SSAX 1111 1010 1110 xxxx 1111 xxxx 0000 xxxx */ - /* SSUB16 1111 1010 1101 xxxx 1111 xxxx 0000 xxxx */ - /* SADD8 1111 1010 1000 xxxx 1111 xxxx 0000 xxxx */ - /* SSUB8 1111 1010 1100 xxxx 1111 xxxx 0000 xxxx */ - - /* QADD16 1111 1010 1001 xxxx 1111 xxxx 0001 xxxx */ - /* QASX 1111 1010 1010 xxxx 1111 xxxx 0001 xxxx */ - /* QSAX 1111 1010 1110 xxxx 1111 xxxx 0001 xxxx */ - /* QSUB16 1111 1010 1101 xxxx 1111 xxxx 0001 xxxx */ - /* QADD8 1111 1010 1000 xxxx 1111 xxxx 0001 xxxx */ - /* QSUB8 1111 1010 1100 xxxx 1111 xxxx 0001 xxxx */ - - /* SHADD16 1111 1010 1001 xxxx 1111 xxxx 0010 xxxx */ - /* SHASX 1111 1010 1010 xxxx 1111 xxxx 0010 xxxx */ - /* SHSAX 1111 1010 1110 xxxx 1111 xxxx 0010 xxxx */ - /* SHSUB16 1111 1010 1101 xxxx 1111 xxxx 0010 xxxx */ - /* SHADD8 1111 1010 1000 xxxx 1111 xxxx 0010 xxxx */ - /* SHSUB8 1111 1010 1100 xxxx 1111 xxxx 0010 xxxx */ - - /* UADD16 1111 1010 1001 xxxx 1111 xxxx 0100 xxxx */ - /* UASX 1111 1010 1010 xxxx 1111 xxxx 0100 xxxx */ - /* USAX 1111 1010 1110 xxxx 1111 xxxx 0100 xxxx */ - /* USUB16 1111 1010 1101 xxxx 1111 xxxx 0100 xxxx */ - /* UADD8 1111 1010 1000 xxxx 1111 xxxx 0100 xxxx */ - /* USUB8 1111 1010 1100 xxxx 1111 xxxx 0100 xxxx */ - - /* UQADD16 1111 1010 1001 xxxx 1111 xxxx 0101 xxxx */ - /* UQASX 1111 1010 1010 xxxx 1111 xxxx 0101 xxxx */ - /* UQSAX 1111 1010 1110 xxxx 1111 xxxx 0101 xxxx */ - /* UQSUB16 1111 1010 1101 xxxx 1111 xxxx 0101 xxxx */ - /* UQADD8 1111 1010 1000 xxxx 1111 xxxx 0101 xxxx */ - /* UQSUB8 1111 1010 1100 xxxx 1111 xxxx 0101 xxxx */ - - /* UHADD16 1111 1010 1001 xxxx 1111 xxxx 0110 xxxx */ - /* UHASX 1111 1010 1010 xxxx 1111 xxxx 0110 xxxx */ - /* UHSAX 1111 1010 1110 xxxx 1111 xxxx 0110 xxxx */ - /* UHSUB16 1111 1010 1101 xxxx 1111 xxxx 0110 xxxx */ - /* UHADD8 1111 1010 1000 xxxx 1111 xxxx 0110 xxxx */ - /* UHSUB8 1111 1010 1100 xxxx 1111 xxxx 0110 xxxx */ - DECODE_OR (0xff80f080, 0xfa80f000), - - /* SXTAH 1111 1010 0000 xxxx 1111 xxxx 1xxx xxxx */ - /* UXTAH 1111 1010 0001 xxxx 1111 xxxx 1xxx xxxx */ - /* SXTAB16 1111 1010 0010 xxxx 1111 xxxx 1xxx xxxx */ - /* UXTAB16 1111 1010 0011 xxxx 1111 xxxx 1xxx xxxx */ - /* SXTAB 1111 1010 0100 xxxx 1111 xxxx 1xxx xxxx */ - /* UXTAB 1111 1010 0101 xxxx 1111 xxxx 1xxx xxxx */ - DECODE_OR (0xff80f080, 0xfa00f080), - - /* QADD 1111 1010 1000 xxxx 1111 xxxx 1000 xxxx */ - /* QDADD 1111 1010 1000 xxxx 1111 xxxx 1001 xxxx */ - /* QSUB 1111 1010 1000 xxxx 1111 xxxx 1010 xxxx */ - /* QDSUB 1111 1010 1000 xxxx 1111 xxxx 1011 xxxx */ - DECODE_OR (0xfff0f0c0, 0xfa80f080), - - /* SEL 1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */ - DECODE_OR (0xfff0f0f0, 0xfaa0f080), - - /* LSL 1111 1010 000x xxxx 1111 xxxx 0000 xxxx */ - /* LSR 1111 1010 001x xxxx 1111 xxxx 0000 xxxx */ - /* ASR 1111 1010 010x xxxx 1111 xxxx 0000 xxxx */ - /* ROR 1111 1010 011x xxxx 1111 xxxx 0000 xxxx */ - DECODE_EMULATEX (0xff80f0f0, 0xfa00f000, PROBES_T32_MEDIA, - REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)), - - /* CLZ 1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */ - DECODE_OR (0xfff0f0f0, 0xfab0f080), - - /* REV 1111 1010 1001 xxxx 1111 xxxx 1000 xxxx */ - /* REV16 1111 1010 1001 xxxx 1111 xxxx 1001 xxxx */ - /* RBIT 1111 1010 1001 xxxx 1111 xxxx 1010 xxxx */ - /* REVSH 1111 1010 1001 xxxx 1111 xxxx 1011 xxxx */ - DECODE_EMULATEX (0xfff0f0c0, 0xfa90f080, PROBES_T32_REVERSE, - REGS(NOSPPC, 0, NOSPPC, 0, SAMEAS16)), - - /* Other unallocated instructions... */ - DECODE_END -}; - -static const union decode_item t32_table_1111_1011_0[] = { - /* Multiply, multiply accumulate, and absolute difference */ - - /* ??? 1111 1011 0000 xxxx 1111 xxxx 0001 xxxx */ - DECODE_REJECT (0xfff0f0f0, 0xfb00f010), - /* ??? 1111 1011 0111 xxxx 1111 xxxx 0001 xxxx */ - DECODE_REJECT (0xfff0f0f0, 0xfb70f010), - - /* SMULxy 1111 1011 0001 xxxx 1111 xxxx 00xx xxxx */ - DECODE_OR (0xfff0f0c0, 0xfb10f000), - /* MUL 1111 1011 0000 xxxx 1111 xxxx 0000 xxxx */ - /* SMUAD{X} 1111 1011 0010 xxxx 1111 xxxx 000x xxxx */ - /* SMULWy 1111 1011 0011 xxxx 1111 xxxx 000x xxxx */ - /* SMUSD{X} 1111 1011 0100 xxxx 1111 xxxx 000x xxxx */ - /* SMMUL{R} 1111 1011 0101 xxxx 1111 xxxx 000x xxxx */ - /* USAD8 1111 1011 0111 xxxx 1111 xxxx 0000 xxxx */ - DECODE_EMULATEX (0xff80f0e0, 0xfb00f000, PROBES_T32_MUL_ADD, - REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)), - - /* ??? 1111 1011 0111 xxxx xxxx xxxx 0001 xxxx */ - DECODE_REJECT (0xfff000f0, 0xfb700010), - - /* SMLAxy 1111 1011 0001 xxxx xxxx xxxx 00xx xxxx */ - DECODE_OR (0xfff000c0, 0xfb100000), - /* MLA 1111 1011 0000 xxxx xxxx xxxx 0000 xxxx */ - /* MLS 1111 1011 0000 xxxx xxxx xxxx 0001 xxxx */ - /* SMLAD{X} 1111 1011 0010 xxxx xxxx xxxx 000x xxxx */ - /* SMLAWy 1111 1011 0011 xxxx xxxx xxxx 000x xxxx */ - /* SMLSD{X} 1111 1011 0100 xxxx xxxx xxxx 000x xxxx */ - /* SMMLA{R} 1111 1011 0101 xxxx xxxx xxxx 000x xxxx */ - /* SMMLS{R} 1111 1011 0110 xxxx xxxx xxxx 000x xxxx */ - /* USADA8 1111 1011 0111 xxxx xxxx xxxx 0000 xxxx */ - DECODE_EMULATEX (0xff8000c0, 0xfb000000, PROBES_T32_MUL_ADD2, - REGS(NOSPPC, NOSPPCX, NOSPPC, 0, NOSPPC)), - - /* Other unallocated instructions... */ - DECODE_END -}; - -static const union decode_item t32_table_1111_1011_1[] = { - /* Long multiply, long multiply accumulate, and divide */ - - /* UMAAL 1111 1011 1110 xxxx xxxx xxxx 0110 xxxx */ - DECODE_OR (0xfff000f0, 0xfbe00060), - /* SMLALxy 1111 1011 1100 xxxx xxxx xxxx 10xx xxxx */ - DECODE_OR (0xfff000c0, 0xfbc00080), - /* SMLALD{X} 1111 1011 1100 xxxx xxxx xxxx 110x xxxx */ - /* SMLSLD{X} 1111 1011 1101 xxxx xxxx xxxx 110x xxxx */ - DECODE_OR (0xffe000e0, 0xfbc000c0), - /* SMULL 1111 1011 1000 xxxx xxxx xxxx 0000 xxxx */ - /* UMULL 1111 1011 1010 xxxx xxxx xxxx 0000 xxxx */ - /* SMLAL 1111 1011 1100 xxxx xxxx xxxx 0000 xxxx */ - /* UMLAL 1111 1011 1110 xxxx xxxx xxxx 0000 xxxx */ - DECODE_EMULATEX (0xff9000f0, 0xfb800000, PROBES_T32_MUL_ADD_LONG, - REGS(NOSPPC, NOSPPC, NOSPPC, 0, NOSPPC)), - - /* SDIV 1111 1011 1001 xxxx xxxx xxxx 1111 xxxx */ - /* UDIV 1111 1011 1011 xxxx xxxx xxxx 1111 xxxx */ - /* Other unallocated instructions... */ - DECODE_END -}; - -const union decode_item probes_decode_thumb32_table[] = { - - /* - * Load/store multiple instructions - * 1110 100x x0xx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xfe400000, 0xe8000000, t32_table_1110_100x_x0xx), - - /* - * Load/store dual, load/store exclusive, table branch - * 1110 100x x1xx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xfe400000, 0xe8400000, t32_table_1110_100x_x1xx), - - /* - * Data-processing (shifted register) - * 1110 101x xxxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xfe000000, 0xea000000, t32_table_1110_101x), - - /* - * Coprocessor instructions - * 1110 11xx xxxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_REJECT (0xfc000000, 0xec000000), - - /* - * Data-processing (modified immediate) - * 1111 0x0x xxxx xxxx 0xxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xfa008000, 0xf0000000, t32_table_1111_0x0x___0), - - /* - * Data-processing (plain binary immediate) - * 1111 0x1x xxxx xxxx 0xxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xfa008000, 0xf2000000, t32_table_1111_0x1x___0), - - /* - * Branches and miscellaneous control - * 1111 0xxx xxxx xxxx 1xxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xf8008000, 0xf0008000, t32_table_1111_0xxx___1), - - /* - * Advanced SIMD element or structure load/store instructions - * 1111 1001 xxx0 xxxx xxxx xxxx xxxx xxxx - */ - DECODE_REJECT (0xff100000, 0xf9000000), - - /* - * Memory hints - * 1111 100x x0x1 xxxx 1111 xxxx xxxx xxxx - */ - DECODE_TABLE (0xfe50f000, 0xf810f000, t32_table_1111_100x_x0x1__1111), - - /* - * Store single data item - * 1111 1000 xxx0 xxxx xxxx xxxx xxxx xxxx - * Load single data items - * 1111 100x xxx1 xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xfe000000, 0xf8000000, t32_table_1111_100x), - - /* - * Data-processing (register) - * 1111 1010 xxxx xxxx 1111 xxxx xxxx xxxx - */ - DECODE_TABLE (0xff00f000, 0xfa00f000, t32_table_1111_1010___1111), - - /* - * Multiply, multiply accumulate, and absolute difference - * 1111 1011 0xxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xff800000, 0xfb000000, t32_table_1111_1011_0), - - /* - * Long multiply, long multiply accumulate, and divide - * 1111 1011 1xxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xff800000, 0xfb800000, t32_table_1111_1011_1), - - /* - * Coprocessor instructions - * 1111 11xx xxxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_END -}; -#ifdef CONFIG_ARM_KPROBES_TEST_MODULE -EXPORT_SYMBOL_GPL(probes_decode_thumb32_table); -#endif - -static const union decode_item t16_table_1011[] = { - /* Miscellaneous 16-bit instructions */ - - /* ADD (SP plus immediate) 1011 0000 0xxx xxxx */ - /* SUB (SP minus immediate) 1011 0000 1xxx xxxx */ - DECODE_SIMULATE (0xff00, 0xb000, PROBES_T16_ADD_SP), - - /* CBZ 1011 00x1 xxxx xxxx */ - /* CBNZ 1011 10x1 xxxx xxxx */ - DECODE_SIMULATE (0xf500, 0xb100, PROBES_T16_CBZ), - - /* SXTH 1011 0010 00xx xxxx */ - /* SXTB 1011 0010 01xx xxxx */ - /* UXTH 1011 0010 10xx xxxx */ - /* UXTB 1011 0010 11xx xxxx */ - /* REV 1011 1010 00xx xxxx */ - /* REV16 1011 1010 01xx xxxx */ - /* ??? 1011 1010 10xx xxxx */ - /* REVSH 1011 1010 11xx xxxx */ - DECODE_REJECT (0xffc0, 0xba80), - DECODE_EMULATE (0xf500, 0xb000, PROBES_T16_SIGN_EXTEND), - - /* PUSH 1011 010x xxxx xxxx */ - DECODE_CUSTOM (0xfe00, 0xb400, PROBES_T16_PUSH), - /* POP 1011 110x xxxx xxxx */ - DECODE_CUSTOM (0xfe00, 0xbc00, PROBES_T16_POP), - - /* - * If-Then, and hints - * 1011 1111 xxxx xxxx - */ - - /* YIELD 1011 1111 0001 0000 */ - DECODE_OR (0xffff, 0xbf10), - /* SEV 1011 1111 0100 0000 */ - DECODE_EMULATE (0xffff, 0xbf40, PROBES_T16_SEV), - /* NOP 1011 1111 0000 0000 */ - /* WFE 1011 1111 0010 0000 */ - /* WFI 1011 1111 0011 0000 */ - DECODE_SIMULATE (0xffcf, 0xbf00, PROBES_T16_WFE), - /* Unassigned hints 1011 1111 xxxx 0000 */ - DECODE_REJECT (0xff0f, 0xbf00), - /* IT 1011 1111 xxxx xxxx */ - DECODE_CUSTOM (0xff00, 0xbf00, PROBES_T16_IT), - - /* SETEND 1011 0110 010x xxxx */ - /* CPS 1011 0110 011x xxxx */ - /* BKPT 1011 1110 xxxx xxxx */ - /* And unallocated instructions... */ - DECODE_END -}; - -const union decode_item probes_decode_thumb16_table[] = { - - /* - * Shift (immediate), add, subtract, move, and compare - * 00xx xxxx xxxx xxxx - */ - - /* CMP (immediate) 0010 1xxx xxxx xxxx */ - DECODE_EMULATE (0xf800, 0x2800, PROBES_T16_CMP), - - /* ADD (register) 0001 100x xxxx xxxx */ - /* SUB (register) 0001 101x xxxx xxxx */ - /* LSL (immediate) 0000 0xxx xxxx xxxx */ - /* LSR (immediate) 0000 1xxx xxxx xxxx */ - /* ASR (immediate) 0001 0xxx xxxx xxxx */ - /* ADD (immediate, Thumb) 0001 110x xxxx xxxx */ - /* SUB (immediate, Thumb) 0001 111x xxxx xxxx */ - /* MOV (immediate) 0010 0xxx xxxx xxxx */ - /* ADD (immediate, Thumb) 0011 0xxx xxxx xxxx */ - /* SUB (immediate, Thumb) 0011 1xxx xxxx xxxx */ - DECODE_EMULATE (0xc000, 0x0000, PROBES_T16_ADDSUB), - - /* - * 16-bit Thumb data-processing instructions - * 0100 00xx xxxx xxxx - */ - - /* TST (register) 0100 0010 00xx xxxx */ - DECODE_EMULATE (0xffc0, 0x4200, PROBES_T16_CMP), - /* CMP (register) 0100 0010 10xx xxxx */ - /* CMN (register) 0100 0010 11xx xxxx */ - DECODE_EMULATE (0xff80, 0x4280, PROBES_T16_CMP), - /* AND (register) 0100 0000 00xx xxxx */ - /* EOR (register) 0100 0000 01xx xxxx */ - /* LSL (register) 0100 0000 10xx xxxx */ - /* LSR (register) 0100 0000 11xx xxxx */ - /* ASR (register) 0100 0001 00xx xxxx */ - /* ADC (register) 0100 0001 01xx xxxx */ - /* SBC (register) 0100 0001 10xx xxxx */ - /* ROR (register) 0100 0001 11xx xxxx */ - /* RSB (immediate) 0100 0010 01xx xxxx */ - /* ORR (register) 0100 0011 00xx xxxx */ - /* MUL 0100 0011 00xx xxxx */ - /* BIC (register) 0100 0011 10xx xxxx */ - /* MVN (register) 0100 0011 10xx xxxx */ - DECODE_EMULATE (0xfc00, 0x4000, PROBES_T16_LOGICAL), - - /* - * Special data instructions and branch and exchange - * 0100 01xx xxxx xxxx - */ - - /* BLX pc 0100 0111 1111 1xxx */ - DECODE_REJECT (0xfff8, 0x47f8), - - /* BX (register) 0100 0111 0xxx xxxx */ - /* BLX (register) 0100 0111 1xxx xxxx */ - DECODE_SIMULATE (0xff00, 0x4700, PROBES_T16_BLX), - - /* ADD pc, pc 0100 0100 1111 1111 */ - DECODE_REJECT (0xffff, 0x44ff), - - /* ADD (register) 0100 0100 xxxx xxxx */ - /* CMP (register) 0100 0101 xxxx xxxx */ - /* MOV (register) 0100 0110 xxxx xxxx */ - DECODE_CUSTOM (0xfc00, 0x4400, PROBES_T16_HIREGOPS), - - /* - * Load from Literal Pool - * LDR (literal) 0100 1xxx xxxx xxxx - */ - DECODE_SIMULATE (0xf800, 0x4800, PROBES_T16_LDR_LIT), - - /* - * 16-bit Thumb Load/store instructions - * 0101 xxxx xxxx xxxx - * 011x xxxx xxxx xxxx - * 100x xxxx xxxx xxxx - */ - - /* STR (register) 0101 000x xxxx xxxx */ - /* STRH (register) 0101 001x xxxx xxxx */ - /* STRB (register) 0101 010x xxxx xxxx */ - /* LDRSB (register) 0101 011x xxxx xxxx */ - /* LDR (register) 0101 100x xxxx xxxx */ - /* LDRH (register) 0101 101x xxxx xxxx */ - /* LDRB (register) 0101 110x xxxx xxxx */ - /* LDRSH (register) 0101 111x xxxx xxxx */ - /* STR (immediate, Thumb) 0110 0xxx xxxx xxxx */ - /* LDR (immediate, Thumb) 0110 1xxx xxxx xxxx */ - /* STRB (immediate, Thumb) 0111 0xxx xxxx xxxx */ - /* LDRB (immediate, Thumb) 0111 1xxx xxxx xxxx */ - DECODE_EMULATE (0xc000, 0x4000, PROBES_T16_LDRHSTRH), - /* STRH (immediate, Thumb) 1000 0xxx xxxx xxxx */ - /* LDRH (immediate, Thumb) 1000 1xxx xxxx xxxx */ - DECODE_EMULATE (0xf000, 0x8000, PROBES_T16_LDRHSTRH), - /* STR (immediate, Thumb) 1001 0xxx xxxx xxxx */ - /* LDR (immediate, Thumb) 1001 1xxx xxxx xxxx */ - DECODE_SIMULATE (0xf000, 0x9000, PROBES_T16_LDRSTR), - - /* - * Generate PC-/SP-relative address - * ADR (literal) 1010 0xxx xxxx xxxx - * ADD (SP plus immediate) 1010 1xxx xxxx xxxx - */ - DECODE_SIMULATE (0xf000, 0xa000, PROBES_T16_ADR), - - /* - * Miscellaneous 16-bit instructions - * 1011 xxxx xxxx xxxx - */ - DECODE_TABLE (0xf000, 0xb000, t16_table_1011), - - /* STM 1100 0xxx xxxx xxxx */ - /* LDM 1100 1xxx xxxx xxxx */ - DECODE_EMULATE (0xf000, 0xc000, PROBES_T16_LDMSTM), - - /* - * Conditional branch, and Supervisor Call - */ - - /* Permanently UNDEFINED 1101 1110 xxxx xxxx */ - /* SVC 1101 1111 xxxx xxxx */ - DECODE_REJECT (0xfe00, 0xde00), - - /* Conditional branch 1101 xxxx xxxx xxxx */ - DECODE_CUSTOM (0xf000, 0xd000, PROBES_T16_BRANCH_COND), - - /* - * Unconditional branch - * B 1110 0xxx xxxx xxxx - */ - DECODE_SIMULATE (0xf800, 0xe000, PROBES_T16_BRANCH), - - DECODE_END -}; -#ifdef CONFIG_ARM_KPROBES_TEST_MODULE -EXPORT_SYMBOL_GPL(probes_decode_thumb16_table); -#endif - -static unsigned long __kprobes thumb_check_cc(unsigned long cpsr) -{ - if (unlikely(in_it_block(cpsr))) - return probes_condition_checks[current_cond(cpsr)](cpsr); - return true; -} - -static void __kprobes thumb16_singlestep(probes_opcode_t opcode, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - regs->ARM_pc += 2; - asi->insn_handler(opcode, asi, regs); - regs->ARM_cpsr = it_advance(regs->ARM_cpsr); -} - -static void __kprobes thumb32_singlestep(probes_opcode_t opcode, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - regs->ARM_pc += 4; - asi->insn_handler(opcode, asi, regs); - regs->ARM_cpsr = it_advance(regs->ARM_cpsr); -} - -enum probes_insn __kprobes -thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions) -{ - asi->insn_singlestep = thumb16_singlestep; - asi->insn_check_cc = thumb_check_cc; - return probes_decode_insn(insn, asi, probes_decode_thumb16_table, true, - emulate, actions); -} - -enum probes_insn __kprobes -thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions) -{ - asi->insn_singlestep = thumb32_singlestep; - asi->insn_check_cc = thumb_check_cc; - return probes_decode_insn(insn, asi, probes_decode_thumb32_table, true, - emulate, actions); -} diff --git a/arch/arm/kernel/probes-thumb.h b/arch/arm/kernel/probes-thumb.h deleted file mode 100644 index 7c6f6eb..0000000 --- a/arch/arm/kernel/probes-thumb.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * arch/arm/kernel/probes-thumb.h - * - * Copyright 2013 Linaro Ltd. - * Written by: David A. Long - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#ifndef _ARM_KERNEL_PROBES_THUMB_H -#define _ARM_KERNEL_PROBES_THUMB_H - -/* - * True if current instruction is in an IT block. - */ -#define in_it_block(cpsr) ((cpsr & 0x06000c00) != 0x00000000) - -/* - * Return the condition code to check for the currently executing instruction. - * This is in ITSTATE<7:4> which is in CPSR<15:12> but is only valid if - * in_it_block returns true. - */ -#define current_cond(cpsr) ((cpsr >> 12) & 0xf) - -enum probes_t32_action { - PROBES_T32_EMULATE_NONE, - PROBES_T32_SIMULATE_NOP, - PROBES_T32_LDMSTM, - PROBES_T32_LDRDSTRD, - PROBES_T32_TABLE_BRANCH, - PROBES_T32_TST, - PROBES_T32_CMP, - PROBES_T32_MOV, - PROBES_T32_ADDSUB, - PROBES_T32_LOGICAL, - PROBES_T32_ADDWSUBW_PC, - PROBES_T32_ADDWSUBW, - PROBES_T32_MOVW, - PROBES_T32_SAT, - PROBES_T32_BITFIELD, - PROBES_T32_SEV, - PROBES_T32_WFE, - PROBES_T32_MRS, - PROBES_T32_BRANCH_COND, - PROBES_T32_BRANCH, - PROBES_T32_PLDI, - PROBES_T32_LDR_LIT, - PROBES_T32_LDRSTR, - PROBES_T32_SIGN_EXTEND, - PROBES_T32_MEDIA, - PROBES_T32_REVERSE, - PROBES_T32_MUL_ADD, - PROBES_T32_MUL_ADD2, - PROBES_T32_MUL_ADD_LONG, - NUM_PROBES_T32_ACTIONS -}; - -enum probes_t16_action { - PROBES_T16_ADD_SP, - PROBES_T16_CBZ, - PROBES_T16_SIGN_EXTEND, - PROBES_T16_PUSH, - PROBES_T16_POP, - PROBES_T16_SEV, - PROBES_T16_WFE, - PROBES_T16_IT, - PROBES_T16_CMP, - PROBES_T16_ADDSUB, - PROBES_T16_LOGICAL, - PROBES_T16_BLX, - PROBES_T16_HIREGOPS, - PROBES_T16_LDR_LIT, - PROBES_T16_LDRHSTRH, - PROBES_T16_LDRSTR, - PROBES_T16_ADR, - PROBES_T16_LDMSTM, - PROBES_T16_BRANCH_COND, - PROBES_T16_BRANCH, - NUM_PROBES_T16_ACTIONS -}; - -extern const union decode_item probes_decode_thumb32_table[]; -extern const union decode_item probes_decode_thumb16_table[]; - -enum probes_insn __kprobes -thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions); -enum probes_insn __kprobes -thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions); - -#endif diff --git a/arch/arm/kernel/probes.c b/arch/arm/kernel/probes.c deleted file mode 100644 index a8ab540..0000000 --- a/arch/arm/kernel/probes.c +++ /dev/null @@ -1,456 +0,0 @@ -/* - * arch/arm/kernel/probes.c - * - * Copyright (C) 2011 Jon Medhurst . - * - * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is - * Copyright (C) 2006, 2007 Motorola 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 -#include -#include -#include -#include - -#include "probes.h" - - -#ifndef find_str_pc_offset - -/* - * For STR and STM instructions, an ARM core may choose to use either - * a +8 or a +12 displacement from the current instruction's address. - * Whichever value is chosen for a given core, it must be the same for - * both instructions and may not change. This function measures it. - */ - -int str_pc_offset; - -void __init find_str_pc_offset(void) -{ - int addr, scratch, ret; - - __asm__ ( - "sub %[ret], pc, #4 \n\t" - "str pc, %[addr] \n\t" - "ldr %[scr], %[addr] \n\t" - "sub %[ret], %[scr], %[ret] \n\t" - : [ret] "=r" (ret), [scr] "=r" (scratch), [addr] "+m" (addr)); - - str_pc_offset = ret; -} - -#endif /* !find_str_pc_offset */ - - -#ifndef test_load_write_pc_interworking - -bool load_write_pc_interworks; - -void __init test_load_write_pc_interworking(void) -{ - int arch = cpu_architecture(); - BUG_ON(arch == CPU_ARCH_UNKNOWN); - load_write_pc_interworks = arch >= CPU_ARCH_ARMv5T; -} - -#endif /* !test_load_write_pc_interworking */ - - -#ifndef test_alu_write_pc_interworking - -bool alu_write_pc_interworks; - -void __init test_alu_write_pc_interworking(void) -{ - int arch = cpu_architecture(); - BUG_ON(arch == CPU_ARCH_UNKNOWN); - alu_write_pc_interworks = arch >= CPU_ARCH_ARMv7; -} - -#endif /* !test_alu_write_pc_interworking */ - - -void __init arm_probes_decode_init(void) -{ - find_str_pc_offset(); - test_load_write_pc_interworking(); - test_alu_write_pc_interworking(); -} - - -static unsigned long __kprobes __check_eq(unsigned long cpsr) -{ - return cpsr & PSR_Z_BIT; -} - -static unsigned long __kprobes __check_ne(unsigned long cpsr) -{ - return (~cpsr) & PSR_Z_BIT; -} - -static unsigned long __kprobes __check_cs(unsigned long cpsr) -{ - return cpsr & PSR_C_BIT; -} - -static unsigned long __kprobes __check_cc(unsigned long cpsr) -{ - return (~cpsr) & PSR_C_BIT; -} - -static unsigned long __kprobes __check_mi(unsigned long cpsr) -{ - return cpsr & PSR_N_BIT; -} - -static unsigned long __kprobes __check_pl(unsigned long cpsr) -{ - return (~cpsr) & PSR_N_BIT; -} - -static unsigned long __kprobes __check_vs(unsigned long cpsr) -{ - return cpsr & PSR_V_BIT; -} - -static unsigned long __kprobes __check_vc(unsigned long cpsr) -{ - return (~cpsr) & PSR_V_BIT; -} - -static unsigned long __kprobes __check_hi(unsigned long cpsr) -{ - cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */ - return cpsr & PSR_C_BIT; -} - -static unsigned long __kprobes __check_ls(unsigned long cpsr) -{ - cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */ - return (~cpsr) & PSR_C_BIT; -} - -static unsigned long __kprobes __check_ge(unsigned long cpsr) -{ - cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ - return (~cpsr) & PSR_N_BIT; -} - -static unsigned long __kprobes __check_lt(unsigned long cpsr) -{ - cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ - return cpsr & PSR_N_BIT; -} - -static unsigned long __kprobes __check_gt(unsigned long cpsr) -{ - unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ - temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */ - return (~temp) & PSR_N_BIT; -} - -static unsigned long __kprobes __check_le(unsigned long cpsr) -{ - unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ - temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */ - return temp & PSR_N_BIT; -} - -static unsigned long __kprobes __check_al(unsigned long cpsr) -{ - return true; -} - -probes_check_cc * const probes_condition_checks[16] = { - &__check_eq, &__check_ne, &__check_cs, &__check_cc, - &__check_mi, &__check_pl, &__check_vs, &__check_vc, - &__check_hi, &__check_ls, &__check_ge, &__check_lt, - &__check_gt, &__check_le, &__check_al, &__check_al -}; - - -void __kprobes probes_simulate_nop(probes_opcode_t opcode, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ -} - -void __kprobes probes_emulate_none(probes_opcode_t opcode, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - asi->insn_fn(); -} - -/* - * Prepare an instruction slot to receive an instruction for emulating. - * This is done by placing a subroutine return after the location where the - * instruction will be placed. We also modify ARM instructions to be - * unconditional as the condition code will already be checked before any - * emulation handler is called. - */ -static probes_opcode_t __kprobes -prepare_emulated_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool thumb) -{ -#ifdef CONFIG_THUMB2_KERNEL - if (thumb) { - u16 *thumb_insn = (u16 *)asi->insn; - /* Thumb bx lr */ - thumb_insn[1] = __opcode_to_mem_thumb16(0x4770); - thumb_insn[2] = __opcode_to_mem_thumb16(0x4770); - return insn; - } - asi->insn[1] = __opcode_to_mem_arm(0xe12fff1e); /* ARM bx lr */ -#else - asi->insn[1] = __opcode_to_mem_arm(0xe1a0f00e); /* mov pc, lr */ -#endif - /* Make an ARM instruction unconditional */ - if (insn < 0xe0000000) - insn = (insn | 0xe0000000) & ~0x10000000; - return insn; -} - -/* - * Write a (probably modified) instruction into the slot previously prepared by - * prepare_emulated_insn - */ -static void __kprobes -set_emulated_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool thumb) -{ -#ifdef CONFIG_THUMB2_KERNEL - if (thumb) { - u16 *ip = (u16 *)asi->insn; - if (is_wide_instruction(insn)) - *ip++ = __opcode_to_mem_thumb16(insn >> 16); - *ip++ = __opcode_to_mem_thumb16(insn); - return; - } -#endif - asi->insn[0] = __opcode_to_mem_arm(insn); -} - -/* - * When we modify the register numbers encoded in an instruction to be emulated, - * the new values come from this define. For ARM and 32-bit Thumb instructions - * this gives... - * - * bit position 16 12 8 4 0 - * ---------------+---+---+---+---+---+ - * register r2 r0 r1 -- r3 - */ -#define INSN_NEW_BITS 0x00020103 - -/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */ -#define INSN_SAMEAS16_BITS 0x22222222 - -/* - * Validate and modify each of the registers encoded in an instruction. - * - * Each nibble in regs contains a value from enum decode_reg_type. For each - * non-zero value, the corresponding nibble in pinsn is validated and modified - * according to the type. - */ -static bool __kprobes decode_regs(probes_opcode_t *pinsn, u32 regs, bool modify) -{ - probes_opcode_t insn = *pinsn; - probes_opcode_t mask = 0xf; /* Start at least significant nibble */ - - for (; regs != 0; regs >>= 4, mask <<= 4) { - - probes_opcode_t new_bits = INSN_NEW_BITS; - - switch (regs & 0xf) { - - case REG_TYPE_NONE: - /* Nibble not a register, skip to next */ - continue; - - case REG_TYPE_ANY: - /* Any register is allowed */ - break; - - case REG_TYPE_SAMEAS16: - /* Replace register with same as at bit position 16 */ - new_bits = INSN_SAMEAS16_BITS; - break; - - case REG_TYPE_SP: - /* Only allow SP (R13) */ - if ((insn ^ 0xdddddddd) & mask) - goto reject; - break; - - case REG_TYPE_PC: - /* Only allow PC (R15) */ - if ((insn ^ 0xffffffff) & mask) - goto reject; - break; - - case REG_TYPE_NOSP: - /* Reject SP (R13) */ - if (((insn ^ 0xdddddddd) & mask) == 0) - goto reject; - break; - - case REG_TYPE_NOSPPC: - case REG_TYPE_NOSPPCX: - /* Reject SP and PC (R13 and R15) */ - if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0) - goto reject; - break; - - case REG_TYPE_NOPCWB: - if (!is_writeback(insn)) - break; /* No writeback, so any register is OK */ - /* fall through... */ - case REG_TYPE_NOPC: - case REG_TYPE_NOPCX: - /* Reject PC (R15) */ - if (((insn ^ 0xffffffff) & mask) == 0) - goto reject; - break; - } - - /* Replace value of nibble with new register number... */ - insn &= ~mask; - insn |= new_bits & mask; - } - - if (modify) - *pinsn = insn; - - return true; - -reject: - return false; -} - -static const int decode_struct_sizes[NUM_DECODE_TYPES] = { - [DECODE_TYPE_TABLE] = sizeof(struct decode_table), - [DECODE_TYPE_CUSTOM] = sizeof(struct decode_custom), - [DECODE_TYPE_SIMULATE] = sizeof(struct decode_simulate), - [DECODE_TYPE_EMULATE] = sizeof(struct decode_emulate), - [DECODE_TYPE_OR] = sizeof(struct decode_or), - [DECODE_TYPE_REJECT] = sizeof(struct decode_reject) -}; - -/* - * probes_decode_insn operates on data tables in order to decode an ARM - * architecture instruction onto which a kprobe has been placed. - * - * These instruction decoding tables are a concatenation of entries each - * of which consist of one of the following structs: - * - * decode_table - * decode_custom - * decode_simulate - * decode_emulate - * decode_or - * decode_reject - * - * Each of these starts with a struct decode_header which has the following - * fields: - * - * type_regs - * mask - * value - * - * The least significant DECODE_TYPE_BITS of type_regs contains a value - * from enum decode_type, this indicates which of the decode_* structs - * the entry contains. The value DECODE_TYPE_END indicates the end of the - * table. - * - * When the table is parsed, each entry is checked in turn to see if it - * matches the instruction to be decoded using the test: - * - * (insn & mask) == value - * - * If no match is found before the end of the table is reached then decoding - * fails with INSN_REJECTED. - * - * When a match is found, decode_regs() is called to validate and modify each - * of the registers encoded in the instruction; the data it uses to do this - * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding - * to fail with INSN_REJECTED. - * - * Once the instruction has passed the above tests, further processing - * depends on the type of the table entry's decode struct. - * - */ -int __kprobes -probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - const union decode_item *table, bool thumb, - bool emulate, const union decode_action *actions) -{ - const struct decode_header *h = (struct decode_header *)table; - const struct decode_header *next; - bool matched = false; - - if (emulate) - insn = prepare_emulated_insn(insn, asi, thumb); - - for (;; h = next) { - enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; - u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS; - - if (type == DECODE_TYPE_END) - return INSN_REJECTED; - - next = (struct decode_header *) - ((uintptr_t)h + decode_struct_sizes[type]); - - if (!matched && (insn & h->mask.bits) != h->value.bits) - continue; - - if (!decode_regs(&insn, regs, emulate)) - return INSN_REJECTED; - - switch (type) { - - case DECODE_TYPE_TABLE: { - struct decode_table *d = (struct decode_table *)h; - next = (struct decode_header *)d->table.table; - break; - } - - case DECODE_TYPE_CUSTOM: { - struct decode_custom *d = (struct decode_custom *)h; - return actions[d->decoder.action].decoder(insn, asi, h); - } - - case DECODE_TYPE_SIMULATE: { - struct decode_simulate *d = (struct decode_simulate *)h; - asi->insn_handler = actions[d->handler.action].handler; - return INSN_GOOD_NO_SLOT; - } - - case DECODE_TYPE_EMULATE: { - struct decode_emulate *d = (struct decode_emulate *)h; - - if (!emulate) - return actions[d->handler.action].decoder(insn, - asi, h); - - asi->insn_handler = actions[d->handler.action].handler; - set_emulated_insn(insn, asi, thumb); - return INSN_GOOD; - } - - case DECODE_TYPE_OR: - matched = true; - break; - - case DECODE_TYPE_REJECT: - default: - return INSN_REJECTED; - } - } -} diff --git a/arch/arm/kernel/probes.h b/arch/arm/kernel/probes.h deleted file mode 100644 index dba9f24..0000000 --- a/arch/arm/kernel/probes.h +++ /dev/null @@ -1,407 +0,0 @@ -/* - * arch/arm/kernel/probes.h - * - * Copyright (C) 2011 Jon Medhurst . - * - * Some contents moved here from arch/arm/include/asm/kprobes.h which is - * Copyright (C) 2006, 2007 Motorola 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. - * - * 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 _ARM_KERNEL_PROBES_H -#define _ARM_KERNEL_PROBES_H - -#include -#include -#include - -void __init arm_probes_decode_init(void); - -extern probes_check_cc * const probes_condition_checks[16]; - -#if __LINUX_ARM_ARCH__ >= 7 - -/* str_pc_offset is architecturally defined from ARMv7 onwards */ -#define str_pc_offset 8 -#define find_str_pc_offset() - -#else /* __LINUX_ARM_ARCH__ < 7 */ - -/* We need a run-time check to determine str_pc_offset */ -extern int str_pc_offset; -void __init find_str_pc_offset(void); - -#endif - - -/* - * Update ITSTATE after normal execution of an IT block instruction. - * - * The 8 IT state bits are split into two parts in CPSR: - * ITSTATE<1:0> are in CPSR<26:25> - * ITSTATE<7:2> are in CPSR<15:10> - */ -static inline unsigned long it_advance(unsigned long cpsr) - { - if ((cpsr & 0x06000400) == 0) { - /* ITSTATE<2:0> == 0 means end of IT block, so clear IT state */ - cpsr &= ~PSR_IT_MASK; - } else { - /* We need to shift left ITSTATE<4:0> */ - const unsigned long mask = 0x06001c00; /* Mask ITSTATE<4:0> */ - unsigned long it = cpsr & mask; - it <<= 1; - it |= it >> (27 - 10); /* Carry ITSTATE<2> to correct place */ - it &= mask; - cpsr &= ~mask; - cpsr |= it; - } - return cpsr; -} - -static inline void __kprobes bx_write_pc(long pcv, struct pt_regs *regs) -{ - long cpsr = regs->ARM_cpsr; - if (pcv & 0x1) { - cpsr |= PSR_T_BIT; - pcv &= ~0x1; - } else { - cpsr &= ~PSR_T_BIT; - pcv &= ~0x2; /* Avoid UNPREDICTABLE address allignment */ - } - regs->ARM_cpsr = cpsr; - regs->ARM_pc = pcv; -} - - -#if __LINUX_ARM_ARCH__ >= 6 - -/* Kernels built for >= ARMv6 should never run on <= ARMv5 hardware, so... */ -#define load_write_pc_interworks true -#define test_load_write_pc_interworking() - -#else /* __LINUX_ARM_ARCH__ < 6 */ - -/* We need run-time testing to determine if load_write_pc() should interwork. */ -extern bool load_write_pc_interworks; -void __init test_load_write_pc_interworking(void); - -#endif - -static inline void __kprobes load_write_pc(long pcv, struct pt_regs *regs) -{ - if (load_write_pc_interworks) - bx_write_pc(pcv, regs); - else - regs->ARM_pc = pcv; -} - - -#if __LINUX_ARM_ARCH__ >= 7 - -#define alu_write_pc_interworks true -#define test_alu_write_pc_interworking() - -#elif __LINUX_ARM_ARCH__ <= 5 - -/* Kernels built for <= ARMv5 should never run on >= ARMv6 hardware, so... */ -#define alu_write_pc_interworks false -#define test_alu_write_pc_interworking() - -#else /* __LINUX_ARM_ARCH__ == 6 */ - -/* We could be an ARMv6 binary on ARMv7 hardware so we need a run-time check. */ -extern bool alu_write_pc_interworks; -void __init test_alu_write_pc_interworking(void); - -#endif /* __LINUX_ARM_ARCH__ == 6 */ - -static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs) -{ - if (alu_write_pc_interworks) - bx_write_pc(pcv, regs); - else - regs->ARM_pc = pcv; -} - - -/* - * Test if load/store instructions writeback the address register. - * if P (bit 24) == 0 or W (bit 21) == 1 - */ -#define is_writeback(insn) ((insn ^ 0x01000000) & 0x01200000) - -/* - * The following definitions and macros are used to build instruction - * decoding tables for use by probes_decode_insn. - * - * These tables are a concatenation of entries each of which consist of one of - * the decode_* structs. All of the fields in every type of decode structure - * are of the union type decode_item, therefore the entire decode table can be - * viewed as an array of these and declared like: - * - * static const union decode_item table_name[] = {}; - * - * In order to construct each entry in the table, macros are used to - * initialise a number of sequential decode_item values in a layout which - * matches the relevant struct. E.g. DECODE_SIMULATE initialise a struct - * decode_simulate by initialising four decode_item objects like this... - * - * {.bits = _type}, - * {.bits = _mask}, - * {.bits = _value}, - * {.action = _handler}, - * - * Initialising a specified member of the union means that the compiler - * will produce a warning if the argument is of an incorrect type. - * - * Below is a list of each of the macros used to initialise entries and a - * description of the action performed when that entry is matched to an - * instruction. A match is found when (instruction & mask) == value. - * - * DECODE_TABLE(mask, value, table) - * Instruction decoding jumps to parsing the new sub-table 'table'. - * - * DECODE_CUSTOM(mask, value, decoder) - * The value of 'decoder' is used as an index into the array of - * action functions, and the retrieved decoder function is invoked - * to complete decoding of the instruction. - * - * DECODE_SIMULATE(mask, value, handler) - * The probes instruction handler is set to the value found by - * indexing into the action array using the value of 'handler'. This - * will be used to simulate the instruction when the probe is hit. - * Decoding returns with INSN_GOOD_NO_SLOT. - * - * DECODE_EMULATE(mask, value, handler) - * The probes instruction handler is set to the value found by - * indexing into the action array using the value of 'handler'. This - * will be used to emulate the instruction when the probe is hit. The - * modified instruction (see below) is placed in the probes instruction - * slot so it may be called by the emulation code. Decoding returns - * with INSN_GOOD. - * - * DECODE_REJECT(mask, value) - * Instruction decoding fails with INSN_REJECTED - * - * DECODE_OR(mask, value) - * This allows the mask/value test of multiple table entries to be - * logically ORed. Once an 'or' entry is matched the decoding action to - * be performed is that of the next entry which isn't an 'or'. E.g. - * - * DECODE_OR (mask1, value1) - * DECODE_OR (mask2, value2) - * DECODE_SIMULATE (mask3, value3, simulation_handler) - * - * This means that if any of the three mask/value pairs match the - * instruction being decoded, then 'simulation_handler' will be used - * for it. - * - * Both the SIMULATE and EMULATE macros have a second form which take an - * additional 'regs' argument. - * - * DECODE_SIMULATEX(mask, value, handler, regs) - * DECODE_EMULATEX (mask, value, handler, regs) - * - * These are used to specify what kind of CPU register is encoded in each of the - * least significant 5 nibbles of the instruction being decoded. The regs value - * is specified using the REGS macro, this takes any of the REG_TYPE_* values - * from enum decode_reg_type as arguments; only the '*' part of the name is - * given. E.g. - * - * REGS(0, ANY, NOPC, 0, ANY) - * - * This indicates an instruction is encoded like: - * - * bits 19..16 ignore - * bits 15..12 any register allowed here - * bits 11.. 8 any register except PC allowed here - * bits 7.. 4 ignore - * bits 3.. 0 any register allowed here - * - * This register specification is checked after a decode table entry is found to - * match an instruction (through the mask/value test). Any invalid register then - * found in the instruction will cause decoding to fail with INSN_REJECTED. In - * the above example this would happen if bits 11..8 of the instruction were - * 1111, indicating R15 or PC. - * - * As well as checking for legal combinations of registers, this data is also - * used to modify the registers encoded in the instructions so that an - * emulation routines can use it. (See decode_regs() and INSN_NEW_BITS.) - * - * Here is a real example which matches ARM instructions of the form - * "AND ,,, " - * - * DECODE_EMULATEX (0x0e000090, 0x00000010, PROBES_DATA_PROCESSING_REG, - * REGS(ANY, ANY, NOPC, 0, ANY)), - * ^ ^ ^ ^ - * Rn Rd Rs Rm - * - * Decoding the instruction "AND R4, R5, R6, ASL R15" will be rejected because - * Rs == R15 - * - * Decoding the instruction "AND R4, R5, R6, ASL R7" will be accepted and the - * instruction will be modified to "AND R0, R2, R3, ASL R1" and then placed into - * the kprobes instruction slot. This can then be called later by the handler - * function emulate_rd12rn16rm0rs8_rwflags (a pointer to which is retrieved from - * the indicated slot in the action array), in order to simulate the instruction. - */ - -enum decode_type { - DECODE_TYPE_END, - DECODE_TYPE_TABLE, - DECODE_TYPE_CUSTOM, - DECODE_TYPE_SIMULATE, - DECODE_TYPE_EMULATE, - DECODE_TYPE_OR, - DECODE_TYPE_REJECT, - NUM_DECODE_TYPES /* Must be last enum */ -}; - -#define DECODE_TYPE_BITS 4 -#define DECODE_TYPE_MASK ((1 << DECODE_TYPE_BITS) - 1) - -enum decode_reg_type { - REG_TYPE_NONE = 0, /* Not a register, ignore */ - REG_TYPE_ANY, /* Any register allowed */ - REG_TYPE_SAMEAS16, /* Register should be same as that at bits 19..16 */ - REG_TYPE_SP, /* Register must be SP */ - REG_TYPE_PC, /* Register must be PC */ - REG_TYPE_NOSP, /* Register must not be SP */ - REG_TYPE_NOSPPC, /* Register must not be SP or PC */ - REG_TYPE_NOPC, /* Register must not be PC */ - REG_TYPE_NOPCWB, /* No PC if load/store write-back flag also set */ - - /* The following types are used when the encoding for PC indicates - * another instruction form. This distiction only matters for test - * case coverage checks. - */ - REG_TYPE_NOPCX, /* Register must not be PC */ - REG_TYPE_NOSPPCX, /* Register must not be SP or PC */ - - /* Alias to allow '0' arg to be used in REGS macro. */ - REG_TYPE_0 = REG_TYPE_NONE -}; - -#define REGS(r16, r12, r8, r4, r0) \ - (((REG_TYPE_##r16) << 16) + \ - ((REG_TYPE_##r12) << 12) + \ - ((REG_TYPE_##r8) << 8) + \ - ((REG_TYPE_##r4) << 4) + \ - (REG_TYPE_##r0)) - -union decode_item { - u32 bits; - const union decode_item *table; - int action; -}; - -struct decode_header; -typedef enum probes_insn (probes_custom_decode_t)(probes_opcode_t, - struct arch_probes_insn *, - const struct decode_header *); - -union decode_action { - probes_insn_handler_t *handler; - probes_custom_decode_t *decoder; -}; - -#define DECODE_END \ - {.bits = DECODE_TYPE_END} - - -struct decode_header { - union decode_item type_regs; - union decode_item mask; - union decode_item value; -}; - -#define DECODE_HEADER(_type, _mask, _value, _regs) \ - {.bits = (_type) | ((_regs) << DECODE_TYPE_BITS)}, \ - {.bits = (_mask)}, \ - {.bits = (_value)} - - -struct decode_table { - struct decode_header header; - union decode_item table; -}; - -#define DECODE_TABLE(_mask, _value, _table) \ - DECODE_HEADER(DECODE_TYPE_TABLE, _mask, _value, 0), \ - {.table = (_table)} - - -struct decode_custom { - struct decode_header header; - union decode_item decoder; -}; - -#define DECODE_CUSTOM(_mask, _value, _decoder) \ - DECODE_HEADER(DECODE_TYPE_CUSTOM, _mask, _value, 0), \ - {.action = (_decoder)} - - -struct decode_simulate { - struct decode_header header; - union decode_item handler; -}; - -#define DECODE_SIMULATEX(_mask, _value, _handler, _regs) \ - DECODE_HEADER(DECODE_TYPE_SIMULATE, _mask, _value, _regs), \ - {.action = (_handler)} - -#define DECODE_SIMULATE(_mask, _value, _handler) \ - DECODE_SIMULATEX(_mask, _value, _handler, 0) - - -struct decode_emulate { - struct decode_header header; - union decode_item handler; -}; - -#define DECODE_EMULATEX(_mask, _value, _handler, _regs) \ - DECODE_HEADER(DECODE_TYPE_EMULATE, _mask, _value, _regs), \ - {.action = (_handler)} - -#define DECODE_EMULATE(_mask, _value, _handler) \ - DECODE_EMULATEX(_mask, _value, _handler, 0) - - -struct decode_or { - struct decode_header header; -}; - -#define DECODE_OR(_mask, _value) \ - DECODE_HEADER(DECODE_TYPE_OR, _mask, _value, 0) - -enum probes_insn { - INSN_REJECTED, - INSN_GOOD, - INSN_GOOD_NO_SLOT -}; - -struct decode_reject { - struct decode_header header; -}; - -#define DECODE_REJECT(_mask, _value) \ - DECODE_HEADER(DECODE_TYPE_REJECT, _mask, _value, 0) - -probes_insn_handler_t probes_simulate_nop; -probes_insn_handler_t probes_emulate_none; - -int __kprobes -probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - const union decode_item *table, bool thumb, bool emulate, - const union decode_action *actions); - -#endif diff --git a/arch/arm/kernel/uprobes-arm.c b/arch/arm/kernel/uprobes-arm.c deleted file mode 100644 index d3b655f..0000000 --- a/arch/arm/kernel/uprobes-arm.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2012 Rabin Vincent - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include "probes.h" -#include "probes-arm.h" -#include "uprobes.h" - -static int uprobes_substitute_pc(unsigned long *pinsn, u32 oregs) -{ - probes_opcode_t insn = __mem_to_opcode_arm(*pinsn); - probes_opcode_t temp; - probes_opcode_t mask; - int freereg; - u32 free = 0xffff; - u32 regs; - - for (regs = oregs; regs; regs >>= 4, insn >>= 4) { - if ((regs & 0xf) == REG_TYPE_NONE) - continue; - - free &= ~(1 << (insn & 0xf)); - } - - /* No PC, no problem */ - if (free & (1 << 15)) - return 15; - - if (!free) - return -1; - - /* - * fls instead of ffs ensures that for "ldrd r0, r1, [pc]" we would - * pick LR instead of R1. - */ - freereg = free = fls(free) - 1; - - temp = __mem_to_opcode_arm(*pinsn); - insn = temp; - regs = oregs; - mask = 0xf; - - for (; regs; regs >>= 4, mask <<= 4, free <<= 4, temp >>= 4) { - if ((regs & 0xf) == REG_TYPE_NONE) - continue; - - if ((temp & 0xf) != 15) - continue; - - insn &= ~mask; - insn |= free & mask; - } - - *pinsn = __opcode_to_mem_arm(insn); - return freereg; -} - -static void uprobe_set_pc(struct arch_uprobe *auprobe, - struct arch_uprobe_task *autask, - struct pt_regs *regs) -{ - u32 pcreg = auprobe->pcreg; - - autask->backup = regs->uregs[pcreg]; - regs->uregs[pcreg] = regs->ARM_pc + 8; -} - -static void uprobe_unset_pc(struct arch_uprobe *auprobe, - struct arch_uprobe_task *autask, - struct pt_regs *regs) -{ - /* PC will be taken care of by common code */ - regs->uregs[auprobe->pcreg] = autask->backup; -} - -static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe, - struct arch_uprobe_task *autask, - struct pt_regs *regs) -{ - u32 pcreg = auprobe->pcreg; - - alu_write_pc(regs->uregs[pcreg], regs); - regs->uregs[pcreg] = autask->backup; -} - -static void uprobe_write_pc(struct arch_uprobe *auprobe, - struct arch_uprobe_task *autask, - struct pt_regs *regs) -{ - u32 pcreg = auprobe->pcreg; - - load_write_pc(regs->uregs[pcreg], regs); - regs->uregs[pcreg] = autask->backup; -} - -enum probes_insn -decode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe, - asi); - struct decode_emulate *decode = (struct decode_emulate *) d; - u32 regs = decode->header.type_regs.bits >> DECODE_TYPE_BITS; - int reg; - - reg = uprobes_substitute_pc(&auprobe->ixol[0], regs); - if (reg == 15) - return INSN_GOOD; - - if (reg == -1) - return INSN_REJECTED; - - auprobe->pcreg = reg; - auprobe->prehandler = uprobe_set_pc; - auprobe->posthandler = uprobe_unset_pc; - - return INSN_GOOD; -} - -enum probes_insn -decode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d, bool alu) -{ - struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe, - asi); - enum probes_insn ret = decode_pc_ro(insn, asi, d); - - if (((insn >> 12) & 0xf) == 15) - auprobe->posthandler = alu ? uprobe_aluwrite_pc - : uprobe_write_pc; - - return ret; -} - -enum probes_insn -decode_rd12rn16rm0rs8_rwflags(probes_opcode_t insn, - struct arch_probes_insn *asi, - const struct decode_header *d) -{ - return decode_wb_pc(insn, asi, d, true); -} - -enum probes_insn -decode_ldr(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - return decode_wb_pc(insn, asi, d, false); -} - -enum probes_insn -uprobe_decode_ldmstm(probes_opcode_t insn, - struct arch_probes_insn *asi, - const struct decode_header *d) -{ - struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe, - asi); - unsigned reglist = insn & 0xffff; - int rn = (insn >> 16) & 0xf; - int lbit = insn & (1 << 20); - unsigned used = reglist | (1 << rn); - - if (rn == 15) - return INSN_REJECTED; - - if (!(used & (1 << 15))) - return INSN_GOOD; - - if (used & (1 << 14)) - return INSN_REJECTED; - - /* Use LR instead of PC */ - insn ^= 0xc000; - - auprobe->pcreg = 14; - auprobe->ixol[0] = __opcode_to_mem_arm(insn); - - auprobe->prehandler = uprobe_set_pc; - if (lbit) - auprobe->posthandler = uprobe_write_pc; - else - auprobe->posthandler = uprobe_unset_pc; - - return INSN_GOOD; -} - -const union decode_action uprobes_probes_actions[] = { - [PROBES_EMULATE_NONE] = {.handler = probes_simulate_nop}, - [PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop}, - [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop}, - [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop}, - [PROBES_BRANCH_IMM] = {.handler = simulate_blx1}, - [PROBES_MRS] = {.handler = simulate_mrs}, - [PROBES_BRANCH_REG] = {.handler = simulate_blx2bx}, - [PROBES_CLZ] = {.handler = probes_simulate_nop}, - [PROBES_SATURATING_ARITHMETIC] = {.handler = probes_simulate_nop}, - [PROBES_MUL1] = {.handler = probes_simulate_nop}, - [PROBES_MUL2] = {.handler = probes_simulate_nop}, - [PROBES_SWP] = {.handler = probes_simulate_nop}, - [PROBES_LDRSTRD] = {.decoder = decode_pc_ro}, - [PROBES_LOAD_EXTRA] = {.decoder = decode_pc_ro}, - [PROBES_LOAD] = {.decoder = decode_ldr}, - [PROBES_STORE_EXTRA] = {.decoder = decode_pc_ro}, - [PROBES_STORE] = {.decoder = decode_pc_ro}, - [PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp}, - [PROBES_DATA_PROCESSING_REG] = { - .decoder = decode_rd12rn16rm0rs8_rwflags}, - [PROBES_DATA_PROCESSING_IMM] = { - .decoder = decode_rd12rn16rm0rs8_rwflags}, - [PROBES_MOV_HALFWORD] = {.handler = probes_simulate_nop}, - [PROBES_SEV] = {.handler = probes_simulate_nop}, - [PROBES_WFE] = {.handler = probes_simulate_nop}, - [PROBES_SATURATE] = {.handler = probes_simulate_nop}, - [PROBES_REV] = {.handler = probes_simulate_nop}, - [PROBES_MMI] = {.handler = probes_simulate_nop}, - [PROBES_PACK] = {.handler = probes_simulate_nop}, - [PROBES_EXTEND] = {.handler = probes_simulate_nop}, - [PROBES_EXTEND_ADD] = {.handler = probes_simulate_nop}, - [PROBES_MUL_ADD_LONG] = {.handler = probes_simulate_nop}, - [PROBES_MUL_ADD] = {.handler = probes_simulate_nop}, - [PROBES_BITFIELD] = {.handler = probes_simulate_nop}, - [PROBES_BRANCH] = {.handler = simulate_bbl}, - [PROBES_LDMSTM] = {.decoder = uprobe_decode_ldmstm} -}; diff --git a/arch/arm/kernel/uprobes.c b/arch/arm/kernel/uprobes.c deleted file mode 100644 index 56adf9c..0000000 --- a/arch/arm/kernel/uprobes.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2012 Rabin Vincent - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "probes.h" -#include "probes-arm.h" -#include "uprobes.h" - -#define UPROBE_TRAP_NR UINT_MAX - -bool is_swbp_insn(uprobe_opcode_t *insn) -{ - return (__mem_to_opcode_arm(*insn) & 0x0fffffff) == - (UPROBE_SWBP_ARM_INSN & 0x0fffffff); -} - -int set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, - unsigned long vaddr) -{ - return uprobe_write_opcode(mm, vaddr, - __opcode_to_mem_arm(auprobe->bpinsn)); -} - -bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs) -{ - if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) { - regs->ARM_pc += 4; - return true; - } - - return false; -} - -bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) -{ - probes_opcode_t opcode; - - if (!auprobe->simulate) - return false; - - opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn); - - auprobe->asi.insn_singlestep(opcode, &auprobe->asi, regs); - - return true; -} - -unsigned long -arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, - struct pt_regs *regs) -{ - unsigned long orig_ret_vaddr; - - orig_ret_vaddr = regs->ARM_lr; - /* Replace the return addr with trampoline addr */ - regs->ARM_lr = trampoline_vaddr; - return orig_ret_vaddr; -} - -int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, - unsigned long addr) -{ - unsigned int insn; - unsigned int bpinsn; - enum probes_insn ret; - - /* Thumb not yet support */ - if (addr & 0x3) - return -EINVAL; - - insn = __mem_to_opcode_arm(*(unsigned int *)auprobe->insn); - auprobe->ixol[0] = __opcode_to_mem_arm(insn); - auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN); - - ret = arm_probes_decode_insn(insn, &auprobe->asi, false, - uprobes_probes_actions); - switch (ret) { - case INSN_REJECTED: - return -EINVAL; - - case INSN_GOOD_NO_SLOT: - auprobe->simulate = true; - break; - - case INSN_GOOD: - default: - break; - } - - bpinsn = UPROBE_SWBP_ARM_INSN & 0x0fffffff; - if (insn >= 0xe0000000) - bpinsn |= 0xe0000000; /* Unconditional instruction */ - else - bpinsn |= insn & 0xf0000000; /* Copy condition from insn */ - - auprobe->bpinsn = bpinsn; - - return 0; -} - -void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr, - void *src, unsigned long len) -{ - void *xol_page_kaddr = kmap_atomic(page); - void *dst = xol_page_kaddr + (vaddr & ~PAGE_MASK); - - preempt_disable(); - - /* Initialize the slot */ - memcpy(dst, src, len); - - /* flush caches (dcache/icache) */ - flush_uprobe_xol_access(page, vaddr, dst, len); - - preempt_enable(); - - kunmap_atomic(xol_page_kaddr); -} - - -int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) -{ - struct uprobe_task *utask = current->utask; - - if (auprobe->prehandler) - auprobe->prehandler(auprobe, &utask->autask, regs); - - utask->autask.saved_trap_no = current->thread.trap_no; - current->thread.trap_no = UPROBE_TRAP_NR; - regs->ARM_pc = utask->xol_vaddr; - - return 0; -} - -int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) -{ - struct uprobe_task *utask = current->utask; - - WARN_ON_ONCE(current->thread.trap_no != UPROBE_TRAP_NR); - - current->thread.trap_no = utask->autask.saved_trap_no; - regs->ARM_pc = utask->vaddr + 4; - - if (auprobe->posthandler) - auprobe->posthandler(auprobe, &utask->autask, regs); - - return 0; -} - -bool arch_uprobe_xol_was_trapped(struct task_struct *t) -{ - if (t->thread.trap_no != UPROBE_TRAP_NR) - return true; - - return false; -} - -void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) -{ - struct uprobe_task *utask = current->utask; - - current->thread.trap_no = utask->autask.saved_trap_no; - instruction_pointer_set(regs, utask->vaddr); -} - -int arch_uprobe_exception_notify(struct notifier_block *self, - unsigned long val, void *data) -{ - return NOTIFY_DONE; -} - -static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr) -{ - unsigned long flags; - - local_irq_save(flags); - instr &= 0x0fffffff; - if (instr == (UPROBE_SWBP_ARM_INSN & 0x0fffffff)) - uprobe_pre_sstep_notifier(regs); - else if (instr == (UPROBE_SS_ARM_INSN & 0x0fffffff)) - uprobe_post_sstep_notifier(regs); - local_irq_restore(flags); - - return 0; -} - -unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) -{ - return instruction_pointer(regs); -} - -static struct undef_hook uprobes_arm_break_hook = { - .instr_mask = 0x0fffffff, - .instr_val = (UPROBE_SWBP_ARM_INSN & 0x0fffffff), - .cpsr_mask = MODE_MASK, - .cpsr_val = USR_MODE, - .fn = uprobe_trap_handler, -}; - -static struct undef_hook uprobes_arm_ss_hook = { - .instr_mask = 0x0fffffff, - .instr_val = (UPROBE_SS_ARM_INSN & 0x0fffffff), - .cpsr_mask = MODE_MASK, - .cpsr_val = USR_MODE, - .fn = uprobe_trap_handler, -}; - -static int arch_uprobes_init(void) -{ - register_undef_hook(&uprobes_arm_break_hook); - register_undef_hook(&uprobes_arm_ss_hook); - - return 0; -} -device_initcall(arch_uprobes_init); diff --git a/arch/arm/kernel/uprobes.h b/arch/arm/kernel/uprobes.h deleted file mode 100644 index 1d0c12d..0000000 --- a/arch/arm/kernel/uprobes.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2012 Rabin Vincent - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __ARM_KERNEL_UPROBES_H -#define __ARM_KERNEL_UPROBES_H - -enum probes_insn uprobe_decode_ldmstm(probes_opcode_t insn, - struct arch_probes_insn *asi, - const struct decode_header *d); - -enum probes_insn decode_ldr(probes_opcode_t insn, - struct arch_probes_insn *asi, - const struct decode_header *d); - -enum probes_insn -decode_rd12rn16rm0rs8_rwflags(probes_opcode_t insn, - struct arch_probes_insn *asi, - const struct decode_header *d); - -enum probes_insn -decode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d, bool alu); - -enum probes_insn -decode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d); - -extern const union decode_action uprobes_probes_actions[]; - -#endif diff --git a/arch/arm/probes/Makefile b/arch/arm/probes/Makefile new file mode 100644 index 0000000..aa1f859 --- /dev/null +++ b/arch/arm/probes/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_UPROBES) += decode.o decode-arm.o uprobes/ +obj-$(CONFIG_KPROBES) += decode.o kprobes/ +ifdef CONFIG_THUMB2_KERNEL +obj-$(CONFIG_KPROBES) += decode-thumb.o +else +obj-$(CONFIG_KPROBES) += decode-arm.o +endif diff --git a/arch/arm/probes/decode-arm.c b/arch/arm/probes/decode-arm.c new file mode 100644 index 0000000..e39cc75 --- /dev/null +++ b/arch/arm/probes/decode-arm.c @@ -0,0 +1,735 @@ +/* + * + * arch/arm/probes/decode-arm.c + * + * Some code moved here from arch/arm/kernel/kprobes-arm.c + * + * Copyright (C) 2006, 2007 Motorola 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include + +#include "decode.h" +#include "decode-arm.h" + +#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit))))) + +#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25) + +/* + * To avoid the complications of mimicing single-stepping on a + * processor without a Next-PC or a single-step mode, and to + * avoid having to deal with the side-effects of boosting, we + * simulate or emulate (almost) all ARM instructions. + * + * "Simulation" is where the instruction's behavior is duplicated in + * C code. "Emulation" is where the original instruction is rewritten + * and executed, often by altering its registers. + * + * By having all behavior of the kprobe'd instruction completed before + * returning from the kprobe_handler(), all locks (scheduler and + * interrupt) can safely be released. There is no need for secondary + * breakpoints, no race with MP or preemptable kernels, nor having to + * clean up resources counts at a later time impacting overall system + * performance. By rewriting the instruction, only the minimum registers + * need to be loaded and saved back optimizing performance. + * + * Calling the insnslot_*_rwflags version of a function doesn't hurt + * anything even when the CPSR flags aren't updated by the + * instruction. It's just a little slower in return for saving + * a little space by not having a duplicate function that doesn't + * update the flags. (The same optimization can be said for + * instructions that do or don't perform register writeback) + * Also, instructions can either read the flags, only write the + * flags, or read and write the flags. To save combinations + * rather than for sheer performance, flag functions just assume + * read and write of flags. + */ + +void __kprobes simulate_bbl(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + long iaddr = (long) regs->ARM_pc - 4; + int disp = branch_displacement(insn); + + if (insn & (1 << 24)) + regs->ARM_lr = iaddr + 4; + + regs->ARM_pc = iaddr + 8 + disp; +} + +void __kprobes simulate_blx1(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + long iaddr = (long) regs->ARM_pc - 4; + int disp = branch_displacement(insn); + + regs->ARM_lr = iaddr + 4; + regs->ARM_pc = iaddr + 8 + disp + ((insn >> 23) & 0x2); + regs->ARM_cpsr |= PSR_T_BIT; +} + +void __kprobes simulate_blx2bx(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rm = insn & 0xf; + long rmv = regs->uregs[rm]; + + if (insn & (1 << 5)) + regs->ARM_lr = (long) regs->ARM_pc; + + regs->ARM_pc = rmv & ~0x1; + regs->ARM_cpsr &= ~PSR_T_BIT; + if (rmv & 0x1) + regs->ARM_cpsr |= PSR_T_BIT; +} + +void __kprobes simulate_mrs(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rd = (insn >> 12) & 0xf; + unsigned long mask = 0xf8ff03df; /* Mask out execution state */ + regs->uregs[rd] = regs->ARM_cpsr & mask; +} + +void __kprobes simulate_mov_ipsp(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + regs->uregs[12] = regs->uregs[13]; +} + +/* + * For the instruction masking and comparisons in all the "space_*" + * functions below, Do _not_ rearrange the order of tests unless + * you're very, very sure of what you are doing. For the sake of + * efficiency, the masks for some tests sometimes assume other test + * have been done prior to them so the number of patterns to test + * for an instruction set can be as broad as possible to reduce the + * number of tests needed. + */ + +static const union decode_item arm_1111_table[] = { + /* Unconditional instructions */ + + /* memory hint 1111 0100 x001 xxxx xxxx xxxx xxxx xxxx */ + /* PLDI (immediate) 1111 0100 x101 xxxx xxxx xxxx xxxx xxxx */ + /* PLDW (immediate) 1111 0101 x001 xxxx xxxx xxxx xxxx xxxx */ + /* PLD (immediate) 1111 0101 x101 xxxx xxxx xxxx xxxx xxxx */ + DECODE_SIMULATE (0xfe300000, 0xf4100000, PROBES_PRELOAD_IMM), + + /* memory hint 1111 0110 x001 xxxx xxxx xxxx xxx0 xxxx */ + /* PLDI (register) 1111 0110 x101 xxxx xxxx xxxx xxx0 xxxx */ + /* PLDW (register) 1111 0111 x001 xxxx xxxx xxxx xxx0 xxxx */ + /* PLD (register) 1111 0111 x101 xxxx xxxx xxxx xxx0 xxxx */ + DECODE_SIMULATE (0xfe300010, 0xf6100000, PROBES_PRELOAD_REG), + + /* BLX (immediate) 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx */ + DECODE_SIMULATE (0xfe000000, 0xfa000000, PROBES_BRANCH_IMM), + + /* CPS 1111 0001 0000 xxx0 xxxx xxxx xx0x xxxx */ + /* SETEND 1111 0001 0000 0001 xxxx xxxx 0000 xxxx */ + /* SRS 1111 100x x1x0 xxxx xxxx xxxx xxxx xxxx */ + /* RFE 1111 100x x0x1 xxxx xxxx xxxx xxxx xxxx */ + + /* Coprocessor instructions... */ + /* MCRR2 1111 1100 0100 xxxx xxxx xxxx xxxx xxxx */ + /* MRRC2 1111 1100 0101 xxxx xxxx xxxx xxxx xxxx */ + /* LDC2 1111 110x xxx1 xxxx xxxx xxxx xxxx xxxx */ + /* STC2 1111 110x xxx0 xxxx xxxx xxxx xxxx xxxx */ + /* CDP2 1111 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */ + /* MCR2 1111 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */ + /* MRC2 1111 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */ + + /* Other unallocated instructions... */ + DECODE_END +}; + +static const union decode_item arm_cccc_0001_0xx0____0xxx_table[] = { + /* Miscellaneous instructions */ + + /* MRS cpsr cccc 0001 0000 xxxx xxxx xxxx 0000 xxxx */ + DECODE_SIMULATEX(0x0ff000f0, 0x01000000, PROBES_MRS, + REGS(0, NOPC, 0, 0, 0)), + + /* BX cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx */ + DECODE_SIMULATE (0x0ff000f0, 0x01200010, PROBES_BRANCH_REG), + + /* BLX (register) cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx */ + DECODE_SIMULATEX(0x0ff000f0, 0x01200030, PROBES_BRANCH_REG, + REGS(0, 0, 0, 0, NOPC)), + + /* CLZ cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx */ + DECODE_EMULATEX (0x0ff000f0, 0x01600010, PROBES_CLZ, + REGS(0, NOPC, 0, 0, NOPC)), + + /* QADD cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx */ + /* QSUB cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx */ + /* QDADD cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx */ + /* QDSUB cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx */ + DECODE_EMULATEX (0x0f9000f0, 0x01000050, PROBES_SATURATING_ARITHMETIC, + REGS(NOPC, NOPC, 0, 0, NOPC)), + + /* BXJ cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx */ + /* MSR cccc 0001 0x10 xxxx xxxx xxxx 0000 xxxx */ + /* MRS spsr cccc 0001 0100 xxxx xxxx xxxx 0000 xxxx */ + /* BKPT 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */ + /* SMC cccc 0001 0110 xxxx xxxx xxxx 0111 xxxx */ + /* And unallocated instructions... */ + DECODE_END +}; + +static const union decode_item arm_cccc_0001_0xx0____1xx0_table[] = { + /* Halfword multiply and multiply-accumulate */ + + /* SMLALxy cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx */ + DECODE_EMULATEX (0x0ff00090, 0x01400080, PROBES_MUL1, + REGS(NOPC, NOPC, NOPC, 0, NOPC)), + + /* SMULWy cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx */ + DECODE_OR (0x0ff000b0, 0x012000a0), + /* SMULxy cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx */ + DECODE_EMULATEX (0x0ff00090, 0x01600080, PROBES_MUL2, + REGS(NOPC, 0, NOPC, 0, NOPC)), + + /* SMLAxy cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx */ + DECODE_OR (0x0ff00090, 0x01000080), + /* SMLAWy cccc 0001 0010 xxxx xxxx xxxx 1x00 xxxx */ + DECODE_EMULATEX (0x0ff000b0, 0x01200080, PROBES_MUL2, + REGS(NOPC, NOPC, NOPC, 0, NOPC)), + + DECODE_END +}; + +static const union decode_item arm_cccc_0000_____1001_table[] = { + /* Multiply and multiply-accumulate */ + + /* MUL cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx */ + /* MULS cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx */ + DECODE_EMULATEX (0x0fe000f0, 0x00000090, PROBES_MUL2, + REGS(NOPC, 0, NOPC, 0, NOPC)), + + /* MLA cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx */ + /* MLAS cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx */ + DECODE_OR (0x0fe000f0, 0x00200090), + /* MLS cccc 0000 0110 xxxx xxxx xxxx 1001 xxxx */ + DECODE_EMULATEX (0x0ff000f0, 0x00600090, PROBES_MUL2, + REGS(NOPC, NOPC, NOPC, 0, NOPC)), + + /* UMAAL cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx */ + DECODE_OR (0x0ff000f0, 0x00400090), + /* UMULL cccc 0000 1000 xxxx xxxx xxxx 1001 xxxx */ + /* UMULLS cccc 0000 1001 xxxx xxxx xxxx 1001 xxxx */ + /* UMLAL cccc 0000 1010 xxxx xxxx xxxx 1001 xxxx */ + /* UMLALS cccc 0000 1011 xxxx xxxx xxxx 1001 xxxx */ + /* SMULL cccc 0000 1100 xxxx xxxx xxxx 1001 xxxx */ + /* SMULLS cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx */ + /* SMLAL cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx */ + /* SMLALS cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx */ + DECODE_EMULATEX (0x0f8000f0, 0x00800090, PROBES_MUL1, + REGS(NOPC, NOPC, NOPC, 0, NOPC)), + + DECODE_END +}; + +static const union decode_item arm_cccc_0001_____1001_table[] = { + /* Synchronization primitives */ + +#if __LINUX_ARM_ARCH__ < 6 + /* Deprecated on ARMv6 and may be UNDEFINED on v7 */ + /* SMP/SWPB cccc 0001 0x00 xxxx xxxx xxxx 1001 xxxx */ + DECODE_EMULATEX (0x0fb000f0, 0x01000090, PROBES_SWP, + REGS(NOPC, NOPC, 0, 0, NOPC)), +#endif + /* LDREX/STREX{,D,B,H} cccc 0001 1xxx xxxx xxxx xxxx 1001 xxxx */ + /* And unallocated instructions... */ + DECODE_END +}; + +static const union decode_item arm_cccc_000x_____1xx1_table[] = { + /* Extra load/store instructions */ + + /* STRHT cccc 0000 xx10 xxxx xxxx xxxx 1011 xxxx */ + /* ??? cccc 0000 xx10 xxxx xxxx xxxx 11x1 xxxx */ + /* LDRHT cccc 0000 xx11 xxxx xxxx xxxx 1011 xxxx */ + /* LDRSBT cccc 0000 xx11 xxxx xxxx xxxx 1101 xxxx */ + /* LDRSHT cccc 0000 xx11 xxxx xxxx xxxx 1111 xxxx */ + DECODE_REJECT (0x0f200090, 0x00200090), + + /* LDRD/STRD lr,pc,{... cccc 000x x0x0 xxxx 111x xxxx 1101 xxxx */ + DECODE_REJECT (0x0e10e0d0, 0x0000e0d0), + + /* LDRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1101 xxxx */ + /* STRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx */ + DECODE_EMULATEX (0x0e5000d0, 0x000000d0, PROBES_LDRSTRD, + REGS(NOPCWB, NOPCX, 0, 0, NOPC)), + + /* LDRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1101 xxxx */ + /* STRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1111 xxxx */ + DECODE_EMULATEX (0x0e5000d0, 0x004000d0, PROBES_LDRSTRD, + REGS(NOPCWB, NOPCX, 0, 0, 0)), + + /* STRH (register) cccc 000x x0x0 xxxx xxxx xxxx 1011 xxxx */ + DECODE_EMULATEX (0x0e5000f0, 0x000000b0, PROBES_STORE_EXTRA, + REGS(NOPCWB, NOPC, 0, 0, NOPC)), + + /* LDRH (register) cccc 000x x0x1 xxxx xxxx xxxx 1011 xxxx */ + /* LDRSB (register) cccc 000x x0x1 xxxx xxxx xxxx 1101 xxxx */ + /* LDRSH (register) cccc 000x x0x1 xxxx xxxx xxxx 1111 xxxx */ + DECODE_EMULATEX (0x0e500090, 0x00100090, PROBES_LOAD_EXTRA, + REGS(NOPCWB, NOPC, 0, 0, NOPC)), + + /* STRH (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1011 xxxx */ + DECODE_EMULATEX (0x0e5000f0, 0x004000b0, PROBES_STORE_EXTRA, + REGS(NOPCWB, NOPC, 0, 0, 0)), + + /* LDRH (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1011 xxxx */ + /* LDRSB (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1101 xxxx */ + /* LDRSH (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1111 xxxx */ + DECODE_EMULATEX (0x0e500090, 0x00500090, PROBES_LOAD_EXTRA, + REGS(NOPCWB, NOPC, 0, 0, 0)), + + DECODE_END +}; + +static const union decode_item arm_cccc_000x_table[] = { + /* Data-processing (register) */ + + /* S PC, ... cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx */ + DECODE_REJECT (0x0e10f000, 0x0010f000), + + /* MOV IP, SP 1110 0001 1010 0000 1100 0000 0000 1101 */ + DECODE_SIMULATE (0xffffffff, 0xe1a0c00d, PROBES_MOV_IP_SP), + + /* TST (register) cccc 0001 0001 xxxx xxxx xxxx xxx0 xxxx */ + /* TEQ (register) cccc 0001 0011 xxxx xxxx xxxx xxx0 xxxx */ + /* CMP (register) cccc 0001 0101 xxxx xxxx xxxx xxx0 xxxx */ + /* CMN (register) cccc 0001 0111 xxxx xxxx xxxx xxx0 xxxx */ + DECODE_EMULATEX (0x0f900010, 0x01100000, PROBES_DATA_PROCESSING_REG, + REGS(ANY, 0, 0, 0, ANY)), + + /* MOV (register) cccc 0001 101x xxxx xxxx xxxx xxx0 xxxx */ + /* MVN (register) cccc 0001 111x xxxx xxxx xxxx xxx0 xxxx */ + DECODE_EMULATEX (0x0fa00010, 0x01a00000, PROBES_DATA_PROCESSING_REG, + REGS(0, ANY, 0, 0, ANY)), + + /* AND (register) cccc 0000 000x xxxx xxxx xxxx xxx0 xxxx */ + /* EOR (register) cccc 0000 001x xxxx xxxx xxxx xxx0 xxxx */ + /* SUB (register) cccc 0000 010x xxxx xxxx xxxx xxx0 xxxx */ + /* RSB (register) cccc 0000 011x xxxx xxxx xxxx xxx0 xxxx */ + /* ADD (register) cccc 0000 100x xxxx xxxx xxxx xxx0 xxxx */ + /* ADC (register) cccc 0000 101x xxxx xxxx xxxx xxx0 xxxx */ + /* SBC (register) cccc 0000 110x xxxx xxxx xxxx xxx0 xxxx */ + /* RSC (register) cccc 0000 111x xxxx xxxx xxxx xxx0 xxxx */ + /* ORR (register) cccc 0001 100x xxxx xxxx xxxx xxx0 xxxx */ + /* BIC (register) cccc 0001 110x xxxx xxxx xxxx xxx0 xxxx */ + DECODE_EMULATEX (0x0e000010, 0x00000000, PROBES_DATA_PROCESSING_REG, + REGS(ANY, ANY, 0, 0, ANY)), + + /* TST (reg-shift reg) cccc 0001 0001 xxxx xxxx xxxx 0xx1 xxxx */ + /* TEQ (reg-shift reg) cccc 0001 0011 xxxx xxxx xxxx 0xx1 xxxx */ + /* CMP (reg-shift reg) cccc 0001 0101 xxxx xxxx xxxx 0xx1 xxxx */ + /* CMN (reg-shift reg) cccc 0001 0111 xxxx xxxx xxxx 0xx1 xxxx */ + DECODE_EMULATEX (0x0f900090, 0x01100010, PROBES_DATA_PROCESSING_REG, + REGS(NOPC, 0, NOPC, 0, NOPC)), + + /* MOV (reg-shift reg) cccc 0001 101x xxxx xxxx xxxx 0xx1 xxxx */ + /* MVN (reg-shift reg) cccc 0001 111x xxxx xxxx xxxx 0xx1 xxxx */ + DECODE_EMULATEX (0x0fa00090, 0x01a00010, PROBES_DATA_PROCESSING_REG, + REGS(0, NOPC, NOPC, 0, NOPC)), + + /* AND (reg-shift reg) cccc 0000 000x xxxx xxxx xxxx 0xx1 xxxx */ + /* EOR (reg-shift reg) cccc 0000 001x xxxx xxxx xxxx 0xx1 xxxx */ + /* SUB (reg-shift reg) cccc 0000 010x xxxx xxxx xxxx 0xx1 xxxx */ + /* RSB (reg-shift reg) cccc 0000 011x xxxx xxxx xxxx 0xx1 xxxx */ + /* ADD (reg-shift reg) cccc 0000 100x xxxx xxxx xxxx 0xx1 xxxx */ + /* ADC (reg-shift reg) cccc 0000 101x xxxx xxxx xxxx 0xx1 xxxx */ + /* SBC (reg-shift reg) cccc 0000 110x xxxx xxxx xxxx 0xx1 xxxx */ + /* RSC (reg-shift reg) cccc 0000 111x xxxx xxxx xxxx 0xx1 xxxx */ + /* ORR (reg-shift reg) cccc 0001 100x xxxx xxxx xxxx 0xx1 xxxx */ + /* BIC (reg-shift reg) cccc 0001 110x xxxx xxxx xxxx 0xx1 xxxx */ + DECODE_EMULATEX (0x0e000090, 0x00000010, PROBES_DATA_PROCESSING_REG, + REGS(NOPC, NOPC, NOPC, 0, NOPC)), + + DECODE_END +}; + +static const union decode_item arm_cccc_001x_table[] = { + /* Data-processing (immediate) */ + + /* MOVW cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */ + /* MOVT cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0x0fb00000, 0x03000000, PROBES_DATA_PROCESSING_IMM, + REGS(0, NOPC, 0, 0, 0)), + + /* YIELD cccc 0011 0010 0000 xxxx xxxx 0000 0001 */ + DECODE_OR (0x0fff00ff, 0x03200001), + /* SEV cccc 0011 0010 0000 xxxx xxxx 0000 0100 */ + DECODE_EMULATE (0x0fff00ff, 0x03200004, PROBES_EMULATE_NONE), + /* NOP cccc 0011 0010 0000 xxxx xxxx 0000 0000 */ + /* WFE cccc 0011 0010 0000 xxxx xxxx 0000 0010 */ + /* WFI cccc 0011 0010 0000 xxxx xxxx 0000 0011 */ + DECODE_SIMULATE (0x0fff00fc, 0x03200000, PROBES_SIMULATE_NOP), + /* DBG cccc 0011 0010 0000 xxxx xxxx ffff xxxx */ + /* unallocated hints cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */ + /* MSR (immediate) cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0x0fb00000, 0x03200000), + + /* S PC, ... cccc 001x xxx1 xxxx 1111 xxxx xxxx xxxx */ + DECODE_REJECT (0x0e10f000, 0x0210f000), + + /* TST (immediate) cccc 0011 0001 xxxx xxxx xxxx xxxx xxxx */ + /* TEQ (immediate) cccc 0011 0011 xxxx xxxx xxxx xxxx xxxx */ + /* CMP (immediate) cccc 0011 0101 xxxx xxxx xxxx xxxx xxxx */ + /* CMN (immediate) cccc 0011 0111 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0x0f900000, 0x03100000, PROBES_DATA_PROCESSING_IMM, + REGS(ANY, 0, 0, 0, 0)), + + /* MOV (immediate) cccc 0011 101x xxxx xxxx xxxx xxxx xxxx */ + /* MVN (immediate) cccc 0011 111x xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0x0fa00000, 0x03a00000, PROBES_DATA_PROCESSING_IMM, + REGS(0, ANY, 0, 0, 0)), + + /* AND (immediate) cccc 0010 000x xxxx xxxx xxxx xxxx xxxx */ + /* EOR (immediate) cccc 0010 001x xxxx xxxx xxxx xxxx xxxx */ + /* SUB (immediate) cccc 0010 010x xxxx xxxx xxxx xxxx xxxx */ + /* RSB (immediate) cccc 0010 011x xxxx xxxx xxxx xxxx xxxx */ + /* ADD (immediate) cccc 0010 100x xxxx xxxx xxxx xxxx xxxx */ + /* ADC (immediate) cccc 0010 101x xxxx xxxx xxxx xxxx xxxx */ + /* SBC (immediate) cccc 0010 110x xxxx xxxx xxxx xxxx xxxx */ + /* RSC (immediate) cccc 0010 111x xxxx xxxx xxxx xxxx xxxx */ + /* ORR (immediate) cccc 0011 100x xxxx xxxx xxxx xxxx xxxx */ + /* BIC (immediate) cccc 0011 110x xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0x0e000000, 0x02000000, PROBES_DATA_PROCESSING_IMM, + REGS(ANY, ANY, 0, 0, 0)), + + DECODE_END +}; + +static const union decode_item arm_cccc_0110_____xxx1_table[] = { + /* Media instructions */ + + /* SEL cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx */ + DECODE_EMULATEX (0x0ff000f0, 0x068000b0, PROBES_SATURATE, + REGS(NOPC, NOPC, 0, 0, NOPC)), + + /* SSAT cccc 0110 101x xxxx xxxx xxxx xx01 xxxx */ + /* USAT cccc 0110 111x xxxx xxxx xxxx xx01 xxxx */ + DECODE_OR(0x0fa00030, 0x06a00010), + /* SSAT16 cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx */ + /* USAT16 cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx */ + DECODE_EMULATEX (0x0fb000f0, 0x06a00030, PROBES_SATURATE, + REGS(0, NOPC, 0, 0, NOPC)), + + /* REV cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx */ + /* REV16 cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx */ + /* RBIT cccc 0110 1111 xxxx xxxx xxxx 0011 xxxx */ + /* REVSH cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx */ + DECODE_EMULATEX (0x0fb00070, 0x06b00030, PROBES_REV, + REGS(0, NOPC, 0, 0, NOPC)), + + /* ??? cccc 0110 0x00 xxxx xxxx xxxx xxx1 xxxx */ + DECODE_REJECT (0x0fb00010, 0x06000010), + /* ??? cccc 0110 0xxx xxxx xxxx xxxx 1011 xxxx */ + DECODE_REJECT (0x0f8000f0, 0x060000b0), + /* ??? cccc 0110 0xxx xxxx xxxx xxxx 1101 xxxx */ + DECODE_REJECT (0x0f8000f0, 0x060000d0), + /* SADD16 cccc 0110 0001 xxxx xxxx xxxx 0001 xxxx */ + /* SADDSUBX cccc 0110 0001 xxxx xxxx xxxx 0011 xxxx */ + /* SSUBADDX cccc 0110 0001 xxxx xxxx xxxx 0101 xxxx */ + /* SSUB16 cccc 0110 0001 xxxx xxxx xxxx 0111 xxxx */ + /* SADD8 cccc 0110 0001 xxxx xxxx xxxx 1001 xxxx */ + /* SSUB8 cccc 0110 0001 xxxx xxxx xxxx 1111 xxxx */ + /* QADD16 cccc 0110 0010 xxxx xxxx xxxx 0001 xxxx */ + /* QADDSUBX cccc 0110 0010 xxxx xxxx xxxx 0011 xxxx */ + /* QSUBADDX cccc 0110 0010 xxxx xxxx xxxx 0101 xxxx */ + /* QSUB16 cccc 0110 0010 xxxx xxxx xxxx 0111 xxxx */ + /* QADD8 cccc 0110 0010 xxxx xxxx xxxx 1001 xxxx */ + /* QSUB8 cccc 0110 0010 xxxx xxxx xxxx 1111 xxxx */ + /* SHADD16 cccc 0110 0011 xxxx xxxx xxxx 0001 xxxx */ + /* SHADDSUBX cccc 0110 0011 xxxx xxxx xxxx 0011 xxxx */ + /* SHSUBADDX cccc 0110 0011 xxxx xxxx xxxx 0101 xxxx */ + /* SHSUB16 cccc 0110 0011 xxxx xxxx xxxx 0111 xxxx */ + /* SHADD8 cccc 0110 0011 xxxx xxxx xxxx 1001 xxxx */ + /* SHSUB8 cccc 0110 0011 xxxx xxxx xxxx 1111 xxxx */ + /* UADD16 cccc 0110 0101 xxxx xxxx xxxx 0001 xxxx */ + /* UADDSUBX cccc 0110 0101 xxxx xxxx xxxx 0011 xxxx */ + /* USUBADDX cccc 0110 0101 xxxx xxxx xxxx 0101 xxxx */ + /* USUB16 cccc 0110 0101 xxxx xxxx xxxx 0111 xxxx */ + /* UADD8 cccc 0110 0101 xxxx xxxx xxxx 1001 xxxx */ + /* USUB8 cccc 0110 0101 xxxx xxxx xxxx 1111 xxxx */ + /* UQADD16 cccc 0110 0110 xxxx xxxx xxxx 0001 xxxx */ + /* UQADDSUBX cccc 0110 0110 xxxx xxxx xxxx 0011 xxxx */ + /* UQSUBADDX cccc 0110 0110 xxxx xxxx xxxx 0101 xxxx */ + /* UQSUB16 cccc 0110 0110 xxxx xxxx xxxx 0111 xxxx */ + /* UQADD8 cccc 0110 0110 xxxx xxxx xxxx 1001 xxxx */ + /* UQSUB8 cccc 0110 0110 xxxx xxxx xxxx 1111 xxxx */ + /* UHADD16 cccc 0110 0111 xxxx xxxx xxxx 0001 xxxx */ + /* UHADDSUBX cccc 0110 0111 xxxx xxxx xxxx 0011 xxxx */ + /* UHSUBADDX cccc 0110 0111 xxxx xxxx xxxx 0101 xxxx */ + /* UHSUB16 cccc 0110 0111 xxxx xxxx xxxx 0111 xxxx */ + /* UHADD8 cccc 0110 0111 xxxx xxxx xxxx 1001 xxxx */ + /* UHSUB8 cccc 0110 0111 xxxx xxxx xxxx 1111 xxxx */ + DECODE_EMULATEX (0x0f800010, 0x06000010, PROBES_MMI, + REGS(NOPC, NOPC, 0, 0, NOPC)), + + /* PKHBT cccc 0110 1000 xxxx xxxx xxxx x001 xxxx */ + /* PKHTB cccc 0110 1000 xxxx xxxx xxxx x101 xxxx */ + DECODE_EMULATEX (0x0ff00030, 0x06800010, PROBES_PACK, + REGS(NOPC, NOPC, 0, 0, NOPC)), + + /* ??? cccc 0110 1001 xxxx xxxx xxxx 0111 xxxx */ + /* ??? cccc 0110 1101 xxxx xxxx xxxx 0111 xxxx */ + DECODE_REJECT (0x0fb000f0, 0x06900070), + + /* SXTB16 cccc 0110 1000 1111 xxxx xxxx 0111 xxxx */ + /* SXTB cccc 0110 1010 1111 xxxx xxxx 0111 xxxx */ + /* SXTH cccc 0110 1011 1111 xxxx xxxx 0111 xxxx */ + /* UXTB16 cccc 0110 1100 1111 xxxx xxxx 0111 xxxx */ + /* UXTB cccc 0110 1110 1111 xxxx xxxx 0111 xxxx */ + /* UXTH cccc 0110 1111 1111 xxxx xxxx 0111 xxxx */ + DECODE_EMULATEX (0x0f8f00f0, 0x068f0070, PROBES_EXTEND, + REGS(0, NOPC, 0, 0, NOPC)), + + /* SXTAB16 cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx */ + /* SXTAB cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx */ + /* SXTAH cccc 0110 1011 xxxx xxxx xxxx 0111 xxxx */ + /* UXTAB16 cccc 0110 1100 xxxx xxxx xxxx 0111 xxxx */ + /* UXTAB cccc 0110 1110 xxxx xxxx xxxx 0111 xxxx */ + /* UXTAH cccc 0110 1111 xxxx xxxx xxxx 0111 xxxx */ + DECODE_EMULATEX (0x0f8000f0, 0x06800070, PROBES_EXTEND_ADD, + REGS(NOPCX, NOPC, 0, 0, NOPC)), + + DECODE_END +}; + +static const union decode_item arm_cccc_0111_____xxx1_table[] = { + /* Media instructions */ + + /* UNDEFINED cccc 0111 1111 xxxx xxxx xxxx 1111 xxxx */ + DECODE_REJECT (0x0ff000f0, 0x07f000f0), + + /* SMLALD cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx */ + /* SMLSLD cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx */ + DECODE_EMULATEX (0x0ff00090, 0x07400010, PROBES_MUL_ADD_LONG, + REGS(NOPC, NOPC, NOPC, 0, NOPC)), + + /* SMUAD cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx */ + /* SMUSD cccc 0111 0000 xxxx 1111 xxxx 01x1 xxxx */ + DECODE_OR (0x0ff0f090, 0x0700f010), + /* SMMUL cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx */ + DECODE_OR (0x0ff0f0d0, 0x0750f010), + /* USAD8 cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx */ + DECODE_EMULATEX (0x0ff0f0f0, 0x0780f010, PROBES_MUL_ADD, + REGS(NOPC, 0, NOPC, 0, NOPC)), + + /* SMLAD cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx */ + /* SMLSD cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx */ + DECODE_OR (0x0ff00090, 0x07000010), + /* SMMLA cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx */ + DECODE_OR (0x0ff000d0, 0x07500010), + /* USADA8 cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx */ + DECODE_EMULATEX (0x0ff000f0, 0x07800010, PROBES_MUL_ADD, + REGS(NOPC, NOPCX, NOPC, 0, NOPC)), + + /* SMMLS cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx */ + DECODE_EMULATEX (0x0ff000d0, 0x075000d0, PROBES_MUL_ADD, + REGS(NOPC, NOPC, NOPC, 0, NOPC)), + + /* SBFX cccc 0111 101x xxxx xxxx xxxx x101 xxxx */ + /* UBFX cccc 0111 111x xxxx xxxx xxxx x101 xxxx */ + DECODE_EMULATEX (0x0fa00070, 0x07a00050, PROBES_BITFIELD, + REGS(0, NOPC, 0, 0, NOPC)), + + /* BFC cccc 0111 110x xxxx xxxx xxxx x001 1111 */ + DECODE_EMULATEX (0x0fe0007f, 0x07c0001f, PROBES_BITFIELD, + REGS(0, NOPC, 0, 0, 0)), + + /* BFI cccc 0111 110x xxxx xxxx xxxx x001 xxxx */ + DECODE_EMULATEX (0x0fe00070, 0x07c00010, PROBES_BITFIELD, + REGS(0, NOPC, 0, 0, NOPCX)), + + DECODE_END +}; + +static const union decode_item arm_cccc_01xx_table[] = { + /* Load/store word and unsigned byte */ + + /* LDRB/STRB pc,[...] cccc 01xx x0xx xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0x0c40f000, 0x0440f000), + + /* STRT cccc 01x0 x010 xxxx xxxx xxxx xxxx xxxx */ + /* LDRT cccc 01x0 x011 xxxx xxxx xxxx xxxx xxxx */ + /* STRBT cccc 01x0 x110 xxxx xxxx xxxx xxxx xxxx */ + /* LDRBT cccc 01x0 x111 xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0x0d200000, 0x04200000), + + /* STR (immediate) cccc 010x x0x0 xxxx xxxx xxxx xxxx xxxx */ + /* STRB (immediate) cccc 010x x1x0 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0x0e100000, 0x04000000, PROBES_STORE, + REGS(NOPCWB, ANY, 0, 0, 0)), + + /* LDR (immediate) cccc 010x x0x1 xxxx xxxx xxxx xxxx xxxx */ + /* LDRB (immediate) cccc 010x x1x1 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0x0e100000, 0x04100000, PROBES_LOAD, + REGS(NOPCWB, ANY, 0, 0, 0)), + + /* STR (register) cccc 011x x0x0 xxxx xxxx xxxx xxxx xxxx */ + /* STRB (register) cccc 011x x1x0 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0x0e100000, 0x06000000, PROBES_STORE, + REGS(NOPCWB, ANY, 0, 0, NOPC)), + + /* LDR (register) cccc 011x x0x1 xxxx xxxx xxxx xxxx xxxx */ + /* LDRB (register) cccc 011x x1x1 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0x0e100000, 0x06100000, PROBES_LOAD, + REGS(NOPCWB, ANY, 0, 0, NOPC)), + + DECODE_END +}; + +static const union decode_item arm_cccc_100x_table[] = { + /* Block data transfer instructions */ + + /* LDM cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx */ + /* STM cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx */ + DECODE_CUSTOM (0x0e400000, 0x08000000, PROBES_LDMSTM), + + /* STM (user registers) cccc 100x x1x0 xxxx xxxx xxxx xxxx xxxx */ + /* LDM (user registers) cccc 100x x1x1 xxxx 0xxx xxxx xxxx xxxx */ + /* LDM (exception ret) cccc 100x x1x1 xxxx 1xxx xxxx xxxx xxxx */ + DECODE_END +}; + +const union decode_item probes_decode_arm_table[] = { + /* + * Unconditional instructions + * 1111 xxxx xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xf0000000, 0xf0000000, arm_1111_table), + + /* + * Miscellaneous instructions + * cccc 0001 0xx0 xxxx xxxx xxxx 0xxx xxxx + */ + DECODE_TABLE (0x0f900080, 0x01000000, arm_cccc_0001_0xx0____0xxx_table), + + /* + * Halfword multiply and multiply-accumulate + * cccc 0001 0xx0 xxxx xxxx xxxx 1xx0 xxxx + */ + DECODE_TABLE (0x0f900090, 0x01000080, arm_cccc_0001_0xx0____1xx0_table), + + /* + * Multiply and multiply-accumulate + * cccc 0000 xxxx xxxx xxxx xxxx 1001 xxxx + */ + DECODE_TABLE (0x0f0000f0, 0x00000090, arm_cccc_0000_____1001_table), + + /* + * Synchronization primitives + * cccc 0001 xxxx xxxx xxxx xxxx 1001 xxxx + */ + DECODE_TABLE (0x0f0000f0, 0x01000090, arm_cccc_0001_____1001_table), + + /* + * Extra load/store instructions + * cccc 000x xxxx xxxx xxxx xxxx 1xx1 xxxx + */ + DECODE_TABLE (0x0e000090, 0x00000090, arm_cccc_000x_____1xx1_table), + + /* + * Data-processing (register) + * cccc 000x xxxx xxxx xxxx xxxx xxx0 xxxx + * Data-processing (register-shifted register) + * cccc 000x xxxx xxxx xxxx xxxx 0xx1 xxxx + */ + DECODE_TABLE (0x0e000000, 0x00000000, arm_cccc_000x_table), + + /* + * Data-processing (immediate) + * cccc 001x xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0x0e000000, 0x02000000, arm_cccc_001x_table), + + /* + * Media instructions + * cccc 011x xxxx xxxx xxxx xxxx xxx1 xxxx + */ + DECODE_TABLE (0x0f000010, 0x06000010, arm_cccc_0110_____xxx1_table), + DECODE_TABLE (0x0f000010, 0x07000010, arm_cccc_0111_____xxx1_table), + + /* + * Load/store word and unsigned byte + * cccc 01xx xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0x0c000000, 0x04000000, arm_cccc_01xx_table), + + /* + * Block data transfer instructions + * cccc 100x xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0x0e000000, 0x08000000, arm_cccc_100x_table), + + /* B cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx */ + /* BL cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx */ + DECODE_SIMULATE (0x0e000000, 0x0a000000, PROBES_BRANCH), + + /* + * Supervisor Call, and coprocessor instructions + */ + + /* MCRR cccc 1100 0100 xxxx xxxx xxxx xxxx xxxx */ + /* MRRC cccc 1100 0101 xxxx xxxx xxxx xxxx xxxx */ + /* LDC cccc 110x xxx1 xxxx xxxx xxxx xxxx xxxx */ + /* STC cccc 110x xxx0 xxxx xxxx xxxx xxxx xxxx */ + /* CDP cccc 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */ + /* MCR cccc 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */ + /* MRC cccc 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */ + /* SVC cccc 1111 xxxx xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0x0c000000, 0x0c000000), + + DECODE_END +}; +#ifdef CONFIG_ARM_KPROBES_TEST_MODULE +EXPORT_SYMBOL_GPL(probes_decode_arm_table); +#endif + +static void __kprobes arm_singlestep(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + regs->ARM_pc += 4; + asi->insn_handler(insn, asi, regs); +} + +/* Return: + * INSN_REJECTED If instruction is one not allowed to kprobe, + * INSN_GOOD If instruction is supported and uses instruction slot, + * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot. + * + * For instructions we don't want to kprobe (INSN_REJECTED return result): + * These are generally ones that modify the processor state making + * them "hard" to simulate such as switches processor modes or + * make accesses in alternate modes. Any of these could be simulated + * if the work was put into it, but low return considering they + * should also be very rare. + */ +enum probes_insn __kprobes +arm_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + bool emulate, const union decode_action *actions) +{ + asi->insn_singlestep = arm_singlestep; + asi->insn_check_cc = probes_condition_checks[insn>>28]; + return probes_decode_insn(insn, asi, probes_decode_arm_table, false, + emulate, actions); +} diff --git a/arch/arm/probes/decode-arm.h b/arch/arm/probes/decode-arm.h new file mode 100644 index 0000000..9c56b40 --- /dev/null +++ b/arch/arm/probes/decode-arm.h @@ -0,0 +1,75 @@ +/* + * arch/arm/probes/decode-arm.h + * + * Copyright 2013 Linaro Ltd. + * Written by: David A. Long + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef _ARM_KERNEL_PROBES_ARM_H +#define _ARM_KERNEL_PROBES_ARM_H + +#include "decode.h" + +enum probes_arm_action { + PROBES_EMULATE_NONE, + PROBES_SIMULATE_NOP, + PROBES_PRELOAD_IMM, + PROBES_PRELOAD_REG, + PROBES_BRANCH_IMM, + PROBES_BRANCH_REG, + PROBES_MRS, + PROBES_CLZ, + PROBES_SATURATING_ARITHMETIC, + PROBES_MUL1, + PROBES_MUL2, + PROBES_SWP, + PROBES_LDRSTRD, + PROBES_LOAD, + PROBES_STORE, + PROBES_LOAD_EXTRA, + PROBES_STORE_EXTRA, + PROBES_MOV_IP_SP, + PROBES_DATA_PROCESSING_REG, + PROBES_DATA_PROCESSING_IMM, + PROBES_MOV_HALFWORD, + PROBES_SEV, + PROBES_WFE, + PROBES_SATURATE, + PROBES_REV, + PROBES_MMI, + PROBES_PACK, + PROBES_EXTEND, + PROBES_EXTEND_ADD, + PROBES_MUL_ADD_LONG, + PROBES_MUL_ADD, + PROBES_BITFIELD, + PROBES_BRANCH, + PROBES_LDMSTM, + NUM_PROBES_ARM_ACTIONS +}; + +void __kprobes simulate_bbl(probes_opcode_t opcode, + struct arch_probes_insn *asi, struct pt_regs *regs); +void __kprobes simulate_blx1(probes_opcode_t opcode, + struct arch_probes_insn *asi, struct pt_regs *regs); +void __kprobes simulate_blx2bx(probes_opcode_t opcode, + struct arch_probes_insn *asi, struct pt_regs *regs); +void __kprobes simulate_mrs(probes_opcode_t opcode, + struct arch_probes_insn *asi, struct pt_regs *regs); +void __kprobes simulate_mov_ipsp(probes_opcode_t opcode, + struct arch_probes_insn *asi, struct pt_regs *regs); + +extern const union decode_item probes_decode_arm_table[]; + +enum probes_insn arm_probes_decode_insn(probes_opcode_t, + struct arch_probes_insn *, bool emulate, + const union decode_action *actions); + +#endif diff --git a/arch/arm/probes/decode-thumb.c b/arch/arm/probes/decode-thumb.c new file mode 100644 index 0000000..2f0453a --- /dev/null +++ b/arch/arm/probes/decode-thumb.c @@ -0,0 +1,882 @@ +/* + * arch/arm/probes/decode-thumb.c + * + * Copyright (C) 2011 Jon Medhurst . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "decode.h" +#include "decode-thumb.h" + + +static const union decode_item t32_table_1110_100x_x0xx[] = { + /* Load/store multiple instructions */ + + /* Rn is PC 1110 100x x0xx 1111 xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfe4f0000, 0xe80f0000), + + /* SRS 1110 1000 00x0 xxxx xxxx xxxx xxxx xxxx */ + /* RFE 1110 1000 00x1 xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xffc00000, 0xe8000000), + /* SRS 1110 1001 10x0 xxxx xxxx xxxx xxxx xxxx */ + /* RFE 1110 1001 10x1 xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xffc00000, 0xe9800000), + + /* STM Rn, {...pc} 1110 100x x0x0 xxxx 1xxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfe508000, 0xe8008000), + /* LDM Rn, {...lr,pc} 1110 100x x0x1 xxxx 11xx xxxx xxxx xxxx */ + DECODE_REJECT (0xfe50c000, 0xe810c000), + /* LDM/STM Rn, {...sp} 1110 100x x0xx xxxx xx1x xxxx xxxx xxxx */ + DECODE_REJECT (0xfe402000, 0xe8002000), + + /* STMIA 1110 1000 10x0 xxxx xxxx xxxx xxxx xxxx */ + /* LDMIA 1110 1000 10x1 xxxx xxxx xxxx xxxx xxxx */ + /* STMDB 1110 1001 00x0 xxxx xxxx xxxx xxxx xxxx */ + /* LDMDB 1110 1001 00x1 xxxx xxxx xxxx xxxx xxxx */ + DECODE_CUSTOM (0xfe400000, 0xe8000000, PROBES_T32_LDMSTM), + + DECODE_END +}; + +static const union decode_item t32_table_1110_100x_x1xx[] = { + /* Load/store dual, load/store exclusive, table branch */ + + /* STRD (immediate) 1110 1000 x110 xxxx xxxx xxxx xxxx xxxx */ + /* LDRD (immediate) 1110 1000 x111 xxxx xxxx xxxx xxxx xxxx */ + DECODE_OR (0xff600000, 0xe8600000), + /* STRD (immediate) 1110 1001 x1x0 xxxx xxxx xxxx xxxx xxxx */ + /* LDRD (immediate) 1110 1001 x1x1 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xff400000, 0xe9400000, PROBES_T32_LDRDSTRD, + REGS(NOPCWB, NOSPPC, NOSPPC, 0, 0)), + + /* TBB 1110 1000 1101 xxxx xxxx xxxx 0000 xxxx */ + /* TBH 1110 1000 1101 xxxx xxxx xxxx 0001 xxxx */ + DECODE_SIMULATEX(0xfff000e0, 0xe8d00000, PROBES_T32_TABLE_BRANCH, + REGS(NOSP, 0, 0, 0, NOSPPC)), + + /* STREX 1110 1000 0100 xxxx xxxx xxxx xxxx xxxx */ + /* LDREX 1110 1000 0101 xxxx xxxx xxxx xxxx xxxx */ + /* STREXB 1110 1000 1100 xxxx xxxx xxxx 0100 xxxx */ + /* STREXH 1110 1000 1100 xxxx xxxx xxxx 0101 xxxx */ + /* STREXD 1110 1000 1100 xxxx xxxx xxxx 0111 xxxx */ + /* LDREXB 1110 1000 1101 xxxx xxxx xxxx 0100 xxxx */ + /* LDREXH 1110 1000 1101 xxxx xxxx xxxx 0101 xxxx */ + /* LDREXD 1110 1000 1101 xxxx xxxx xxxx 0111 xxxx */ + /* And unallocated instructions... */ + DECODE_END +}; + +static const union decode_item t32_table_1110_101x[] = { + /* Data-processing (shifted register) */ + + /* TST 1110 1010 0001 xxxx xxxx 1111 xxxx xxxx */ + /* TEQ 1110 1010 1001 xxxx xxxx 1111 xxxx xxxx */ + DECODE_EMULATEX (0xff700f00, 0xea100f00, PROBES_T32_TST, + REGS(NOSPPC, 0, 0, 0, NOSPPC)), + + /* CMN 1110 1011 0001 xxxx xxxx 1111 xxxx xxxx */ + DECODE_OR (0xfff00f00, 0xeb100f00), + /* CMP 1110 1011 1011 xxxx xxxx 1111 xxxx xxxx */ + DECODE_EMULATEX (0xfff00f00, 0xebb00f00, PROBES_T32_TST, + REGS(NOPC, 0, 0, 0, NOSPPC)), + + /* MOV 1110 1010 010x 1111 xxxx xxxx xxxx xxxx */ + /* MVN 1110 1010 011x 1111 xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xffcf0000, 0xea4f0000, PROBES_T32_MOV, + REGS(0, 0, NOSPPC, 0, NOSPPC)), + + /* ??? 1110 1010 101x xxxx xxxx xxxx xxxx xxxx */ + /* ??? 1110 1010 111x xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xffa00000, 0xeaa00000), + /* ??? 1110 1011 001x xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xffe00000, 0xeb200000), + /* ??? 1110 1011 100x xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xffe00000, 0xeb800000), + /* ??? 1110 1011 111x xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xffe00000, 0xebe00000), + + /* ADD/SUB SP, SP, Rm, LSL #0..3 */ + /* 1110 1011 x0xx 1101 x000 1101 xx00 xxxx */ + DECODE_EMULATEX (0xff4f7f30, 0xeb0d0d00, PROBES_T32_ADDSUB, + REGS(SP, 0, SP, 0, NOSPPC)), + + /* ADD/SUB SP, SP, Rm, shift */ + /* 1110 1011 x0xx 1101 xxxx 1101 xxxx xxxx */ + DECODE_REJECT (0xff4f0f00, 0xeb0d0d00), + + /* ADD/SUB Rd, SP, Rm, shift */ + /* 1110 1011 x0xx 1101 xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xff4f0000, 0xeb0d0000, PROBES_T32_ADDSUB, + REGS(SP, 0, NOPC, 0, NOSPPC)), + + /* AND 1110 1010 000x xxxx xxxx xxxx xxxx xxxx */ + /* BIC 1110 1010 001x xxxx xxxx xxxx xxxx xxxx */ + /* ORR 1110 1010 010x xxxx xxxx xxxx xxxx xxxx */ + /* ORN 1110 1010 011x xxxx xxxx xxxx xxxx xxxx */ + /* EOR 1110 1010 100x xxxx xxxx xxxx xxxx xxxx */ + /* PKH 1110 1010 110x xxxx xxxx xxxx xxxx xxxx */ + /* ADD 1110 1011 000x xxxx xxxx xxxx xxxx xxxx */ + /* ADC 1110 1011 010x xxxx xxxx xxxx xxxx xxxx */ + /* SBC 1110 1011 011x xxxx xxxx xxxx xxxx xxxx */ + /* SUB 1110 1011 101x xxxx xxxx xxxx xxxx xxxx */ + /* RSB 1110 1011 110x xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfe000000, 0xea000000, PROBES_T32_LOGICAL, + REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)), + + DECODE_END +}; + +static const union decode_item t32_table_1111_0x0x___0[] = { + /* Data-processing (modified immediate) */ + + /* TST 1111 0x00 0001 xxxx 0xxx 1111 xxxx xxxx */ + /* TEQ 1111 0x00 1001 xxxx 0xxx 1111 xxxx xxxx */ + DECODE_EMULATEX (0xfb708f00, 0xf0100f00, PROBES_T32_TST, + REGS(NOSPPC, 0, 0, 0, 0)), + + /* CMN 1111 0x01 0001 xxxx 0xxx 1111 xxxx xxxx */ + DECODE_OR (0xfbf08f00, 0xf1100f00), + /* CMP 1111 0x01 1011 xxxx 0xxx 1111 xxxx xxxx */ + DECODE_EMULATEX (0xfbf08f00, 0xf1b00f00, PROBES_T32_CMP, + REGS(NOPC, 0, 0, 0, 0)), + + /* MOV 1111 0x00 010x 1111 0xxx xxxx xxxx xxxx */ + /* MVN 1111 0x00 011x 1111 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfbcf8000, 0xf04f0000, PROBES_T32_MOV, + REGS(0, 0, NOSPPC, 0, 0)), + + /* ??? 1111 0x00 101x xxxx 0xxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfbe08000, 0xf0a00000), + /* ??? 1111 0x00 110x xxxx 0xxx xxxx xxxx xxxx */ + /* ??? 1111 0x00 111x xxxx 0xxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfbc08000, 0xf0c00000), + /* ??? 1111 0x01 001x xxxx 0xxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfbe08000, 0xf1200000), + /* ??? 1111 0x01 100x xxxx 0xxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfbe08000, 0xf1800000), + /* ??? 1111 0x01 111x xxxx 0xxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfbe08000, 0xf1e00000), + + /* ADD Rd, SP, #imm 1111 0x01 000x 1101 0xxx xxxx xxxx xxxx */ + /* SUB Rd, SP, #imm 1111 0x01 101x 1101 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfb4f8000, 0xf10d0000, PROBES_T32_ADDSUB, + REGS(SP, 0, NOPC, 0, 0)), + + /* AND 1111 0x00 000x xxxx 0xxx xxxx xxxx xxxx */ + /* BIC 1111 0x00 001x xxxx 0xxx xxxx xxxx xxxx */ + /* ORR 1111 0x00 010x xxxx 0xxx xxxx xxxx xxxx */ + /* ORN 1111 0x00 011x xxxx 0xxx xxxx xxxx xxxx */ + /* EOR 1111 0x00 100x xxxx 0xxx xxxx xxxx xxxx */ + /* ADD 1111 0x01 000x xxxx 0xxx xxxx xxxx xxxx */ + /* ADC 1111 0x01 010x xxxx 0xxx xxxx xxxx xxxx */ + /* SBC 1111 0x01 011x xxxx 0xxx xxxx xxxx xxxx */ + /* SUB 1111 0x01 101x xxxx 0xxx xxxx xxxx xxxx */ + /* RSB 1111 0x01 110x xxxx 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfa008000, 0xf0000000, PROBES_T32_LOGICAL, + REGS(NOSPPC, 0, NOSPPC, 0, 0)), + + DECODE_END +}; + +static const union decode_item t32_table_1111_0x1x___0[] = { + /* Data-processing (plain binary immediate) */ + + /* ADDW Rd, PC, #imm 1111 0x10 0000 1111 0xxx xxxx xxxx xxxx */ + DECODE_OR (0xfbff8000, 0xf20f0000), + /* SUBW Rd, PC, #imm 1111 0x10 1010 1111 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfbff8000, 0xf2af0000, PROBES_T32_ADDWSUBW_PC, + REGS(PC, 0, NOSPPC, 0, 0)), + + /* ADDW SP, SP, #imm 1111 0x10 0000 1101 0xxx 1101 xxxx xxxx */ + DECODE_OR (0xfbff8f00, 0xf20d0d00), + /* SUBW SP, SP, #imm 1111 0x10 1010 1101 0xxx 1101 xxxx xxxx */ + DECODE_EMULATEX (0xfbff8f00, 0xf2ad0d00, PROBES_T32_ADDWSUBW, + REGS(SP, 0, SP, 0, 0)), + + /* ADDW 1111 0x10 0000 xxxx 0xxx xxxx xxxx xxxx */ + DECODE_OR (0xfbf08000, 0xf2000000), + /* SUBW 1111 0x10 1010 xxxx 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfbf08000, 0xf2a00000, PROBES_T32_ADDWSUBW, + REGS(NOPCX, 0, NOSPPC, 0, 0)), + + /* MOVW 1111 0x10 0100 xxxx 0xxx xxxx xxxx xxxx */ + /* MOVT 1111 0x10 1100 xxxx 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfb708000, 0xf2400000, PROBES_T32_MOVW, + REGS(0, 0, NOSPPC, 0, 0)), + + /* SSAT16 1111 0x11 0010 xxxx 0000 xxxx 00xx xxxx */ + /* SSAT 1111 0x11 00x0 xxxx 0xxx xxxx xxxx xxxx */ + /* USAT16 1111 0x11 1010 xxxx 0000 xxxx 00xx xxxx */ + /* USAT 1111 0x11 10x0 xxxx 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfb508000, 0xf3000000, PROBES_T32_SAT, + REGS(NOSPPC, 0, NOSPPC, 0, 0)), + + /* SFBX 1111 0x11 0100 xxxx 0xxx xxxx xxxx xxxx */ + /* UFBX 1111 0x11 1100 xxxx 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfb708000, 0xf3400000, PROBES_T32_BITFIELD, + REGS(NOSPPC, 0, NOSPPC, 0, 0)), + + /* BFC 1111 0x11 0110 1111 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfbff8000, 0xf36f0000, PROBES_T32_BITFIELD, + REGS(0, 0, NOSPPC, 0, 0)), + + /* BFI 1111 0x11 0110 xxxx 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfbf08000, 0xf3600000, PROBES_T32_BITFIELD, + REGS(NOSPPCX, 0, NOSPPC, 0, 0)), + + DECODE_END +}; + +static const union decode_item t32_table_1111_0xxx___1[] = { + /* Branches and miscellaneous control */ + + /* YIELD 1111 0011 1010 xxxx 10x0 x000 0000 0001 */ + DECODE_OR (0xfff0d7ff, 0xf3a08001), + /* SEV 1111 0011 1010 xxxx 10x0 x000 0000 0100 */ + DECODE_EMULATE (0xfff0d7ff, 0xf3a08004, PROBES_T32_SEV), + /* NOP 1111 0011 1010 xxxx 10x0 x000 0000 0000 */ + /* WFE 1111 0011 1010 xxxx 10x0 x000 0000 0010 */ + /* WFI 1111 0011 1010 xxxx 10x0 x000 0000 0011 */ + DECODE_SIMULATE (0xfff0d7fc, 0xf3a08000, PROBES_T32_WFE), + + /* MRS Rd, CPSR 1111 0011 1110 xxxx 10x0 xxxx xxxx xxxx */ + DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, PROBES_T32_MRS, + REGS(0, 0, NOSPPC, 0, 0)), + + /* + * Unsupported instructions + * 1111 0x11 1xxx xxxx 10x0 xxxx xxxx xxxx + * + * MSR 1111 0011 100x xxxx 10x0 xxxx xxxx xxxx + * DBG hint 1111 0011 1010 xxxx 10x0 x000 1111 xxxx + * Unallocated hints 1111 0011 1010 xxxx 10x0 x000 xxxx xxxx + * CPS 1111 0011 1010 xxxx 10x0 xxxx xxxx xxxx + * CLREX/DSB/DMB/ISB 1111 0011 1011 xxxx 10x0 xxxx xxxx xxxx + * BXJ 1111 0011 1100 xxxx 10x0 xxxx xxxx xxxx + * SUBS PC,LR,# 1111 0011 1101 xxxx 10x0 xxxx xxxx xxxx + * MRS Rd, SPSR 1111 0011 1111 xxxx 10x0 xxxx xxxx xxxx + * SMC 1111 0111 1111 xxxx 1000 xxxx xxxx xxxx + * UNDEFINED 1111 0111 1111 xxxx 1010 xxxx xxxx xxxx + * ??? 1111 0111 1xxx xxxx 1010 xxxx xxxx xxxx + */ + DECODE_REJECT (0xfb80d000, 0xf3808000), + + /* Bcc 1111 0xxx xxxx xxxx 10x0 xxxx xxxx xxxx */ + DECODE_CUSTOM (0xf800d000, 0xf0008000, PROBES_T32_BRANCH_COND), + + /* BLX 1111 0xxx xxxx xxxx 11x0 xxxx xxxx xxx0 */ + DECODE_OR (0xf800d001, 0xf000c000), + /* B 1111 0xxx xxxx xxxx 10x1 xxxx xxxx xxxx */ + /* BL 1111 0xxx xxxx xxxx 11x1 xxxx xxxx xxxx */ + DECODE_SIMULATE (0xf8009000, 0xf0009000, PROBES_T32_BRANCH), + + DECODE_END +}; + +static const union decode_item t32_table_1111_100x_x0x1__1111[] = { + /* Memory hints */ + + /* PLD (literal) 1111 1000 x001 1111 1111 xxxx xxxx xxxx */ + /* PLI (literal) 1111 1001 x001 1111 1111 xxxx xxxx xxxx */ + DECODE_SIMULATE (0xfe7ff000, 0xf81ff000, PROBES_T32_PLDI), + + /* PLD{W} (immediate) 1111 1000 10x1 xxxx 1111 xxxx xxxx xxxx */ + DECODE_OR (0xffd0f000, 0xf890f000), + /* PLD{W} (immediate) 1111 1000 00x1 xxxx 1111 1100 xxxx xxxx */ + DECODE_OR (0xffd0ff00, 0xf810fc00), + /* PLI (immediate) 1111 1001 1001 xxxx 1111 xxxx xxxx xxxx */ + DECODE_OR (0xfff0f000, 0xf990f000), + /* PLI (immediate) 1111 1001 0001 xxxx 1111 1100 xxxx xxxx */ + DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, PROBES_T32_PLDI, + REGS(NOPCX, 0, 0, 0, 0)), + + /* PLD{W} (register) 1111 1000 00x1 xxxx 1111 0000 00xx xxxx */ + DECODE_OR (0xffd0ffc0, 0xf810f000), + /* PLI (register) 1111 1001 0001 xxxx 1111 0000 00xx xxxx */ + DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, PROBES_T32_PLDI, + REGS(NOPCX, 0, 0, 0, NOSPPC)), + + /* Other unallocated instructions... */ + DECODE_END +}; + +static const union decode_item t32_table_1111_100x[] = { + /* Store/Load single data item */ + + /* ??? 1111 100x x11x xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfe600000, 0xf8600000), + + /* ??? 1111 1001 0101 xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfff00000, 0xf9500000), + + /* ??? 1111 100x 0xxx xxxx xxxx 10x0 xxxx xxxx */ + DECODE_REJECT (0xfe800d00, 0xf8000800), + + /* STRBT 1111 1000 0000 xxxx xxxx 1110 xxxx xxxx */ + /* STRHT 1111 1000 0010 xxxx xxxx 1110 xxxx xxxx */ + /* STRT 1111 1000 0100 xxxx xxxx 1110 xxxx xxxx */ + /* LDRBT 1111 1000 0001 xxxx xxxx 1110 xxxx xxxx */ + /* LDRSBT 1111 1001 0001 xxxx xxxx 1110 xxxx xxxx */ + /* LDRHT 1111 1000 0011 xxxx xxxx 1110 xxxx xxxx */ + /* LDRSHT 1111 1001 0011 xxxx xxxx 1110 xxxx xxxx */ + /* LDRT 1111 1000 0101 xxxx xxxx 1110 xxxx xxxx */ + DECODE_REJECT (0xfe800f00, 0xf8000e00), + + /* STR{,B,H} Rn,[PC...] 1111 1000 xxx0 1111 xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xff1f0000, 0xf80f0000), + + /* STR{,B,H} PC,[Rn...] 1111 1000 xxx0 xxxx 1111 xxxx xxxx xxxx */ + DECODE_REJECT (0xff10f000, 0xf800f000), + + /* LDR (literal) 1111 1000 x101 1111 xxxx xxxx xxxx xxxx */ + DECODE_SIMULATEX(0xff7f0000, 0xf85f0000, PROBES_T32_LDR_LIT, + REGS(PC, ANY, 0, 0, 0)), + + /* STR (immediate) 1111 1000 0100 xxxx xxxx 1xxx xxxx xxxx */ + /* LDR (immediate) 1111 1000 0101 xxxx xxxx 1xxx xxxx xxxx */ + DECODE_OR (0xffe00800, 0xf8400800), + /* STR (immediate) 1111 1000 1100 xxxx xxxx xxxx xxxx xxxx */ + /* LDR (immediate) 1111 1000 1101 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xffe00000, 0xf8c00000, PROBES_T32_LDRSTR, + REGS(NOPCX, ANY, 0, 0, 0)), + + /* STR (register) 1111 1000 0100 xxxx xxxx 0000 00xx xxxx */ + /* LDR (register) 1111 1000 0101 xxxx xxxx 0000 00xx xxxx */ + DECODE_EMULATEX (0xffe00fc0, 0xf8400000, PROBES_T32_LDRSTR, + REGS(NOPCX, ANY, 0, 0, NOSPPC)), + + /* LDRB (literal) 1111 1000 x001 1111 xxxx xxxx xxxx xxxx */ + /* LDRSB (literal) 1111 1001 x001 1111 xxxx xxxx xxxx xxxx */ + /* LDRH (literal) 1111 1000 x011 1111 xxxx xxxx xxxx xxxx */ + /* LDRSH (literal) 1111 1001 x011 1111 xxxx xxxx xxxx xxxx */ + DECODE_SIMULATEX(0xfe5f0000, 0xf81f0000, PROBES_T32_LDR_LIT, + REGS(PC, NOSPPCX, 0, 0, 0)), + + /* STRB (immediate) 1111 1000 0000 xxxx xxxx 1xxx xxxx xxxx */ + /* STRH (immediate) 1111 1000 0010 xxxx xxxx 1xxx xxxx xxxx */ + /* LDRB (immediate) 1111 1000 0001 xxxx xxxx 1xxx xxxx xxxx */ + /* LDRSB (immediate) 1111 1001 0001 xxxx xxxx 1xxx xxxx xxxx */ + /* LDRH (immediate) 1111 1000 0011 xxxx xxxx 1xxx xxxx xxxx */ + /* LDRSH (immediate) 1111 1001 0011 xxxx xxxx 1xxx xxxx xxxx */ + DECODE_OR (0xfec00800, 0xf8000800), + /* STRB (immediate) 1111 1000 1000 xxxx xxxx xxxx xxxx xxxx */ + /* STRH (immediate) 1111 1000 1010 xxxx xxxx xxxx xxxx xxxx */ + /* LDRB (immediate) 1111 1000 1001 xxxx xxxx xxxx xxxx xxxx */ + /* LDRSB (immediate) 1111 1001 1001 xxxx xxxx xxxx xxxx xxxx */ + /* LDRH (immediate) 1111 1000 1011 xxxx xxxx xxxx xxxx xxxx */ + /* LDRSH (immediate) 1111 1001 1011 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfec00000, 0xf8800000, PROBES_T32_LDRSTR, + REGS(NOPCX, NOSPPCX, 0, 0, 0)), + + /* STRB (register) 1111 1000 0000 xxxx xxxx 0000 00xx xxxx */ + /* STRH (register) 1111 1000 0010 xxxx xxxx 0000 00xx xxxx */ + /* LDRB (register) 1111 1000 0001 xxxx xxxx 0000 00xx xxxx */ + /* LDRSB (register) 1111 1001 0001 xxxx xxxx 0000 00xx xxxx */ + /* LDRH (register) 1111 1000 0011 xxxx xxxx 0000 00xx xxxx */ + /* LDRSH (register) 1111 1001 0011 xxxx xxxx 0000 00xx xxxx */ + DECODE_EMULATEX (0xfe800fc0, 0xf8000000, PROBES_T32_LDRSTR, + REGS(NOPCX, NOSPPCX, 0, 0, NOSPPC)), + + /* Other unallocated instructions... */ + DECODE_END +}; + +static const union decode_item t32_table_1111_1010___1111[] = { + /* Data-processing (register) */ + + /* ??? 1111 1010 011x xxxx 1111 xxxx 1xxx xxxx */ + DECODE_REJECT (0xffe0f080, 0xfa60f080), + + /* SXTH 1111 1010 0000 1111 1111 xxxx 1xxx xxxx */ + /* UXTH 1111 1010 0001 1111 1111 xxxx 1xxx xxxx */ + /* SXTB16 1111 1010 0010 1111 1111 xxxx 1xxx xxxx */ + /* UXTB16 1111 1010 0011 1111 1111 xxxx 1xxx xxxx */ + /* SXTB 1111 1010 0100 1111 1111 xxxx 1xxx xxxx */ + /* UXTB 1111 1010 0101 1111 1111 xxxx 1xxx xxxx */ + DECODE_EMULATEX (0xff8ff080, 0xfa0ff080, PROBES_T32_SIGN_EXTEND, + REGS(0, 0, NOSPPC, 0, NOSPPC)), + + + /* ??? 1111 1010 1xxx xxxx 1111 xxxx 0x11 xxxx */ + DECODE_REJECT (0xff80f0b0, 0xfa80f030), + /* ??? 1111 1010 1x11 xxxx 1111 xxxx 0xxx xxxx */ + DECODE_REJECT (0xffb0f080, 0xfab0f000), + + /* SADD16 1111 1010 1001 xxxx 1111 xxxx 0000 xxxx */ + /* SASX 1111 1010 1010 xxxx 1111 xxxx 0000 xxxx */ + /* SSAX 1111 1010 1110 xxxx 1111 xxxx 0000 xxxx */ + /* SSUB16 1111 1010 1101 xxxx 1111 xxxx 0000 xxxx */ + /* SADD8 1111 1010 1000 xxxx 1111 xxxx 0000 xxxx */ + /* SSUB8 1111 1010 1100 xxxx 1111 xxxx 0000 xxxx */ + + /* QADD16 1111 1010 1001 xxxx 1111 xxxx 0001 xxxx */ + /* QASX 1111 1010 1010 xxxx 1111 xxxx 0001 xxxx */ + /* QSAX 1111 1010 1110 xxxx 1111 xxxx 0001 xxxx */ + /* QSUB16 1111 1010 1101 xxxx 1111 xxxx 0001 xxxx */ + /* QADD8 1111 1010 1000 xxxx 1111 xxxx 0001 xxxx */ + /* QSUB8 1111 1010 1100 xxxx 1111 xxxx 0001 xxxx */ + + /* SHADD16 1111 1010 1001 xxxx 1111 xxxx 0010 xxxx */ + /* SHASX 1111 1010 1010 xxxx 1111 xxxx 0010 xxxx */ + /* SHSAX 1111 1010 1110 xxxx 1111 xxxx 0010 xxxx */ + /* SHSUB16 1111 1010 1101 xxxx 1111 xxxx 0010 xxxx */ + /* SHADD8 1111 1010 1000 xxxx 1111 xxxx 0010 xxxx */ + /* SHSUB8 1111 1010 1100 xxxx 1111 xxxx 0010 xxxx */ + + /* UADD16 1111 1010 1001 xxxx 1111 xxxx 0100 xxxx */ + /* UASX 1111 1010 1010 xxxx 1111 xxxx 0100 xxxx */ + /* USAX 1111 1010 1110 xxxx 1111 xxxx 0100 xxxx */ + /* USUB16 1111 1010 1101 xxxx 1111 xxxx 0100 xxxx */ + /* UADD8 1111 1010 1000 xxxx 1111 xxxx 0100 xxxx */ + /* USUB8 1111 1010 1100 xxxx 1111 xxxx 0100 xxxx */ + + /* UQADD16 1111 1010 1001 xxxx 1111 xxxx 0101 xxxx */ + /* UQASX 1111 1010 1010 xxxx 1111 xxxx 0101 xxxx */ + /* UQSAX 1111 1010 1110 xxxx 1111 xxxx 0101 xxxx */ + /* UQSUB16 1111 1010 1101 xxxx 1111 xxxx 0101 xxxx */ + /* UQADD8 1111 1010 1000 xxxx 1111 xxxx 0101 xxxx */ + /* UQSUB8 1111 1010 1100 xxxx 1111 xxxx 0101 xxxx */ + + /* UHADD16 1111 1010 1001 xxxx 1111 xxxx 0110 xxxx */ + /* UHASX 1111 1010 1010 xxxx 1111 xxxx 0110 xxxx */ + /* UHSAX 1111 1010 1110 xxxx 1111 xxxx 0110 xxxx */ + /* UHSUB16 1111 1010 1101 xxxx 1111 xxxx 0110 xxxx */ + /* UHADD8 1111 1010 1000 xxxx 1111 xxxx 0110 xxxx */ + /* UHSUB8 1111 1010 1100 xxxx 1111 xxxx 0110 xxxx */ + DECODE_OR (0xff80f080, 0xfa80f000), + + /* SXTAH 1111 1010 0000 xxxx 1111 xxxx 1xxx xxxx */ + /* UXTAH 1111 1010 0001 xxxx 1111 xxxx 1xxx xxxx */ + /* SXTAB16 1111 1010 0010 xxxx 1111 xxxx 1xxx xxxx */ + /* UXTAB16 1111 1010 0011 xxxx 1111 xxxx 1xxx xxxx */ + /* SXTAB 1111 1010 0100 xxxx 1111 xxxx 1xxx xxxx */ + /* UXTAB 1111 1010 0101 xxxx 1111 xxxx 1xxx xxxx */ + DECODE_OR (0xff80f080, 0xfa00f080), + + /* QADD 1111 1010 1000 xxxx 1111 xxxx 1000 xxxx */ + /* QDADD 1111 1010 1000 xxxx 1111 xxxx 1001 xxxx */ + /* QSUB 1111 1010 1000 xxxx 1111 xxxx 1010 xxxx */ + /* QDSUB 1111 1010 1000 xxxx 1111 xxxx 1011 xxxx */ + DECODE_OR (0xfff0f0c0, 0xfa80f080), + + /* SEL 1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */ + DECODE_OR (0xfff0f0f0, 0xfaa0f080), + + /* LSL 1111 1010 000x xxxx 1111 xxxx 0000 xxxx */ + /* LSR 1111 1010 001x xxxx 1111 xxxx 0000 xxxx */ + /* ASR 1111 1010 010x xxxx 1111 xxxx 0000 xxxx */ + /* ROR 1111 1010 011x xxxx 1111 xxxx 0000 xxxx */ + DECODE_EMULATEX (0xff80f0f0, 0xfa00f000, PROBES_T32_MEDIA, + REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)), + + /* CLZ 1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */ + DECODE_OR (0xfff0f0f0, 0xfab0f080), + + /* REV 1111 1010 1001 xxxx 1111 xxxx 1000 xxxx */ + /* REV16 1111 1010 1001 xxxx 1111 xxxx 1001 xxxx */ + /* RBIT 1111 1010 1001 xxxx 1111 xxxx 1010 xxxx */ + /* REVSH 1111 1010 1001 xxxx 1111 xxxx 1011 xxxx */ + DECODE_EMULATEX (0xfff0f0c0, 0xfa90f080, PROBES_T32_REVERSE, + REGS(NOSPPC, 0, NOSPPC, 0, SAMEAS16)), + + /* Other unallocated instructions... */ + DECODE_END +}; + +static const union decode_item t32_table_1111_1011_0[] = { + /* Multiply, multiply accumulate, and absolute difference */ + + /* ??? 1111 1011 0000 xxxx 1111 xxxx 0001 xxxx */ + DECODE_REJECT (0xfff0f0f0, 0xfb00f010), + /* ??? 1111 1011 0111 xxxx 1111 xxxx 0001 xxxx */ + DECODE_REJECT (0xfff0f0f0, 0xfb70f010), + + /* SMULxy 1111 1011 0001 xxxx 1111 xxxx 00xx xxxx */ + DECODE_OR (0xfff0f0c0, 0xfb10f000), + /* MUL 1111 1011 0000 xxxx 1111 xxxx 0000 xxxx */ + /* SMUAD{X} 1111 1011 0010 xxxx 1111 xxxx 000x xxxx */ + /* SMULWy 1111 1011 0011 xxxx 1111 xxxx 000x xxxx */ + /* SMUSD{X} 1111 1011 0100 xxxx 1111 xxxx 000x xxxx */ + /* SMMUL{R} 1111 1011 0101 xxxx 1111 xxxx 000x xxxx */ + /* USAD8 1111 1011 0111 xxxx 1111 xxxx 0000 xxxx */ + DECODE_EMULATEX (0xff80f0e0, 0xfb00f000, PROBES_T32_MUL_ADD, + REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)), + + /* ??? 1111 1011 0111 xxxx xxxx xxxx 0001 xxxx */ + DECODE_REJECT (0xfff000f0, 0xfb700010), + + /* SMLAxy 1111 1011 0001 xxxx xxxx xxxx 00xx xxxx */ + DECODE_OR (0xfff000c0, 0xfb100000), + /* MLA 1111 1011 0000 xxxx xxxx xxxx 0000 xxxx */ + /* MLS 1111 1011 0000 xxxx xxxx xxxx 0001 xxxx */ + /* SMLAD{X} 1111 1011 0010 xxxx xxxx xxxx 000x xxxx */ + /* SMLAWy 1111 1011 0011 xxxx xxxx xxxx 000x xxxx */ + /* SMLSD{X} 1111 1011 0100 xxxx xxxx xxxx 000x xxxx */ + /* SMMLA{R} 1111 1011 0101 xxxx xxxx xxxx 000x xxxx */ + /* SMMLS{R} 1111 1011 0110 xxxx xxxx xxxx 000x xxxx */ + /* USADA8 1111 1011 0111 xxxx xxxx xxxx 0000 xxxx */ + DECODE_EMULATEX (0xff8000c0, 0xfb000000, PROBES_T32_MUL_ADD2, + REGS(NOSPPC, NOSPPCX, NOSPPC, 0, NOSPPC)), + + /* Other unallocated instructions... */ + DECODE_END +}; + +static const union decode_item t32_table_1111_1011_1[] = { + /* Long multiply, long multiply accumulate, and divide */ + + /* UMAAL 1111 1011 1110 xxxx xxxx xxxx 0110 xxxx */ + DECODE_OR (0xfff000f0, 0xfbe00060), + /* SMLALxy 1111 1011 1100 xxxx xxxx xxxx 10xx xxxx */ + DECODE_OR (0xfff000c0, 0xfbc00080), + /* SMLALD{X} 1111 1011 1100 xxxx xxxx xxxx 110x xxxx */ + /* SMLSLD{X} 1111 1011 1101 xxxx xxxx xxxx 110x xxxx */ + DECODE_OR (0xffe000e0, 0xfbc000c0), + /* SMULL 1111 1011 1000 xxxx xxxx xxxx 0000 xxxx */ + /* UMULL 1111 1011 1010 xxxx xxxx xxxx 0000 xxxx */ + /* SMLAL 1111 1011 1100 xxxx xxxx xxxx 0000 xxxx */ + /* UMLAL 1111 1011 1110 xxxx xxxx xxxx 0000 xxxx */ + DECODE_EMULATEX (0xff9000f0, 0xfb800000, PROBES_T32_MUL_ADD_LONG, + REGS(NOSPPC, NOSPPC, NOSPPC, 0, NOSPPC)), + + /* SDIV 1111 1011 1001 xxxx xxxx xxxx 1111 xxxx */ + /* UDIV 1111 1011 1011 xxxx xxxx xxxx 1111 xxxx */ + /* Other unallocated instructions... */ + DECODE_END +}; + +const union decode_item probes_decode_thumb32_table[] = { + + /* + * Load/store multiple instructions + * 1110 100x x0xx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xfe400000, 0xe8000000, t32_table_1110_100x_x0xx), + + /* + * Load/store dual, load/store exclusive, table branch + * 1110 100x x1xx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xfe400000, 0xe8400000, t32_table_1110_100x_x1xx), + + /* + * Data-processing (shifted register) + * 1110 101x xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xfe000000, 0xea000000, t32_table_1110_101x), + + /* + * Coprocessor instructions + * 1110 11xx xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_REJECT (0xfc000000, 0xec000000), + + /* + * Data-processing (modified immediate) + * 1111 0x0x xxxx xxxx 0xxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xfa008000, 0xf0000000, t32_table_1111_0x0x___0), + + /* + * Data-processing (plain binary immediate) + * 1111 0x1x xxxx xxxx 0xxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xfa008000, 0xf2000000, t32_table_1111_0x1x___0), + + /* + * Branches and miscellaneous control + * 1111 0xxx xxxx xxxx 1xxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xf8008000, 0xf0008000, t32_table_1111_0xxx___1), + + /* + * Advanced SIMD element or structure load/store instructions + * 1111 1001 xxx0 xxxx xxxx xxxx xxxx xxxx + */ + DECODE_REJECT (0xff100000, 0xf9000000), + + /* + * Memory hints + * 1111 100x x0x1 xxxx 1111 xxxx xxxx xxxx + */ + DECODE_TABLE (0xfe50f000, 0xf810f000, t32_table_1111_100x_x0x1__1111), + + /* + * Store single data item + * 1111 1000 xxx0 xxxx xxxx xxxx xxxx xxxx + * Load single data items + * 1111 100x xxx1 xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xfe000000, 0xf8000000, t32_table_1111_100x), + + /* + * Data-processing (register) + * 1111 1010 xxxx xxxx 1111 xxxx xxxx xxxx + */ + DECODE_TABLE (0xff00f000, 0xfa00f000, t32_table_1111_1010___1111), + + /* + * Multiply, multiply accumulate, and absolute difference + * 1111 1011 0xxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xff800000, 0xfb000000, t32_table_1111_1011_0), + + /* + * Long multiply, long multiply accumulate, and divide + * 1111 1011 1xxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xff800000, 0xfb800000, t32_table_1111_1011_1), + + /* + * Coprocessor instructions + * 1111 11xx xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_END +}; +#ifdef CONFIG_ARM_KPROBES_TEST_MODULE +EXPORT_SYMBOL_GPL(probes_decode_thumb32_table); +#endif + +static const union decode_item t16_table_1011[] = { + /* Miscellaneous 16-bit instructions */ + + /* ADD (SP plus immediate) 1011 0000 0xxx xxxx */ + /* SUB (SP minus immediate) 1011 0000 1xxx xxxx */ + DECODE_SIMULATE (0xff00, 0xb000, PROBES_T16_ADD_SP), + + /* CBZ 1011 00x1 xxxx xxxx */ + /* CBNZ 1011 10x1 xxxx xxxx */ + DECODE_SIMULATE (0xf500, 0xb100, PROBES_T16_CBZ), + + /* SXTH 1011 0010 00xx xxxx */ + /* SXTB 1011 0010 01xx xxxx */ + /* UXTH 1011 0010 10xx xxxx */ + /* UXTB 1011 0010 11xx xxxx */ + /* REV 1011 1010 00xx xxxx */ + /* REV16 1011 1010 01xx xxxx */ + /* ??? 1011 1010 10xx xxxx */ + /* REVSH 1011 1010 11xx xxxx */ + DECODE_REJECT (0xffc0, 0xba80), + DECODE_EMULATE (0xf500, 0xb000, PROBES_T16_SIGN_EXTEND), + + /* PUSH 1011 010x xxxx xxxx */ + DECODE_CUSTOM (0xfe00, 0xb400, PROBES_T16_PUSH), + /* POP 1011 110x xxxx xxxx */ + DECODE_CUSTOM (0xfe00, 0xbc00, PROBES_T16_POP), + + /* + * If-Then, and hints + * 1011 1111 xxxx xxxx + */ + + /* YIELD 1011 1111 0001 0000 */ + DECODE_OR (0xffff, 0xbf10), + /* SEV 1011 1111 0100 0000 */ + DECODE_EMULATE (0xffff, 0xbf40, PROBES_T16_SEV), + /* NOP 1011 1111 0000 0000 */ + /* WFE 1011 1111 0010 0000 */ + /* WFI 1011 1111 0011 0000 */ + DECODE_SIMULATE (0xffcf, 0xbf00, PROBES_T16_WFE), + /* Unassigned hints 1011 1111 xxxx 0000 */ + DECODE_REJECT (0xff0f, 0xbf00), + /* IT 1011 1111 xxxx xxxx */ + DECODE_CUSTOM (0xff00, 0xbf00, PROBES_T16_IT), + + /* SETEND 1011 0110 010x xxxx */ + /* CPS 1011 0110 011x xxxx */ + /* BKPT 1011 1110 xxxx xxxx */ + /* And unallocated instructions... */ + DECODE_END +}; + +const union decode_item probes_decode_thumb16_table[] = { + + /* + * Shift (immediate), add, subtract, move, and compare + * 00xx xxxx xxxx xxxx + */ + + /* CMP (immediate) 0010 1xxx xxxx xxxx */ + DECODE_EMULATE (0xf800, 0x2800, PROBES_T16_CMP), + + /* ADD (register) 0001 100x xxxx xxxx */ + /* SUB (register) 0001 101x xxxx xxxx */ + /* LSL (immediate) 0000 0xxx xxxx xxxx */ + /* LSR (immediate) 0000 1xxx xxxx xxxx */ + /* ASR (immediate) 0001 0xxx xxxx xxxx */ + /* ADD (immediate, Thumb) 0001 110x xxxx xxxx */ + /* SUB (immediate, Thumb) 0001 111x xxxx xxxx */ + /* MOV (immediate) 0010 0xxx xxxx xxxx */ + /* ADD (immediate, Thumb) 0011 0xxx xxxx xxxx */ + /* SUB (immediate, Thumb) 0011 1xxx xxxx xxxx */ + DECODE_EMULATE (0xc000, 0x0000, PROBES_T16_ADDSUB), + + /* + * 16-bit Thumb data-processing instructions + * 0100 00xx xxxx xxxx + */ + + /* TST (register) 0100 0010 00xx xxxx */ + DECODE_EMULATE (0xffc0, 0x4200, PROBES_T16_CMP), + /* CMP (register) 0100 0010 10xx xxxx */ + /* CMN (register) 0100 0010 11xx xxxx */ + DECODE_EMULATE (0xff80, 0x4280, PROBES_T16_CMP), + /* AND (register) 0100 0000 00xx xxxx */ + /* EOR (register) 0100 0000 01xx xxxx */ + /* LSL (register) 0100 0000 10xx xxxx */ + /* LSR (register) 0100 0000 11xx xxxx */ + /* ASR (register) 0100 0001 00xx xxxx */ + /* ADC (register) 0100 0001 01xx xxxx */ + /* SBC (register) 0100 0001 10xx xxxx */ + /* ROR (register) 0100 0001 11xx xxxx */ + /* RSB (immediate) 0100 0010 01xx xxxx */ + /* ORR (register) 0100 0011 00xx xxxx */ + /* MUL 0100 0011 00xx xxxx */ + /* BIC (register) 0100 0011 10xx xxxx */ + /* MVN (register) 0100 0011 10xx xxxx */ + DECODE_EMULATE (0xfc00, 0x4000, PROBES_T16_LOGICAL), + + /* + * Special data instructions and branch and exchange + * 0100 01xx xxxx xxxx + */ + + /* BLX pc 0100 0111 1111 1xxx */ + DECODE_REJECT (0xfff8, 0x47f8), + + /* BX (register) 0100 0111 0xxx xxxx */ + /* BLX (register) 0100 0111 1xxx xxxx */ + DECODE_SIMULATE (0xff00, 0x4700, PROBES_T16_BLX), + + /* ADD pc, pc 0100 0100 1111 1111 */ + DECODE_REJECT (0xffff, 0x44ff), + + /* ADD (register) 0100 0100 xxxx xxxx */ + /* CMP (register) 0100 0101 xxxx xxxx */ + /* MOV (register) 0100 0110 xxxx xxxx */ + DECODE_CUSTOM (0xfc00, 0x4400, PROBES_T16_HIREGOPS), + + /* + * Load from Literal Pool + * LDR (literal) 0100 1xxx xxxx xxxx + */ + DECODE_SIMULATE (0xf800, 0x4800, PROBES_T16_LDR_LIT), + + /* + * 16-bit Thumb Load/store instructions + * 0101 xxxx xxxx xxxx + * 011x xxxx xxxx xxxx + * 100x xxxx xxxx xxxx + */ + + /* STR (register) 0101 000x xxxx xxxx */ + /* STRH (register) 0101 001x xxxx xxxx */ + /* STRB (register) 0101 010x xxxx xxxx */ + /* LDRSB (register) 0101 011x xxxx xxxx */ + /* LDR (register) 0101 100x xxxx xxxx */ + /* LDRH (register) 0101 101x xxxx xxxx */ + /* LDRB (register) 0101 110x xxxx xxxx */ + /* LDRSH (register) 0101 111x xxxx xxxx */ + /* STR (immediate, Thumb) 0110 0xxx xxxx xxxx */ + /* LDR (immediate, Thumb) 0110 1xxx xxxx xxxx */ + /* STRB (immediate, Thumb) 0111 0xxx xxxx xxxx */ + /* LDRB (immediate, Thumb) 0111 1xxx xxxx xxxx */ + DECODE_EMULATE (0xc000, 0x4000, PROBES_T16_LDRHSTRH), + /* STRH (immediate, Thumb) 1000 0xxx xxxx xxxx */ + /* LDRH (immediate, Thumb) 1000 1xxx xxxx xxxx */ + DECODE_EMULATE (0xf000, 0x8000, PROBES_T16_LDRHSTRH), + /* STR (immediate, Thumb) 1001 0xxx xxxx xxxx */ + /* LDR (immediate, Thumb) 1001 1xxx xxxx xxxx */ + DECODE_SIMULATE (0xf000, 0x9000, PROBES_T16_LDRSTR), + + /* + * Generate PC-/SP-relative address + * ADR (literal) 1010 0xxx xxxx xxxx + * ADD (SP plus immediate) 1010 1xxx xxxx xxxx + */ + DECODE_SIMULATE (0xf000, 0xa000, PROBES_T16_ADR), + + /* + * Miscellaneous 16-bit instructions + * 1011 xxxx xxxx xxxx + */ + DECODE_TABLE (0xf000, 0xb000, t16_table_1011), + + /* STM 1100 0xxx xxxx xxxx */ + /* LDM 1100 1xxx xxxx xxxx */ + DECODE_EMULATE (0xf000, 0xc000, PROBES_T16_LDMSTM), + + /* + * Conditional branch, and Supervisor Call + */ + + /* Permanently UNDEFINED 1101 1110 xxxx xxxx */ + /* SVC 1101 1111 xxxx xxxx */ + DECODE_REJECT (0xfe00, 0xde00), + + /* Conditional branch 1101 xxxx xxxx xxxx */ + DECODE_CUSTOM (0xf000, 0xd000, PROBES_T16_BRANCH_COND), + + /* + * Unconditional branch + * B 1110 0xxx xxxx xxxx + */ + DECODE_SIMULATE (0xf800, 0xe000, PROBES_T16_BRANCH), + + DECODE_END +}; +#ifdef CONFIG_ARM_KPROBES_TEST_MODULE +EXPORT_SYMBOL_GPL(probes_decode_thumb16_table); +#endif + +static unsigned long __kprobes thumb_check_cc(unsigned long cpsr) +{ + if (unlikely(in_it_block(cpsr))) + return probes_condition_checks[current_cond(cpsr)](cpsr); + return true; +} + +static void __kprobes thumb16_singlestep(probes_opcode_t opcode, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + regs->ARM_pc += 2; + asi->insn_handler(opcode, asi, regs); + regs->ARM_cpsr = it_advance(regs->ARM_cpsr); +} + +static void __kprobes thumb32_singlestep(probes_opcode_t opcode, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + regs->ARM_pc += 4; + asi->insn_handler(opcode, asi, regs); + regs->ARM_cpsr = it_advance(regs->ARM_cpsr); +} + +enum probes_insn __kprobes +thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + bool emulate, const union decode_action *actions) +{ + asi->insn_singlestep = thumb16_singlestep; + asi->insn_check_cc = thumb_check_cc; + return probes_decode_insn(insn, asi, probes_decode_thumb16_table, true, + emulate, actions); +} + +enum probes_insn __kprobes +thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + bool emulate, const union decode_action *actions) +{ + asi->insn_singlestep = thumb32_singlestep; + asi->insn_check_cc = thumb_check_cc; + return probes_decode_insn(insn, asi, probes_decode_thumb32_table, true, + emulate, actions); +} diff --git a/arch/arm/probes/decode-thumb.h b/arch/arm/probes/decode-thumb.h new file mode 100644 index 0000000..039013c --- /dev/null +++ b/arch/arm/probes/decode-thumb.h @@ -0,0 +1,99 @@ +/* + * arch/arm/probes/decode-thumb.h + * + * Copyright 2013 Linaro Ltd. + * Written by: David A. Long + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef _ARM_KERNEL_PROBES_THUMB_H +#define _ARM_KERNEL_PROBES_THUMB_H + +#include "decode.h" + +/* + * True if current instruction is in an IT block. + */ +#define in_it_block(cpsr) ((cpsr & 0x06000c00) != 0x00000000) + +/* + * Return the condition code to check for the currently executing instruction. + * This is in ITSTATE<7:4> which is in CPSR<15:12> but is only valid if + * in_it_block returns true. + */ +#define current_cond(cpsr) ((cpsr >> 12) & 0xf) + +enum probes_t32_action { + PROBES_T32_EMULATE_NONE, + PROBES_T32_SIMULATE_NOP, + PROBES_T32_LDMSTM, + PROBES_T32_LDRDSTRD, + PROBES_T32_TABLE_BRANCH, + PROBES_T32_TST, + PROBES_T32_CMP, + PROBES_T32_MOV, + PROBES_T32_ADDSUB, + PROBES_T32_LOGICAL, + PROBES_T32_ADDWSUBW_PC, + PROBES_T32_ADDWSUBW, + PROBES_T32_MOVW, + PROBES_T32_SAT, + PROBES_T32_BITFIELD, + PROBES_T32_SEV, + PROBES_T32_WFE, + PROBES_T32_MRS, + PROBES_T32_BRANCH_COND, + PROBES_T32_BRANCH, + PROBES_T32_PLDI, + PROBES_T32_LDR_LIT, + PROBES_T32_LDRSTR, + PROBES_T32_SIGN_EXTEND, + PROBES_T32_MEDIA, + PROBES_T32_REVERSE, + PROBES_T32_MUL_ADD, + PROBES_T32_MUL_ADD2, + PROBES_T32_MUL_ADD_LONG, + NUM_PROBES_T32_ACTIONS +}; + +enum probes_t16_action { + PROBES_T16_ADD_SP, + PROBES_T16_CBZ, + PROBES_T16_SIGN_EXTEND, + PROBES_T16_PUSH, + PROBES_T16_POP, + PROBES_T16_SEV, + PROBES_T16_WFE, + PROBES_T16_IT, + PROBES_T16_CMP, + PROBES_T16_ADDSUB, + PROBES_T16_LOGICAL, + PROBES_T16_BLX, + PROBES_T16_HIREGOPS, + PROBES_T16_LDR_LIT, + PROBES_T16_LDRHSTRH, + PROBES_T16_LDRSTR, + PROBES_T16_ADR, + PROBES_T16_LDMSTM, + PROBES_T16_BRANCH_COND, + PROBES_T16_BRANCH, + NUM_PROBES_T16_ACTIONS +}; + +extern const union decode_item probes_decode_thumb32_table[]; +extern const union decode_item probes_decode_thumb16_table[]; + +enum probes_insn __kprobes +thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + bool emulate, const union decode_action *actions); +enum probes_insn __kprobes +thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + bool emulate, const union decode_action *actions); + +#endif diff --git a/arch/arm/probes/decode.c b/arch/arm/probes/decode.c new file mode 100644 index 0000000..3b05d57 --- /dev/null +++ b/arch/arm/probes/decode.c @@ -0,0 +1,456 @@ +/* + * arch/arm/probes/decode.c + * + * Copyright (C) 2011 Jon Medhurst . + * + * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is + * Copyright (C) 2006, 2007 Motorola 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 +#include +#include +#include +#include + +#include "decode.h" + + +#ifndef find_str_pc_offset + +/* + * For STR and STM instructions, an ARM core may choose to use either + * a +8 or a +12 displacement from the current instruction's address. + * Whichever value is chosen for a given core, it must be the same for + * both instructions and may not change. This function measures it. + */ + +int str_pc_offset; + +void __init find_str_pc_offset(void) +{ + int addr, scratch, ret; + + __asm__ ( + "sub %[ret], pc, #4 \n\t" + "str pc, %[addr] \n\t" + "ldr %[scr], %[addr] \n\t" + "sub %[ret], %[scr], %[ret] \n\t" + : [ret] "=r" (ret), [scr] "=r" (scratch), [addr] "+m" (addr)); + + str_pc_offset = ret; +} + +#endif /* !find_str_pc_offset */ + + +#ifndef test_load_write_pc_interworking + +bool load_write_pc_interworks; + +void __init test_load_write_pc_interworking(void) +{ + int arch = cpu_architecture(); + BUG_ON(arch == CPU_ARCH_UNKNOWN); + load_write_pc_interworks = arch >= CPU_ARCH_ARMv5T; +} + +#endif /* !test_load_write_pc_interworking */ + + +#ifndef test_alu_write_pc_interworking + +bool alu_write_pc_interworks; + +void __init test_alu_write_pc_interworking(void) +{ + int arch = cpu_architecture(); + BUG_ON(arch == CPU_ARCH_UNKNOWN); + alu_write_pc_interworks = arch >= CPU_ARCH_ARMv7; +} + +#endif /* !test_alu_write_pc_interworking */ + + +void __init arm_probes_decode_init(void) +{ + find_str_pc_offset(); + test_load_write_pc_interworking(); + test_alu_write_pc_interworking(); +} + + +static unsigned long __kprobes __check_eq(unsigned long cpsr) +{ + return cpsr & PSR_Z_BIT; +} + +static unsigned long __kprobes __check_ne(unsigned long cpsr) +{ + return (~cpsr) & PSR_Z_BIT; +} + +static unsigned long __kprobes __check_cs(unsigned long cpsr) +{ + return cpsr & PSR_C_BIT; +} + +static unsigned long __kprobes __check_cc(unsigned long cpsr) +{ + return (~cpsr) & PSR_C_BIT; +} + +static unsigned long __kprobes __check_mi(unsigned long cpsr) +{ + return cpsr & PSR_N_BIT; +} + +static unsigned long __kprobes __check_pl(unsigned long cpsr) +{ + return (~cpsr) & PSR_N_BIT; +} + +static unsigned long __kprobes __check_vs(unsigned long cpsr) +{ + return cpsr & PSR_V_BIT; +} + +static unsigned long __kprobes __check_vc(unsigned long cpsr) +{ + return (~cpsr) & PSR_V_BIT; +} + +static unsigned long __kprobes __check_hi(unsigned long cpsr) +{ + cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */ + return cpsr & PSR_C_BIT; +} + +static unsigned long __kprobes __check_ls(unsigned long cpsr) +{ + cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */ + return (~cpsr) & PSR_C_BIT; +} + +static unsigned long __kprobes __check_ge(unsigned long cpsr) +{ + cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ + return (~cpsr) & PSR_N_BIT; +} + +static unsigned long __kprobes __check_lt(unsigned long cpsr) +{ + cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ + return cpsr & PSR_N_BIT; +} + +static unsigned long __kprobes __check_gt(unsigned long cpsr) +{ + unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ + temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */ + return (~temp) & PSR_N_BIT; +} + +static unsigned long __kprobes __check_le(unsigned long cpsr) +{ + unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ + temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */ + return temp & PSR_N_BIT; +} + +static unsigned long __kprobes __check_al(unsigned long cpsr) +{ + return true; +} + +probes_check_cc * const probes_condition_checks[16] = { + &__check_eq, &__check_ne, &__check_cs, &__check_cc, + &__check_mi, &__check_pl, &__check_vs, &__check_vc, + &__check_hi, &__check_ls, &__check_ge, &__check_lt, + &__check_gt, &__check_le, &__check_al, &__check_al +}; + + +void __kprobes probes_simulate_nop(probes_opcode_t opcode, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ +} + +void __kprobes probes_emulate_none(probes_opcode_t opcode, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + asi->insn_fn(); +} + +/* + * Prepare an instruction slot to receive an instruction for emulating. + * This is done by placing a subroutine return after the location where the + * instruction will be placed. We also modify ARM instructions to be + * unconditional as the condition code will already be checked before any + * emulation handler is called. + */ +static probes_opcode_t __kprobes +prepare_emulated_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + bool thumb) +{ +#ifdef CONFIG_THUMB2_KERNEL + if (thumb) { + u16 *thumb_insn = (u16 *)asi->insn; + /* Thumb bx lr */ + thumb_insn[1] = __opcode_to_mem_thumb16(0x4770); + thumb_insn[2] = __opcode_to_mem_thumb16(0x4770); + return insn; + } + asi->insn[1] = __opcode_to_mem_arm(0xe12fff1e); /* ARM bx lr */ +#else + asi->insn[1] = __opcode_to_mem_arm(0xe1a0f00e); /* mov pc, lr */ +#endif + /* Make an ARM instruction unconditional */ + if (insn < 0xe0000000) + insn = (insn | 0xe0000000) & ~0x10000000; + return insn; +} + +/* + * Write a (probably modified) instruction into the slot previously prepared by + * prepare_emulated_insn + */ +static void __kprobes +set_emulated_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + bool thumb) +{ +#ifdef CONFIG_THUMB2_KERNEL + if (thumb) { + u16 *ip = (u16 *)asi->insn; + if (is_wide_instruction(insn)) + *ip++ = __opcode_to_mem_thumb16(insn >> 16); + *ip++ = __opcode_to_mem_thumb16(insn); + return; + } +#endif + asi->insn[0] = __opcode_to_mem_arm(insn); +} + +/* + * When we modify the register numbers encoded in an instruction to be emulated, + * the new values come from this define. For ARM and 32-bit Thumb instructions + * this gives... + * + * bit position 16 12 8 4 0 + * ---------------+---+---+---+---+---+ + * register r2 r0 r1 -- r3 + */ +#define INSN_NEW_BITS 0x00020103 + +/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */ +#define INSN_SAMEAS16_BITS 0x22222222 + +/* + * Validate and modify each of the registers encoded in an instruction. + * + * Each nibble in regs contains a value from enum decode_reg_type. For each + * non-zero value, the corresponding nibble in pinsn is validated and modified + * according to the type. + */ +static bool __kprobes decode_regs(probes_opcode_t *pinsn, u32 regs, bool modify) +{ + probes_opcode_t insn = *pinsn; + probes_opcode_t mask = 0xf; /* Start at least significant nibble */ + + for (; regs != 0; regs >>= 4, mask <<= 4) { + + probes_opcode_t new_bits = INSN_NEW_BITS; + + switch (regs & 0xf) { + + case REG_TYPE_NONE: + /* Nibble not a register, skip to next */ + continue; + + case REG_TYPE_ANY: + /* Any register is allowed */ + break; + + case REG_TYPE_SAMEAS16: + /* Replace register with same as at bit position 16 */ + new_bits = INSN_SAMEAS16_BITS; + break; + + case REG_TYPE_SP: + /* Only allow SP (R13) */ + if ((insn ^ 0xdddddddd) & mask) + goto reject; + break; + + case REG_TYPE_PC: + /* Only allow PC (R15) */ + if ((insn ^ 0xffffffff) & mask) + goto reject; + break; + + case REG_TYPE_NOSP: + /* Reject SP (R13) */ + if (((insn ^ 0xdddddddd) & mask) == 0) + goto reject; + break; + + case REG_TYPE_NOSPPC: + case REG_TYPE_NOSPPCX: + /* Reject SP and PC (R13 and R15) */ + if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0) + goto reject; + break; + + case REG_TYPE_NOPCWB: + if (!is_writeback(insn)) + break; /* No writeback, so any register is OK */ + /* fall through... */ + case REG_TYPE_NOPC: + case REG_TYPE_NOPCX: + /* Reject PC (R15) */ + if (((insn ^ 0xffffffff) & mask) == 0) + goto reject; + break; + } + + /* Replace value of nibble with new register number... */ + insn &= ~mask; + insn |= new_bits & mask; + } + + if (modify) + *pinsn = insn; + + return true; + +reject: + return false; +} + +static const int decode_struct_sizes[NUM_DECODE_TYPES] = { + [DECODE_TYPE_TABLE] = sizeof(struct decode_table), + [DECODE_TYPE_CUSTOM] = sizeof(struct decode_custom), + [DECODE_TYPE_SIMULATE] = sizeof(struct decode_simulate), + [DECODE_TYPE_EMULATE] = sizeof(struct decode_emulate), + [DECODE_TYPE_OR] = sizeof(struct decode_or), + [DECODE_TYPE_REJECT] = sizeof(struct decode_reject) +}; + +/* + * probes_decode_insn operates on data tables in order to decode an ARM + * architecture instruction onto which a kprobe has been placed. + * + * These instruction decoding tables are a concatenation of entries each + * of which consist of one of the following structs: + * + * decode_table + * decode_custom + * decode_simulate + * decode_emulate + * decode_or + * decode_reject + * + * Each of these starts with a struct decode_header which has the following + * fields: + * + * type_regs + * mask + * value + * + * The least significant DECODE_TYPE_BITS of type_regs contains a value + * from enum decode_type, this indicates which of the decode_* structs + * the entry contains. The value DECODE_TYPE_END indicates the end of the + * table. + * + * When the table is parsed, each entry is checked in turn to see if it + * matches the instruction to be decoded using the test: + * + * (insn & mask) == value + * + * If no match is found before the end of the table is reached then decoding + * fails with INSN_REJECTED. + * + * When a match is found, decode_regs() is called to validate and modify each + * of the registers encoded in the instruction; the data it uses to do this + * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding + * to fail with INSN_REJECTED. + * + * Once the instruction has passed the above tests, further processing + * depends on the type of the table entry's decode struct. + * + */ +int __kprobes +probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + const union decode_item *table, bool thumb, + bool emulate, const union decode_action *actions) +{ + const struct decode_header *h = (struct decode_header *)table; + const struct decode_header *next; + bool matched = false; + + if (emulate) + insn = prepare_emulated_insn(insn, asi, thumb); + + for (;; h = next) { + enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; + u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS; + + if (type == DECODE_TYPE_END) + return INSN_REJECTED; + + next = (struct decode_header *) + ((uintptr_t)h + decode_struct_sizes[type]); + + if (!matched && (insn & h->mask.bits) != h->value.bits) + continue; + + if (!decode_regs(&insn, regs, emulate)) + return INSN_REJECTED; + + switch (type) { + + case DECODE_TYPE_TABLE: { + struct decode_table *d = (struct decode_table *)h; + next = (struct decode_header *)d->table.table; + break; + } + + case DECODE_TYPE_CUSTOM: { + struct decode_custom *d = (struct decode_custom *)h; + return actions[d->decoder.action].decoder(insn, asi, h); + } + + case DECODE_TYPE_SIMULATE: { + struct decode_simulate *d = (struct decode_simulate *)h; + asi->insn_handler = actions[d->handler.action].handler; + return INSN_GOOD_NO_SLOT; + } + + case DECODE_TYPE_EMULATE: { + struct decode_emulate *d = (struct decode_emulate *)h; + + if (!emulate) + return actions[d->handler.action].decoder(insn, + asi, h); + + asi->insn_handler = actions[d->handler.action].handler; + set_emulated_insn(insn, asi, thumb); + return INSN_GOOD; + } + + case DECODE_TYPE_OR: + matched = true; + break; + + case DECODE_TYPE_REJECT: + default: + return INSN_REJECTED; + } + } +} diff --git a/arch/arm/probes/decode.h b/arch/arm/probes/decode.h new file mode 100644 index 0000000..1d0b531 --- /dev/null +++ b/arch/arm/probes/decode.h @@ -0,0 +1,407 @@ +/* + * arch/arm/probes/decode.h + * + * Copyright (C) 2011 Jon Medhurst . + * + * Some contents moved here from arch/arm/include/asm/kprobes.h which is + * Copyright (C) 2006, 2007 Motorola 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. + * + * 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 _ARM_KERNEL_PROBES_H +#define _ARM_KERNEL_PROBES_H + +#include +#include +#include + +void __init arm_probes_decode_init(void); + +extern probes_check_cc * const probes_condition_checks[16]; + +#if __LINUX_ARM_ARCH__ >= 7 + +/* str_pc_offset is architecturally defined from ARMv7 onwards */ +#define str_pc_offset 8 +#define find_str_pc_offset() + +#else /* __LINUX_ARM_ARCH__ < 7 */ + +/* We need a run-time check to determine str_pc_offset */ +extern int str_pc_offset; +void __init find_str_pc_offset(void); + +#endif + + +/* + * Update ITSTATE after normal execution of an IT block instruction. + * + * The 8 IT state bits are split into two parts in CPSR: + * ITSTATE<1:0> are in CPSR<26:25> + * ITSTATE<7:2> are in CPSR<15:10> + */ +static inline unsigned long it_advance(unsigned long cpsr) + { + if ((cpsr & 0x06000400) == 0) { + /* ITSTATE<2:0> == 0 means end of IT block, so clear IT state */ + cpsr &= ~PSR_IT_MASK; + } else { + /* We need to shift left ITSTATE<4:0> */ + const unsigned long mask = 0x06001c00; /* Mask ITSTATE<4:0> */ + unsigned long it = cpsr & mask; + it <<= 1; + it |= it >> (27 - 10); /* Carry ITSTATE<2> to correct place */ + it &= mask; + cpsr &= ~mask; + cpsr |= it; + } + return cpsr; +} + +static inline void __kprobes bx_write_pc(long pcv, struct pt_regs *regs) +{ + long cpsr = regs->ARM_cpsr; + if (pcv & 0x1) { + cpsr |= PSR_T_BIT; + pcv &= ~0x1; + } else { + cpsr &= ~PSR_T_BIT; + pcv &= ~0x2; /* Avoid UNPREDICTABLE address allignment */ + } + regs->ARM_cpsr = cpsr; + regs->ARM_pc = pcv; +} + + +#if __LINUX_ARM_ARCH__ >= 6 + +/* Kernels built for >= ARMv6 should never run on <= ARMv5 hardware, so... */ +#define load_write_pc_interworks true +#define test_load_write_pc_interworking() + +#else /* __LINUX_ARM_ARCH__ < 6 */ + +/* We need run-time testing to determine if load_write_pc() should interwork. */ +extern bool load_write_pc_interworks; +void __init test_load_write_pc_interworking(void); + +#endif + +static inline void __kprobes load_write_pc(long pcv, struct pt_regs *regs) +{ + if (load_write_pc_interworks) + bx_write_pc(pcv, regs); + else + regs->ARM_pc = pcv; +} + + +#if __LINUX_ARM_ARCH__ >= 7 + +#define alu_write_pc_interworks true +#define test_alu_write_pc_interworking() + +#elif __LINUX_ARM_ARCH__ <= 5 + +/* Kernels built for <= ARMv5 should never run on >= ARMv6 hardware, so... */ +#define alu_write_pc_interworks false +#define test_alu_write_pc_interworking() + +#else /* __LINUX_ARM_ARCH__ == 6 */ + +/* We could be an ARMv6 binary on ARMv7 hardware so we need a run-time check. */ +extern bool alu_write_pc_interworks; +void __init test_alu_write_pc_interworking(void); + +#endif /* __LINUX_ARM_ARCH__ == 6 */ + +static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs) +{ + if (alu_write_pc_interworks) + bx_write_pc(pcv, regs); + else + regs->ARM_pc = pcv; +} + + +/* + * Test if load/store instructions writeback the address register. + * if P (bit 24) == 0 or W (bit 21) == 1 + */ +#define is_writeback(insn) ((insn ^ 0x01000000) & 0x01200000) + +/* + * The following definitions and macros are used to build instruction + * decoding tables for use by probes_decode_insn. + * + * These tables are a concatenation of entries each of which consist of one of + * the decode_* structs. All of the fields in every type of decode structure + * are of the union type decode_item, therefore the entire decode table can be + * viewed as an array of these and declared like: + * + * static const union decode_item table_name[] = {}; + * + * In order to construct each entry in the table, macros are used to + * initialise a number of sequential decode_item values in a layout which + * matches the relevant struct. E.g. DECODE_SIMULATE initialise a struct + * decode_simulate by initialising four decode_item objects like this... + * + * {.bits = _type}, + * {.bits = _mask}, + * {.bits = _value}, + * {.action = _handler}, + * + * Initialising a specified member of the union means that the compiler + * will produce a warning if the argument is of an incorrect type. + * + * Below is a list of each of the macros used to initialise entries and a + * description of the action performed when that entry is matched to an + * instruction. A match is found when (instruction & mask) == value. + * + * DECODE_TABLE(mask, value, table) + * Instruction decoding jumps to parsing the new sub-table 'table'. + * + * DECODE_CUSTOM(mask, value, decoder) + * The value of 'decoder' is used as an index into the array of + * action functions, and the retrieved decoder function is invoked + * to complete decoding of the instruction. + * + * DECODE_SIMULATE(mask, value, handler) + * The probes instruction handler is set to the value found by + * indexing into the action array using the value of 'handler'. This + * will be used to simulate the instruction when the probe is hit. + * Decoding returns with INSN_GOOD_NO_SLOT. + * + * DECODE_EMULATE(mask, value, handler) + * The probes instruction handler is set to the value found by + * indexing into the action array using the value of 'handler'. This + * will be used to emulate the instruction when the probe is hit. The + * modified instruction (see below) is placed in the probes instruction + * slot so it may be called by the emulation code. Decoding returns + * with INSN_GOOD. + * + * DECODE_REJECT(mask, value) + * Instruction decoding fails with INSN_REJECTED + * + * DECODE_OR(mask, value) + * This allows the mask/value test of multiple table entries to be + * logically ORed. Once an 'or' entry is matched the decoding action to + * be performed is that of the next entry which isn't an 'or'. E.g. + * + * DECODE_OR (mask1, value1) + * DECODE_OR (mask2, value2) + * DECODE_SIMULATE (mask3, value3, simulation_handler) + * + * This means that if any of the three mask/value pairs match the + * instruction being decoded, then 'simulation_handler' will be used + * for it. + * + * Both the SIMULATE and EMULATE macros have a second form which take an + * additional 'regs' argument. + * + * DECODE_SIMULATEX(mask, value, handler, regs) + * DECODE_EMULATEX (mask, value, handler, regs) + * + * These are used to specify what kind of CPU register is encoded in each of the + * least significant 5 nibbles of the instruction being decoded. The regs value + * is specified using the REGS macro, this takes any of the REG_TYPE_* values + * from enum decode_reg_type as arguments; only the '*' part of the name is + * given. E.g. + * + * REGS(0, ANY, NOPC, 0, ANY) + * + * This indicates an instruction is encoded like: + * + * bits 19..16 ignore + * bits 15..12 any register allowed here + * bits 11.. 8 any register except PC allowed here + * bits 7.. 4 ignore + * bits 3.. 0 any register allowed here + * + * This register specification is checked after a decode table entry is found to + * match an instruction (through the mask/value test). Any invalid register then + * found in the instruction will cause decoding to fail with INSN_REJECTED. In + * the above example this would happen if bits 11..8 of the instruction were + * 1111, indicating R15 or PC. + * + * As well as checking for legal combinations of registers, this data is also + * used to modify the registers encoded in the instructions so that an + * emulation routines can use it. (See decode_regs() and INSN_NEW_BITS.) + * + * Here is a real example which matches ARM instructions of the form + * "AND ,,, " + * + * DECODE_EMULATEX (0x0e000090, 0x00000010, PROBES_DATA_PROCESSING_REG, + * REGS(ANY, ANY, NOPC, 0, ANY)), + * ^ ^ ^ ^ + * Rn Rd Rs Rm + * + * Decoding the instruction "AND R4, R5, R6, ASL R15" will be rejected because + * Rs == R15 + * + * Decoding the instruction "AND R4, R5, R6, ASL R7" will be accepted and the + * instruction will be modified to "AND R0, R2, R3, ASL R1" and then placed into + * the kprobes instruction slot. This can then be called later by the handler + * function emulate_rd12rn16rm0rs8_rwflags (a pointer to which is retrieved from + * the indicated slot in the action array), in order to simulate the instruction. + */ + +enum decode_type { + DECODE_TYPE_END, + DECODE_TYPE_TABLE, + DECODE_TYPE_CUSTOM, + DECODE_TYPE_SIMULATE, + DECODE_TYPE_EMULATE, + DECODE_TYPE_OR, + DECODE_TYPE_REJECT, + NUM_DECODE_TYPES /* Must be last enum */ +}; + +#define DECODE_TYPE_BITS 4 +#define DECODE_TYPE_MASK ((1 << DECODE_TYPE_BITS) - 1) + +enum decode_reg_type { + REG_TYPE_NONE = 0, /* Not a register, ignore */ + REG_TYPE_ANY, /* Any register allowed */ + REG_TYPE_SAMEAS16, /* Register should be same as that at bits 19..16 */ + REG_TYPE_SP, /* Register must be SP */ + REG_TYPE_PC, /* Register must be PC */ + REG_TYPE_NOSP, /* Register must not be SP */ + REG_TYPE_NOSPPC, /* Register must not be SP or PC */ + REG_TYPE_NOPC, /* Register must not be PC */ + REG_TYPE_NOPCWB, /* No PC if load/store write-back flag also set */ + + /* The following types are used when the encoding for PC indicates + * another instruction form. This distiction only matters for test + * case coverage checks. + */ + REG_TYPE_NOPCX, /* Register must not be PC */ + REG_TYPE_NOSPPCX, /* Register must not be SP or PC */ + + /* Alias to allow '0' arg to be used in REGS macro. */ + REG_TYPE_0 = REG_TYPE_NONE +}; + +#define REGS(r16, r12, r8, r4, r0) \ + (((REG_TYPE_##r16) << 16) + \ + ((REG_TYPE_##r12) << 12) + \ + ((REG_TYPE_##r8) << 8) + \ + ((REG_TYPE_##r4) << 4) + \ + (REG_TYPE_##r0)) + +union decode_item { + u32 bits; + const union decode_item *table; + int action; +}; + +struct decode_header; +typedef enum probes_insn (probes_custom_decode_t)(probes_opcode_t, + struct arch_probes_insn *, + const struct decode_header *); + +union decode_action { + probes_insn_handler_t *handler; + probes_custom_decode_t *decoder; +}; + +#define DECODE_END \ + {.bits = DECODE_TYPE_END} + + +struct decode_header { + union decode_item type_regs; + union decode_item mask; + union decode_item value; +}; + +#define DECODE_HEADER(_type, _mask, _value, _regs) \ + {.bits = (_type) | ((_regs) << DECODE_TYPE_BITS)}, \ + {.bits = (_mask)}, \ + {.bits = (_value)} + + +struct decode_table { + struct decode_header header; + union decode_item table; +}; + +#define DECODE_TABLE(_mask, _value, _table) \ + DECODE_HEADER(DECODE_TYPE_TABLE, _mask, _value, 0), \ + {.table = (_table)} + + +struct decode_custom { + struct decode_header header; + union decode_item decoder; +}; + +#define DECODE_CUSTOM(_mask, _value, _decoder) \ + DECODE_HEADER(DECODE_TYPE_CUSTOM, _mask, _value, 0), \ + {.action = (_decoder)} + + +struct decode_simulate { + struct decode_header header; + union decode_item handler; +}; + +#define DECODE_SIMULATEX(_mask, _value, _handler, _regs) \ + DECODE_HEADER(DECODE_TYPE_SIMULATE, _mask, _value, _regs), \ + {.action = (_handler)} + +#define DECODE_SIMULATE(_mask, _value, _handler) \ + DECODE_SIMULATEX(_mask, _value, _handler, 0) + + +struct decode_emulate { + struct decode_header header; + union decode_item handler; +}; + +#define DECODE_EMULATEX(_mask, _value, _handler, _regs) \ + DECODE_HEADER(DECODE_TYPE_EMULATE, _mask, _value, _regs), \ + {.action = (_handler)} + +#define DECODE_EMULATE(_mask, _value, _handler) \ + DECODE_EMULATEX(_mask, _value, _handler, 0) + + +struct decode_or { + struct decode_header header; +}; + +#define DECODE_OR(_mask, _value) \ + DECODE_HEADER(DECODE_TYPE_OR, _mask, _value, 0) + +enum probes_insn { + INSN_REJECTED, + INSN_GOOD, + INSN_GOOD_NO_SLOT +}; + +struct decode_reject { + struct decode_header header; +}; + +#define DECODE_REJECT(_mask, _value) \ + DECODE_HEADER(DECODE_TYPE_REJECT, _mask, _value, 0) + +probes_insn_handler_t probes_simulate_nop; +probes_insn_handler_t probes_emulate_none; + +int __kprobes +probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + const union decode_item *table, bool thumb, bool emulate, + const union decode_action *actions); + +#endif diff --git a/arch/arm/probes/kprobes/Makefile b/arch/arm/probes/kprobes/Makefile new file mode 100644 index 0000000..eb38a42 --- /dev/null +++ b/arch/arm/probes/kprobes/Makefile @@ -0,0 +1,11 @@ +obj-$(CONFIG_KPROBES) += core.o actions-common.o +obj-$(CONFIG_ARM_KPROBES_TEST) += test-kprobes.o +test-kprobes-objs := test-core.o + +ifdef CONFIG_THUMB2_KERNEL +obj-$(CONFIG_KPROBES) += actions-thumb.o +test-kprobes-objs += test-thumb.o +else +obj-$(CONFIG_KPROBES) += actions-arm.o +test-kprobes-objs += test-arm.o +endif diff --git a/arch/arm/probes/kprobes/actions-arm.c b/arch/arm/probes/kprobes/actions-arm.c new file mode 100644 index 0000000..8797879 --- /dev/null +++ b/arch/arm/probes/kprobes/actions-arm.c @@ -0,0 +1,343 @@ +/* + * arch/arm/probes/kprobes/actions-arm.c + * + * Copyright (C) 2006, 2007 Motorola 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. + * + * 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. + */ + +/* + * We do not have hardware single-stepping on ARM, This + * effort is further complicated by the ARM not having a + * "next PC" register. Instructions that change the PC + * can't be safely single-stepped in a MP environment, so + * we have a lot of work to do: + * + * In the prepare phase: + * *) If it is an instruction that does anything + * with the CPU mode, we reject it for a kprobe. + * (This is out of laziness rather than need. The + * instructions could be simulated.) + * + * *) Otherwise, decode the instruction rewriting its + * registers to take fixed, ordered registers and + * setting a handler for it to run the instruction. + * + * In the execution phase by an instruction's handler: + * + * *) If the PC is written to by the instruction, the + * instruction must be fully simulated in software. + * + * *) Otherwise, a modified form of the instruction is + * directly executed. Its handler calls the + * instruction in insn[0]. In insn[1] is a + * "mov pc, lr" to return. + * + * Before calling, load up the reordered registers + * from the original instruction's registers. If one + * of the original input registers is the PC, compute + * and adjust the appropriate input register. + * + * After call completes, copy the output registers to + * the original instruction's original registers. + * + * We don't use a real breakpoint instruction since that + * would have us in the kernel go from SVC mode to SVC + * mode losing the link register. Instead we use an + * undefined instruction. To simplify processing, the + * undefined instruction used for kprobes must be reserved + * exclusively for kprobes use. + * + * TODO: ifdef out some instruction decoding based on architecture. + */ + +#include +#include +#include + +#include "../decode-arm.h" +#include "core.h" + +#if __LINUX_ARM_ARCH__ >= 6 +#define BLX(reg) "blx "reg" \n\t" +#else +#define BLX(reg) "mov lr, pc \n\t" \ + "mov pc, "reg" \n\t" +#endif + +static void __kprobes +emulate_ldrdstrd(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc + 4; + int rt = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + + register unsigned long rtv asm("r0") = regs->uregs[rt]; + register unsigned long rt2v asm("r1") = regs->uregs[rt+1]; + register unsigned long rnv asm("r2") = (rn == 15) ? pc + : regs->uregs[rn]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + + __asm__ __volatile__ ( + BLX("%[fn]") + : "=r" (rtv), "=r" (rt2v), "=r" (rnv) + : "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv), + [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rt] = rtv; + regs->uregs[rt+1] = rt2v; + if (is_writeback(insn)) + regs->uregs[rn] = rnv; +} + +static void __kprobes +emulate_ldr(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc + 4; + int rt = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + + register unsigned long rtv asm("r0"); + register unsigned long rnv asm("r2") = (rn == 15) ? pc + : regs->uregs[rn]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + + __asm__ __volatile__ ( + BLX("%[fn]") + : "=r" (rtv), "=r" (rnv) + : "1" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + if (rt == 15) + load_write_pc(rtv, regs); + else + regs->uregs[rt] = rtv; + + if (is_writeback(insn)) + regs->uregs[rn] = rnv; +} + +static void __kprobes +emulate_str(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long rtpc = regs->ARM_pc - 4 + str_pc_offset; + unsigned long rnpc = regs->ARM_pc + 4; + int rt = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + + register unsigned long rtv asm("r0") = (rt == 15) ? rtpc + : regs->uregs[rt]; + register unsigned long rnv asm("r2") = (rn == 15) ? rnpc + : regs->uregs[rn]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + + __asm__ __volatile__ ( + BLX("%[fn]") + : "=r" (rnv) + : "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + if (is_writeback(insn)) + regs->uregs[rn] = rnv; +} + +static void __kprobes +emulate_rd12rn16rm0rs8_rwflags(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc + 4; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + int rs = (insn >> 8) & 0xf; + + register unsigned long rdv asm("r0") = regs->uregs[rd]; + register unsigned long rnv asm("r2") = (rn == 15) ? pc + : regs->uregs[rn]; + register unsigned long rmv asm("r3") = (rm == 15) ? pc + : regs->uregs[rm]; + register unsigned long rsv asm("r1") = regs->uregs[rs]; + unsigned long cpsr = regs->ARM_cpsr; + + __asm__ __volatile__ ( + "msr cpsr_fs, %[cpsr] \n\t" + BLX("%[fn]") + "mrs %[cpsr], cpsr \n\t" + : "=r" (rdv), [cpsr] "=r" (cpsr) + : "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv), + "1" (cpsr), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + if (rd == 15) + alu_write_pc(rdv, regs); + else + regs->uregs[rd] = rdv; + regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); +} + +static void __kprobes +emulate_rd12rn16rm0_rwflags_nopc(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + + register unsigned long rdv asm("r0") = regs->uregs[rd]; + register unsigned long rnv asm("r2") = regs->uregs[rn]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + unsigned long cpsr = regs->ARM_cpsr; + + __asm__ __volatile__ ( + "msr cpsr_fs, %[cpsr] \n\t" + BLX("%[fn]") + "mrs %[cpsr], cpsr \n\t" + : "=r" (rdv), [cpsr] "=r" (cpsr) + : "0" (rdv), "r" (rnv), "r" (rmv), + "1" (cpsr), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rd] = rdv; + regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); +} + +static void __kprobes +emulate_rd16rn12rm0rs8_rwflags_nopc(probes_opcode_t insn, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + int rd = (insn >> 16) & 0xf; + int rn = (insn >> 12) & 0xf; + int rm = insn & 0xf; + int rs = (insn >> 8) & 0xf; + + register unsigned long rdv asm("r2") = regs->uregs[rd]; + register unsigned long rnv asm("r0") = regs->uregs[rn]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + register unsigned long rsv asm("r1") = regs->uregs[rs]; + unsigned long cpsr = regs->ARM_cpsr; + + __asm__ __volatile__ ( + "msr cpsr_fs, %[cpsr] \n\t" + BLX("%[fn]") + "mrs %[cpsr], cpsr \n\t" + : "=r" (rdv), [cpsr] "=r" (cpsr) + : "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv), + "1" (cpsr), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rd] = rdv; + regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); +} + +static void __kprobes +emulate_rd12rm0_noflags_nopc(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rd = (insn >> 12) & 0xf; + int rm = insn & 0xf; + + register unsigned long rdv asm("r0") = regs->uregs[rd]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + + __asm__ __volatile__ ( + BLX("%[fn]") + : "=r" (rdv) + : "0" (rdv), "r" (rmv), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rd] = rdv; +} + +static void __kprobes +emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(probes_opcode_t insn, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + int rdlo = (insn >> 12) & 0xf; + int rdhi = (insn >> 16) & 0xf; + int rn = insn & 0xf; + int rm = (insn >> 8) & 0xf; + + register unsigned long rdlov asm("r0") = regs->uregs[rdlo]; + register unsigned long rdhiv asm("r2") = regs->uregs[rdhi]; + register unsigned long rnv asm("r3") = regs->uregs[rn]; + register unsigned long rmv asm("r1") = regs->uregs[rm]; + unsigned long cpsr = regs->ARM_cpsr; + + __asm__ __volatile__ ( + "msr cpsr_fs, %[cpsr] \n\t" + BLX("%[fn]") + "mrs %[cpsr], cpsr \n\t" + : "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr) + : "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv), + "2" (cpsr), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rdlo] = rdlov; + regs->uregs[rdhi] = rdhiv; + regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); +} + +const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = { + [PROBES_EMULATE_NONE] = {.handler = probes_emulate_none}, + [PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop}, + [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop}, + [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop}, + [PROBES_BRANCH_IMM] = {.handler = simulate_blx1}, + [PROBES_MRS] = {.handler = simulate_mrs}, + [PROBES_BRANCH_REG] = {.handler = simulate_blx2bx}, + [PROBES_CLZ] = {.handler = emulate_rd12rm0_noflags_nopc}, + [PROBES_SATURATING_ARITHMETIC] = { + .handler = emulate_rd12rn16rm0_rwflags_nopc}, + [PROBES_MUL1] = {.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc}, + [PROBES_MUL2] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc}, + [PROBES_SWP] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, + [PROBES_LDRSTRD] = {.handler = emulate_ldrdstrd}, + [PROBES_LOAD_EXTRA] = {.handler = emulate_ldr}, + [PROBES_LOAD] = {.handler = emulate_ldr}, + [PROBES_STORE_EXTRA] = {.handler = emulate_str}, + [PROBES_STORE] = {.handler = emulate_str}, + [PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp}, + [PROBES_DATA_PROCESSING_REG] = { + .handler = emulate_rd12rn16rm0rs8_rwflags}, + [PROBES_DATA_PROCESSING_IMM] = { + .handler = emulate_rd12rn16rm0rs8_rwflags}, + [PROBES_MOV_HALFWORD] = {.handler = emulate_rd12rm0_noflags_nopc}, + [PROBES_SEV] = {.handler = probes_emulate_none}, + [PROBES_WFE] = {.handler = probes_simulate_nop}, + [PROBES_SATURATE] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, + [PROBES_REV] = {.handler = emulate_rd12rm0_noflags_nopc}, + [PROBES_MMI] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, + [PROBES_PACK] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, + [PROBES_EXTEND] = {.handler = emulate_rd12rm0_noflags_nopc}, + [PROBES_EXTEND_ADD] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, + [PROBES_MUL_ADD_LONG] = { + .handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc}, + [PROBES_MUL_ADD] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc}, + [PROBES_BITFIELD] = {.handler = emulate_rd12rm0_noflags_nopc}, + [PROBES_BRANCH] = {.handler = simulate_bbl}, + [PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm} +}; diff --git a/arch/arm/probes/kprobes/actions-common.c b/arch/arm/probes/kprobes/actions-common.c new file mode 100644 index 0000000..bd20a71 --- /dev/null +++ b/arch/arm/probes/kprobes/actions-common.c @@ -0,0 +1,171 @@ +/* + * arch/arm/probes/kprobes/actions-common.c + * + * Copyright (C) 2011 Jon Medhurst . + * + * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is + * Copyright (C) 2006, 2007 Motorola 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 +#include +#include + +#include "core.h" + + +static void __kprobes simulate_ldm1stm1(probes_opcode_t insn, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + int rn = (insn >> 16) & 0xf; + int lbit = insn & (1 << 20); + int wbit = insn & (1 << 21); + int ubit = insn & (1 << 23); + int pbit = insn & (1 << 24); + long *addr = (long *)regs->uregs[rn]; + int reg_bit_vector; + int reg_count; + + reg_count = 0; + reg_bit_vector = insn & 0xffff; + while (reg_bit_vector) { + reg_bit_vector &= (reg_bit_vector - 1); + ++reg_count; + } + + if (!ubit) + addr -= reg_count; + addr += (!pbit == !ubit); + + reg_bit_vector = insn & 0xffff; + while (reg_bit_vector) { + int reg = __ffs(reg_bit_vector); + reg_bit_vector &= (reg_bit_vector - 1); + if (lbit) + regs->uregs[reg] = *addr++; + else + *addr++ = regs->uregs[reg]; + } + + if (wbit) { + if (!ubit) + addr -= reg_count; + addr -= (!pbit == !ubit); + regs->uregs[rn] = (long)addr; + } +} + +static void __kprobes simulate_stm1_pc(probes_opcode_t insn, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + unsigned long addr = regs->ARM_pc - 4; + + regs->ARM_pc = (long)addr + str_pc_offset; + simulate_ldm1stm1(insn, asi, regs); + regs->ARM_pc = (long)addr + 4; +} + +static void __kprobes simulate_ldm1_pc(probes_opcode_t insn, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + simulate_ldm1stm1(insn, asi, regs); + load_write_pc(regs->ARM_pc, regs); +} + +static void __kprobes +emulate_generic_r0_12_noflags(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + register void *rregs asm("r1") = regs; + register void *rfn asm("lr") = asi->insn_fn; + + __asm__ __volatile__ ( + "stmdb sp!, {%[regs], r11} \n\t" + "ldmia %[regs], {r0-r12} \n\t" +#if __LINUX_ARM_ARCH__ >= 6 + "blx %[fn] \n\t" +#else + "str %[fn], [sp, #-4]! \n\t" + "adr lr, 1f \n\t" + "ldr pc, [sp], #4 \n\t" + "1: \n\t" +#endif + "ldr lr, [sp], #4 \n\t" /* lr = regs */ + "stmia lr, {r0-r12} \n\t" + "ldr r11, [sp], #4 \n\t" + : [regs] "=r" (rregs), [fn] "=r" (rfn) + : "0" (rregs), "1" (rfn) + : "r0", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r12", "memory", "cc" + ); +} + +static void __kprobes +emulate_generic_r2_14_noflags(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + emulate_generic_r0_12_noflags(insn, asi, + (struct pt_regs *)(regs->uregs+2)); +} + +static void __kprobes +emulate_ldm_r3_15(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + emulate_generic_r0_12_noflags(insn, asi, + (struct pt_regs *)(regs->uregs+3)); + load_write_pc(regs->ARM_pc, regs); +} + +enum probes_insn __kprobes +kprobe_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *h) +{ + probes_insn_handler_t *handler = 0; + unsigned reglist = insn & 0xffff; + int is_ldm = insn & 0x100000; + int rn = (insn >> 16) & 0xf; + + if (rn <= 12 && (reglist & 0xe000) == 0) { + /* Instruction only uses registers in the range R0..R12 */ + handler = emulate_generic_r0_12_noflags; + + } else if (rn >= 2 && (reglist & 0x8003) == 0) { + /* Instruction only uses registers in the range R2..R14 */ + rn -= 2; + reglist >>= 2; + handler = emulate_generic_r2_14_noflags; + + } else if (rn >= 3 && (reglist & 0x0007) == 0) { + /* Instruction only uses registers in the range R3..R15 */ + if (is_ldm && (reglist & 0x8000)) { + rn -= 3; + reglist >>= 3; + handler = emulate_ldm_r3_15; + } + } + + if (handler) { + /* We can emulate the instruction in (possibly) modified form */ + asi->insn[0] = __opcode_to_mem_arm((insn & 0xfff00000) | + (rn << 16) | reglist); + asi->insn_handler = handler; + return INSN_GOOD; + } + + /* Fallback to slower simulation... */ + if (reglist & 0x8000) + handler = is_ldm ? simulate_ldm1_pc : simulate_stm1_pc; + else + handler = simulate_ldm1stm1; + asi->insn_handler = handler; + return INSN_GOOD_NO_SLOT; +} + diff --git a/arch/arm/probes/kprobes/actions-thumb.c b/arch/arm/probes/kprobes/actions-thumb.c new file mode 100644 index 0000000..6c4e60b --- /dev/null +++ b/arch/arm/probes/kprobes/actions-thumb.c @@ -0,0 +1,666 @@ +/* + * arch/arm/probes/kprobes/actions-thumb.c + * + * Copyright (C) 2011 Jon Medhurst . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include "../decode-thumb.h" +#include "core.h" + +/* These emulation encodings are functionally equivalent... */ +#define t32_emulate_rd8rn16rm0ra12_noflags \ + t32_emulate_rdlo12rdhi8rn16rm0_noflags + +/* t32 thumb actions */ + +static void __kprobes +t32_simulate_table_branch(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + + unsigned long rnv = (rn == 15) ? pc : regs->uregs[rn]; + unsigned long rmv = regs->uregs[rm]; + unsigned int halfwords; + + if (insn & 0x10) /* TBH */ + halfwords = ((u16 *)rnv)[rmv]; + else /* TBB */ + halfwords = ((u8 *)rnv)[rmv]; + + regs->ARM_pc = pc + 2 * halfwords; +} + +static void __kprobes +t32_simulate_mrs(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rd = (insn >> 8) & 0xf; + unsigned long mask = 0xf8ff03df; /* Mask out execution state */ + regs->uregs[rd] = regs->ARM_cpsr & mask; +} + +static void __kprobes +t32_simulate_cond_branch(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc; + + long offset = insn & 0x7ff; /* imm11 */ + offset += (insn & 0x003f0000) >> 5; /* imm6 */ + offset += (insn & 0x00002000) << 4; /* J1 */ + offset += (insn & 0x00000800) << 7; /* J2 */ + offset -= (insn & 0x04000000) >> 7; /* Apply sign bit */ + + regs->ARM_pc = pc + (offset * 2); +} + +static enum probes_insn __kprobes +t32_decode_cond_branch(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + int cc = (insn >> 22) & 0xf; + asi->insn_check_cc = probes_condition_checks[cc]; + asi->insn_handler = t32_simulate_cond_branch; + return INSN_GOOD_NO_SLOT; +} + +static void __kprobes +t32_simulate_branch(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc; + + long offset = insn & 0x7ff; /* imm11 */ + offset += (insn & 0x03ff0000) >> 5; /* imm10 */ + offset += (insn & 0x00002000) << 9; /* J1 */ + offset += (insn & 0x00000800) << 10; /* J2 */ + if (insn & 0x04000000) + offset -= 0x00800000; /* Apply sign bit */ + else + offset ^= 0x00600000; /* Invert J1 and J2 */ + + if (insn & (1 << 14)) { + /* BL or BLX */ + regs->ARM_lr = regs->ARM_pc | 1; + if (!(insn & (1 << 12))) { + /* BLX so switch to ARM mode */ + regs->ARM_cpsr &= ~PSR_T_BIT; + pc &= ~3; + } + } + + regs->ARM_pc = pc + (offset * 2); +} + +static void __kprobes +t32_simulate_ldr_literal(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long addr = regs->ARM_pc & ~3; + int rt = (insn >> 12) & 0xf; + unsigned long rtv; + + long offset = insn & 0xfff; + if (insn & 0x00800000) + addr += offset; + else + addr -= offset; + + if (insn & 0x00400000) { + /* LDR */ + rtv = *(unsigned long *)addr; + if (rt == 15) { + bx_write_pc(rtv, regs); + return; + } + } else if (insn & 0x00200000) { + /* LDRH */ + if (insn & 0x01000000) + rtv = *(s16 *)addr; + else + rtv = *(u16 *)addr; + } else { + /* LDRB */ + if (insn & 0x01000000) + rtv = *(s8 *)addr; + else + rtv = *(u8 *)addr; + } + + regs->uregs[rt] = rtv; +} + +static enum probes_insn __kprobes +t32_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + enum probes_insn ret = kprobe_decode_ldmstm(insn, asi, d); + + /* Fixup modified instruction to have halfwords in correct order...*/ + insn = __mem_to_opcode_arm(asi->insn[0]); + ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn >> 16); + ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0xffff); + + return ret; +} + +static void __kprobes +t32_emulate_ldrdstrd(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc & ~3; + int rt1 = (insn >> 12) & 0xf; + int rt2 = (insn >> 8) & 0xf; + int rn = (insn >> 16) & 0xf; + + register unsigned long rt1v asm("r0") = regs->uregs[rt1]; + register unsigned long rt2v asm("r1") = regs->uregs[rt2]; + register unsigned long rnv asm("r2") = (rn == 15) ? pc + : regs->uregs[rn]; + + __asm__ __volatile__ ( + "blx %[fn]" + : "=r" (rt1v), "=r" (rt2v), "=r" (rnv) + : "0" (rt1v), "1" (rt2v), "2" (rnv), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + if (rn != 15) + regs->uregs[rn] = rnv; /* Writeback base register */ + regs->uregs[rt1] = rt1v; + regs->uregs[rt2] = rt2v; +} + +static void __kprobes +t32_emulate_ldrstr(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rt = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + + register unsigned long rtv asm("r0") = regs->uregs[rt]; + register unsigned long rnv asm("r2") = regs->uregs[rn]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + + __asm__ __volatile__ ( + "blx %[fn]" + : "=r" (rtv), "=r" (rnv) + : "0" (rtv), "1" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rn] = rnv; /* Writeback base register */ + if (rt == 15) /* Can't be true for a STR as they aren't allowed */ + bx_write_pc(rtv, regs); + else + regs->uregs[rt] = rtv; +} + +static void __kprobes +t32_emulate_rd8rn16rm0_rwflags(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rd = (insn >> 8) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + + register unsigned long rdv asm("r1") = regs->uregs[rd]; + register unsigned long rnv asm("r2") = regs->uregs[rn]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + unsigned long cpsr = regs->ARM_cpsr; + + __asm__ __volatile__ ( + "msr cpsr_fs, %[cpsr] \n\t" + "blx %[fn] \n\t" + "mrs %[cpsr], cpsr \n\t" + : "=r" (rdv), [cpsr] "=r" (cpsr) + : "0" (rdv), "r" (rnv), "r" (rmv), + "1" (cpsr), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rd] = rdv; + regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); +} + +static void __kprobes +t32_emulate_rd8pc16_noflags(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc; + int rd = (insn >> 8) & 0xf; + + register unsigned long rdv asm("r1") = regs->uregs[rd]; + register unsigned long rnv asm("r2") = pc & ~3; + + __asm__ __volatile__ ( + "blx %[fn]" + : "=r" (rdv) + : "0" (rdv), "r" (rnv), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rd] = rdv; +} + +static void __kprobes +t32_emulate_rd8rn16_noflags(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rd = (insn >> 8) & 0xf; + int rn = (insn >> 16) & 0xf; + + register unsigned long rdv asm("r1") = regs->uregs[rd]; + register unsigned long rnv asm("r2") = regs->uregs[rn]; + + __asm__ __volatile__ ( + "blx %[fn]" + : "=r" (rdv) + : "0" (rdv), "r" (rnv), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rd] = rdv; +} + +static void __kprobes +t32_emulate_rdlo12rdhi8rn16rm0_noflags(probes_opcode_t insn, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + int rdlo = (insn >> 12) & 0xf; + int rdhi = (insn >> 8) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + + register unsigned long rdlov asm("r0") = regs->uregs[rdlo]; + register unsigned long rdhiv asm("r1") = regs->uregs[rdhi]; + register unsigned long rnv asm("r2") = regs->uregs[rn]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + + __asm__ __volatile__ ( + "blx %[fn]" + : "=r" (rdlov), "=r" (rdhiv) + : "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv), + [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rdlo] = rdlov; + regs->uregs[rdhi] = rdhiv; +} +/* t16 thumb actions */ + +static void __kprobes +t16_simulate_bxblx(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc + 2; + int rm = (insn >> 3) & 0xf; + unsigned long rmv = (rm == 15) ? pc : regs->uregs[rm]; + + if (insn & (1 << 7)) /* BLX ? */ + regs->ARM_lr = regs->ARM_pc | 1; + + bx_write_pc(rmv, regs); +} + +static void __kprobes +t16_simulate_ldr_literal(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long *base = (unsigned long *)((regs->ARM_pc + 2) & ~3); + long index = insn & 0xff; + int rt = (insn >> 8) & 0x7; + regs->uregs[rt] = base[index]; +} + +static void __kprobes +t16_simulate_ldrstr_sp_relative(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long* base = (unsigned long *)regs->ARM_sp; + long index = insn & 0xff; + int rt = (insn >> 8) & 0x7; + if (insn & 0x800) /* LDR */ + regs->uregs[rt] = base[index]; + else /* STR */ + base[index] = regs->uregs[rt]; +} + +static void __kprobes +t16_simulate_reladr(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long base = (insn & 0x800) ? regs->ARM_sp + : ((regs->ARM_pc + 2) & ~3); + long offset = insn & 0xff; + int rt = (insn >> 8) & 0x7; + regs->uregs[rt] = base + offset * 4; +} + +static void __kprobes +t16_simulate_add_sp_imm(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + long imm = insn & 0x7f; + if (insn & 0x80) /* SUB */ + regs->ARM_sp -= imm * 4; + else /* ADD */ + regs->ARM_sp += imm * 4; +} + +static void __kprobes +t16_simulate_cbz(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rn = insn & 0x7; + probes_opcode_t nonzero = regs->uregs[rn] ? insn : ~insn; + if (nonzero & 0x800) { + long i = insn & 0x200; + long imm5 = insn & 0xf8; + unsigned long pc = regs->ARM_pc + 2; + regs->ARM_pc = pc + (i >> 3) + (imm5 >> 2); + } +} + +static void __kprobes +t16_simulate_it(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + /* + * The 8 IT state bits are split into two parts in CPSR: + * ITSTATE<1:0> are in CPSR<26:25> + * ITSTATE<7:2> are in CPSR<15:10> + * The new IT state is in the lower byte of insn. + */ + unsigned long cpsr = regs->ARM_cpsr; + cpsr &= ~PSR_IT_MASK; + cpsr |= (insn & 0xfc) << 8; + cpsr |= (insn & 0x03) << 25; + regs->ARM_cpsr = cpsr; +} + +static void __kprobes +t16_singlestep_it(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + regs->ARM_pc += 2; + t16_simulate_it(insn, asi, regs); +} + +static enum probes_insn __kprobes +t16_decode_it(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + asi->insn_singlestep = t16_singlestep_it; + return INSN_GOOD_NO_SLOT; +} + +static void __kprobes +t16_simulate_cond_branch(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc + 2; + long offset = insn & 0x7f; + offset -= insn & 0x80; /* Apply sign bit */ + regs->ARM_pc = pc + (offset * 2); +} + +static enum probes_insn __kprobes +t16_decode_cond_branch(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + int cc = (insn >> 8) & 0xf; + asi->insn_check_cc = probes_condition_checks[cc]; + asi->insn_handler = t16_simulate_cond_branch; + return INSN_GOOD_NO_SLOT; +} + +static void __kprobes +t16_simulate_branch(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc + 2; + long offset = insn & 0x3ff; + offset -= insn & 0x400; /* Apply sign bit */ + regs->ARM_pc = pc + (offset * 2); +} + +static unsigned long __kprobes +t16_emulate_loregs(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long oldcpsr = regs->ARM_cpsr; + unsigned long newcpsr; + + __asm__ __volatile__ ( + "msr cpsr_fs, %[oldcpsr] \n\t" + "ldmia %[regs], {r0-r7} \n\t" + "blx %[fn] \n\t" + "stmia %[regs], {r0-r7} \n\t" + "mrs %[newcpsr], cpsr \n\t" + : [newcpsr] "=r" (newcpsr) + : [oldcpsr] "r" (oldcpsr), [regs] "r" (regs), + [fn] "r" (asi->insn_fn) + : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "lr", "memory", "cc" + ); + + return (oldcpsr & ~APSR_MASK) | (newcpsr & APSR_MASK); +} + +static void __kprobes +t16_emulate_loregs_rwflags(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + regs->ARM_cpsr = t16_emulate_loregs(insn, asi, regs); +} + +static void __kprobes +t16_emulate_loregs_noitrwflags(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long cpsr = t16_emulate_loregs(insn, asi, regs); + if (!in_it_block(cpsr)) + regs->ARM_cpsr = cpsr; +} + +static void __kprobes +t16_emulate_hiregs(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc + 2; + int rdn = (insn & 0x7) | ((insn & 0x80) >> 4); + int rm = (insn >> 3) & 0xf; + + register unsigned long rdnv asm("r1"); + register unsigned long rmv asm("r0"); + unsigned long cpsr = regs->ARM_cpsr; + + rdnv = (rdn == 15) ? pc : regs->uregs[rdn]; + rmv = (rm == 15) ? pc : regs->uregs[rm]; + + __asm__ __volatile__ ( + "msr cpsr_fs, %[cpsr] \n\t" + "blx %[fn] \n\t" + "mrs %[cpsr], cpsr \n\t" + : "=r" (rdnv), [cpsr] "=r" (cpsr) + : "0" (rdnv), "r" (rmv), "1" (cpsr), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + if (rdn == 15) + rdnv &= ~1; + + regs->uregs[rdn] = rdnv; + regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); +} + +static enum probes_insn __kprobes +t16_decode_hiregs(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + insn &= ~0x00ff; + insn |= 0x001; /* Set Rdn = R1 and Rm = R0 */ + ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn); + asi->insn_handler = t16_emulate_hiregs; + return INSN_GOOD; +} + +static void __kprobes +t16_emulate_push(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + __asm__ __volatile__ ( + "ldr r9, [%[regs], #13*4] \n\t" + "ldr r8, [%[regs], #14*4] \n\t" + "ldmia %[regs], {r0-r7} \n\t" + "blx %[fn] \n\t" + "str r9, [%[regs], #13*4] \n\t" + : + : [regs] "r" (regs), [fn] "r" (asi->insn_fn) + : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", + "lr", "memory", "cc" + ); +} + +static enum probes_insn __kprobes +t16_decode_push(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + /* + * To simulate a PUSH we use a Thumb-2 "STMDB R9!, {registers}" + * and call it with R9=SP and LR in the register list represented + * by R8. + */ + /* 1st half STMDB R9!,{} */ + ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe929); + /* 2nd half (register list) */ + ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff); + asi->insn_handler = t16_emulate_push; + return INSN_GOOD; +} + +static void __kprobes +t16_emulate_pop_nopc(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + __asm__ __volatile__ ( + "ldr r9, [%[regs], #13*4] \n\t" + "ldmia %[regs], {r0-r7} \n\t" + "blx %[fn] \n\t" + "stmia %[regs], {r0-r7} \n\t" + "str r9, [%[regs], #13*4] \n\t" + : + : [regs] "r" (regs), [fn] "r" (asi->insn_fn) + : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9", + "lr", "memory", "cc" + ); +} + +static void __kprobes +t16_emulate_pop_pc(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + register unsigned long pc asm("r8"); + + __asm__ __volatile__ ( + "ldr r9, [%[regs], #13*4] \n\t" + "ldmia %[regs], {r0-r7} \n\t" + "blx %[fn] \n\t" + "stmia %[regs], {r0-r7} \n\t" + "str r9, [%[regs], #13*4] \n\t" + : "=r" (pc) + : [regs] "r" (regs), [fn] "r" (asi->insn_fn) + : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9", + "lr", "memory", "cc" + ); + + bx_write_pc(pc, regs); +} + +static enum probes_insn __kprobes +t16_decode_pop(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + /* + * To simulate a POP we use a Thumb-2 "LDMDB R9!, {registers}" + * and call it with R9=SP and PC in the register list represented + * by R8. + */ + /* 1st half LDMIA R9!,{} */ + ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe8b9); + /* 2nd half (register list) */ + ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff); + asi->insn_handler = insn & 0x100 ? t16_emulate_pop_pc + : t16_emulate_pop_nopc; + return INSN_GOOD; +} + +const union decode_action kprobes_t16_actions[NUM_PROBES_T16_ACTIONS] = { + [PROBES_T16_ADD_SP] = {.handler = t16_simulate_add_sp_imm}, + [PROBES_T16_CBZ] = {.handler = t16_simulate_cbz}, + [PROBES_T16_SIGN_EXTEND] = {.handler = t16_emulate_loregs_rwflags}, + [PROBES_T16_PUSH] = {.decoder = t16_decode_push}, + [PROBES_T16_POP] = {.decoder = t16_decode_pop}, + [PROBES_T16_SEV] = {.handler = probes_emulate_none}, + [PROBES_T16_WFE] = {.handler = probes_simulate_nop}, + [PROBES_T16_IT] = {.decoder = t16_decode_it}, + [PROBES_T16_CMP] = {.handler = t16_emulate_loregs_rwflags}, + [PROBES_T16_ADDSUB] = {.handler = t16_emulate_loregs_noitrwflags}, + [PROBES_T16_LOGICAL] = {.handler = t16_emulate_loregs_noitrwflags}, + [PROBES_T16_LDR_LIT] = {.handler = t16_simulate_ldr_literal}, + [PROBES_T16_BLX] = {.handler = t16_simulate_bxblx}, + [PROBES_T16_HIREGOPS] = {.decoder = t16_decode_hiregs}, + [PROBES_T16_LDRHSTRH] = {.handler = t16_emulate_loregs_rwflags}, + [PROBES_T16_LDRSTR] = {.handler = t16_simulate_ldrstr_sp_relative}, + [PROBES_T16_ADR] = {.handler = t16_simulate_reladr}, + [PROBES_T16_LDMSTM] = {.handler = t16_emulate_loregs_rwflags}, + [PROBES_T16_BRANCH_COND] = {.decoder = t16_decode_cond_branch}, + [PROBES_T16_BRANCH] = {.handler = t16_simulate_branch}, +}; + +const union decode_action kprobes_t32_actions[NUM_PROBES_T32_ACTIONS] = { + [PROBES_T32_LDMSTM] = {.decoder = t32_decode_ldmstm}, + [PROBES_T32_LDRDSTRD] = {.handler = t32_emulate_ldrdstrd}, + [PROBES_T32_TABLE_BRANCH] = {.handler = t32_simulate_table_branch}, + [PROBES_T32_TST] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_MOV] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_ADDSUB] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_LOGICAL] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_CMP] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_ADDWSUBW_PC] = {.handler = t32_emulate_rd8pc16_noflags,}, + [PROBES_T32_ADDWSUBW] = {.handler = t32_emulate_rd8rn16_noflags}, + [PROBES_T32_MOVW] = {.handler = t32_emulate_rd8rn16_noflags}, + [PROBES_T32_SAT] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_BITFIELD] = {.handler = t32_emulate_rd8rn16_noflags}, + [PROBES_T32_SEV] = {.handler = probes_emulate_none}, + [PROBES_T32_WFE] = {.handler = probes_simulate_nop}, + [PROBES_T32_MRS] = {.handler = t32_simulate_mrs}, + [PROBES_T32_BRANCH_COND] = {.decoder = t32_decode_cond_branch}, + [PROBES_T32_BRANCH] = {.handler = t32_simulate_branch}, + [PROBES_T32_PLDI] = {.handler = probes_simulate_nop}, + [PROBES_T32_LDR_LIT] = {.handler = t32_simulate_ldr_literal}, + [PROBES_T32_LDRSTR] = {.handler = t32_emulate_ldrstr}, + [PROBES_T32_SIGN_EXTEND] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_MEDIA] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_REVERSE] = {.handler = t32_emulate_rd8rn16_noflags}, + [PROBES_T32_MUL_ADD] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_MUL_ADD2] = {.handler = t32_emulate_rd8rn16rm0ra12_noflags}, + [PROBES_T32_MUL_ADD_LONG] = { + .handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags}, +}; diff --git a/arch/arm/probes/kprobes/core.c b/arch/arm/probes/kprobes/core.c new file mode 100644 index 0000000..701f49d --- /dev/null +++ b/arch/arm/probes/kprobes/core.c @@ -0,0 +1,628 @@ +/* + * arch/arm/kernel/kprobes.c + * + * Kprobes on ARM + * + * Abhishek Sagar + * Copyright (C) 2006, 2007 Motorola Inc. + * + * Nicolas Pitre + * Copyright (C) 2007 Marvell Ltd. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../decode-arm.h" +#include "../decode-thumb.h" +#include "core.h" + +#define MIN_STACK_SIZE(addr) \ + min((unsigned long)MAX_STACK_SIZE, \ + (unsigned long)current_thread_info() + THREAD_START_SP - (addr)) + +#define flush_insns(addr, size) \ + flush_icache_range((unsigned long)(addr), \ + (unsigned long)(addr) + \ + (size)) + +/* Used as a marker in ARM_pc to note when we're in a jprobe. */ +#define JPROBE_MAGIC_ADDR 0xffffffff + +DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; +DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); + + +int __kprobes arch_prepare_kprobe(struct kprobe *p) +{ + kprobe_opcode_t insn; + kprobe_opcode_t tmp_insn[MAX_INSN_SIZE]; + unsigned long addr = (unsigned long)p->addr; + bool thumb; + kprobe_decode_insn_t *decode_insn; + const union decode_action *actions; + int is; + + if (in_exception_text(addr)) + return -EINVAL; + +#ifdef CONFIG_THUMB2_KERNEL + thumb = true; + addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */ + insn = __mem_to_opcode_thumb16(((u16 *)addr)[0]); + if (is_wide_instruction(insn)) { + u16 inst2 = __mem_to_opcode_thumb16(((u16 *)addr)[1]); + insn = __opcode_thumb32_compose(insn, inst2); + decode_insn = thumb32_probes_decode_insn; + actions = kprobes_t32_actions; + } else { + decode_insn = thumb16_probes_decode_insn; + actions = kprobes_t16_actions; + } +#else /* !CONFIG_THUMB2_KERNEL */ + thumb = false; + if (addr & 0x3) + return -EINVAL; + insn = __mem_to_opcode_arm(*p->addr); + decode_insn = arm_probes_decode_insn; + actions = kprobes_arm_actions; +#endif + + p->opcode = insn; + p->ainsn.insn = tmp_insn; + + switch ((*decode_insn)(insn, &p->ainsn, true, actions)) { + case INSN_REJECTED: /* not supported */ + return -EINVAL; + + case INSN_GOOD: /* instruction uses slot */ + p->ainsn.insn = get_insn_slot(); + if (!p->ainsn.insn) + return -ENOMEM; + for (is = 0; is < MAX_INSN_SIZE; ++is) + p->ainsn.insn[is] = tmp_insn[is]; + flush_insns(p->ainsn.insn, + sizeof(p->ainsn.insn[0]) * MAX_INSN_SIZE); + p->ainsn.insn_fn = (probes_insn_fn_t *) + ((uintptr_t)p->ainsn.insn | thumb); + break; + + case INSN_GOOD_NO_SLOT: /* instruction doesn't need insn slot */ + p->ainsn.insn = NULL; + break; + } + + return 0; +} + +void __kprobes arch_arm_kprobe(struct kprobe *p) +{ + unsigned int brkp; + void *addr; + + if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) { + /* Remove any Thumb flag */ + addr = (void *)((uintptr_t)p->addr & ~1); + + if (is_wide_instruction(p->opcode)) + brkp = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION; + else + brkp = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION; + } else { + kprobe_opcode_t insn = p->opcode; + + addr = p->addr; + brkp = KPROBE_ARM_BREAKPOINT_INSTRUCTION; + + if (insn >= 0xe0000000) + brkp |= 0xe0000000; /* Unconditional instruction */ + else + brkp |= insn & 0xf0000000; /* Copy condition from insn */ + } + + patch_text(addr, brkp); +} + +/* + * The actual disarming is done here on each CPU and synchronized using + * stop_machine. This synchronization is necessary on SMP to avoid removing + * a probe between the moment the 'Undefined Instruction' exception is raised + * and the moment the exception handler reads the faulting instruction from + * memory. It is also needed to atomically set the two half-words of a 32-bit + * Thumb breakpoint. + */ +int __kprobes __arch_disarm_kprobe(void *p) +{ + struct kprobe *kp = p; + void *addr = (void *)((uintptr_t)kp->addr & ~1); + + __patch_text(addr, kp->opcode); + + return 0; +} + +void __kprobes arch_disarm_kprobe(struct kprobe *p) +{ + stop_machine(__arch_disarm_kprobe, p, cpu_online_mask); +} + +void __kprobes arch_remove_kprobe(struct kprobe *p) +{ + if (p->ainsn.insn) { + free_insn_slot(p->ainsn.insn, 0); + p->ainsn.insn = NULL; + } +} + +static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) +{ + kcb->prev_kprobe.kp = kprobe_running(); + kcb->prev_kprobe.status = kcb->kprobe_status; +} + +static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) +{ + __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp); + kcb->kprobe_status = kcb->prev_kprobe.status; +} + +static void __kprobes set_current_kprobe(struct kprobe *p) +{ + __this_cpu_write(current_kprobe, p); +} + +static void __kprobes +singlestep_skip(struct kprobe *p, struct pt_regs *regs) +{ +#ifdef CONFIG_THUMB2_KERNEL + regs->ARM_cpsr = it_advance(regs->ARM_cpsr); + if (is_wide_instruction(p->opcode)) + regs->ARM_pc += 4; + else + regs->ARM_pc += 2; +#else + regs->ARM_pc += 4; +#endif +} + +static inline void __kprobes +singlestep(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb) +{ + p->ainsn.insn_singlestep(p->opcode, &p->ainsn, regs); +} + +/* + * Called with IRQs disabled. IRQs must remain disabled from that point + * all the way until processing this kprobe is complete. The current + * kprobes implementation cannot process more than one nested level of + * kprobe, and that level is reserved for user kprobe handlers, so we can't + * risk encountering a new kprobe in an interrupt handler. + */ +void __kprobes kprobe_handler(struct pt_regs *regs) +{ + struct kprobe *p, *cur; + struct kprobe_ctlblk *kcb; + + kcb = get_kprobe_ctlblk(); + cur = kprobe_running(); + +#ifdef CONFIG_THUMB2_KERNEL + /* + * First look for a probe which was registered using an address with + * bit 0 set, this is the usual situation for pointers to Thumb code. + * If not found, fallback to looking for one with bit 0 clear. + */ + p = get_kprobe((kprobe_opcode_t *)(regs->ARM_pc | 1)); + if (!p) + p = get_kprobe((kprobe_opcode_t *)regs->ARM_pc); + +#else /* ! CONFIG_THUMB2_KERNEL */ + p = get_kprobe((kprobe_opcode_t *)regs->ARM_pc); +#endif + + if (p) { + if (cur) { + /* Kprobe is pending, so we're recursing. */ + switch (kcb->kprobe_status) { + case KPROBE_HIT_ACTIVE: + case KPROBE_HIT_SSDONE: + /* A pre- or post-handler probe got us here. */ + kprobes_inc_nmissed_count(p); + save_previous_kprobe(kcb); + set_current_kprobe(p); + kcb->kprobe_status = KPROBE_REENTER; + singlestep(p, regs, kcb); + restore_previous_kprobe(kcb); + break; + default: + /* impossible cases */ + BUG(); + } + } else if (p->ainsn.insn_check_cc(regs->ARM_cpsr)) { + /* Probe hit and conditional execution check ok. */ + set_current_kprobe(p); + kcb->kprobe_status = KPROBE_HIT_ACTIVE; + + /* + * If we have no pre-handler or it returned 0, we + * continue with normal processing. If we have a + * pre-handler and it returned non-zero, it prepped + * for calling the break_handler below on re-entry, + * so get out doing nothing more here. + */ + if (!p->pre_handler || !p->pre_handler(p, regs)) { + kcb->kprobe_status = KPROBE_HIT_SS; + singlestep(p, regs, kcb); + if (p->post_handler) { + kcb->kprobe_status = KPROBE_HIT_SSDONE; + p->post_handler(p, regs, 0); + } + reset_current_kprobe(); + } + } else { + /* + * Probe hit but conditional execution check failed, + * so just skip the instruction and continue as if + * nothing had happened. + */ + singlestep_skip(p, regs); + } + } else if (cur) { + /* We probably hit a jprobe. Call its break handler. */ + if (cur->break_handler && cur->break_handler(cur, regs)) { + kcb->kprobe_status = KPROBE_HIT_SS; + singlestep(cur, regs, kcb); + if (cur->post_handler) { + kcb->kprobe_status = KPROBE_HIT_SSDONE; + cur->post_handler(cur, regs, 0); + } + } + reset_current_kprobe(); + } else { + /* + * The probe was removed and a race is in progress. + * There is nothing we can do about it. Let's restart + * the instruction. By the time we can restart, the + * real instruction will be there. + */ + } +} + +static int __kprobes kprobe_trap_handler(struct pt_regs *regs, unsigned int instr) +{ + unsigned long flags; + local_irq_save(flags); + kprobe_handler(regs); + local_irq_restore(flags); + return 0; +} + +int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr) +{ + struct kprobe *cur = kprobe_running(); + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + + switch (kcb->kprobe_status) { + case KPROBE_HIT_SS: + case KPROBE_REENTER: + /* + * We are here because the instruction being single + * stepped caused a page fault. We reset the current + * kprobe and the PC to point back to the probe address + * and allow the page fault handler to continue as a + * normal page fault. + */ + regs->ARM_pc = (long)cur->addr; + if (kcb->kprobe_status == KPROBE_REENTER) { + restore_previous_kprobe(kcb); + } else { + reset_current_kprobe(); + } + break; + + case KPROBE_HIT_ACTIVE: + case KPROBE_HIT_SSDONE: + /* + * We increment the nmissed count for accounting, + * we can also use npre/npostfault count for accounting + * these specific fault cases. + */ + kprobes_inc_nmissed_count(cur); + + /* + * We come here because instructions in the pre/post + * handler caused the page_fault, this could happen + * if handler tries to access user space by + * copy_from_user(), get_user() etc. Let the + * user-specified handler try to fix it. + */ + if (cur->fault_handler && cur->fault_handler(cur, regs, fsr)) + return 1; + break; + + default: + break; + } + + return 0; +} + +int __kprobes kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + /* + * notify_die() is currently never called on ARM, + * so this callback is currently empty. + */ + return NOTIFY_DONE; +} + +/* + * When a retprobed function returns, trampoline_handler() is called, + * calling the kretprobe's handler. We construct a struct pt_regs to + * give a view of registers r0-r11 to the user return-handler. This is + * not a complete pt_regs structure, but that should be plenty sufficient + * for kretprobe handlers which should normally be interested in r0 only + * anyway. + */ +void __naked __kprobes kretprobe_trampoline(void) +{ + __asm__ __volatile__ ( + "stmdb sp!, {r0 - r11} \n\t" + "mov r0, sp \n\t" + "bl trampoline_handler \n\t" + "mov lr, r0 \n\t" + "ldmia sp!, {r0 - r11} \n\t" +#ifdef CONFIG_THUMB2_KERNEL + "bx lr \n\t" +#else + "mov pc, lr \n\t" +#endif + : : : "memory"); +} + +/* Called from kretprobe_trampoline */ +static __used __kprobes void *trampoline_handler(struct pt_regs *regs) +{ + struct kretprobe_instance *ri = NULL; + struct hlist_head *head, empty_rp; + struct hlist_node *tmp; + unsigned long flags, orig_ret_address = 0; + unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; + + INIT_HLIST_HEAD(&empty_rp); + kretprobe_hash_lock(current, &head, &flags); + + /* + * It is possible to have multiple instances associated with a given + * task either because multiple functions in the call path have + * a return probe installed on them, and/or more than one return + * probe was registered for a target function. + * + * We can handle this because: + * - instances are always inserted at the head of the list + * - when multiple return probes are registered for the same + * function, the first instance's ret_addr will point to the + * real return address, and all the rest will point to + * kretprobe_trampoline + */ + hlist_for_each_entry_safe(ri, tmp, head, hlist) { + if (ri->task != current) + /* another task is sharing our hash bucket */ + continue; + + if (ri->rp && ri->rp->handler) { + __this_cpu_write(current_kprobe, &ri->rp->kp); + get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE; + ri->rp->handler(ri, regs); + __this_cpu_write(current_kprobe, NULL); + } + + orig_ret_address = (unsigned long)ri->ret_addr; + recycle_rp_inst(ri, &empty_rp); + + if (orig_ret_address != trampoline_address) + /* + * This is the real return address. Any other + * instances associated with this task are for + * other calls deeper on the call stack + */ + break; + } + + kretprobe_assert(ri, orig_ret_address, trampoline_address); + kretprobe_hash_unlock(current, &flags); + + hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) { + hlist_del(&ri->hlist); + kfree(ri); + } + + return (void *)orig_ret_address; +} + +void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr; + + /* Replace the return addr with trampoline addr. */ + regs->ARM_lr = (unsigned long)&kretprobe_trampoline; +} + +int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct jprobe *jp = container_of(p, struct jprobe, kp); + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + long sp_addr = regs->ARM_sp; + long cpsr; + + kcb->jprobe_saved_regs = *regs; + memcpy(kcb->jprobes_stack, (void *)sp_addr, MIN_STACK_SIZE(sp_addr)); + regs->ARM_pc = (long)jp->entry; + + cpsr = regs->ARM_cpsr | PSR_I_BIT; +#ifdef CONFIG_THUMB2_KERNEL + /* Set correct Thumb state in cpsr */ + if (regs->ARM_pc & 1) + cpsr |= PSR_T_BIT; + else + cpsr &= ~PSR_T_BIT; +#endif + regs->ARM_cpsr = cpsr; + + preempt_disable(); + return 1; +} + +void __kprobes jprobe_return(void) +{ + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + + __asm__ __volatile__ ( + /* + * Setup an empty pt_regs. Fill SP and PC fields as + * they're needed by longjmp_break_handler. + * + * We allocate some slack between the original SP and start of + * our fabricated regs. To be precise we want to have worst case + * covered which is STMFD with all 16 regs so we allocate 2 * + * sizeof(struct_pt_regs)). + * + * This is to prevent any simulated instruction from writing + * over the regs when they are accessing the stack. + */ +#ifdef CONFIG_THUMB2_KERNEL + "sub r0, %0, %1 \n\t" + "mov sp, r0 \n\t" +#else + "sub sp, %0, %1 \n\t" +#endif + "ldr r0, ="__stringify(JPROBE_MAGIC_ADDR)"\n\t" + "str %0, [sp, %2] \n\t" + "str r0, [sp, %3] \n\t" + "mov r0, sp \n\t" + "bl kprobe_handler \n\t" + + /* + * Return to the context saved by setjmp_pre_handler + * and restored by longjmp_break_handler. + */ +#ifdef CONFIG_THUMB2_KERNEL + "ldr lr, [sp, %2] \n\t" /* lr = saved sp */ + "ldrd r0, r1, [sp, %5] \n\t" /* r0,r1 = saved lr,pc */ + "ldr r2, [sp, %4] \n\t" /* r2 = saved psr */ + "stmdb lr!, {r0, r1, r2} \n\t" /* push saved lr and */ + /* rfe context */ + "ldmia sp, {r0 - r12} \n\t" + "mov sp, lr \n\t" + "ldr lr, [sp], #4 \n\t" + "rfeia sp! \n\t" +#else + "ldr r0, [sp, %4] \n\t" + "msr cpsr_cxsf, r0 \n\t" + "ldmia sp, {r0 - pc} \n\t" +#endif + : + : "r" (kcb->jprobe_saved_regs.ARM_sp), + "I" (sizeof(struct pt_regs) * 2), + "J" (offsetof(struct pt_regs, ARM_sp)), + "J" (offsetof(struct pt_regs, ARM_pc)), + "J" (offsetof(struct pt_regs, ARM_cpsr)), + "J" (offsetof(struct pt_regs, ARM_lr)) + : "memory", "cc"); +} + +int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + long stack_addr = kcb->jprobe_saved_regs.ARM_sp; + long orig_sp = regs->ARM_sp; + struct jprobe *jp = container_of(p, struct jprobe, kp); + + if (regs->ARM_pc == JPROBE_MAGIC_ADDR) { + if (orig_sp != stack_addr) { + struct pt_regs *saved_regs = + (struct pt_regs *)kcb->jprobe_saved_regs.ARM_sp; + printk("current sp %lx does not match saved sp %lx\n", + orig_sp, stack_addr); + printk("Saved registers for jprobe %p\n", jp); + show_regs(saved_regs); + printk("Current registers\n"); + show_regs(regs); + BUG(); + } + *regs = kcb->jprobe_saved_regs; + memcpy((void *)stack_addr, kcb->jprobes_stack, + MIN_STACK_SIZE(stack_addr)); + preempt_enable_no_resched(); + return 1; + } + return 0; +} + +int __kprobes arch_trampoline_kprobe(struct kprobe *p) +{ + return 0; +} + +#ifdef CONFIG_THUMB2_KERNEL + +static struct undef_hook kprobes_thumb16_break_hook = { + .instr_mask = 0xffff, + .instr_val = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION, + .cpsr_mask = MODE_MASK, + .cpsr_val = SVC_MODE, + .fn = kprobe_trap_handler, +}; + +static struct undef_hook kprobes_thumb32_break_hook = { + .instr_mask = 0xffffffff, + .instr_val = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION, + .cpsr_mask = MODE_MASK, + .cpsr_val = SVC_MODE, + .fn = kprobe_trap_handler, +}; + +#else /* !CONFIG_THUMB2_KERNEL */ + +static struct undef_hook kprobes_arm_break_hook = { + .instr_mask = 0x0fffffff, + .instr_val = KPROBE_ARM_BREAKPOINT_INSTRUCTION, + .cpsr_mask = MODE_MASK, + .cpsr_val = SVC_MODE, + .fn = kprobe_trap_handler, +}; + +#endif /* !CONFIG_THUMB2_KERNEL */ + +int __init arch_init_kprobes() +{ + arm_probes_decode_init(); +#ifdef CONFIG_THUMB2_KERNEL + register_undef_hook(&kprobes_thumb16_break_hook); + register_undef_hook(&kprobes_thumb32_break_hook); +#else + register_undef_hook(&kprobes_arm_break_hook); +#endif + return 0; +} diff --git a/arch/arm/probes/kprobes/core.h b/arch/arm/probes/kprobes/core.h new file mode 100644 index 0000000..2e1e5a3 --- /dev/null +++ b/arch/arm/probes/kprobes/core.h @@ -0,0 +1,53 @@ +/* + * arch/arm/kernel/kprobes.h + * + * Copyright (C) 2011 Jon Medhurst . + * + * Some contents moved here from arch/arm/include/asm/kprobes.h which is + * Copyright (C) 2006, 2007 Motorola 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. + * + * 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 _ARM_KERNEL_KPROBES_H +#define _ARM_KERNEL_KPROBES_H + +#include +#include "../decode.h" + +/* + * These undefined instructions must be unique and + * reserved solely for kprobes' use. + */ +#define KPROBE_ARM_BREAKPOINT_INSTRUCTION 0x07f001f8 +#define KPROBE_THUMB16_BREAKPOINT_INSTRUCTION 0xde18 +#define KPROBE_THUMB32_BREAKPOINT_INSTRUCTION 0xf7f0a018 + +enum probes_insn __kprobes +kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *h); + +typedef enum probes_insn (kprobe_decode_insn_t)(probes_opcode_t, + struct arch_probes_insn *, + bool, + const union decode_action *); + +#ifdef CONFIG_THUMB2_KERNEL + +extern const union decode_action kprobes_t32_actions[]; +extern const union decode_action kprobes_t16_actions[]; + +#else /* !CONFIG_THUMB2_KERNEL */ + +extern const union decode_action kprobes_arm_actions[]; + +#endif + +#endif /* _ARM_KERNEL_KPROBES_H */ diff --git a/arch/arm/probes/kprobes/test-arm.c b/arch/arm/probes/kprobes/test-arm.c new file mode 100644 index 0000000..d9a1255 --- /dev/null +++ b/arch/arm/probes/kprobes/test-arm.c @@ -0,0 +1,1346 @@ +/* + * arch/arm/kernel/kprobes-test-arm.c + * + * Copyright (C) 2011 Jon Medhurst . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include "test-core.h" + + +#define TEST_ISA "32" + +#define TEST_ARM_TO_THUMB_INTERWORK_R(code1, reg, val, code2) \ + TESTCASE_START(code1 #reg code2) \ + TEST_ARG_REG(reg, val) \ + TEST_ARG_REG(14, 99f) \ + TEST_ARG_END("") \ + "50: nop \n\t" \ + "1: "code1 #reg code2" \n\t" \ + " bx lr \n\t" \ + ".thumb \n\t" \ + "3: adr lr, 2f \n\t" \ + " bx lr \n\t" \ + ".arm \n\t" \ + "2: nop \n\t" \ + TESTCASE_END + +#define TEST_ARM_TO_THUMB_INTERWORK_P(code1, reg, val, code2) \ + TESTCASE_START(code1 #reg code2) \ + TEST_ARG_PTR(reg, val) \ + TEST_ARG_REG(14, 99f) \ + TEST_ARG_MEM(15, 3f+1) \ + TEST_ARG_END("") \ + "50: nop \n\t" \ + "1: "code1 #reg code2" \n\t" \ + " bx lr \n\t" \ + ".thumb \n\t" \ + "3: adr lr, 2f \n\t" \ + " bx lr \n\t" \ + ".arm \n\t" \ + "2: nop \n\t" \ + TESTCASE_END + + +void kprobe_arm_test_cases(void) +{ + kprobe_test_flags = 0; + + TEST_GROUP("Data-processing (register), (register-shifted register), (immediate)") + +#define _DATA_PROCESSING_DNM(op,s,val) \ + TEST_RR( op "eq" s " r0, r",1, VAL1,", r",2, val, "") \ + TEST_RR( op "ne" s " r1, r",1, VAL1,", r",2, val, ", lsl #3") \ + TEST_RR( op "cs" s " r2, r",3, VAL1,", r",2, val, ", lsr #4") \ + TEST_RR( op "cc" s " r3, r",3, VAL1,", r",2, val, ", asr #5") \ + TEST_RR( op "mi" s " r4, r",5, VAL1,", r",2, N(val),", asr #6") \ + TEST_RR( op "pl" s " r5, r",5, VAL1,", r",2, val, ", ror #7") \ + TEST_RR( op "vs" s " r6, r",7, VAL1,", r",2, val, ", rrx") \ + TEST_R( op "vc" s " r6, r",7, VAL1,", pc, lsl #3") \ + TEST_R( op "vc" s " r6, r",7, VAL1,", sp, lsr #4") \ + TEST_R( op "vc" s " r6, pc, r",7, VAL1,", asr #5") \ + TEST_R( op "vc" s " r6, sp, r",7, VAL1,", ror #6") \ + TEST_RRR( op "hi" s " r8, r",9, VAL1,", r",14,val, ", lsl r",0, 3,"")\ + TEST_RRR( op "ls" s " r9, r",9, VAL1,", r",14,val, ", lsr r",7, 4,"")\ + TEST_RRR( op "ge" s " r10, r",11,VAL1,", r",14,val, ", asr r",7, 5,"")\ + TEST_RRR( op "lt" s " r11, r",11,VAL1,", r",14,N(val),", asr r",7, 6,"")\ + TEST_RR( op "gt" s " r12, r13" ", r",14,val, ", ror r",14,7,"")\ + TEST_RR( op "le" s " r14, r",0, val, ", r13" ", lsl r",14,8,"")\ + TEST_R( op "eq" s " r0, r",11,VAL1,", #0xf5") \ + TEST_R( op "ne" s " r11, r",0, VAL1,", #0xf5000000") \ + TEST_R( op s " r7, r",8, VAL2,", #0x000af000") \ + TEST( op s " r4, pc" ", #0x00005a00") + +#define DATA_PROCESSING_DNM(op,val) \ + _DATA_PROCESSING_DNM(op,"",val) \ + _DATA_PROCESSING_DNM(op,"s",val) + +#define DATA_PROCESSING_NM(op,val) \ + TEST_RR( op "ne r",1, VAL1,", r",2, val, "") \ + TEST_RR( op "eq r",1, VAL1,", r",2, val, ", lsl #3") \ + TEST_RR( op "cc r",3, VAL1,", r",2, val, ", lsr #4") \ + TEST_RR( op "cs r",3, VAL1,", r",2, val, ", asr #5") \ + TEST_RR( op "pl r",5, VAL1,", r",2, N(val),", asr #6") \ + TEST_RR( op "mi r",5, VAL1,", r",2, val, ", ror #7") \ + TEST_RR( op "vc r",7, VAL1,", r",2, val, ", rrx") \ + TEST_R ( op "vs r",7, VAL1,", pc, lsl #3") \ + TEST_R ( op "vs r",7, VAL1,", sp, lsr #4") \ + TEST_R( op "vs pc, r",7, VAL1,", asr #5") \ + TEST_R( op "vs sp, r",7, VAL1,", ror #6") \ + TEST_RRR( op "ls r",9, VAL1,", r",14,val, ", lsl r",0, 3,"") \ + TEST_RRR( op "hi r",9, VAL1,", r",14,val, ", lsr r",7, 4,"") \ + TEST_RRR( op "lt r",11,VAL1,", r",14,val, ", asr r",7, 5,"") \ + TEST_RRR( op "ge r",11,VAL1,", r",14,N(val),", asr r",7, 6,"") \ + TEST_RR( op "le r13" ", r",14,val, ", ror r",14,7,"") \ + TEST_RR( op "gt r",0, val, ", r13" ", lsl r",14,8,"") \ + TEST_R( op "eq r",11,VAL1,", #0xf5") \ + TEST_R( op "ne r",0, VAL1,", #0xf5000000") \ + TEST_R( op " r",8, VAL2,", #0x000af000") + +#define _DATA_PROCESSING_DM(op,s,val) \ + TEST_R( op "eq" s " r0, r",1, val, "") \ + TEST_R( op "ne" s " r1, r",1, val, ", lsl #3") \ + TEST_R( op "cs" s " r2, r",3, val, ", lsr #4") \ + TEST_R( op "cc" s " r3, r",3, val, ", asr #5") \ + TEST_R( op "mi" s " r4, r",5, N(val),", asr #6") \ + TEST_R( op "pl" s " r5, r",5, val, ", ror #7") \ + TEST_R( op "vs" s " r6, r",10,val, ", rrx") \ + TEST( op "vs" s " r7, pc, lsl #3") \ + TEST( op "vs" s " r7, sp, lsr #4") \ + TEST_RR( op "vc" s " r8, r",7, val, ", lsl r",0, 3,"") \ + TEST_RR( op "hi" s " r9, r",9, val, ", lsr r",7, 4,"") \ + TEST_RR( op "ls" s " r10, r",9, val, ", asr r",7, 5,"") \ + TEST_RR( op "ge" s " r11, r",11,N(val),", asr r",7, 6,"") \ + TEST_RR( op "lt" s " r12, r",11,val, ", ror r",14,7,"") \ + TEST_R( op "gt" s " r14, r13" ", lsl r",14,8,"") \ + TEST( op "eq" s " r0, #0xf5") \ + TEST( op "ne" s " r11, #0xf5000000") \ + TEST( op s " r7, #0x000af000") \ + TEST( op s " r4, #0x00005a00") + +#define DATA_PROCESSING_DM(op,val) \ + _DATA_PROCESSING_DM(op,"",val) \ + _DATA_PROCESSING_DM(op,"s",val) + + DATA_PROCESSING_DNM("and",0xf00f00ff) + DATA_PROCESSING_DNM("eor",0xf00f00ff) + DATA_PROCESSING_DNM("sub",VAL2) + DATA_PROCESSING_DNM("rsb",VAL2) + DATA_PROCESSING_DNM("add",VAL2) + DATA_PROCESSING_DNM("adc",VAL2) + DATA_PROCESSING_DNM("sbc",VAL2) + DATA_PROCESSING_DNM("rsc",VAL2) + DATA_PROCESSING_NM("tst",0xf00f00ff) + DATA_PROCESSING_NM("teq",0xf00f00ff) + DATA_PROCESSING_NM("cmp",VAL2) + DATA_PROCESSING_NM("cmn",VAL2) + DATA_PROCESSING_DNM("orr",0xf00f00ff) + DATA_PROCESSING_DM("mov",VAL2) + DATA_PROCESSING_DNM("bic",0xf00f00ff) + DATA_PROCESSING_DM("mvn",VAL2) + + TEST("mov ip, sp") /* This has special case emulation code */ + + TEST_SUPPORTED("mov pc, #0x1000"); + TEST_SUPPORTED("mov sp, #0x1000"); + TEST_SUPPORTED("cmp pc, #0x1000"); + TEST_SUPPORTED("cmp sp, #0x1000"); + + /* Data-processing with PC and a shift count in a register */ + TEST_UNSUPPORTED(__inst_arm(0xe15c0f1e) " @ cmp r12, r14, asl pc") + TEST_UNSUPPORTED(__inst_arm(0xe1a0cf1e) " @ mov r12, r14, asl pc") + TEST_UNSUPPORTED(__inst_arm(0xe08caf1e) " @ add r10, r12, r14, asl pc") + TEST_UNSUPPORTED(__inst_arm(0xe151021f) " @ cmp r1, pc, lsl r2") + TEST_UNSUPPORTED(__inst_arm(0xe17f0211) " @ cmn pc, r1, lsl r2") + TEST_UNSUPPORTED(__inst_arm(0xe1a0121f) " @ mov r1, pc, lsl r2") + TEST_UNSUPPORTED(__inst_arm(0xe1a0f211) " @ mov pc, r1, lsl r2") + TEST_UNSUPPORTED(__inst_arm(0xe042131f) " @ sub r1, r2, pc, lsl r3") + TEST_UNSUPPORTED(__inst_arm(0xe1cf1312) " @ bic r1, pc, r2, lsl r3") + TEST_UNSUPPORTED(__inst_arm(0xe081f312) " @ add pc, r1, r2, lsl r3") + + /* Data-processing with PC as a target and status registers updated */ + TEST_UNSUPPORTED("movs pc, r1") + TEST_UNSUPPORTED("movs pc, r1, lsl r2") + TEST_UNSUPPORTED("movs pc, #0x10000") + TEST_UNSUPPORTED("adds pc, lr, r1") + TEST_UNSUPPORTED("adds pc, lr, r1, lsl r2") + TEST_UNSUPPORTED("adds pc, lr, #4") + + /* Data-processing with SP as target */ + TEST("add sp, sp, #16") + TEST("sub sp, sp, #8") + TEST("bic sp, sp, #0x20") + TEST("orr sp, sp, #0x20") + TEST_PR( "add sp, r",10,0,", r",11,4,"") + TEST_PRR("add sp, r",10,0,", r",11,4,", asl r",12,1,"") + TEST_P( "mov sp, r",10,0,"") + TEST_PR( "mov sp, r",10,0,", asl r",12,0,"") + + /* Data-processing with PC as target */ + TEST_BF( "add pc, pc, #2f-1b-8") + TEST_BF_R ("add pc, pc, r",14,2f-1f-8,"") + TEST_BF_R ("add pc, r",14,2f-1f-8,", pc") + TEST_BF_R ("mov pc, r",0,2f,"") + TEST_BF_R ("add pc, pc, r",14,(2f-1f-8)*2,", asr #1") + TEST_BB( "sub pc, pc, #1b-2b+8") +#if __LINUX_ARM_ARCH__ == 6 && !defined(CONFIG_CPU_V7) + TEST_BB( "sub pc, pc, #1b-2b+8-2") /* UNPREDICTABLE before and after ARMv6 */ +#endif + TEST_BB_R( "sub pc, pc, r",14, 1f-2f+8,"") + TEST_BB_R( "rsb pc, r",14,1f-2f+8,", pc") + TEST_R( "add pc, pc, r",10,-2,", asl #1") +#ifdef CONFIG_THUMB2_KERNEL + TEST_ARM_TO_THUMB_INTERWORK_R("add pc, pc, r",0,3f-1f-8+1,"") + TEST_ARM_TO_THUMB_INTERWORK_R("sub pc, r",0,3f+8+1,", #8") +#endif + TEST_GROUP("Miscellaneous instructions") + + TEST("mrs r0, cpsr") + TEST("mrspl r7, cpsr") + TEST("mrs r14, cpsr") + TEST_UNSUPPORTED(__inst_arm(0xe10ff000) " @ mrs r15, cpsr") + TEST_UNSUPPORTED("mrs r0, spsr") + TEST_UNSUPPORTED("mrs lr, spsr") + + TEST_UNSUPPORTED("msr cpsr, r0") + TEST_UNSUPPORTED("msr cpsr_f, lr") + TEST_UNSUPPORTED("msr spsr, r0") + + TEST_BF_R("bx r",0,2f,"") + TEST_BB_R("bx r",7,2f,"") + TEST_BF_R("bxeq r",14,2f,"") + +#if __LINUX_ARM_ARCH__ >= 5 + TEST_R("clz r0, r",0, 0x0,"") + TEST_R("clzeq r7, r",14,0x1,"") + TEST_R("clz lr, r",7, 0xffffffff,"") + TEST( "clz r4, sp") + TEST_UNSUPPORTED(__inst_arm(0x016fff10) " @ clz pc, r0") + TEST_UNSUPPORTED(__inst_arm(0x016f0f1f) " @ clz r0, pc") + +#if __LINUX_ARM_ARCH__ >= 6 + TEST_UNSUPPORTED("bxj r0") +#endif + + TEST_BF_R("blx r",0,2f,"") + TEST_BB_R("blx r",7,2f,"") + TEST_BF_R("blxeq r",14,2f,"") + TEST_UNSUPPORTED(__inst_arm(0x0120003f) " @ blx pc") + + TEST_RR( "qadd r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "qaddvs lr, r",9, VAL2,", r",8, VAL1,"") + TEST_R( "qadd lr, r",9, VAL2,", r13") + TEST_RR( "qsub r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "qsubvs lr, r",9, VAL2,", r",8, VAL1,"") + TEST_R( "qsub lr, r",9, VAL2,", r13") + TEST_RR( "qdadd r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "qdaddvs lr, r",9, VAL2,", r",8, VAL1,"") + TEST_R( "qdadd lr, r",9, VAL2,", r13") + TEST_RR( "qdsub r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "qdsubvs lr, r",9, VAL2,", r",8, VAL1,"") + TEST_R( "qdsub lr, r",9, VAL2,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe101f050) " @ qadd pc, r0, r1") + TEST_UNSUPPORTED(__inst_arm(0xe121f050) " @ qsub pc, r0, r1") + TEST_UNSUPPORTED(__inst_arm(0xe141f050) " @ qdadd pc, r0, r1") + TEST_UNSUPPORTED(__inst_arm(0xe161f050) " @ qdsub pc, r0, r1") + TEST_UNSUPPORTED(__inst_arm(0xe16f2050) " @ qdsub r2, r0, pc") + TEST_UNSUPPORTED(__inst_arm(0xe161205f) " @ qdsub r2, pc, r1") + + TEST_UNSUPPORTED("bkpt 0xffff") + TEST_UNSUPPORTED("bkpt 0x0000") + + TEST_UNSUPPORTED(__inst_arm(0xe1600070) " @ smc #0") + + TEST_GROUP("Halfword multiply and multiply-accumulate") + + TEST_RRR( "smlabb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlabbge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "smlabb lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe10f3281) " @ smlabb pc, r1, r2, r3") + TEST_RRR( "smlatb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlatbge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "smlatb lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe10f32a1) " @ smlatb pc, r1, r2, r3") + TEST_RRR( "smlabt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlabtge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "smlabt lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe10f32c1) " @ smlabt pc, r1, r2, r3") + TEST_RRR( "smlatt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlattge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "smlatt lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe10f32e1) " @ smlatt pc, r1, r2, r3") + + TEST_RRR( "smlawb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlawbge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "smlawb lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe12f3281) " @ smlawb pc, r1, r2, r3") + TEST_RRR( "smlawt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlawtge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "smlawt lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe12f32c1) " @ smlawt pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe12032cf) " @ smlawt r0, pc, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe1203fc1) " @ smlawt r0, r1, pc, r3") + TEST_UNSUPPORTED(__inst_arm(0xe120f2c1) " @ smlawt r0, r1, r2, pc") + + TEST_RR( "smulwb r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulwbge r7, r",8, VAL3,", r",9, VAL1,"") + TEST_R( "smulwb lr, r",1, VAL2,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe12f02a1) " @ smulwb pc, r1, r2") + TEST_RR( "smulwt r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulwtge r7, r",8, VAL3,", r",9, VAL1,"") + TEST_R( "smulwt lr, r",1, VAL2,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe12f02e1) " @ smulwt pc, r1, r2") + + TEST_RRRR( "smlalbb r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlalbble r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRR( "smlalbb r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe14f1382) " @ smlalbb pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe141f382) " @ smlalbb r1, pc, r2, r3") + TEST_RRRR( "smlaltb r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlaltble r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRR( "smlaltb r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe14f13a2) " @ smlaltb pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe141f3a2) " @ smlaltb r1, pc, r2, r3") + TEST_RRRR( "smlalbt r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlalbtle r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRR( "smlalbt r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe14f13c2) " @ smlalbt pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe141f3c2) " @ smlalbt r1, pc, r2, r3") + TEST_RRRR( "smlaltt r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlalttle r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRR( "smlaltt r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe14f13e2) " @ smlalbb pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe140f3e2) " @ smlalbb r0, pc, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe14013ef) " @ smlalbb r0, r1, pc, r3") + TEST_UNSUPPORTED(__inst_arm(0xe1401fe2) " @ smlalbb r0, r1, r2, pc") + + TEST_RR( "smulbb r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulbbge r7, r",8, VAL3,", r",9, VAL1,"") + TEST_R( "smulbb lr, r",1, VAL2,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe16f0281) " @ smulbb pc, r1, r2") + TEST_RR( "smultb r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smultbge r7, r",8, VAL3,", r",9, VAL1,"") + TEST_R( "smultb lr, r",1, VAL2,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe16f02a1) " @ smultb pc, r1, r2") + TEST_RR( "smulbt r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulbtge r7, r",8, VAL3,", r",9, VAL1,"") + TEST_R( "smulbt lr, r",1, VAL2,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe16f02c1) " @ smultb pc, r1, r2") + TEST_RR( "smultt r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulttge r7, r",8, VAL3,", r",9, VAL1,"") + TEST_R( "smultt lr, r",1, VAL2,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe16f02e1) " @ smultt pc, r1, r2") + TEST_UNSUPPORTED(__inst_arm(0xe16002ef) " @ smultt r0, pc, r2") + TEST_UNSUPPORTED(__inst_arm(0xe1600fe1) " @ smultt r0, r1, pc") +#endif + + TEST_GROUP("Multiply and multiply-accumulate") + + TEST_RR( "mul r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "mulls r7, r",8, VAL2,", r",9, VAL2,"") + TEST_R( "mul lr, r",4, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe00f0291) " @ mul pc, r1, r2") + TEST_UNSUPPORTED(__inst_arm(0xe000029f) " @ mul r0, pc, r2") + TEST_UNSUPPORTED(__inst_arm(0xe0000f91) " @ mul r0, r1, pc") + TEST_RR( "muls r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "mullss r7, r",8, VAL2,", r",9, VAL2,"") + TEST_R( "muls lr, r",4, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe01f0291) " @ muls pc, r1, r2") + + TEST_RRR( "mla r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "mlahi r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "mla lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe02f3291) " @ mla pc, r1, r2, r3") + TEST_RRR( "mlas r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "mlahis r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "mlas lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe03f3291) " @ mlas pc, r1, r2, r3") + +#if __LINUX_ARM_ARCH__ >= 6 + TEST_RR( "umaal r0, r1, r",2, VAL1,", r",3, VAL2,"") + TEST_RR( "umaalls r7, r8, r",9, VAL2,", r",10, VAL1,"") + TEST_R( "umaal lr, r12, r",11,VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe041f392) " @ umaal pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe04f0392) " @ umaal r0, pc, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0500090) " @ undef") + TEST_UNSUPPORTED(__inst_arm(0xe05fff9f) " @ undef") +#endif + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_RRR( "mls r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "mlshi r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "mls lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe06f3291) " @ mls pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe060329f) " @ mls r0, pc, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0603f91) " @ mls r0, r1, pc, r3") + TEST_UNSUPPORTED(__inst_arm(0xe060f291) " @ mls r0, r1, r2, pc") +#endif + + TEST_UNSUPPORTED(__inst_arm(0xe0700090) " @ undef") + TEST_UNSUPPORTED(__inst_arm(0xe07fff9f) " @ undef") + + TEST_RR( "umull r0, r1, r",2, VAL1,", r",3, VAL2,"") + TEST_RR( "umullls r7, r8, r",9, VAL2,", r",10, VAL1,"") + TEST_R( "umull lr, r12, r",11,VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe081f392) " @ umull pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe08f1392) " @ umull r1, pc, r2, r3") + TEST_RR( "umulls r0, r1, r",2, VAL1,", r",3, VAL2,"") + TEST_RR( "umulllss r7, r8, r",9, VAL2,", r",10, VAL1,"") + TEST_R( "umulls lr, r12, r",11,VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe091f392) " @ umulls pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe09f1392) " @ umulls r1, pc, r2, r3") + + TEST_RRRR( "umlal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "umlalle r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRR( "umlal r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe0af1392) " @ umlal pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0a1f392) " @ umlal r1, pc, r2, r3") + TEST_RRRR( "umlals r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "umlalles r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRR( "umlals r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe0bf1392) " @ umlals pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0b1f392) " @ umlals r1, pc, r2, r3") + + TEST_RR( "smull r0, r1, r",2, VAL1,", r",3, VAL2,"") + TEST_RR( "smullls r7, r8, r",9, VAL2,", r",10, VAL1,"") + TEST_R( "smull lr, r12, r",11,VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe0c1f392) " @ smull pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0cf1392) " @ smull r1, pc, r2, r3") + TEST_RR( "smulls r0, r1, r",2, VAL1,", r",3, VAL2,"") + TEST_RR( "smulllss r7, r8, r",9, VAL2,", r",10, VAL1,"") + TEST_R( "smulls lr, r12, r",11,VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe0d1f392) " @ smulls pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0df1392) " @ smulls r1, pc, r2, r3") + + TEST_RRRR( "smlal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlalle r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRR( "smlal r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe0ef1392) " @ smlal pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0e1f392) " @ smlal r1, pc, r2, r3") + TEST_RRRR( "smlals r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlalles r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRR( "smlals r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe0ff1392) " @ smlals pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0f0f392) " @ smlals r0, pc, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0f0139f) " @ smlals r0, r1, pc, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0f01f92) " @ smlals r0, r1, r2, pc") + + TEST_GROUP("Synchronization primitives") + +#if __LINUX_ARM_ARCH__ < 6 + TEST_RP("swp lr, r",7,VAL2,", [r",8,0,"]") + TEST_R( "swpvs r0, r",1,VAL1,", [sp]") + TEST_RP("swp sp, r",14,VAL2,", [r",12,13*4,"]") +#else + TEST_UNSUPPORTED(__inst_arm(0xe108e097) " @ swp lr, r7, [r8]") + TEST_UNSUPPORTED(__inst_arm(0x610d0091) " @ swpvs r0, r1, [sp]") + TEST_UNSUPPORTED(__inst_arm(0xe10cd09e) " @ swp sp, r14 [r12]") +#endif + TEST_UNSUPPORTED(__inst_arm(0xe102f091) " @ swp pc, r1, [r2]") + TEST_UNSUPPORTED(__inst_arm(0xe102009f) " @ swp r0, pc, [r2]") + TEST_UNSUPPORTED(__inst_arm(0xe10f0091) " @ swp r0, r1, [pc]") +#if __LINUX_ARM_ARCH__ < 6 + TEST_RP("swpb lr, r",7,VAL2,", [r",8,0,"]") + TEST_R( "swpvsb r0, r",1,VAL1,", [sp]") +#else + TEST_UNSUPPORTED(__inst_arm(0xe148e097) " @ swpb lr, r7, [r8]") + TEST_UNSUPPORTED(__inst_arm(0x614d0091) " @ swpvsb r0, r1, [sp]") +#endif + TEST_UNSUPPORTED(__inst_arm(0xe142f091) " @ swpb pc, r1, [r2]") + + TEST_UNSUPPORTED(__inst_arm(0xe1100090)) /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe1200090)) /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe1300090)) /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe1500090)) /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe1600090)) /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe1700090)) /* Unallocated space */ +#if __LINUX_ARM_ARCH__ >= 6 + TEST_UNSUPPORTED("ldrex r2, [sp]") +#endif +#if (__LINUX_ARM_ARCH__ >= 7) || defined(CONFIG_CPU_32v6K) + TEST_UNSUPPORTED("strexd r0, r2, r3, [sp]") + TEST_UNSUPPORTED("ldrexd r2, r3, [sp]") + TEST_UNSUPPORTED("strexb r0, r2, [sp]") + TEST_UNSUPPORTED("ldrexb r2, [sp]") + TEST_UNSUPPORTED("strexh r0, r2, [sp]") + TEST_UNSUPPORTED("ldrexh r2, [sp]") +#endif + TEST_GROUP("Extra load/store instructions") + + TEST_RPR( "strh r",0, VAL1,", [r",1, 48,", -r",2, 24,"]") + TEST_RPR( "streqh r",14,VAL2,", [r",13,0, ", r",12, 48,"]") + TEST_RPR( "strh r",1, VAL1,", [r",2, 24,", r",3, 48,"]!") + TEST_RPR( "strneh r",12,VAL2,", [r",11,48,", -r",10,24,"]!") + TEST_RPR( "strh r",2, VAL1,", [r",3, 24,"], r",4, 48,"") + TEST_RPR( "strh r",10,VAL2,", [r",9, 48,"], -r",11,24,"") + TEST_UNSUPPORTED(__inst_arm(0xe1afc0ba) " @ strh r12, [pc, r10]!") + TEST_UNSUPPORTED(__inst_arm(0xe089f0bb) " @ strh pc, [r9], r11") + TEST_UNSUPPORTED(__inst_arm(0xe089a0bf) " @ strh r10, [r9], pc") + + TEST_PR( "ldrh r0, [r",0, 48,", -r",2, 24,"]") + TEST_PR( "ldrcsh r14, [r",13,0, ", r",12, 48,"]") + TEST_PR( "ldrh r1, [r",2, 24,", r",3, 48,"]!") + TEST_PR( "ldrcch r12, [r",11,48,", -r",10,24,"]!") + TEST_PR( "ldrh r2, [r",3, 24,"], r",4, 48,"") + TEST_PR( "ldrh r10, [r",9, 48,"], -r",11,24,"") + TEST_UNSUPPORTED(__inst_arm(0xe1bfc0ba) " @ ldrh r12, [pc, r10]!") + TEST_UNSUPPORTED(__inst_arm(0xe099f0bb) " @ ldrh pc, [r9], r11") + TEST_UNSUPPORTED(__inst_arm(0xe099a0bf) " @ ldrh r10, [r9], pc") + + TEST_RP( "strh r",0, VAL1,", [r",1, 24,", #-2]") + TEST_RP( "strmih r",14,VAL2,", [r",13,0, ", #2]") + TEST_RP( "strh r",1, VAL1,", [r",2, 24,", #4]!") + TEST_RP( "strplh r",12,VAL2,", [r",11,24,", #-4]!") + TEST_RP( "strh r",2, VAL1,", [r",3, 24,"], #48") + TEST_RP( "strh r",10,VAL2,", [r",9, 64,"], #-48") + TEST_UNSUPPORTED(__inst_arm(0xe1efc3b0) " @ strh r12, [pc, #48]!") + TEST_UNSUPPORTED(__inst_arm(0xe0c9f3b0) " @ strh pc, [r9], #48") + + TEST_P( "ldrh r0, [r",0, 24,", #-2]") + TEST_P( "ldrvsh r14, [r",13,0, ", #2]") + TEST_P( "ldrh r1, [r",2, 24,", #4]!") + TEST_P( "ldrvch r12, [r",11,24,", #-4]!") + TEST_P( "ldrh r2, [r",3, 24,"], #48") + TEST_P( "ldrh r10, [r",9, 64,"], #-48") + TEST( "ldrh r0, [pc, #0]") + TEST_UNSUPPORTED(__inst_arm(0xe1ffc3b0) " @ ldrh r12, [pc, #48]!") + TEST_UNSUPPORTED(__inst_arm(0xe0d9f3b0) " @ ldrh pc, [r9], #48") + + TEST_PR( "ldrsb r0, [r",0, 48,", -r",2, 24,"]") + TEST_PR( "ldrhisb r14, [r",13,0,", r",12, 48,"]") + TEST_PR( "ldrsb r1, [r",2, 24,", r",3, 48,"]!") + TEST_PR( "ldrlssb r12, [r",11,48,", -r",10,24,"]!") + TEST_PR( "ldrsb r2, [r",3, 24,"], r",4, 48,"") + TEST_PR( "ldrsb r10, [r",9, 48,"], -r",11,24,"") + TEST_UNSUPPORTED(__inst_arm(0xe1bfc0da) " @ ldrsb r12, [pc, r10]!") + TEST_UNSUPPORTED(__inst_arm(0xe099f0db) " @ ldrsb pc, [r9], r11") + + TEST_P( "ldrsb r0, [r",0, 24,", #-1]") + TEST_P( "ldrgesb r14, [r",13,0, ", #1]") + TEST_P( "ldrsb r1, [r",2, 24,", #4]!") + TEST_P( "ldrltsb r12, [r",11,24,", #-4]!") + TEST_P( "ldrsb r2, [r",3, 24,"], #48") + TEST_P( "ldrsb r10, [r",9, 64,"], #-48") + TEST( "ldrsb r0, [pc, #0]") + TEST_UNSUPPORTED(__inst_arm(0xe1ffc3d0) " @ ldrsb r12, [pc, #48]!") + TEST_UNSUPPORTED(__inst_arm(0xe0d9f3d0) " @ ldrsb pc, [r9], #48") + + TEST_PR( "ldrsh r0, [r",0, 48,", -r",2, 24,"]") + TEST_PR( "ldrgtsh r14, [r",13,0, ", r",12, 48,"]") + TEST_PR( "ldrsh r1, [r",2, 24,", r",3, 48,"]!") + TEST_PR( "ldrlesh r12, [r",11,48,", -r",10,24,"]!") + TEST_PR( "ldrsh r2, [r",3, 24,"], r",4, 48,"") + TEST_PR( "ldrsh r10, [r",9, 48,"], -r",11,24,"") + TEST_UNSUPPORTED(__inst_arm(0xe1bfc0fa) " @ ldrsh r12, [pc, r10]!") + TEST_UNSUPPORTED(__inst_arm(0xe099f0fb) " @ ldrsh pc, [r9], r11") + + TEST_P( "ldrsh r0, [r",0, 24,", #-1]") + TEST_P( "ldreqsh r14, [r",13,0 ,", #1]") + TEST_P( "ldrsh r1, [r",2, 24,", #4]!") + TEST_P( "ldrnesh r12, [r",11,24,", #-4]!") + TEST_P( "ldrsh r2, [r",3, 24,"], #48") + TEST_P( "ldrsh r10, [r",9, 64,"], #-48") + TEST( "ldrsh r0, [pc, #0]") + TEST_UNSUPPORTED(__inst_arm(0xe1ffc3f0) " @ ldrsh r12, [pc, #48]!") + TEST_UNSUPPORTED(__inst_arm(0xe0d9f3f0) " @ ldrsh pc, [r9], #48") + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_UNSUPPORTED("strht r1, [r2], r3") + TEST_UNSUPPORTED("ldrht r1, [r2], r3") + TEST_UNSUPPORTED("strht r1, [r2], #48") + TEST_UNSUPPORTED("ldrht r1, [r2], #48") + TEST_UNSUPPORTED("ldrsbt r1, [r2], r3") + TEST_UNSUPPORTED("ldrsbt r1, [r2], #48") + TEST_UNSUPPORTED("ldrsht r1, [r2], r3") + TEST_UNSUPPORTED("ldrsht r1, [r2], #48") +#endif + +#if __LINUX_ARM_ARCH__ >= 5 + TEST_RPR( "strd r",0, VAL1,", [r",1, 48,", -r",2,24,"]") + TEST_RPR( "strccd r",8, VAL2,", [r",13,0, ", r",12,48,"]") + TEST_RPR( "strd r",4, VAL1,", [r",2, 24,", r",3, 48,"]!") + TEST_RPR( "strcsd r",12,VAL2,", [r",11,48,", -r",10,24,"]!") + TEST_RPR( "strd r",2, VAL1,", [r",5, 24,"], r",4,48,"") + TEST_RPR( "strd r",10,VAL2,", [r",9, 48,"], -r",7,24,"") + TEST_UNSUPPORTED(__inst_arm(0xe1afc0fa) " @ strd r12, [pc, r10]!") + + TEST_PR( "ldrd r0, [r",0, 48,", -r",2,24,"]") + TEST_PR( "ldrmid r8, [r",13,0, ", r",12,48,"]") + TEST_PR( "ldrd r4, [r",2, 24,", r",3, 48,"]!") + TEST_PR( "ldrpld r6, [r",11,48,", -r",10,24,"]!") + TEST_PR( "ldrd r2, [r",5, 24,"], r",4,48,"") + TEST_PR( "ldrd r10, [r",9,48,"], -r",7,24,"") + TEST_UNSUPPORTED(__inst_arm(0xe1afc0da) " @ ldrd r12, [pc, r10]!") + TEST_UNSUPPORTED(__inst_arm(0xe089f0db) " @ ldrd pc, [r9], r11") + TEST_UNSUPPORTED(__inst_arm(0xe089e0db) " @ ldrd lr, [r9], r11") + TEST_UNSUPPORTED(__inst_arm(0xe089c0df) " @ ldrd r12, [r9], pc") + + TEST_RP( "strd r",0, VAL1,", [r",1, 24,", #-8]") + TEST_RP( "strvsd r",8, VAL2,", [r",13,0, ", #8]") + TEST_RP( "strd r",4, VAL1,", [r",2, 24,", #16]!") + TEST_RP( "strvcd r",12,VAL2,", [r",11,24,", #-16]!") + TEST_RP( "strd r",2, VAL1,", [r",4, 24,"], #48") + TEST_RP( "strd r",10,VAL2,", [r",9, 64,"], #-48") + TEST_UNSUPPORTED(__inst_arm(0xe1efc3f0) " @ strd r12, [pc, #48]!") + + TEST_P( "ldrd r0, [r",0, 24,", #-8]") + TEST_P( "ldrhid r8, [r",13,0, ", #8]") + TEST_P( "ldrd r4, [r",2, 24,", #16]!") + TEST_P( "ldrlsd r6, [r",11,24,", #-16]!") + TEST_P( "ldrd r2, [r",5, 24,"], #48") + TEST_P( "ldrd r10, [r",9,6,"], #-48") + TEST_UNSUPPORTED(__inst_arm(0xe1efc3d0) " @ ldrd r12, [pc, #48]!") + TEST_UNSUPPORTED(__inst_arm(0xe0c9f3d0) " @ ldrd pc, [r9], #48") + TEST_UNSUPPORTED(__inst_arm(0xe0c9e3d0) " @ ldrd lr, [r9], #48") +#endif + + TEST_GROUP("Miscellaneous") + +#if __LINUX_ARM_ARCH__ >= 7 + TEST("movw r0, #0") + TEST("movw r0, #0xffff") + TEST("movw lr, #0xffff") + TEST_UNSUPPORTED(__inst_arm(0xe300f000) " @ movw pc, #0") + TEST_R("movt r",0, VAL1,", #0") + TEST_R("movt r",0, VAL2,", #0xffff") + TEST_R("movt r",14,VAL1,", #0xffff") + TEST_UNSUPPORTED(__inst_arm(0xe340f000) " @ movt pc, #0") +#endif + + TEST_UNSUPPORTED("msr cpsr, 0x13") + TEST_UNSUPPORTED("msr cpsr_f, 0xf0000000") + TEST_UNSUPPORTED("msr spsr, 0x13") + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_SUPPORTED("yield") + TEST("sev") + TEST("nop") + TEST("wfi") + TEST_SUPPORTED("wfe") + TEST_UNSUPPORTED("dbg #0") +#endif + + TEST_GROUP("Load/store word and unsigned byte") + +#define LOAD_STORE(byte) \ + TEST_RP( "str"byte" r",0, VAL1,", [r",1, 24,", #-2]") \ + TEST_RP( "str"byte" r",14,VAL2,", [r",13,0, ", #2]") \ + TEST_RP( "str"byte" r",1, VAL1,", [r",2, 24,", #4]!") \ + TEST_RP( "str"byte" r",12,VAL2,", [r",11,24,", #-4]!") \ + TEST_RP( "str"byte" r",2, VAL1,", [r",3, 24,"], #48") \ + TEST_RP( "str"byte" r",10,VAL2,", [r",9, 64,"], #-48") \ + TEST_RPR("str"byte" r",0, VAL1,", [r",1, 48,", -r",2, 24,"]") \ + TEST_RPR("str"byte" r",14,VAL2,", [r",13,0, ", r",12, 48,"]") \ + TEST_RPR("str"byte" r",1, VAL1,", [r",2, 24,", r",3, 48,"]!") \ + TEST_RPR("str"byte" r",12,VAL2,", [r",11,48,", -r",10,24,"]!") \ + TEST_RPR("str"byte" r",2, VAL1,", [r",3, 24,"], r",4, 48,"") \ + TEST_RPR("str"byte" r",10,VAL2,", [r",9, 48,"], -r",11,24,"") \ + TEST_RPR("str"byte" r",0, VAL1,", [r",1, 24,", r",2, 32,", asl #1]")\ + TEST_RPR("str"byte" r",14,VAL2,", [r",13,0, ", r",12, 32,", lsr #2]")\ + TEST_RPR("str"byte" r",1, VAL1,", [r",2, 24,", r",3, 32,", asr #3]!")\ + TEST_RPR("str"byte" r",12,VAL2,", [r",11,24,", r",10, 4,", ror #31]!")\ + TEST_P( "ldr"byte" r0, [r",0, 24,", #-2]") \ + TEST_P( "ldr"byte" r14, [r",13,0, ", #2]") \ + TEST_P( "ldr"byte" r1, [r",2, 24,", #4]!") \ + TEST_P( "ldr"byte" r12, [r",11,24,", #-4]!") \ + TEST_P( "ldr"byte" r2, [r",3, 24,"], #48") \ + TEST_P( "ldr"byte" r10, [r",9, 64,"], #-48") \ + TEST_PR( "ldr"byte" r0, [r",0, 48,", -r",2, 24,"]") \ + TEST_PR( "ldr"byte" r14, [r",13,0, ", r",12, 48,"]") \ + TEST_PR( "ldr"byte" r1, [r",2, 24,", r",3, 48,"]!") \ + TEST_PR( "ldr"byte" r12, [r",11,48,", -r",10,24,"]!") \ + TEST_PR( "ldr"byte" r2, [r",3, 24,"], r",4, 48,"") \ + TEST_PR( "ldr"byte" r10, [r",9, 48,"], -r",11,24,"") \ + TEST_PR( "ldr"byte" r0, [r",0, 24,", r",2, 32,", asl #1]") \ + TEST_PR( "ldr"byte" r14, [r",13,0, ", r",12, 32,", lsr #2]") \ + TEST_PR( "ldr"byte" r1, [r",2, 24,", r",3, 32,", asr #3]!") \ + TEST_PR( "ldr"byte" r12, [r",11,24,", r",10, 4,", ror #31]!") \ + TEST( "ldr"byte" r0, [pc, #0]") \ + TEST_R( "ldr"byte" r12, [pc, r",14,0,"]") + + LOAD_STORE("") + TEST_P( "str pc, [r",0,0,", #15*4]") + TEST_R( "str pc, [sp, r",2,15*4,"]") + TEST_BF( "ldr pc, [sp, #15*4]") + TEST_BF_R("ldr pc, [sp, r",2,15*4,"]") + + TEST_P( "str sp, [r",0,0,", #13*4]") + TEST_R( "str sp, [sp, r",2,13*4,"]") + TEST_BF( "ldr sp, [sp, #13*4]") + TEST_BF_R("ldr sp, [sp, r",2,13*4,"]") + +#ifdef CONFIG_THUMB2_KERNEL + TEST_ARM_TO_THUMB_INTERWORK_P("ldr pc, [r",0,0,", #15*4]") +#endif + TEST_UNSUPPORTED(__inst_arm(0xe5af6008) " @ str r6, [pc, #8]!") + TEST_UNSUPPORTED(__inst_arm(0xe7af6008) " @ str r6, [pc, r8]!") + TEST_UNSUPPORTED(__inst_arm(0xe5bf6008) " @ ldr r6, [pc, #8]!") + TEST_UNSUPPORTED(__inst_arm(0xe7bf6008) " @ ldr r6, [pc, r8]!") + TEST_UNSUPPORTED(__inst_arm(0xe788600f) " @ str r6, [r8, pc]") + TEST_UNSUPPORTED(__inst_arm(0xe798600f) " @ ldr r6, [r8, pc]") + + LOAD_STORE("b") + TEST_UNSUPPORTED(__inst_arm(0xe5f7f008) " @ ldrb pc, [r7, #8]!") + TEST_UNSUPPORTED(__inst_arm(0xe7f7f008) " @ ldrb pc, [r7, r8]!") + TEST_UNSUPPORTED(__inst_arm(0xe5ef6008) " @ strb r6, [pc, #8]!") + TEST_UNSUPPORTED(__inst_arm(0xe7ef6008) " @ strb r6, [pc, r3]!") + TEST_UNSUPPORTED(__inst_arm(0xe5ff6008) " @ ldrb r6, [pc, #8]!") + TEST_UNSUPPORTED(__inst_arm(0xe7ff6008) " @ ldrb r6, [pc, r3]!") + + TEST_UNSUPPORTED("ldrt r0, [r1], #4") + TEST_UNSUPPORTED("ldrt r1, [r2], r3") + TEST_UNSUPPORTED("strt r2, [r3], #4") + TEST_UNSUPPORTED("strt r3, [r4], r5") + TEST_UNSUPPORTED("ldrbt r4, [r5], #4") + TEST_UNSUPPORTED("ldrbt r5, [r6], r7") + TEST_UNSUPPORTED("strbt r6, [r7], #4") + TEST_UNSUPPORTED("strbt r7, [r8], r9") + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_GROUP("Parallel addition and subtraction, signed") + + TEST_UNSUPPORTED(__inst_arm(0xe6000010) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe60fffff) "") /* Unallocated space */ + + TEST_RR( "sadd16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sadd16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe61cff1a) " @ sadd16 pc, r12, r10") + TEST_RR( "sasx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sasx r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe61cff3a) " @ sasx pc, r12, r10") + TEST_RR( "ssax r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "ssax r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe61cff5a) " @ ssax pc, r12, r10") + TEST_RR( "ssub16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "ssub16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe61cff7a) " @ ssub16 pc, r12, r10") + TEST_RR( "sadd8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sadd8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe61cff9a) " @ sadd8 pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe61000b0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe61fffbf) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe61000d0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe61fffdf) "") /* Unallocated space */ + TEST_RR( "ssub8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "ssub8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe61cfffa) " @ ssub8 pc, r12, r10") + + TEST_RR( "qadd16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "qadd16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe62cff1a) " @ qadd16 pc, r12, r10") + TEST_RR( "qasx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "qasx r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe62cff3a) " @ qasx pc, r12, r10") + TEST_RR( "qsax r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "qsax r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe62cff5a) " @ qsax pc, r12, r10") + TEST_RR( "qsub16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "qsub16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe62cff7a) " @ qsub16 pc, r12, r10") + TEST_RR( "qadd8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "qadd8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe62cff9a) " @ qadd8 pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe62000b0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe62fffbf) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe62000d0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe62fffdf) "") /* Unallocated space */ + TEST_RR( "qsub8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "qsub8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe62cfffa) " @ qsub8 pc, r12, r10") + + TEST_RR( "shadd16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "shadd16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe63cff1a) " @ shadd16 pc, r12, r10") + TEST_RR( "shasx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "shasx r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe63cff3a) " @ shasx pc, r12, r10") + TEST_RR( "shsax r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "shsax r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe63cff5a) " @ shsax pc, r12, r10") + TEST_RR( "shsub16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "shsub16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe63cff7a) " @ shsub16 pc, r12, r10") + TEST_RR( "shadd8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "shadd8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe63cff9a) " @ shadd8 pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe63000b0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe63fffbf) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe63000d0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe63fffdf) "") /* Unallocated space */ + TEST_RR( "shsub8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "shsub8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe63cfffa) " @ shsub8 pc, r12, r10") + + TEST_GROUP("Parallel addition and subtraction, unsigned") + + TEST_UNSUPPORTED(__inst_arm(0xe6400010) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe64fffff) "") /* Unallocated space */ + + TEST_RR( "uadd16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uadd16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe65cff1a) " @ uadd16 pc, r12, r10") + TEST_RR( "uasx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uasx r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe65cff3a) " @ uasx pc, r12, r10") + TEST_RR( "usax r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "usax r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe65cff5a) " @ usax pc, r12, r10") + TEST_RR( "usub16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "usub16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe65cff7a) " @ usub16 pc, r12, r10") + TEST_RR( "uadd8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uadd8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe65cff9a) " @ uadd8 pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe65000b0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe65fffbf) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe65000d0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe65fffdf) "") /* Unallocated space */ + TEST_RR( "usub8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "usub8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe65cfffa) " @ usub8 pc, r12, r10") + + TEST_RR( "uqadd16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uqadd16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe66cff1a) " @ uqadd16 pc, r12, r10") + TEST_RR( "uqasx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uqasx r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe66cff3a) " @ uqasx pc, r12, r10") + TEST_RR( "uqsax r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uqsax r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe66cff5a) " @ uqsax pc, r12, r10") + TEST_RR( "uqsub16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uqsub16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe66cff7a) " @ uqsub16 pc, r12, r10") + TEST_RR( "uqadd8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uqadd8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe66cff9a) " @ uqadd8 pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe66000b0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe66fffbf) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe66000d0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe66fffdf) "") /* Unallocated space */ + TEST_RR( "uqsub8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uqsub8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe66cfffa) " @ uqsub8 pc, r12, r10") + + TEST_RR( "uhadd16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uhadd16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe67cff1a) " @ uhadd16 pc, r12, r10") + TEST_RR( "uhasx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uhasx r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe67cff3a) " @ uhasx pc, r12, r10") + TEST_RR( "uhsax r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uhsax r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe67cff5a) " @ uhsax pc, r12, r10") + TEST_RR( "uhsub16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uhsub16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe67cff7a) " @ uhsub16 pc, r12, r10") + TEST_RR( "uhadd8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uhadd8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe67cff9a) " @ uhadd8 pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe67000b0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe67fffbf) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe67000d0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe67fffdf) "") /* Unallocated space */ + TEST_RR( "uhsub8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uhsub8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe67cfffa) " @ uhsub8 pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe67feffa) " @ uhsub8 r14, pc, r10") + TEST_UNSUPPORTED(__inst_arm(0xe67cefff) " @ uhsub8 r14, r12, pc") +#endif /* __LINUX_ARM_ARCH__ >= 7 */ + +#if __LINUX_ARM_ARCH__ >= 6 + TEST_GROUP("Packing, unpacking, saturation, and reversal") + + TEST_RR( "pkhbt r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "pkhbt r14,r",12, HH1,", r",10,HH2,", lsl #2") + TEST_UNSUPPORTED(__inst_arm(0xe68cf11a) " @ pkhbt pc, r12, r10, lsl #2") + TEST_RR( "pkhtb r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "pkhtb r14,r",12, HH1,", r",10,HH2,", asr #2") + TEST_UNSUPPORTED(__inst_arm(0xe68cf15a) " @ pkhtb pc, r12, r10, asr #2") + TEST_UNSUPPORTED(__inst_arm(0xe68fe15a) " @ pkhtb r14, pc, r10, asr #2") + TEST_UNSUPPORTED(__inst_arm(0xe68ce15f) " @ pkhtb r14, r12, pc, asr #2") + TEST_UNSUPPORTED(__inst_arm(0xe6900010) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe69fffdf) "") /* Unallocated space */ + + TEST_R( "ssat r0, #24, r",0, VAL1,"") + TEST_R( "ssat r14, #24, r",12, VAL2,"") + TEST_R( "ssat r0, #24, r",0, VAL1,", lsl #8") + TEST_R( "ssat r14, #24, r",12, VAL2,", asr #8") + TEST_UNSUPPORTED(__inst_arm(0xe6b7f01c) " @ ssat pc, #24, r12") + + TEST_R( "usat r0, #24, r",0, VAL1,"") + TEST_R( "usat r14, #24, r",12, VAL2,"") + TEST_R( "usat r0, #24, r",0, VAL1,", lsl #8") + TEST_R( "usat r14, #24, r",12, VAL2,", asr #8") + TEST_UNSUPPORTED(__inst_arm(0xe6f7f01c) " @ usat pc, #24, r12") + + TEST_RR( "sxtab16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sxtab16 r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "sxtb16 r8, r",7, HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe68cf47a) " @ sxtab16 pc,r12, r10, ror #8") + + TEST_RR( "sel r0, r",0, VAL1,", r",1, VAL2,"") + TEST_RR( "sel r14, r",12,VAL1,", r",10, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe68cffba) " @ sel pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe68fefba) " @ sel r14, pc, r10") + TEST_UNSUPPORTED(__inst_arm(0xe68cefbf) " @ sel r14, r12, pc") + + TEST_R( "ssat16 r0, #12, r",0, HH1,"") + TEST_R( "ssat16 r14, #12, r",12, HH2,"") + TEST_UNSUPPORTED(__inst_arm(0xe6abff3c) " @ ssat16 pc, #12, r12") + + TEST_RR( "sxtab r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sxtab r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "sxtb r8, r",7, HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe6acf47a) " @ sxtab pc,r12, r10, ror #8") + + TEST_R( "rev r0, r",0, VAL1,"") + TEST_R( "rev r14, r",12, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe6bfff3c) " @ rev pc, r12") + + TEST_RR( "sxtah r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sxtah r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "sxth r8, r",7, HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe6bcf47a) " @ sxtah pc,r12, r10, ror #8") + + TEST_R( "rev16 r0, r",0, VAL1,"") + TEST_R( "rev16 r14, r",12, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe6bfffbc) " @ rev16 pc, r12") + + TEST_RR( "uxtab16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uxtab16 r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "uxtb16 r8, r",7, HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe6ccf47a) " @ uxtab16 pc,r12, r10, ror #8") + + TEST_R( "usat16 r0, #12, r",0, HH1,"") + TEST_R( "usat16 r14, #12, r",12, HH2,"") + TEST_UNSUPPORTED(__inst_arm(0xe6ecff3c) " @ usat16 pc, #12, r12") + TEST_UNSUPPORTED(__inst_arm(0xe6ecef3f) " @ usat16 r14, #12, pc") + + TEST_RR( "uxtab r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uxtab r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "uxtb r8, r",7, HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe6ecf47a) " @ uxtab pc,r12, r10, ror #8") + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_R( "rbit r0, r",0, VAL1,"") + TEST_R( "rbit r14, r",12, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe6ffff3c) " @ rbit pc, r12") +#endif + + TEST_RR( "uxtah r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uxtah r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "uxth r8, r",7, HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe6fff077) " @ uxth pc, r7") + TEST_UNSUPPORTED(__inst_arm(0xe6ff807f) " @ uxth r8, pc") + TEST_UNSUPPORTED(__inst_arm(0xe6fcf47a) " @ uxtah pc, r12, r10, ror #8") + TEST_UNSUPPORTED(__inst_arm(0xe6fce47f) " @ uxtah r14, r12, pc, ror #8") + + TEST_R( "revsh r0, r",0, VAL1,"") + TEST_R( "revsh r14, r",12, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe6ffff3c) " @ revsh pc, r12") + TEST_UNSUPPORTED(__inst_arm(0xe6ffef3f) " @ revsh r14, pc") + + TEST_UNSUPPORTED(__inst_arm(0xe6900070) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe69fff7f) "") /* Unallocated space */ + + TEST_UNSUPPORTED(__inst_arm(0xe6d00070) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe6dfff7f) "") /* Unallocated space */ +#endif /* __LINUX_ARM_ARCH__ >= 6 */ + +#if __LINUX_ARM_ARCH__ >= 6 + TEST_GROUP("Signed multiplies") + + TEST_RRR( "smlad r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") + TEST_RRR( "smlad r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe70f8a1c) " @ smlad pc, r12, r10, r8") + TEST_RRR( "smladx r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") + TEST_RRR( "smladx r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe70f8a3c) " @ smladx pc, r12, r10, r8") + + TEST_RR( "smuad r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "smuad r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe70ffa1c) " @ smuad pc, r12, r10") + TEST_RR( "smuadx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "smuadx r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe70ffa3c) " @ smuadx pc, r12, r10") + + TEST_RRR( "smlsd r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") + TEST_RRR( "smlsd r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe70f8a5c) " @ smlsd pc, r12, r10, r8") + TEST_RRR( "smlsdx r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") + TEST_RRR( "smlsdx r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe70f8a7c) " @ smlsdx pc, r12, r10, r8") + + TEST_RR( "smusd r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "smusd r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe70ffa5c) " @ smusd pc, r12, r10") + TEST_RR( "smusdx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "smusdx r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe70ffa7c) " @ smusdx pc, r12, r10") + + TEST_RRRR( "smlald r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) + TEST_RRRR( "smlald r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) + TEST_UNSUPPORTED(__inst_arm(0xe74af819) " @ smlald pc, r10, r9, r8") + TEST_UNSUPPORTED(__inst_arm(0xe74fb819) " @ smlald r11, pc, r9, r8") + TEST_UNSUPPORTED(__inst_arm(0xe74ab81f) " @ smlald r11, r10, pc, r8") + TEST_UNSUPPORTED(__inst_arm(0xe74abf19) " @ smlald r11, r10, r9, pc") + + TEST_RRRR( "smlaldx r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) + TEST_RRRR( "smlaldx r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) + TEST_UNSUPPORTED(__inst_arm(0xe74af839) " @ smlaldx pc, r10, r9, r8") + TEST_UNSUPPORTED(__inst_arm(0xe74fb839) " @ smlaldx r11, pc, r9, r8") + + TEST_RRR( "smmla r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") + TEST_RRR( "smmla r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe75f8a1c) " @ smmla pc, r12, r10, r8") + TEST_RRR( "smmlar r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") + TEST_RRR( "smmlar r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe75f8a3c) " @ smmlar pc, r12, r10, r8") + + TEST_RR( "smmul r0, r",0, VAL1,", r",1, VAL2,"") + TEST_RR( "smmul r14, r",12,VAL2,", r",10,VAL1,"") + TEST_UNSUPPORTED(__inst_arm(0xe75ffa1c) " @ smmul pc, r12, r10") + TEST_RR( "smmulr r0, r",0, VAL1,", r",1, VAL2,"") + TEST_RR( "smmulr r14, r",12,VAL2,", r",10,VAL1,"") + TEST_UNSUPPORTED(__inst_arm(0xe75ffa3c) " @ smmulr pc, r12, r10") + + TEST_RRR( "smmls r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") + TEST_RRR( "smmls r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe75f8adc) " @ smmls pc, r12, r10, r8") + TEST_RRR( "smmlsr r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") + TEST_RRR( "smmlsr r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe75f8afc) " @ smmlsr pc, r12, r10, r8") + TEST_UNSUPPORTED(__inst_arm(0xe75e8aff) " @ smmlsr r14, pc, r10, r8") + TEST_UNSUPPORTED(__inst_arm(0xe75e8ffc) " @ smmlsr r14, r12, pc, r8") + TEST_UNSUPPORTED(__inst_arm(0xe75efafc) " @ smmlsr r14, r12, r10, pc") + + TEST_RR( "usad8 r0, r",0, VAL1,", r",1, VAL2,"") + TEST_RR( "usad8 r14, r",12,VAL2,", r",10,VAL1,"") + TEST_UNSUPPORTED(__inst_arm(0xe75ffa1c) " @ usad8 pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe75efa1f) " @ usad8 r14, pc, r10") + TEST_UNSUPPORTED(__inst_arm(0xe75eff1c) " @ usad8 r14, r12, pc") + + TEST_RRR( "usada8 r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL3,"") + TEST_RRR( "usada8 r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL3,"") + TEST_UNSUPPORTED(__inst_arm(0xe78f8a1c) " @ usada8 pc, r12, r10, r8") + TEST_UNSUPPORTED(__inst_arm(0xe78e8a1f) " @ usada8 r14, pc, r10, r8") + TEST_UNSUPPORTED(__inst_arm(0xe78e8f1c) " @ usada8 r14, r12, pc, r8") +#endif /* __LINUX_ARM_ARCH__ >= 6 */ + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_GROUP("Bit Field") + + TEST_R( "sbfx r0, r",0 , VAL1,", #0, #31") + TEST_R( "sbfxeq r14, r",12, VAL2,", #8, #16") + TEST_R( "sbfx r4, r",10, VAL1,", #16, #15") + TEST_UNSUPPORTED(__inst_arm(0xe7aff45c) " @ sbfx pc, r12, #8, #16") + + TEST_R( "ubfx r0, r",0 , VAL1,", #0, #31") + TEST_R( "ubfxcs r14, r",12, VAL2,", #8, #16") + TEST_R( "ubfx r4, r",10, VAL1,", #16, #15") + TEST_UNSUPPORTED(__inst_arm(0xe7eff45c) " @ ubfx pc, r12, #8, #16") + TEST_UNSUPPORTED(__inst_arm(0xe7efc45f) " @ ubfx r12, pc, #8, #16") + + TEST_R( "bfc r",0, VAL1,", #4, #20") + TEST_R( "bfcvs r",14,VAL2,", #4, #20") + TEST_R( "bfc r",7, VAL1,", #0, #31") + TEST_R( "bfc r",8, VAL2,", #0, #31") + TEST_UNSUPPORTED(__inst_arm(0xe7def01f) " @ bfc pc, #0, #31"); + + TEST_RR( "bfi r",0, VAL1,", r",0 , VAL2,", #0, #31") + TEST_RR( "bfipl r",12,VAL1,", r",14 , VAL2,", #4, #20") + TEST_UNSUPPORTED(__inst_arm(0xe7d7f21e) " @ bfi pc, r14, #4, #20") + + TEST_UNSUPPORTED(__inst_arm(0x07f000f0) "") /* Permanently UNDEFINED */ + TEST_UNSUPPORTED(__inst_arm(0x07ffffff) "") /* Permanently UNDEFINED */ +#endif /* __LINUX_ARM_ARCH__ >= 6 */ + + TEST_GROUP("Branch, branch with link, and block data transfer") + + TEST_P( "stmda r",0, 16*4,", {r0}") + TEST_P( "stmeqda r",4, 16*4,", {r0-r15}") + TEST_P( "stmneda r",8, 16*4,"!, {r8-r15}") + TEST_P( "stmda r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_P( "stmda r",13,0, "!, {pc}") + + TEST_P( "ldmda r",0, 16*4,", {r0}") + TEST_BF_P("ldmcsda r",4, 15*4,", {r0-r15}") + TEST_BF_P("ldmccda r",7, 15*4,"!, {r8-r15}") + TEST_P( "ldmda r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_BF_P("ldmda r",14,15*4,"!, {pc}") + + TEST_P( "stmia r",0, 16*4,", {r0}") + TEST_P( "stmmiia r",4, 16*4,", {r0-r15}") + TEST_P( "stmplia r",8, 16*4,"!, {r8-r15}") + TEST_P( "stmia r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_P( "stmia r",14,0, "!, {pc}") + + TEST_P( "ldmia r",0, 16*4,", {r0}") + TEST_BF_P("ldmvsia r",4, 0, ", {r0-r15}") + TEST_BF_P("ldmvcia r",7, 8*4, "!, {r8-r15}") + TEST_P( "ldmia r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_BF_P("ldmia r",14,15*4,"!, {pc}") + + TEST_P( "stmdb r",0, 16*4,", {r0}") + TEST_P( "stmhidb r",4, 16*4,", {r0-r15}") + TEST_P( "stmlsdb r",8, 16*4,"!, {r8-r15}") + TEST_P( "stmdb r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_P( "stmdb r",13,4, "!, {pc}") + + TEST_P( "ldmdb r",0, 16*4,", {r0}") + TEST_BF_P("ldmgedb r",4, 16*4,", {r0-r15}") + TEST_BF_P("ldmltdb r",7, 16*4,"!, {r8-r15}") + TEST_P( "ldmdb r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_BF_P("ldmdb r",14,16*4,"!, {pc}") + + TEST_P( "stmib r",0, 16*4,", {r0}") + TEST_P( "stmgtib r",4, 16*4,", {r0-r15}") + TEST_P( "stmleib r",8, 16*4,"!, {r8-r15}") + TEST_P( "stmib r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_P( "stmib r",13,-4, "!, {pc}") + + TEST_P( "ldmib r",0, 16*4,", {r0}") + TEST_BF_P("ldmeqib r",4, -4,", {r0-r15}") + TEST_BF_P("ldmneib r",7, 7*4,"!, {r8-r15}") + TEST_P( "ldmib r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_BF_P("ldmib r",14,14*4,"!, {pc}") + + TEST_P( "stmdb r",13,16*4,"!, {r3-r12,lr}") + TEST_P( "stmeqdb r",13,16*4,"!, {r3-r12}") + TEST_P( "stmnedb r",2, 16*4,", {r3-r12,lr}") + TEST_P( "stmdb r",13,16*4,"!, {r2-r12,lr}") + TEST_P( "stmdb r",0, 16*4,", {r0-r12}") + TEST_P( "stmdb r",0, 16*4,", {r0-r12,lr}") + + TEST_BF_P("ldmia r",13,5*4, "!, {r3-r12,pc}") + TEST_P( "ldmccia r",13,5*4, "!, {r3-r12}") + TEST_BF_P("ldmcsia r",2, 5*4, "!, {r3-r12,pc}") + TEST_BF_P("ldmia r",13,4*4, "!, {r2-r12,pc}") + TEST_P( "ldmia r",0, 16*4,", {r0-r12}") + TEST_P( "ldmia r",0, 16*4,", {r0-r12,lr}") + +#ifdef CONFIG_THUMB2_KERNEL + TEST_ARM_TO_THUMB_INTERWORK_P("ldmplia r",0,15*4,", {pc}") + TEST_ARM_TO_THUMB_INTERWORK_P("ldmmiia r",13,0,", {r0-r15}") +#endif + TEST_BF("b 2f") + TEST_BF("bl 2f") + TEST_BB("b 2b") + TEST_BB("bl 2b") + + TEST_BF("beq 2f") + TEST_BF("bleq 2f") + TEST_BB("bne 2b") + TEST_BB("blne 2b") + + TEST_BF("bgt 2f") + TEST_BF("blgt 2f") + TEST_BB("blt 2b") + TEST_BB("bllt 2b") + + TEST_GROUP("Supervisor Call, and coprocessor instructions") + + /* + * We can't really test these by executing them, so all + * we can do is check that probes are, or are not allowed. + * At the moment none are allowed... + */ +#define TEST_COPROCESSOR(code) TEST_UNSUPPORTED(code) + +#define COPROCESSOR_INSTRUCTIONS_ST_LD(two,cc) \ + TEST_COPROCESSOR("stc"two" 0, cr0, [r13, #4]") \ + TEST_COPROCESSOR("stc"two" 0, cr0, [r13, #-4]") \ + TEST_COPROCESSOR("stc"two" 0, cr0, [r13, #4]!") \ + TEST_COPROCESSOR("stc"two" 0, cr0, [r13, #-4]!") \ + TEST_COPROCESSOR("stc"two" 0, cr0, [r13], #4") \ + TEST_COPROCESSOR("stc"two" 0, cr0, [r13], #-4") \ + TEST_COPROCESSOR("stc"two" 0, cr0, [r13], {1}") \ + TEST_COPROCESSOR("stc"two"l 0, cr0, [r13, #4]") \ + TEST_COPROCESSOR("stc"two"l 0, cr0, [r13, #-4]") \ + TEST_COPROCESSOR("stc"two"l 0, cr0, [r13, #4]!") \ + TEST_COPROCESSOR("stc"two"l 0, cr0, [r13, #-4]!") \ + TEST_COPROCESSOR("stc"two"l 0, cr0, [r13], #4") \ + TEST_COPROCESSOR("stc"two"l 0, cr0, [r13], #-4") \ + TEST_COPROCESSOR("stc"two"l 0, cr0, [r13], {1}") \ + TEST_COPROCESSOR("ldc"two" 0, cr0, [r13, #4]") \ + TEST_COPROCESSOR("ldc"two" 0, cr0, [r13, #-4]") \ + TEST_COPROCESSOR("ldc"two" 0, cr0, [r13, #4]!") \ + TEST_COPROCESSOR("ldc"two" 0, cr0, [r13, #-4]!") \ + TEST_COPROCESSOR("ldc"two" 0, cr0, [r13], #4") \ + TEST_COPROCESSOR("ldc"two" 0, cr0, [r13], #-4") \ + TEST_COPROCESSOR("ldc"two" 0, cr0, [r13], {1}") \ + TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13, #4]") \ + TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13, #-4]") \ + TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13, #4]!") \ + TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13, #-4]!") \ + TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13], #4") \ + TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13], #-4") \ + TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13], {1}") \ + \ + TEST_COPROCESSOR( "stc"two" 0, cr0, [r15, #4]") \ + TEST_COPROCESSOR( "stc"two" 0, cr0, [r15, #-4]") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##daf0001) " @ stc"two" 0, cr0, [r15, #4]!") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##d2f0001) " @ stc"two" 0, cr0, [r15, #-4]!") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##caf0001) " @ stc"two" 0, cr0, [r15], #4") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##c2f0001) " @ stc"two" 0, cr0, [r15], #-4") \ + TEST_COPROCESSOR( "stc"two" 0, cr0, [r15], {1}") \ + TEST_COPROCESSOR( "stc"two"l 0, cr0, [r15, #4]") \ + TEST_COPROCESSOR( "stc"two"l 0, cr0, [r15, #-4]") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##def0001) " @ stc"two"l 0, cr0, [r15, #4]!") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##d6f0001) " @ stc"two"l 0, cr0, [r15, #-4]!") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##cef0001) " @ stc"two"l 0, cr0, [r15], #4") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##c6f0001) " @ stc"two"l 0, cr0, [r15], #-4") \ + TEST_COPROCESSOR( "stc"two"l 0, cr0, [r15], {1}") \ + TEST_COPROCESSOR( "ldc"two" 0, cr0, [r15, #4]") \ + TEST_COPROCESSOR( "ldc"two" 0, cr0, [r15, #-4]") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##dbf0001) " @ ldc"two" 0, cr0, [r15, #4]!") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##d3f0001) " @ ldc"two" 0, cr0, [r15, #-4]!") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##cbf0001) " @ ldc"two" 0, cr0, [r15], #4") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##c3f0001) " @ ldc"two" 0, cr0, [r15], #-4") \ + TEST_COPROCESSOR( "ldc"two" 0, cr0, [r15], {1}") \ + TEST_COPROCESSOR( "ldc"two"l 0, cr0, [r15, #4]") \ + TEST_COPROCESSOR( "ldc"two"l 0, cr0, [r15, #-4]") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##dff0001) " @ ldc"two"l 0, cr0, [r15, #4]!") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##d7f0001) " @ ldc"two"l 0, cr0, [r15, #-4]!") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##cff0001) " @ ldc"two"l 0, cr0, [r15], #4") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##c7f0001) " @ ldc"two"l 0, cr0, [r15], #-4") \ + TEST_COPROCESSOR( "ldc"two"l 0, cr0, [r15], {1}") + +#define COPROCESSOR_INSTRUCTIONS_MC_MR(two,cc) \ + \ + TEST_COPROCESSOR( "mcrr"two" 0, 15, r0, r14, cr0") \ + TEST_COPROCESSOR( "mcrr"two" 15, 0, r14, r0, cr15") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##c4f00f0) " @ mcrr"two" 0, 15, r0, r15, cr0") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##c40ff0f) " @ mcrr"two" 15, 0, r15, r0, cr15") \ + TEST_COPROCESSOR( "mrrc"two" 0, 15, r0, r14, cr0") \ + TEST_COPROCESSOR( "mrrc"two" 15, 0, r14, r0, cr15") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##c5f00f0) " @ mrrc"two" 0, 15, r0, r15, cr0") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##c50ff0f) " @ mrrc"two" 15, 0, r15, r0, cr15") \ + TEST_COPROCESSOR( "cdp"two" 15, 15, cr15, cr15, cr15, 7") \ + TEST_COPROCESSOR( "cdp"two" 0, 0, cr0, cr0, cr0, 0") \ + TEST_COPROCESSOR( "mcr"two" 15, 7, r15, cr15, cr15, 7") \ + TEST_COPROCESSOR( "mcr"two" 0, 0, r0, cr0, cr0, 0") \ + TEST_COPROCESSOR( "mrc"two" 15, 7, r15, cr15, cr15, 7") \ + TEST_COPROCESSOR( "mrc"two" 0, 0, r0, cr0, cr0, 0") + + COPROCESSOR_INSTRUCTIONS_ST_LD("",e) +#if __LINUX_ARM_ARCH__ >= 5 + COPROCESSOR_INSTRUCTIONS_MC_MR("",e) +#endif + TEST_UNSUPPORTED("svc 0") + TEST_UNSUPPORTED("svc 0xffffff") + + TEST_UNSUPPORTED("svc 0") + + TEST_GROUP("Unconditional instruction") + +#if __LINUX_ARM_ARCH__ >= 6 + TEST_UNSUPPORTED("srsda sp, 0x13") + TEST_UNSUPPORTED("srsdb sp, 0x13") + TEST_UNSUPPORTED("srsia sp, 0x13") + TEST_UNSUPPORTED("srsib sp, 0x13") + TEST_UNSUPPORTED("srsda sp!, 0x13") + TEST_UNSUPPORTED("srsdb sp!, 0x13") + TEST_UNSUPPORTED("srsia sp!, 0x13") + TEST_UNSUPPORTED("srsib sp!, 0x13") + + TEST_UNSUPPORTED("rfeda sp") + TEST_UNSUPPORTED("rfedb sp") + TEST_UNSUPPORTED("rfeia sp") + TEST_UNSUPPORTED("rfeib sp") + TEST_UNSUPPORTED("rfeda sp!") + TEST_UNSUPPORTED("rfedb sp!") + TEST_UNSUPPORTED("rfeia sp!") + TEST_UNSUPPORTED("rfeib sp!") + TEST_UNSUPPORTED(__inst_arm(0xf81d0a00) " @ rfeda pc") + TEST_UNSUPPORTED(__inst_arm(0xf91d0a00) " @ rfedb pc") + TEST_UNSUPPORTED(__inst_arm(0xf89d0a00) " @ rfeia pc") + TEST_UNSUPPORTED(__inst_arm(0xf99d0a00) " @ rfeib pc") + TEST_UNSUPPORTED(__inst_arm(0xf83d0a00) " @ rfeda pc!") + TEST_UNSUPPORTED(__inst_arm(0xf93d0a00) " @ rfedb pc!") + TEST_UNSUPPORTED(__inst_arm(0xf8bd0a00) " @ rfeia pc!") + TEST_UNSUPPORTED(__inst_arm(0xf9bd0a00) " @ rfeib pc!") +#endif /* __LINUX_ARM_ARCH__ >= 6 */ + +#if __LINUX_ARM_ARCH__ >= 6 + TEST_X( "blx __dummy_thumb_subroutine_even", + ".thumb \n\t" + ".space 4 \n\t" + ".type __dummy_thumb_subroutine_even, %%function \n\t" + "__dummy_thumb_subroutine_even: \n\t" + "mov r0, pc \n\t" + "bx lr \n\t" + ".arm \n\t" + ) + TEST( "blx __dummy_thumb_subroutine_even") + + TEST_X( "blx __dummy_thumb_subroutine_odd", + ".thumb \n\t" + ".space 2 \n\t" + ".type __dummy_thumb_subroutine_odd, %%function \n\t" + "__dummy_thumb_subroutine_odd: \n\t" + "mov r0, pc \n\t" + "bx lr \n\t" + ".arm \n\t" + ) + TEST( "blx __dummy_thumb_subroutine_odd") +#endif /* __LINUX_ARM_ARCH__ >= 6 */ + +#if __LINUX_ARM_ARCH__ >= 5 + COPROCESSOR_INSTRUCTIONS_ST_LD("2",f) +#endif +#if __LINUX_ARM_ARCH__ >= 6 + COPROCESSOR_INSTRUCTIONS_MC_MR("2",f) +#endif + + TEST_GROUP("Miscellaneous instructions, memory hints, and Advanced SIMD instructions") + +#if __LINUX_ARM_ARCH__ >= 6 + TEST_UNSUPPORTED("cps 0x13") + TEST_UNSUPPORTED("cpsie i") + TEST_UNSUPPORTED("cpsid i") + TEST_UNSUPPORTED("cpsie i,0x13") + TEST_UNSUPPORTED("cpsid i,0x13") + TEST_UNSUPPORTED("setend le") + TEST_UNSUPPORTED("setend be") +#endif + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_P("pli [r",0,0b,", #16]") + TEST( "pli [pc, #0]") + TEST_RR("pli [r",12,0b,", r",0, 16,"]") + TEST_RR("pli [r",0, 0b,", -r",12,16,", lsl #4]") +#endif + +#if __LINUX_ARM_ARCH__ >= 5 + TEST_P("pld [r",0,32,", #-16]") + TEST( "pld [pc, #0]") + TEST_PR("pld [r",7, 24, ", r",0, 16,"]") + TEST_PR("pld [r",8, 24, ", -r",12,16,", lsl #4]") +#endif + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_SUPPORTED( __inst_arm(0xf590f000) " @ pldw [r0, #0]") + TEST_SUPPORTED( __inst_arm(0xf797f000) " @ pldw [r7, r0]") + TEST_SUPPORTED( __inst_arm(0xf798f18c) " @ pldw [r8, r12, lsl #3]"); +#endif + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_UNSUPPORTED("clrex") + TEST_UNSUPPORTED("dsb") + TEST_UNSUPPORTED("dmb") + TEST_UNSUPPORTED("isb") +#endif + + verbose("\n"); +} + diff --git a/arch/arm/probes/kprobes/test-core.c b/arch/arm/probes/kprobes/test-core.c new file mode 100644 index 0000000..7ab633d --- /dev/null +++ b/arch/arm/probes/kprobes/test-core.c @@ -0,0 +1,1713 @@ +/* + * arch/arm/kernel/kprobes-test.c + * + * Copyright (C) 2011 Jon Medhurst . + * + * 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 file contains test code for ARM kprobes. + * + * The top level function run_all_tests() executes tests for all of the + * supported instruction sets: ARM, 16-bit Thumb, and 32-bit Thumb. These tests + * fall into two categories; run_api_tests() checks basic functionality of the + * kprobes API, and run_test_cases() is a comprehensive test for kprobes + * instruction decoding and simulation. + * + * run_test_cases() first checks the kprobes decoding table for self consistency + * (using table_test()) then executes a series of test cases for each of the CPU + * instruction forms. coverage_start() and coverage_end() are used to verify + * that these test cases cover all of the possible combinations of instructions + * described by the kprobes decoding tables. + * + * The individual test cases are in kprobes-test-arm.c and kprobes-test-thumb.c + * which use the macros defined in kprobes-test.h. The rest of this + * documentation will describe the operation of the framework used by these + * test cases. + */ + +/* + * TESTING METHODOLOGY + * ------------------- + * + * The methodology used to test an ARM instruction 'test_insn' is to use + * inline assembler like: + * + * test_before: nop + * test_case: test_insn + * test_after: nop + * + * When the test case is run a kprobe is placed of each nop. The + * post-handler of the test_before probe is used to modify the saved CPU + * register context to that which we require for the test case. The + * pre-handler of the of the test_after probe saves a copy of the CPU + * register context. In this way we can execute test_insn with a specific + * register context and see the results afterwards. + * + * To actually test the kprobes instruction emulation we perform the above + * step a second time but with an additional kprobe on the test_case + * instruction itself. If the emulation is accurate then the results seen + * by the test_after probe will be identical to the first run which didn't + * have a probe on test_case. + * + * Each test case is run several times with a variety of variations in the + * flags value of stored in CPSR, and for Thumb code, different ITState. + * + * For instructions which can modify PC, a second test_after probe is used + * like this: + * + * test_before: nop + * test_case: test_insn + * test_after: nop + * b test_done + * test_after2: nop + * test_done: + * + * The test case is constructed such that test_insn branches to + * test_after2, or, if testing a conditional instruction, it may just + * continue to test_after. The probes inserted at both locations let us + * determine which happened. A similar approach is used for testing + * backwards branches... + * + * b test_before + * b test_done @ helps to cope with off by 1 branches + * test_after2: nop + * b test_done + * test_before: nop + * test_case: test_insn + * test_after: nop + * test_done: + * + * The macros used to generate the assembler instructions describe above + * are TEST_INSTRUCTION, TEST_BRANCH_F (branch forwards) and TEST_BRANCH_B + * (branch backwards). In these, the local variables numbered 1, 50, 2 and + * 99 represent: test_before, test_case, test_after2 and test_done. + * + * FRAMEWORK + * --------- + * + * Each test case is wrapped between the pair of macros TESTCASE_START and + * TESTCASE_END. As well as performing the inline assembler boilerplate, + * these call out to the kprobes_test_case_start() and + * kprobes_test_case_end() functions which drive the execution of the test + * case. The specific arguments to use for each test case are stored as + * inline data constructed using the various TEST_ARG_* macros. Putting + * this all together, a simple test case may look like: + * + * TESTCASE_START("Testing mov r0, r7") + * TEST_ARG_REG(7, 0x12345678) // Set r7=0x12345678 + * TEST_ARG_END("") + * TEST_INSTRUCTION("mov r0, r7") + * TESTCASE_END + * + * Note, in practice the single convenience macro TEST_R would be used for this + * instead. + * + * The above would expand to assembler looking something like: + * + * @ TESTCASE_START + * bl __kprobes_test_case_start + * .pushsection .rodata + * "10: + * .ascii "mov r0, r7" @ text title for test case + * .byte 0 + * .popsection + * @ start of inline data... + * .word 10b @ pointer to title in .rodata section + * + * @ TEST_ARG_REG + * .byte ARG_TYPE_REG + * .byte 7 + * .short 0 + * .word 0x1234567 + * + * @ TEST_ARG_END + * .byte ARG_TYPE_END + * .byte TEST_ISA @ flags, including ISA being tested + * .short 50f-0f @ offset of 'test_before' + * .short 2f-0f @ offset of 'test_after2' (if relevent) + * .short 99f-0f @ offset of 'test_done' + * @ start of test case code... + * 0: + * .code TEST_ISA @ switch to ISA being tested + * + * @ TEST_INSTRUCTION + * 50: nop @ location for 'test_before' probe + * 1: mov r0, r7 @ the test case instruction 'test_insn' + * nop @ location for 'test_after' probe + * + * // TESTCASE_END + * 2: + * 99: bl __kprobes_test_case_end_##TEST_ISA + * .code NONMAL_ISA + * + * When the above is execute the following happens... + * + * __kprobes_test_case_start() is an assembler wrapper which sets up space + * for a stack buffer and calls the C function kprobes_test_case_start(). + * This C function will do some initial processing of the inline data and + * setup some global state. It then inserts the test_before and test_after + * kprobes and returns a value which causes the assembler wrapper to jump + * to the start of the test case code, (local label '0'). + * + * When the test case code executes, the test_before probe will be hit and + * test_before_post_handler will call setup_test_context(). This fills the + * stack buffer and CPU registers with a test pattern and then processes + * the test case arguments. In our example there is one TEST_ARG_REG which + * indicates that R7 should be loaded with the value 0x12345678. + * + * When the test_before probe ends, the test case continues and executes + * the "mov r0, r7" instruction. It then hits the test_after probe and the + * pre-handler for this (test_after_pre_handler) will save a copy of the + * CPU register context. This should now have R0 holding the same value as + * R7. + * + * Finally we get to the call to __kprobes_test_case_end_{32,16}. This is + * an assembler wrapper which switches back to the ISA used by the test + * code and calls the C function kprobes_test_case_end(). + * + * For each run through the test case, test_case_run_count is incremented + * by one. For even runs, kprobes_test_case_end() saves a copy of the + * register and stack buffer contents from the test case just run. It then + * inserts a kprobe on the test case instruction 'test_insn' and returns a + * value to cause the test case code to be re-run. + * + * For odd numbered runs, kprobes_test_case_end() compares the register and + * stack buffer contents to those that were saved on the previous even + * numbered run (the one without the kprobe on test_insn). These should be + * the same if the kprobe instruction simulation routine is correct. + * + * The pair of test case runs is repeated with different combinations of + * flag values in CPSR and, for Thumb, different ITState. This is + * controlled by test_context_cpsr(). + * + * BUILDING TEST CASES + * ------------------- + * + * + * As an aid to building test cases, the stack buffer is initialised with + * some special values: + * + * [SP+13*4] Contains SP+120. This can be used to test instructions + * which load a value into SP. + * + * [SP+15*4] When testing branching instructions using TEST_BRANCH_{F,B}, + * this holds the target address of the branch, 'test_after2'. + * This can be used to test instructions which load a PC value + * from memory. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "test-core.h" +#include "../decode-arm.h" +#include "../decode-thumb.h" + + +#define BENCHMARKING 1 + + +/* + * Test basic API + */ + +static bool test_regs_ok; +static int test_func_instance; +static int pre_handler_called; +static int post_handler_called; +static int jprobe_func_called; +static int kretprobe_handler_called; +static int tests_failed; + +#define FUNC_ARG1 0x12345678 +#define FUNC_ARG2 0xabcdef + + +#ifndef CONFIG_THUMB2_KERNEL + +long arm_func(long r0, long r1); + +static void __used __naked __arm_kprobes_test_func(void) +{ + __asm__ __volatile__ ( + ".arm \n\t" + ".type arm_func, %%function \n\t" + "arm_func: \n\t" + "adds r0, r0, r1 \n\t" + "bx lr \n\t" + ".code "NORMAL_ISA /* Back to Thumb if necessary */ + : : : "r0", "r1", "cc" + ); +} + +#else /* CONFIG_THUMB2_KERNEL */ + +long thumb16_func(long r0, long r1); +long thumb32even_func(long r0, long r1); +long thumb32odd_func(long r0, long r1); + +static void __used __naked __thumb_kprobes_test_funcs(void) +{ + __asm__ __volatile__ ( + ".type thumb16_func, %%function \n\t" + "thumb16_func: \n\t" + "adds.n r0, r0, r1 \n\t" + "bx lr \n\t" + + ".align \n\t" + ".type thumb32even_func, %%function \n\t" + "thumb32even_func: \n\t" + "adds.w r0, r0, r1 \n\t" + "bx lr \n\t" + + ".align \n\t" + "nop.n \n\t" + ".type thumb32odd_func, %%function \n\t" + "thumb32odd_func: \n\t" + "adds.w r0, r0, r1 \n\t" + "bx lr \n\t" + + : : : "r0", "r1", "cc" + ); +} + +#endif /* CONFIG_THUMB2_KERNEL */ + + +static int call_test_func(long (*func)(long, long), bool check_test_regs) +{ + long ret; + + ++test_func_instance; + test_regs_ok = false; + + ret = (*func)(FUNC_ARG1, FUNC_ARG2); + if (ret != FUNC_ARG1 + FUNC_ARG2) { + pr_err("FAIL: call_test_func: func returned %lx\n", ret); + return false; + } + + if (check_test_regs && !test_regs_ok) { + pr_err("FAIL: test regs not OK\n"); + return false; + } + + return true; +} + +static int __kprobes pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + pre_handler_called = test_func_instance; + if (regs->ARM_r0 == FUNC_ARG1 && regs->ARM_r1 == FUNC_ARG2) + test_regs_ok = true; + return 0; +} + +static void __kprobes post_handler(struct kprobe *p, struct pt_regs *regs, + unsigned long flags) +{ + post_handler_called = test_func_instance; + if (regs->ARM_r0 != FUNC_ARG1 + FUNC_ARG2 || regs->ARM_r1 != FUNC_ARG2) + test_regs_ok = false; +} + +static struct kprobe the_kprobe = { + .addr = 0, + .pre_handler = pre_handler, + .post_handler = post_handler +}; + +static int test_kprobe(long (*func)(long, long)) +{ + int ret; + + the_kprobe.addr = (kprobe_opcode_t *)func; + ret = register_kprobe(&the_kprobe); + if (ret < 0) { + pr_err("FAIL: register_kprobe failed with %d\n", ret); + return ret; + } + + ret = call_test_func(func, true); + + unregister_kprobe(&the_kprobe); + the_kprobe.flags = 0; /* Clear disable flag to allow reuse */ + + if (!ret) + return -EINVAL; + if (pre_handler_called != test_func_instance) { + pr_err("FAIL: kprobe pre_handler not called\n"); + return -EINVAL; + } + if (post_handler_called != test_func_instance) { + pr_err("FAIL: kprobe post_handler not called\n"); + return -EINVAL; + } + if (!call_test_func(func, false)) + return -EINVAL; + if (pre_handler_called == test_func_instance || + post_handler_called == test_func_instance) { + pr_err("FAIL: probe called after unregistering\n"); + return -EINVAL; + } + + return 0; +} + +static void __kprobes jprobe_func(long r0, long r1) +{ + jprobe_func_called = test_func_instance; + if (r0 == FUNC_ARG1 && r1 == FUNC_ARG2) + test_regs_ok = true; + jprobe_return(); +} + +static struct jprobe the_jprobe = { + .entry = jprobe_func, +}; + +static int test_jprobe(long (*func)(long, long)) +{ + int ret; + + the_jprobe.kp.addr = (kprobe_opcode_t *)func; + ret = register_jprobe(&the_jprobe); + if (ret < 0) { + pr_err("FAIL: register_jprobe failed with %d\n", ret); + return ret; + } + + ret = call_test_func(func, true); + + unregister_jprobe(&the_jprobe); + the_jprobe.kp.flags = 0; /* Clear disable flag to allow reuse */ + + if (!ret) + return -EINVAL; + if (jprobe_func_called != test_func_instance) { + pr_err("FAIL: jprobe handler function not called\n"); + return -EINVAL; + } + if (!call_test_func(func, false)) + return -EINVAL; + if (jprobe_func_called == test_func_instance) { + pr_err("FAIL: probe called after unregistering\n"); + return -EINVAL; + } + + return 0; +} + +static int __kprobes +kretprobe_handler(struct kretprobe_instance *ri, struct pt_regs *regs) +{ + kretprobe_handler_called = test_func_instance; + if (regs_return_value(regs) == FUNC_ARG1 + FUNC_ARG2) + test_regs_ok = true; + return 0; +} + +static struct kretprobe the_kretprobe = { + .handler = kretprobe_handler, +}; + +static int test_kretprobe(long (*func)(long, long)) +{ + int ret; + + the_kretprobe.kp.addr = (kprobe_opcode_t *)func; + ret = register_kretprobe(&the_kretprobe); + if (ret < 0) { + pr_err("FAIL: register_kretprobe failed with %d\n", ret); + return ret; + } + + ret = call_test_func(func, true); + + unregister_kretprobe(&the_kretprobe); + the_kretprobe.kp.flags = 0; /* Clear disable flag to allow reuse */ + + if (!ret) + return -EINVAL; + if (kretprobe_handler_called != test_func_instance) { + pr_err("FAIL: kretprobe handler not called\n"); + return -EINVAL; + } + if (!call_test_func(func, false)) + return -EINVAL; + if (jprobe_func_called == test_func_instance) { + pr_err("FAIL: kretprobe called after unregistering\n"); + return -EINVAL; + } + + return 0; +} + +static int run_api_tests(long (*func)(long, long)) +{ + int ret; + + pr_info(" kprobe\n"); + ret = test_kprobe(func); + if (ret < 0) + return ret; + + pr_info(" jprobe\n"); + ret = test_jprobe(func); +#if defined(CONFIG_THUMB2_KERNEL) && !defined(MODULE) + if (ret == -EINVAL) { + pr_err("FAIL: Known longtime bug with jprobe on Thumb kernels\n"); + tests_failed = ret; + ret = 0; + } +#endif + if (ret < 0) + return ret; + + pr_info(" kretprobe\n"); + ret = test_kretprobe(func); + if (ret < 0) + return ret; + + return 0; +} + + +/* + * Benchmarking + */ + +#if BENCHMARKING + +static void __naked benchmark_nop(void) +{ + __asm__ __volatile__ ( + "nop \n\t" + "bx lr" + ); +} + +#ifdef CONFIG_THUMB2_KERNEL +#define wide ".w" +#else +#define wide +#endif + +static void __naked benchmark_pushpop1(void) +{ + __asm__ __volatile__ ( + "stmdb"wide" sp!, {r3-r11,lr} \n\t" + "ldmia"wide" sp!, {r3-r11,pc}" + ); +} + +static void __naked benchmark_pushpop2(void) +{ + __asm__ __volatile__ ( + "stmdb"wide" sp!, {r0-r8,lr} \n\t" + "ldmia"wide" sp!, {r0-r8,pc}" + ); +} + +static void __naked benchmark_pushpop3(void) +{ + __asm__ __volatile__ ( + "stmdb"wide" sp!, {r4,lr} \n\t" + "ldmia"wide" sp!, {r4,pc}" + ); +} + +static void __naked benchmark_pushpop4(void) +{ + __asm__ __volatile__ ( + "stmdb"wide" sp!, {r0,lr} \n\t" + "ldmia"wide" sp!, {r0,pc}" + ); +} + + +#ifdef CONFIG_THUMB2_KERNEL + +static void __naked benchmark_pushpop_thumb(void) +{ + __asm__ __volatile__ ( + "push.n {r0-r7,lr} \n\t" + "pop.n {r0-r7,pc}" + ); +} + +#endif + +static int __kprobes +benchmark_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + return 0; +} + +static int benchmark(void(*fn)(void)) +{ + unsigned n, i, t, t0; + + for (n = 1000; ; n *= 2) { + t0 = sched_clock(); + for (i = n; i > 0; --i) + fn(); + t = sched_clock() - t0; + if (t >= 250000000) + break; /* Stop once we took more than 0.25 seconds */ + } + return t / n; /* Time for one iteration in nanoseconds */ +}; + +static int kprobe_benchmark(void(*fn)(void), unsigned offset) +{ + struct kprobe k = { + .addr = (kprobe_opcode_t *)((uintptr_t)fn + offset), + .pre_handler = benchmark_pre_handler, + }; + + int ret = register_kprobe(&k); + if (ret < 0) { + pr_err("FAIL: register_kprobe failed with %d\n", ret); + return ret; + } + + ret = benchmark(fn); + + unregister_kprobe(&k); + return ret; +}; + +struct benchmarks { + void (*fn)(void); + unsigned offset; + const char *title; +}; + +static int run_benchmarks(void) +{ + int ret; + struct benchmarks list[] = { + {&benchmark_nop, 0, "nop"}, + /* + * benchmark_pushpop{1,3} will have the optimised + * instruction emulation, whilst benchmark_pushpop{2,4} will + * be the equivalent unoptimised instructions. + */ + {&benchmark_pushpop1, 0, "stmdb sp!, {r3-r11,lr}"}, + {&benchmark_pushpop1, 4, "ldmia sp!, {r3-r11,pc}"}, + {&benchmark_pushpop2, 0, "stmdb sp!, {r0-r8,lr}"}, + {&benchmark_pushpop2, 4, "ldmia sp!, {r0-r8,pc}"}, + {&benchmark_pushpop3, 0, "stmdb sp!, {r4,lr}"}, + {&benchmark_pushpop3, 4, "ldmia sp!, {r4,pc}"}, + {&benchmark_pushpop4, 0, "stmdb sp!, {r0,lr}"}, + {&benchmark_pushpop4, 4, "ldmia sp!, {r0,pc}"}, +#ifdef CONFIG_THUMB2_KERNEL + {&benchmark_pushpop_thumb, 0, "push.n {r0-r7,lr}"}, + {&benchmark_pushpop_thumb, 2, "pop.n {r0-r7,pc}"}, +#endif + {0} + }; + + struct benchmarks *b; + for (b = list; b->fn; ++b) { + ret = kprobe_benchmark(b->fn, b->offset); + if (ret < 0) + return ret; + pr_info(" %dns for kprobe %s\n", ret, b->title); + } + + pr_info("\n"); + return 0; +} + +#endif /* BENCHMARKING */ + + +/* + * Decoding table self-consistency tests + */ + +static const int decode_struct_sizes[NUM_DECODE_TYPES] = { + [DECODE_TYPE_TABLE] = sizeof(struct decode_table), + [DECODE_TYPE_CUSTOM] = sizeof(struct decode_custom), + [DECODE_TYPE_SIMULATE] = sizeof(struct decode_simulate), + [DECODE_TYPE_EMULATE] = sizeof(struct decode_emulate), + [DECODE_TYPE_OR] = sizeof(struct decode_or), + [DECODE_TYPE_REJECT] = sizeof(struct decode_reject) +}; + +static int table_iter(const union decode_item *table, + int (*fn)(const struct decode_header *, void *), + void *args) +{ + const struct decode_header *h = (struct decode_header *)table; + int result; + + for (;;) { + enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; + + if (type == DECODE_TYPE_END) + return 0; + + result = fn(h, args); + if (result) + return result; + + h = (struct decode_header *) + ((uintptr_t)h + decode_struct_sizes[type]); + + } +} + +static int table_test_fail(const struct decode_header *h, const char* message) +{ + + pr_err("FAIL: kprobes test failure \"%s\" (mask %08x, value %08x)\n", + message, h->mask.bits, h->value.bits); + return -EINVAL; +} + +struct table_test_args { + const union decode_item *root_table; + u32 parent_mask; + u32 parent_value; +}; + +static int table_test_fn(const struct decode_header *h, void *args) +{ + struct table_test_args *a = (struct table_test_args *)args; + enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; + + if (h->value.bits & ~h->mask.bits) + return table_test_fail(h, "Match value has bits not in mask"); + + if ((h->mask.bits & a->parent_mask) != a->parent_mask) + return table_test_fail(h, "Mask has bits not in parent mask"); + + if ((h->value.bits ^ a->parent_value) & a->parent_mask) + return table_test_fail(h, "Value is inconsistent with parent"); + + if (type == DECODE_TYPE_TABLE) { + struct decode_table *d = (struct decode_table *)h; + struct table_test_args args2 = *a; + args2.parent_mask = h->mask.bits; + args2.parent_value = h->value.bits; + return table_iter(d->table.table, table_test_fn, &args2); + } + + return 0; +} + +static int table_test(const union decode_item *table) +{ + struct table_test_args args = { + .root_table = table, + .parent_mask = 0, + .parent_value = 0 + }; + return table_iter(args.root_table, table_test_fn, &args); +} + + +/* + * Decoding table test coverage analysis + * + * coverage_start() builds a coverage_table which contains a list of + * coverage_entry's to match each entry in the specified kprobes instruction + * decoding table. + * + * When test cases are run, coverage_add() is called to process each case. + * This looks up the corresponding entry in the coverage_table and sets it as + * being matched, as well as clearing the regs flag appropriate for the test. + * + * After all test cases have been run, coverage_end() is called to check that + * all entries in coverage_table have been matched and that all regs flags are + * cleared. I.e. that all possible combinations of instructions described by + * the kprobes decoding tables have had a test case executed for them. + */ + +bool coverage_fail; + +#define MAX_COVERAGE_ENTRIES 256 + +struct coverage_entry { + const struct decode_header *header; + unsigned regs; + unsigned nesting; + char matched; +}; + +struct coverage_table { + struct coverage_entry *base; + unsigned num_entries; + unsigned nesting; +}; + +struct coverage_table coverage; + +#define COVERAGE_ANY_REG (1<<0) +#define COVERAGE_SP (1<<1) +#define COVERAGE_PC (1<<2) +#define COVERAGE_PCWB (1<<3) + +static const char coverage_register_lookup[16] = { + [REG_TYPE_ANY] = COVERAGE_ANY_REG | COVERAGE_SP | COVERAGE_PC, + [REG_TYPE_SAMEAS16] = COVERAGE_ANY_REG, + [REG_TYPE_SP] = COVERAGE_SP, + [REG_TYPE_PC] = COVERAGE_PC, + [REG_TYPE_NOSP] = COVERAGE_ANY_REG | COVERAGE_SP, + [REG_TYPE_NOSPPC] = COVERAGE_ANY_REG | COVERAGE_SP | COVERAGE_PC, + [REG_TYPE_NOPC] = COVERAGE_ANY_REG | COVERAGE_PC, + [REG_TYPE_NOPCWB] = COVERAGE_ANY_REG | COVERAGE_PC | COVERAGE_PCWB, + [REG_TYPE_NOPCX] = COVERAGE_ANY_REG, + [REG_TYPE_NOSPPCX] = COVERAGE_ANY_REG | COVERAGE_SP, +}; + +unsigned coverage_start_registers(const struct decode_header *h) +{ + unsigned regs = 0; + int i; + for (i = 0; i < 20; i += 4) { + int r = (h->type_regs.bits >> (DECODE_TYPE_BITS + i)) & 0xf; + regs |= coverage_register_lookup[r] << i; + } + return regs; +} + +static int coverage_start_fn(const struct decode_header *h, void *args) +{ + struct coverage_table *coverage = (struct coverage_table *)args; + enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; + struct coverage_entry *entry = coverage->base + coverage->num_entries; + + if (coverage->num_entries == MAX_COVERAGE_ENTRIES - 1) { + pr_err("FAIL: Out of space for test coverage data"); + return -ENOMEM; + } + + ++coverage->num_entries; + + entry->header = h; + entry->regs = coverage_start_registers(h); + entry->nesting = coverage->nesting; + entry->matched = false; + + if (type == DECODE_TYPE_TABLE) { + struct decode_table *d = (struct decode_table *)h; + int ret; + ++coverage->nesting; + ret = table_iter(d->table.table, coverage_start_fn, coverage); + --coverage->nesting; + return ret; + } + + return 0; +} + +static int coverage_start(const union decode_item *table) +{ + coverage.base = kmalloc(MAX_COVERAGE_ENTRIES * + sizeof(struct coverage_entry), GFP_KERNEL); + coverage.num_entries = 0; + coverage.nesting = 0; + return table_iter(table, coverage_start_fn, &coverage); +} + +static void +coverage_add_registers(struct coverage_entry *entry, kprobe_opcode_t insn) +{ + int regs = entry->header->type_regs.bits >> DECODE_TYPE_BITS; + int i; + for (i = 0; i < 20; i += 4) { + enum decode_reg_type reg_type = (regs >> i) & 0xf; + int reg = (insn >> i) & 0xf; + int flag; + + if (!reg_type) + continue; + + if (reg == 13) + flag = COVERAGE_SP; + else if (reg == 15) + flag = COVERAGE_PC; + else + flag = COVERAGE_ANY_REG; + entry->regs &= ~(flag << i); + + switch (reg_type) { + + case REG_TYPE_NONE: + case REG_TYPE_ANY: + case REG_TYPE_SAMEAS16: + break; + + case REG_TYPE_SP: + if (reg != 13) + return; + break; + + case REG_TYPE_PC: + if (reg != 15) + return; + break; + + case REG_TYPE_NOSP: + if (reg == 13) + return; + break; + + case REG_TYPE_NOSPPC: + case REG_TYPE_NOSPPCX: + if (reg == 13 || reg == 15) + return; + break; + + case REG_TYPE_NOPCWB: + if (!is_writeback(insn)) + break; + if (reg == 15) { + entry->regs &= ~(COVERAGE_PCWB << i); + return; + } + break; + + case REG_TYPE_NOPC: + case REG_TYPE_NOPCX: + if (reg == 15) + return; + break; + } + + } +} + +static void coverage_add(kprobe_opcode_t insn) +{ + struct coverage_entry *entry = coverage.base; + struct coverage_entry *end = coverage.base + coverage.num_entries; + bool matched = false; + unsigned nesting = 0; + + for (; entry < end; ++entry) { + const struct decode_header *h = entry->header; + enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; + + if (entry->nesting > nesting) + continue; /* Skip sub-table we didn't match */ + + if (entry->nesting < nesting) + break; /* End of sub-table we were scanning */ + + if (!matched) { + if ((insn & h->mask.bits) != h->value.bits) + continue; + entry->matched = true; + } + + switch (type) { + + case DECODE_TYPE_TABLE: + ++nesting; + break; + + case DECODE_TYPE_CUSTOM: + case DECODE_TYPE_SIMULATE: + case DECODE_TYPE_EMULATE: + coverage_add_registers(entry, insn); + return; + + case DECODE_TYPE_OR: + matched = true; + break; + + case DECODE_TYPE_REJECT: + default: + return; + } + + } +} + +static void coverage_end(void) +{ + struct coverage_entry *entry = coverage.base; + struct coverage_entry *end = coverage.base + coverage.num_entries; + + for (; entry < end; ++entry) { + u32 mask = entry->header->mask.bits; + u32 value = entry->header->value.bits; + + if (entry->regs) { + pr_err("FAIL: Register test coverage missing for %08x %08x (%05x)\n", + mask, value, entry->regs); + coverage_fail = true; + } + if (!entry->matched) { + pr_err("FAIL: Test coverage entry missing for %08x %08x\n", + mask, value); + coverage_fail = true; + } + } + + kfree(coverage.base); +} + + +/* + * Framework for instruction set test cases + */ + +void __naked __kprobes_test_case_start(void) +{ + __asm__ __volatile__ ( + "stmdb sp!, {r4-r11} \n\t" + "sub sp, sp, #"__stringify(TEST_MEMORY_SIZE)"\n\t" + "bic r0, lr, #1 @ r0 = inline data \n\t" + "mov r1, sp \n\t" + "bl kprobes_test_case_start \n\t" + "bx r0 \n\t" + ); +} + +#ifndef CONFIG_THUMB2_KERNEL + +void __naked __kprobes_test_case_end_32(void) +{ + __asm__ __volatile__ ( + "mov r4, lr \n\t" + "bl kprobes_test_case_end \n\t" + "cmp r0, #0 \n\t" + "movne pc, r0 \n\t" + "mov r0, r4 \n\t" + "add sp, sp, #"__stringify(TEST_MEMORY_SIZE)"\n\t" + "ldmia sp!, {r4-r11} \n\t" + "mov pc, r0 \n\t" + ); +} + +#else /* CONFIG_THUMB2_KERNEL */ + +void __naked __kprobes_test_case_end_16(void) +{ + __asm__ __volatile__ ( + "mov r4, lr \n\t" + "bl kprobes_test_case_end \n\t" + "cmp r0, #0 \n\t" + "bxne r0 \n\t" + "mov r0, r4 \n\t" + "add sp, sp, #"__stringify(TEST_MEMORY_SIZE)"\n\t" + "ldmia sp!, {r4-r11} \n\t" + "bx r0 \n\t" + ); +} + +void __naked __kprobes_test_case_end_32(void) +{ + __asm__ __volatile__ ( + ".arm \n\t" + "orr lr, lr, #1 @ will return to Thumb code \n\t" + "ldr pc, 1f \n\t" + "1: \n\t" + ".word __kprobes_test_case_end_16 \n\t" + ); +} + +#endif + + +int kprobe_test_flags; +int kprobe_test_cc_position; + +static int test_try_count; +static int test_pass_count; +static int test_fail_count; + +static struct pt_regs initial_regs; +static struct pt_regs expected_regs; +static struct pt_regs result_regs; + +static u32 expected_memory[TEST_MEMORY_SIZE/sizeof(u32)]; + +static const char *current_title; +static struct test_arg *current_args; +static u32 *current_stack; +static uintptr_t current_branch_target; + +static uintptr_t current_code_start; +static kprobe_opcode_t current_instruction; + + +#define TEST_CASE_PASSED -1 +#define TEST_CASE_FAILED -2 + +static int test_case_run_count; +static bool test_case_is_thumb; +static int test_instance; + +/* + * We ignore the state of the imprecise abort disable flag (CPSR.A) because this + * can change randomly as the kernel doesn't take care to preserve or initialise + * this across context switches. Also, with Security Extentions, the flag may + * not be under control of the kernel; for this reason we ignore the state of + * the FIQ disable flag CPSR.F as well. + */ +#define PSR_IGNORE_BITS (PSR_A_BIT | PSR_F_BIT) + +static unsigned long test_check_cc(int cc, unsigned long cpsr) +{ + int ret = arm_check_condition(cc << 28, cpsr); + + return (ret != ARM_OPCODE_CONDTEST_FAIL); +} + +static int is_last_scenario; +static int probe_should_run; /* 0 = no, 1 = yes, -1 = unknown */ +static int memory_needs_checking; + +static unsigned long test_context_cpsr(int scenario) +{ + unsigned long cpsr; + + probe_should_run = 1; + + /* Default case is that we cycle through 16 combinations of flags */ + cpsr = (scenario & 0xf) << 28; /* N,Z,C,V flags */ + cpsr |= (scenario & 0xf) << 16; /* GE flags */ + cpsr |= (scenario & 0x1) << 27; /* Toggle Q flag */ + + if (!test_case_is_thumb) { + /* Testing ARM code */ + int cc = current_instruction >> 28; + + probe_should_run = test_check_cc(cc, cpsr) != 0; + if (scenario == 15) + is_last_scenario = true; + + } else if (kprobe_test_flags & TEST_FLAG_NO_ITBLOCK) { + /* Testing Thumb code without setting ITSTATE */ + if (kprobe_test_cc_position) { + int cc = (current_instruction >> kprobe_test_cc_position) & 0xf; + probe_should_run = test_check_cc(cc, cpsr) != 0; + } + + if (scenario == 15) + is_last_scenario = true; + + } else if (kprobe_test_flags & TEST_FLAG_FULL_ITBLOCK) { + /* Testing Thumb code with all combinations of ITSTATE */ + unsigned x = (scenario >> 4); + unsigned cond_base = x % 7; /* ITSTATE<7:5> */ + unsigned mask = x / 7 + 2; /* ITSTATE<4:0>, bits reversed */ + + if (mask > 0x1f) { + /* Finish by testing state from instruction 'itt al' */ + cond_base = 7; + mask = 0x4; + if ((scenario & 0xf) == 0xf) + is_last_scenario = true; + } + + cpsr |= cond_base << 13; /* ITSTATE<7:5> */ + cpsr |= (mask & 0x1) << 12; /* ITSTATE<4> */ + cpsr |= (mask & 0x2) << 10; /* ITSTATE<3> */ + cpsr |= (mask & 0x4) << 8; /* ITSTATE<2> */ + cpsr |= (mask & 0x8) << 23; /* ITSTATE<1> */ + cpsr |= (mask & 0x10) << 21; /* ITSTATE<0> */ + + probe_should_run = test_check_cc((cpsr >> 12) & 0xf, cpsr) != 0; + + } else { + /* Testing Thumb code with several combinations of ITSTATE */ + switch (scenario) { + case 16: /* Clear NZCV flags and 'it eq' state (false as Z=0) */ + cpsr = 0x00000800; + probe_should_run = 0; + break; + case 17: /* Set NZCV flags and 'it vc' state (false as V=1) */ + cpsr = 0xf0007800; + probe_should_run = 0; + break; + case 18: /* Clear NZCV flags and 'it ls' state (true as C=0) */ + cpsr = 0x00009800; + break; + case 19: /* Set NZCV flags and 'it cs' state (true as C=1) */ + cpsr = 0xf0002800; + is_last_scenario = true; + break; + } + } + + return cpsr; +} + +static void setup_test_context(struct pt_regs *regs) +{ + int scenario = test_case_run_count>>1; + unsigned long val; + struct test_arg *args; + int i; + + is_last_scenario = false; + memory_needs_checking = false; + + /* Initialise test memory on stack */ + val = (scenario & 1) ? VALM : ~VALM; + for (i = 0; i < TEST_MEMORY_SIZE / sizeof(current_stack[0]); ++i) + current_stack[i] = val + (i << 8); + /* Put target of branch on stack for tests which load PC from memory */ + if (current_branch_target) + current_stack[15] = current_branch_target; + /* Put a value for SP on stack for tests which load SP from memory */ + current_stack[13] = (u32)current_stack + 120; + + /* Initialise register values to their default state */ + val = (scenario & 2) ? VALR : ~VALR; + for (i = 0; i < 13; ++i) + regs->uregs[i] = val ^ (i << 8); + regs->ARM_lr = val ^ (14 << 8); + regs->ARM_cpsr &= ~(APSR_MASK | PSR_IT_MASK); + regs->ARM_cpsr |= test_context_cpsr(scenario); + + /* Perform testcase specific register setup */ + args = current_args; + for (; args[0].type != ARG_TYPE_END; ++args) + switch (args[0].type) { + case ARG_TYPE_REG: { + struct test_arg_regptr *arg = + (struct test_arg_regptr *)args; + regs->uregs[arg->reg] = arg->val; + break; + } + case ARG_TYPE_PTR: { + struct test_arg_regptr *arg = + (struct test_arg_regptr *)args; + regs->uregs[arg->reg] = + (unsigned long)current_stack + arg->val; + memory_needs_checking = true; + break; + } + case ARG_TYPE_MEM: { + struct test_arg_mem *arg = (struct test_arg_mem *)args; + current_stack[arg->index] = arg->val; + break; + } + default: + break; + } +} + +struct test_probe { + struct kprobe kprobe; + bool registered; + int hit; +}; + +static void unregister_test_probe(struct test_probe *probe) +{ + if (probe->registered) { + unregister_kprobe(&probe->kprobe); + probe->kprobe.flags = 0; /* Clear disable flag to allow reuse */ + } + probe->registered = false; +} + +static int register_test_probe(struct test_probe *probe) +{ + int ret; + + if (probe->registered) + BUG(); + + ret = register_kprobe(&probe->kprobe); + if (ret >= 0) { + probe->registered = true; + probe->hit = -1; + } + return ret; +} + +static int __kprobes +test_before_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + container_of(p, struct test_probe, kprobe)->hit = test_instance; + return 0; +} + +static void __kprobes +test_before_post_handler(struct kprobe *p, struct pt_regs *regs, + unsigned long flags) +{ + setup_test_context(regs); + initial_regs = *regs; + initial_regs.ARM_cpsr &= ~PSR_IGNORE_BITS; +} + +static int __kprobes +test_case_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + container_of(p, struct test_probe, kprobe)->hit = test_instance; + return 0; +} + +static int __kprobes +test_after_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + if (container_of(p, struct test_probe, kprobe)->hit == test_instance) + return 0; /* Already run for this test instance */ + + result_regs = *regs; + result_regs.ARM_cpsr &= ~PSR_IGNORE_BITS; + + /* Undo any changes done to SP by the test case */ + regs->ARM_sp = (unsigned long)current_stack; + + container_of(p, struct test_probe, kprobe)->hit = test_instance; + return 0; +} + +static struct test_probe test_before_probe = { + .kprobe.pre_handler = test_before_pre_handler, + .kprobe.post_handler = test_before_post_handler, +}; + +static struct test_probe test_case_probe = { + .kprobe.pre_handler = test_case_pre_handler, +}; + +static struct test_probe test_after_probe = { + .kprobe.pre_handler = test_after_pre_handler, +}; + +static struct test_probe test_after2_probe = { + .kprobe.pre_handler = test_after_pre_handler, +}; + +static void test_case_cleanup(void) +{ + unregister_test_probe(&test_before_probe); + unregister_test_probe(&test_case_probe); + unregister_test_probe(&test_after_probe); + unregister_test_probe(&test_after2_probe); +} + +static void print_registers(struct pt_regs *regs) +{ + pr_err("r0 %08lx | r1 %08lx | r2 %08lx | r3 %08lx\n", + regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3); + pr_err("r4 %08lx | r5 %08lx | r6 %08lx | r7 %08lx\n", + regs->ARM_r4, regs->ARM_r5, regs->ARM_r6, regs->ARM_r7); + pr_err("r8 %08lx | r9 %08lx | r10 %08lx | r11 %08lx\n", + regs->ARM_r8, regs->ARM_r9, regs->ARM_r10, regs->ARM_fp); + pr_err("r12 %08lx | sp %08lx | lr %08lx | pc %08lx\n", + regs->ARM_ip, regs->ARM_sp, regs->ARM_lr, regs->ARM_pc); + pr_err("cpsr %08lx\n", regs->ARM_cpsr); +} + +static void print_memory(u32 *mem, size_t size) +{ + int i; + for (i = 0; i < size / sizeof(u32); i += 4) + pr_err("%08x %08x %08x %08x\n", mem[i], mem[i+1], + mem[i+2], mem[i+3]); +} + +static size_t expected_memory_size(u32 *sp) +{ + size_t size = sizeof(expected_memory); + int offset = (uintptr_t)sp - (uintptr_t)current_stack; + if (offset > 0) + size -= offset; + return size; +} + +static void test_case_failed(const char *message) +{ + test_case_cleanup(); + + pr_err("FAIL: %s\n", message); + pr_err("FAIL: Test %s\n", current_title); + pr_err("FAIL: Scenario %d\n", test_case_run_count >> 1); +} + +static unsigned long next_instruction(unsigned long pc) +{ +#ifdef CONFIG_THUMB2_KERNEL + if ((pc & 1) && + !is_wide_instruction(__mem_to_opcode_thumb16(*(u16 *)(pc - 1)))) + return pc + 2; + else +#endif + return pc + 4; +} + +static uintptr_t __used kprobes_test_case_start(const char **title, void *stack) +{ + struct test_arg *args; + struct test_arg_end *end_arg; + unsigned long test_code; + + current_title = *title++; + args = (struct test_arg *)title; + current_args = args; + current_stack = stack; + + ++test_try_count; + + while (args->type != ARG_TYPE_END) + ++args; + end_arg = (struct test_arg_end *)args; + + test_code = (unsigned long)(args + 1); /* Code starts after args */ + + test_case_is_thumb = end_arg->flags & ARG_FLAG_THUMB; + if (test_case_is_thumb) + test_code |= 1; + + current_code_start = test_code; + + current_branch_target = 0; + if (end_arg->branch_offset != end_arg->end_offset) + current_branch_target = test_code + end_arg->branch_offset; + + test_code += end_arg->code_offset; + test_before_probe.kprobe.addr = (kprobe_opcode_t *)test_code; + + test_code = next_instruction(test_code); + test_case_probe.kprobe.addr = (kprobe_opcode_t *)test_code; + + if (test_case_is_thumb) { + u16 *p = (u16 *)(test_code & ~1); + current_instruction = __mem_to_opcode_thumb16(p[0]); + if (is_wide_instruction(current_instruction)) { + u16 instr2 = __mem_to_opcode_thumb16(p[1]); + current_instruction = __opcode_thumb32_compose(current_instruction, instr2); + } + } else { + current_instruction = __mem_to_opcode_arm(*(u32 *)test_code); + } + + if (current_title[0] == '.') + verbose("%s\n", current_title); + else + verbose("%s\t@ %0*x\n", current_title, + test_case_is_thumb ? 4 : 8, + current_instruction); + + test_code = next_instruction(test_code); + test_after_probe.kprobe.addr = (kprobe_opcode_t *)test_code; + + if (kprobe_test_flags & TEST_FLAG_NARROW_INSTR) { + if (!test_case_is_thumb || + is_wide_instruction(current_instruction)) { + test_case_failed("expected 16-bit instruction"); + goto fail; + } + } else { + if (test_case_is_thumb && + !is_wide_instruction(current_instruction)) { + test_case_failed("expected 32-bit instruction"); + goto fail; + } + } + + coverage_add(current_instruction); + + if (end_arg->flags & ARG_FLAG_UNSUPPORTED) { + if (register_test_probe(&test_case_probe) < 0) + goto pass; + test_case_failed("registered probe for unsupported instruction"); + goto fail; + } + + if (end_arg->flags & ARG_FLAG_SUPPORTED) { + if (register_test_probe(&test_case_probe) >= 0) + goto pass; + test_case_failed("couldn't register probe for supported instruction"); + goto fail; + } + + if (register_test_probe(&test_before_probe) < 0) { + test_case_failed("register test_before_probe failed"); + goto fail; + } + if (register_test_probe(&test_after_probe) < 0) { + test_case_failed("register test_after_probe failed"); + goto fail; + } + if (current_branch_target) { + test_after2_probe.kprobe.addr = + (kprobe_opcode_t *)current_branch_target; + if (register_test_probe(&test_after2_probe) < 0) { + test_case_failed("register test_after2_probe failed"); + goto fail; + } + } + + /* Start first run of test case */ + test_case_run_count = 0; + ++test_instance; + return current_code_start; +pass: + test_case_run_count = TEST_CASE_PASSED; + return (uintptr_t)test_after_probe.kprobe.addr; +fail: + test_case_run_count = TEST_CASE_FAILED; + return (uintptr_t)test_after_probe.kprobe.addr; +} + +static bool check_test_results(void) +{ + size_t mem_size = 0; + u32 *mem = 0; + + if (memcmp(&expected_regs, &result_regs, sizeof(expected_regs))) { + test_case_failed("registers differ"); + goto fail; + } + + if (memory_needs_checking) { + mem = (u32 *)result_regs.ARM_sp; + mem_size = expected_memory_size(mem); + if (memcmp(expected_memory, mem, mem_size)) { + test_case_failed("test memory differs"); + goto fail; + } + } + + return true; + +fail: + pr_err("initial_regs:\n"); + print_registers(&initial_regs); + pr_err("expected_regs:\n"); + print_registers(&expected_regs); + pr_err("result_regs:\n"); + print_registers(&result_regs); + + if (mem) { + pr_err("current_stack=%p\n", current_stack); + pr_err("expected_memory:\n"); + print_memory(expected_memory, mem_size); + pr_err("result_memory:\n"); + print_memory(mem, mem_size); + } + + return false; +} + +static uintptr_t __used kprobes_test_case_end(void) +{ + if (test_case_run_count < 0) { + if (test_case_run_count == TEST_CASE_PASSED) + /* kprobes_test_case_start did all the needed testing */ + goto pass; + else + /* kprobes_test_case_start failed */ + goto fail; + } + + if (test_before_probe.hit != test_instance) { + test_case_failed("test_before_handler not run"); + goto fail; + } + + if (test_after_probe.hit != test_instance && + test_after2_probe.hit != test_instance) { + test_case_failed("test_after_handler not run"); + goto fail; + } + + /* + * Even numbered test runs ran without a probe on the test case so + * we can gather reference results. The subsequent odd numbered run + * will have the probe inserted. + */ + if ((test_case_run_count & 1) == 0) { + /* Save results from run without probe */ + u32 *mem = (u32 *)result_regs.ARM_sp; + expected_regs = result_regs; + memcpy(expected_memory, mem, expected_memory_size(mem)); + + /* Insert probe onto test case instruction */ + if (register_test_probe(&test_case_probe) < 0) { + test_case_failed("register test_case_probe failed"); + goto fail; + } + } else { + /* Check probe ran as expected */ + if (probe_should_run == 1) { + if (test_case_probe.hit != test_instance) { + test_case_failed("test_case_handler not run"); + goto fail; + } + } else if (probe_should_run == 0) { + if (test_case_probe.hit == test_instance) { + test_case_failed("test_case_handler ran"); + goto fail; + } + } + + /* Remove probe for any subsequent reference run */ + unregister_test_probe(&test_case_probe); + + if (!check_test_results()) + goto fail; + + if (is_last_scenario) + goto pass; + } + + /* Do next test run */ + ++test_case_run_count; + ++test_instance; + return current_code_start; +fail: + ++test_fail_count; + goto end; +pass: + ++test_pass_count; +end: + test_case_cleanup(); + return 0; +} + + +/* + * Top level test functions + */ + +static int run_test_cases(void (*tests)(void), const union decode_item *table) +{ + int ret; + + pr_info(" Check decoding tables\n"); + ret = table_test(table); + if (ret) + return ret; + + pr_info(" Run test cases\n"); + ret = coverage_start(table); + if (ret) + return ret; + + tests(); + + coverage_end(); + return 0; +} + + +static int __init run_all_tests(void) +{ + int ret = 0; + + pr_info("Beginning kprobe tests...\n"); + +#ifndef CONFIG_THUMB2_KERNEL + + pr_info("Probe ARM code\n"); + ret = run_api_tests(arm_func); + if (ret) + goto out; + + pr_info("ARM instruction simulation\n"); + ret = run_test_cases(kprobe_arm_test_cases, probes_decode_arm_table); + if (ret) + goto out; + +#else /* CONFIG_THUMB2_KERNEL */ + + pr_info("Probe 16-bit Thumb code\n"); + ret = run_api_tests(thumb16_func); + if (ret) + goto out; + + pr_info("Probe 32-bit Thumb code, even halfword\n"); + ret = run_api_tests(thumb32even_func); + if (ret) + goto out; + + pr_info("Probe 32-bit Thumb code, odd halfword\n"); + ret = run_api_tests(thumb32odd_func); + if (ret) + goto out; + + pr_info("16-bit Thumb instruction simulation\n"); + ret = run_test_cases(kprobe_thumb16_test_cases, + probes_decode_thumb16_table); + if (ret) + goto out; + + pr_info("32-bit Thumb instruction simulation\n"); + ret = run_test_cases(kprobe_thumb32_test_cases, + probes_decode_thumb32_table); + if (ret) + goto out; +#endif + + pr_info("Total instruction simulation tests=%d, pass=%d fail=%d\n", + test_try_count, test_pass_count, test_fail_count); + if (test_fail_count) { + ret = -EINVAL; + goto out; + } + +#if BENCHMARKING + pr_info("Benchmarks\n"); + ret = run_benchmarks(); + if (ret) + goto out; +#endif + +#if __LINUX_ARM_ARCH__ >= 7 + /* We are able to run all test cases so coverage should be complete */ + if (coverage_fail) { + pr_err("FAIL: Test coverage checks failed\n"); + ret = -EINVAL; + goto out; + } +#endif + +out: + if (ret == 0) + ret = tests_failed; + if (ret == 0) + pr_info("Finished kprobe tests OK\n"); + else + pr_err("kprobe tests failed\n"); + + return ret; +} + + +/* + * Module setup + */ + +#ifdef MODULE + +static void __exit kprobe_test_exit(void) +{ +} + +module_init(run_all_tests) +module_exit(kprobe_test_exit) +MODULE_LICENSE("GPL"); + +#else /* !MODULE */ + +late_initcall(run_all_tests); + +#endif diff --git a/arch/arm/probes/kprobes/test-core.h b/arch/arm/probes/kprobes/test-core.h new file mode 100644 index 0000000..9991754 --- /dev/null +++ b/arch/arm/probes/kprobes/test-core.h @@ -0,0 +1,435 @@ +/* + * arch/arm/probes/kprobes/test-core.h + * + * Copyright (C) 2011 Jon Medhurst . + * + * 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. + */ + +#define VERBOSE 0 /* Set to '1' for more logging of test cases */ + +#ifdef CONFIG_THUMB2_KERNEL +#define NORMAL_ISA "16" +#else +#define NORMAL_ISA "32" +#endif + + +/* Flags used in kprobe_test_flags */ +#define TEST_FLAG_NO_ITBLOCK (1<<0) +#define TEST_FLAG_FULL_ITBLOCK (1<<1) +#define TEST_FLAG_NARROW_INSTR (1<<2) + +extern int kprobe_test_flags; +extern int kprobe_test_cc_position; + + +#define TEST_MEMORY_SIZE 256 + + +/* + * Test case structures. + * + * The arguments given to test cases can be one of three types. + * + * ARG_TYPE_REG + * Load a register with the given value. + * + * ARG_TYPE_PTR + * Load a register with a pointer into the stack buffer (SP + given value). + * + * ARG_TYPE_MEM + * Store the given value into the stack buffer at [SP+index]. + * + */ + +#define ARG_TYPE_END 0 +#define ARG_TYPE_REG 1 +#define ARG_TYPE_PTR 2 +#define ARG_TYPE_MEM 3 + +#define ARG_FLAG_UNSUPPORTED 0x01 +#define ARG_FLAG_SUPPORTED 0x02 +#define ARG_FLAG_THUMB 0x10 /* Must be 16 so TEST_ISA can be used */ +#define ARG_FLAG_ARM 0x20 /* Must be 32 so TEST_ISA can be used */ + +struct test_arg { + u8 type; /* ARG_TYPE_x */ + u8 _padding[7]; +}; + +struct test_arg_regptr { + u8 type; /* ARG_TYPE_REG or ARG_TYPE_PTR */ + u8 reg; + u8 _padding[2]; + u32 val; +}; + +struct test_arg_mem { + u8 type; /* ARG_TYPE_MEM */ + u8 index; + u8 _padding[2]; + u32 val; +}; + +struct test_arg_end { + u8 type; /* ARG_TYPE_END */ + u8 flags; /* ARG_FLAG_x */ + u16 code_offset; + u16 branch_offset; + u16 end_offset; +}; + + +/* + * Building blocks for test cases. + * + * Each test case is wrapped between TESTCASE_START and TESTCASE_END. + * + * To specify arguments for a test case the TEST_ARG_{REG,PTR,MEM} macros are + * used followed by a terminating TEST_ARG_END. + * + * After this, the instruction to be tested is defined with TEST_INSTRUCTION. + * Or for branches, TEST_BRANCH_B and TEST_BRANCH_F (branch forwards/backwards). + * + * Some specific test cases may make use of other custom constructs. + */ + +#if VERBOSE +#define verbose(fmt, ...) pr_info(fmt, ##__VA_ARGS__) +#else +#define verbose(fmt, ...) +#endif + +#define TEST_GROUP(title) \ + verbose("\n"); \ + verbose(title"\n"); \ + verbose("---------------------------------------------------------\n"); + +#define TESTCASE_START(title) \ + __asm__ __volatile__ ( \ + "bl __kprobes_test_case_start \n\t" \ + ".pushsection .rodata \n\t" \ + "10: \n\t" \ + /* don't use .asciz here as 'title' may be */ \ + /* multiple strings to be concatenated. */ \ + ".ascii "#title" \n\t" \ + ".byte 0 \n\t" \ + ".popsection \n\t" \ + ".word 10b \n\t" + +#define TEST_ARG_REG(reg, val) \ + ".byte "__stringify(ARG_TYPE_REG)" \n\t" \ + ".byte "#reg" \n\t" \ + ".short 0 \n\t" \ + ".word "#val" \n\t" + +#define TEST_ARG_PTR(reg, val) \ + ".byte "__stringify(ARG_TYPE_PTR)" \n\t" \ + ".byte "#reg" \n\t" \ + ".short 0 \n\t" \ + ".word "#val" \n\t" + +#define TEST_ARG_MEM(index, val) \ + ".byte "__stringify(ARG_TYPE_MEM)" \n\t" \ + ".byte "#index" \n\t" \ + ".short 0 \n\t" \ + ".word "#val" \n\t" + +#define TEST_ARG_END(flags) \ + ".byte "__stringify(ARG_TYPE_END)" \n\t" \ + ".byte "TEST_ISA flags" \n\t" \ + ".short 50f-0f \n\t" \ + ".short 2f-0f \n\t" \ + ".short 99f-0f \n\t" \ + ".code "TEST_ISA" \n\t" \ + "0: \n\t" + +#define TEST_INSTRUCTION(instruction) \ + "50: nop \n\t" \ + "1: "instruction" \n\t" \ + " nop \n\t" + +#define TEST_BRANCH_F(instruction) \ + TEST_INSTRUCTION(instruction) \ + " b 99f \n\t" \ + "2: nop \n\t" + +#define TEST_BRANCH_B(instruction) \ + " b 50f \n\t" \ + " b 99f \n\t" \ + "2: nop \n\t" \ + " b 99f \n\t" \ + TEST_INSTRUCTION(instruction) + +#define TEST_BRANCH_FX(instruction, codex) \ + TEST_INSTRUCTION(instruction) \ + " b 99f \n\t" \ + codex" \n\t" \ + " b 99f \n\t" \ + "2: nop \n\t" + +#define TEST_BRANCH_BX(instruction, codex) \ + " b 50f \n\t" \ + " b 99f \n\t" \ + "2: nop \n\t" \ + " b 99f \n\t" \ + codex" \n\t" \ + TEST_INSTRUCTION(instruction) + +#define TESTCASE_END \ + "2: \n\t" \ + "99: \n\t" \ + " bl __kprobes_test_case_end_"TEST_ISA" \n\t" \ + ".code "NORMAL_ISA" \n\t" \ + : : \ + : "r0", "r1", "r2", "r3", "ip", "lr", "memory", "cc" \ + ); + + +/* + * Macros to define test cases. + * + * Those of the form TEST_{R,P,M}* can be used to define test cases + * which take combinations of the three basic types of arguments. E.g. + * + * TEST_R One register argument + * TEST_RR Two register arguments + * TEST_RPR A register, a pointer, then a register argument + * + * For testing instructions which may branch, there are macros TEST_BF_* + * and TEST_BB_* for branching forwards and backwards. + * + * TEST_SUPPORTED and TEST_UNSUPPORTED don't cause the code to be executed, + * the just verify that a kprobe is or is not allowed on the given instruction. + */ + +#define TEST(code) \ + TESTCASE_START(code) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code) \ + TESTCASE_END + +#define TEST_UNSUPPORTED(code) \ + TESTCASE_START(code) \ + TEST_ARG_END("|"__stringify(ARG_FLAG_UNSUPPORTED)) \ + TEST_INSTRUCTION(code) \ + TESTCASE_END + +#define TEST_SUPPORTED(code) \ + TESTCASE_START(code) \ + TEST_ARG_END("|"__stringify(ARG_FLAG_SUPPORTED)) \ + TEST_INSTRUCTION(code) \ + TESTCASE_END + +#define TEST_R(code1, reg, val, code2) \ + TESTCASE_START(code1 #reg code2) \ + TEST_ARG_REG(reg, val) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg code2) \ + TESTCASE_END + +#define TEST_RR(code1, reg1, val1, code2, reg2, val2, code3) \ + TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ + TEST_ARG_REG(reg1, val1) \ + TEST_ARG_REG(reg2, val2) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3) \ + TESTCASE_END + +#define TEST_RRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ + TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ + TEST_ARG_REG(reg1, val1) \ + TEST_ARG_REG(reg2, val2) \ + TEST_ARG_REG(reg3, val3) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ + TESTCASE_END + +#define TEST_RRRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4, reg4, val4) \ + TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4 #reg4) \ + TEST_ARG_REG(reg1, val1) \ + TEST_ARG_REG(reg2, val2) \ + TEST_ARG_REG(reg3, val3) \ + TEST_ARG_REG(reg4, val4) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4 #reg4) \ + TESTCASE_END + +#define TEST_P(code1, reg1, val1, code2) \ + TESTCASE_START(code1 #reg1 code2) \ + TEST_ARG_PTR(reg1, val1) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2) \ + TESTCASE_END + +#define TEST_PR(code1, reg1, val1, code2, reg2, val2, code3) \ + TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ + TEST_ARG_PTR(reg1, val1) \ + TEST_ARG_REG(reg2, val2) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3) \ + TESTCASE_END + +#define TEST_RP(code1, reg1, val1, code2, reg2, val2, code3) \ + TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ + TEST_ARG_REG(reg1, val1) \ + TEST_ARG_PTR(reg2, val2) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3) \ + TESTCASE_END + +#define TEST_PRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ + TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ + TEST_ARG_PTR(reg1, val1) \ + TEST_ARG_REG(reg2, val2) \ + TEST_ARG_REG(reg3, val3) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ + TESTCASE_END + +#define TEST_RPR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ + TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ + TEST_ARG_REG(reg1, val1) \ + TEST_ARG_PTR(reg2, val2) \ + TEST_ARG_REG(reg3, val3) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ + TESTCASE_END + +#define TEST_RRP(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ + TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ + TEST_ARG_REG(reg1, val1) \ + TEST_ARG_REG(reg2, val2) \ + TEST_ARG_PTR(reg3, val3) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ + TESTCASE_END + +#define TEST_BF_P(code1, reg1, val1, code2) \ + TESTCASE_START(code1 #reg1 code2) \ + TEST_ARG_PTR(reg1, val1) \ + TEST_ARG_END("") \ + TEST_BRANCH_F(code1 #reg1 code2) \ + TESTCASE_END + +#define TEST_BF(code) \ + TESTCASE_START(code) \ + TEST_ARG_END("") \ + TEST_BRANCH_F(code) \ + TESTCASE_END + +#define TEST_BB(code) \ + TESTCASE_START(code) \ + TEST_ARG_END("") \ + TEST_BRANCH_B(code) \ + TESTCASE_END + +#define TEST_BF_R(code1, reg, val, code2) \ + TESTCASE_START(code1 #reg code2) \ + TEST_ARG_REG(reg, val) \ + TEST_ARG_END("") \ + TEST_BRANCH_F(code1 #reg code2) \ + TESTCASE_END + +#define TEST_BB_R(code1, reg, val, code2) \ + TESTCASE_START(code1 #reg code2) \ + TEST_ARG_REG(reg, val) \ + TEST_ARG_END("") \ + TEST_BRANCH_B(code1 #reg code2) \ + TESTCASE_END + +#define TEST_BF_RR(code1, reg1, val1, code2, reg2, val2, code3) \ + TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ + TEST_ARG_REG(reg1, val1) \ + TEST_ARG_REG(reg2, val2) \ + TEST_ARG_END("") \ + TEST_BRANCH_F(code1 #reg1 code2 #reg2 code3) \ + TESTCASE_END + +#define TEST_BF_X(code, codex) \ + TESTCASE_START(code) \ + TEST_ARG_END("") \ + TEST_BRANCH_FX(code, codex) \ + TESTCASE_END + +#define TEST_BB_X(code, codex) \ + TESTCASE_START(code) \ + TEST_ARG_END("") \ + TEST_BRANCH_BX(code, codex) \ + TESTCASE_END + +#define TEST_BF_RX(code1, reg, val, code2, codex) \ + TESTCASE_START(code1 #reg code2) \ + TEST_ARG_REG(reg, val) \ + TEST_ARG_END("") \ + TEST_BRANCH_FX(code1 #reg code2, codex) \ + TESTCASE_END + +#define TEST_X(code, codex) \ + TESTCASE_START(code) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code) \ + " b 99f \n\t" \ + " "codex" \n\t" \ + TESTCASE_END + +#define TEST_RX(code1, reg, val, code2, codex) \ + TESTCASE_START(code1 #reg code2) \ + TEST_ARG_REG(reg, val) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 __stringify(reg) code2) \ + " b 99f \n\t" \ + " "codex" \n\t" \ + TESTCASE_END + +#define TEST_RRX(code1, reg1, val1, code2, reg2, val2, code3, codex) \ + TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ + TEST_ARG_REG(reg1, val1) \ + TEST_ARG_REG(reg2, val2) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 __stringify(reg1) code2 __stringify(reg2) code3) \ + " b 99f \n\t" \ + " "codex" \n\t" \ + TESTCASE_END + + +/* + * Macros for defining space directives spread over multiple lines. + * These are required so the compiler guesses better the length of inline asm + * code and will spill the literal pool early enough to avoid generating PC + * relative loads with out of range offsets. + */ +#define TWICE(x) x x +#define SPACE_0x8 TWICE(".space 4\n\t") +#define SPACE_0x10 TWICE(SPACE_0x8) +#define SPACE_0x20 TWICE(SPACE_0x10) +#define SPACE_0x40 TWICE(SPACE_0x20) +#define SPACE_0x80 TWICE(SPACE_0x40) +#define SPACE_0x100 TWICE(SPACE_0x80) +#define SPACE_0x200 TWICE(SPACE_0x100) +#define SPACE_0x400 TWICE(SPACE_0x200) +#define SPACE_0x800 TWICE(SPACE_0x400) +#define SPACE_0x1000 TWICE(SPACE_0x800) + + +/* Various values used in test cases... */ +#define N(val) (val ^ 0xffffffff) +#define VAL1 0x12345678 +#define VAL2 N(VAL1) +#define VAL3 0xa5f801 +#define VAL4 N(VAL3) +#define VALM 0x456789ab +#define VALR 0xdeaddead +#define HH1 0x0123fecb +#define HH2 0xa9874567 + + +#ifdef CONFIG_THUMB2_KERNEL +void kprobe_thumb16_test_cases(void); +void kprobe_thumb32_test_cases(void); +#else +void kprobe_arm_test_cases(void); +#endif diff --git a/arch/arm/probes/kprobes/test-thumb.c b/arch/arm/probes/kprobes/test-thumb.c new file mode 100644 index 0000000..6c6e9a9 --- /dev/null +++ b/arch/arm/probes/kprobes/test-thumb.c @@ -0,0 +1,1188 @@ +/* + * arch/arm/probes/kprobes/test-thumb.c + * + * Copyright (C) 2011 Jon Medhurst . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "test-core.h" + + +#define TEST_ISA "16" + +#define DONT_TEST_IN_ITBLOCK(tests) \ + kprobe_test_flags |= TEST_FLAG_NO_ITBLOCK; \ + tests \ + kprobe_test_flags &= ~TEST_FLAG_NO_ITBLOCK; + +#define CONDITION_INSTRUCTIONS(cc_pos, tests) \ + kprobe_test_cc_position = cc_pos; \ + DONT_TEST_IN_ITBLOCK(tests) \ + kprobe_test_cc_position = 0; + +#define TEST_ITBLOCK(code) \ + kprobe_test_flags |= TEST_FLAG_FULL_ITBLOCK; \ + TESTCASE_START(code) \ + TEST_ARG_END("") \ + "50: nop \n\t" \ + "1: "code" \n\t" \ + " mov r1, #0x11 \n\t" \ + " mov r2, #0x22 \n\t" \ + " mov r3, #0x33 \n\t" \ + "2: nop \n\t" \ + TESTCASE_END \ + kprobe_test_flags &= ~TEST_FLAG_FULL_ITBLOCK; + +#define TEST_THUMB_TO_ARM_INTERWORK_P(code1, reg, val, code2) \ + TESTCASE_START(code1 #reg code2) \ + TEST_ARG_PTR(reg, val) \ + TEST_ARG_REG(14, 99f+1) \ + TEST_ARG_MEM(15, 3f) \ + TEST_ARG_END("") \ + " nop \n\t" /* To align 1f */ \ + "50: nop \n\t" \ + "1: "code1 #reg code2" \n\t" \ + " bx lr \n\t" \ + ".arm \n\t" \ + "3: adr lr, 2f+1 \n\t" \ + " bx lr \n\t" \ + ".thumb \n\t" \ + "2: nop \n\t" \ + TESTCASE_END + + +void kprobe_thumb16_test_cases(void) +{ + kprobe_test_flags = TEST_FLAG_NARROW_INSTR; + + TEST_GROUP("Shift (immediate), add, subtract, move, and compare") + + TEST_R( "lsls r7, r",0,VAL1,", #5") + TEST_R( "lsls r0, r",7,VAL2,", #11") + TEST_R( "lsrs r7, r",0,VAL1,", #5") + TEST_R( "lsrs r0, r",7,VAL2,", #11") + TEST_R( "asrs r7, r",0,VAL1,", #5") + TEST_R( "asrs r0, r",7,VAL2,", #11") + TEST_RR( "adds r2, r",0,VAL1,", r",7,VAL2,"") + TEST_RR( "adds r5, r",7,VAL2,", r",0,VAL2,"") + TEST_RR( "subs r2, r",0,VAL1,", r",7,VAL2,"") + TEST_RR( "subs r5, r",7,VAL2,", r",0,VAL2,"") + TEST_R( "adds r7, r",0,VAL1,", #5") + TEST_R( "adds r0, r",7,VAL2,", #2") + TEST_R( "subs r7, r",0,VAL1,", #5") + TEST_R( "subs r0, r",7,VAL2,", #2") + TEST( "movs.n r0, #0x5f") + TEST( "movs.n r7, #0xa0") + TEST_R( "cmp.n r",0,0x5e, ", #0x5f") + TEST_R( "cmp.n r",5,0x15f,", #0x5f") + TEST_R( "cmp.n r",7,0xa0, ", #0xa0") + TEST_R( "adds.n r",0,VAL1,", #0x5f") + TEST_R( "adds.n r",7,VAL2,", #0xa0") + TEST_R( "subs.n r",0,VAL1,", #0x5f") + TEST_R( "subs.n r",7,VAL2,", #0xa0") + + TEST_GROUP("16-bit Thumb data-processing instructions") + +#define DATA_PROCESSING16(op,val) \ + TEST_RR( op" r",0,VAL1,", r",7,val,"") \ + TEST_RR( op" r",7,VAL2,", r",0,val,"") + + DATA_PROCESSING16("ands",0xf00f00ff) + DATA_PROCESSING16("eors",0xf00f00ff) + DATA_PROCESSING16("lsls",11) + DATA_PROCESSING16("lsrs",11) + DATA_PROCESSING16("asrs",11) + DATA_PROCESSING16("adcs",VAL2) + DATA_PROCESSING16("sbcs",VAL2) + DATA_PROCESSING16("rors",11) + DATA_PROCESSING16("tst",0xf00f00ff) + TEST_R("rsbs r",0,VAL1,", #0") + TEST_R("rsbs r",7,VAL2,", #0") + DATA_PROCESSING16("cmp",0xf00f00ff) + DATA_PROCESSING16("cmn",0xf00f00ff) + DATA_PROCESSING16("orrs",0xf00f00ff) + DATA_PROCESSING16("muls",VAL2) + DATA_PROCESSING16("bics",0xf00f00ff) + DATA_PROCESSING16("mvns",VAL2) + + TEST_GROUP("Special data instructions and branch and exchange") + + TEST_RR( "add r",0, VAL1,", r",7,VAL2,"") + TEST_RR( "add r",3, VAL2,", r",8,VAL3,"") + TEST_RR( "add r",8, VAL3,", r",0,VAL1,"") + TEST_R( "add sp" ", r",8,-8, "") + TEST_R( "add r",14,VAL1,", pc") + TEST_BF_R("add pc" ", r",0,2f-1f-8,"") + TEST_UNSUPPORTED(__inst_thumb16(0x44ff) " @ add pc, pc") + + TEST_RR( "cmp r",3,VAL1,", r",8,VAL2,"") + TEST_RR( "cmp r",8,VAL2,", r",0,VAL1,"") + TEST_R( "cmp sp" ", r",8,-8, "") + + TEST_R( "mov r0, r",7,VAL2,"") + TEST_R( "mov r3, r",8,VAL3,"") + TEST_R( "mov r8, r",0,VAL1,"") + TEST_P( "mov sp, r",8,-8, "") + TEST( "mov lr, pc") + TEST_BF_R("mov pc, r",0,2f, "") + + TEST_BF_R("bx r",0, 2f+1,"") + TEST_BF_R("bx r",14,2f+1,"") + TESTCASE_START("bx pc") + TEST_ARG_REG(14, 99f+1) + TEST_ARG_END("") + " nop \n\t" /* To align the bx pc*/ + "50: nop \n\t" + "1: bx pc \n\t" + " bx lr \n\t" + ".arm \n\t" + " adr lr, 2f+1 \n\t" + " bx lr \n\t" + ".thumb \n\t" + "2: nop \n\t" + TESTCASE_END + + TEST_BF_R("blx r",0, 2f+1,"") + TEST_BB_R("blx r",14,2f+1,"") + TEST_UNSUPPORTED(__inst_thumb16(0x47f8) " @ blx pc") + + TEST_GROUP("Load from Literal Pool") + + TEST_X( "ldr r0, 3f", + ".align \n\t" + "3: .word "__stringify(VAL1)) + TEST_X( "ldr r7, 3f", + ".space 128 \n\t" + ".align \n\t" + "3: .word "__stringify(VAL2)) + + TEST_GROUP("16-bit Thumb Load/store instructions") + + TEST_RPR("str r",0, VAL1,", [r",1, 24,", r",2, 48,"]") + TEST_RPR("str r",7, VAL2,", [r",6, 24,", r",5, 48,"]") + TEST_RPR("strh r",0, VAL1,", [r",1, 24,", r",2, 48,"]") + TEST_RPR("strh r",7, VAL2,", [r",6, 24,", r",5, 48,"]") + TEST_RPR("strb r",0, VAL1,", [r",1, 24,", r",2, 48,"]") + TEST_RPR("strb r",7, VAL2,", [r",6, 24,", r",5, 48,"]") + TEST_PR( "ldrsb r0, [r",1, 24,", r",2, 48,"]") + TEST_PR( "ldrsb r7, [r",6, 24,", r",5, 50,"]") + TEST_PR( "ldr r0, [r",1, 24,", r",2, 48,"]") + TEST_PR( "ldr r7, [r",6, 24,", r",5, 48,"]") + TEST_PR( "ldrh r0, [r",1, 24,", r",2, 48,"]") + TEST_PR( "ldrh r7, [r",6, 24,", r",5, 50,"]") + TEST_PR( "ldrb r0, [r",1, 24,", r",2, 48,"]") + TEST_PR( "ldrb r7, [r",6, 24,", r",5, 50,"]") + TEST_PR( "ldrsh r0, [r",1, 24,", r",2, 48,"]") + TEST_PR( "ldrsh r7, [r",6, 24,", r",5, 50,"]") + + TEST_RP("str r",0, VAL1,", [r",1, 24,", #120]") + TEST_RP("str r",7, VAL2,", [r",6, 24,", #120]") + TEST_P( "ldr r0, [r",1, 24,", #120]") + TEST_P( "ldr r7, [r",6, 24,", #120]") + TEST_RP("strb r",0, VAL1,", [r",1, 24,", #30]") + TEST_RP("strb r",7, VAL2,", [r",6, 24,", #30]") + TEST_P( "ldrb r0, [r",1, 24,", #30]") + TEST_P( "ldrb r7, [r",6, 24,", #30]") + TEST_RP("strh r",0, VAL1,", [r",1, 24,", #60]") + TEST_RP("strh r",7, VAL2,", [r",6, 24,", #60]") + TEST_P( "ldrh r0, [r",1, 24,", #60]") + TEST_P( "ldrh r7, [r",6, 24,", #60]") + + TEST_R( "str r",0, VAL1,", [sp, #0]") + TEST_R( "str r",7, VAL2,", [sp, #160]") + TEST( "ldr r0, [sp, #0]") + TEST( "ldr r7, [sp, #160]") + + TEST_RP("str r",0, VAL1,", [r",0, 24,"]") + TEST_P( "ldr r0, [r",0, 24,"]") + + TEST_GROUP("Generate PC-/SP-relative address") + + TEST("add r0, pc, #4") + TEST("add r7, pc, #1020") + TEST("add r0, sp, #4") + TEST("add r7, sp, #1020") + + TEST_GROUP("Miscellaneous 16-bit instructions") + + TEST_UNSUPPORTED( "cpsie i") + TEST_UNSUPPORTED( "cpsid i") + TEST_UNSUPPORTED( "setend le") + TEST_UNSUPPORTED( "setend be") + + TEST("add sp, #"__stringify(TEST_MEMORY_SIZE)) /* Assumes TEST_MEMORY_SIZE < 0x400 */ + TEST("sub sp, #0x7f*4") + +DONT_TEST_IN_ITBLOCK( + TEST_BF_R( "cbnz r",0,0, ", 2f") + TEST_BF_R( "cbz r",2,-1,", 2f") + TEST_BF_RX( "cbnz r",4,1, ", 2f", SPACE_0x20) + TEST_BF_RX( "cbz r",7,0, ", 2f", SPACE_0x40) +) + TEST_R("sxth r0, r",7, HH1,"") + TEST_R("sxth r7, r",0, HH2,"") + TEST_R("sxtb r0, r",7, HH1,"") + TEST_R("sxtb r7, r",0, HH2,"") + TEST_R("uxth r0, r",7, HH1,"") + TEST_R("uxth r7, r",0, HH2,"") + TEST_R("uxtb r0, r",7, HH1,"") + TEST_R("uxtb r7, r",0, HH2,"") + TEST_R("rev r0, r",7, VAL1,"") + TEST_R("rev r7, r",0, VAL2,"") + TEST_R("rev16 r0, r",7, VAL1,"") + TEST_R("rev16 r7, r",0, VAL2,"") + TEST_UNSUPPORTED(__inst_thumb16(0xba80) "") + TEST_UNSUPPORTED(__inst_thumb16(0xbabf) "") + TEST_R("revsh r0, r",7, VAL1,"") + TEST_R("revsh r7, r",0, VAL2,"") + +#define TEST_POPPC(code, offset) \ + TESTCASE_START(code) \ + TEST_ARG_PTR(13, offset) \ + TEST_ARG_END("") \ + TEST_BRANCH_F(code) \ + TESTCASE_END + + TEST("push {r0}") + TEST("push {r7}") + TEST("push {r14}") + TEST("push {r0-r7,r14}") + TEST("push {r0,r2,r4,r6,r14}") + TEST("push {r1,r3,r5,r7}") + TEST("pop {r0}") + TEST("pop {r7}") + TEST("pop {r0,r2,r4,r6}") + TEST_POPPC("pop {pc}",15*4) + TEST_POPPC("pop {r0-r7,pc}",7*4) + TEST_POPPC("pop {r1,r3,r5,r7,pc}",11*4) + TEST_THUMB_TO_ARM_INTERWORK_P("pop {pc} @ ",13,15*4,"") + TEST_THUMB_TO_ARM_INTERWORK_P("pop {r0-r7,pc} @ ",13,7*4,"") + + TEST_UNSUPPORTED("bkpt.n 0") + TEST_UNSUPPORTED("bkpt.n 255") + + TEST_SUPPORTED("yield") + TEST("sev") + TEST("nop") + TEST("wfi") + TEST_SUPPORTED("wfe") + TEST_UNSUPPORTED(__inst_thumb16(0xbf50) "") /* Unassigned hints */ + TEST_UNSUPPORTED(__inst_thumb16(0xbff0) "") /* Unassigned hints */ + +#define TEST_IT(code, code2) \ + TESTCASE_START(code) \ + TEST_ARG_END("") \ + "50: nop \n\t" \ + "1: "code" \n\t" \ + " "code2" \n\t" \ + "2: nop \n\t" \ + TESTCASE_END + +DONT_TEST_IN_ITBLOCK( + TEST_IT("it eq","moveq r0,#0") + TEST_IT("it vc","movvc r0,#0") + TEST_IT("it le","movle r0,#0") + TEST_IT("ite eq","moveq r0,#0\n\t movne r1,#1") + TEST_IT("itet vc","movvc r0,#0\n\t movvs r1,#1\n\t movvc r2,#2") + TEST_IT("itete le","movle r0,#0\n\t movgt r1,#1\n\t movle r2,#2\n\t movgt r3,#3") + TEST_IT("itttt le","movle r0,#0\n\t movle r1,#1\n\t movle r2,#2\n\t movle r3,#3") + TEST_IT("iteee le","movle r0,#0\n\t movgt r1,#1\n\t movgt r2,#2\n\t movgt r3,#3") +) + + TEST_GROUP("Load and store multiple") + + TEST_P("ldmia r",4, 16*4,"!, {r0,r7}") + TEST_P("ldmia r",7, 16*4,"!, {r0-r6}") + TEST_P("stmia r",4, 16*4,"!, {r0,r7}") + TEST_P("stmia r",0, 16*4,"!, {r0-r7}") + + TEST_GROUP("Conditional branch and Supervisor Call instructions") + +CONDITION_INSTRUCTIONS(8, + TEST_BF("beq 2f") + TEST_BB("bne 2b") + TEST_BF("bgt 2f") + TEST_BB("blt 2b") +) + TEST_UNSUPPORTED(__inst_thumb16(0xde00) "") + TEST_UNSUPPORTED(__inst_thumb16(0xdeff) "") + TEST_UNSUPPORTED("svc #0x00") + TEST_UNSUPPORTED("svc #0xff") + + TEST_GROUP("Unconditional branch") + + TEST_BF( "b 2f") + TEST_BB( "b 2b") + TEST_BF_X("b 2f", SPACE_0x400) + TEST_BB_X("b 2b", SPACE_0x400) + + TEST_GROUP("Testing instructions in IT blocks") + + TEST_ITBLOCK("subs.n r0, r0") + + verbose("\n"); +} + + +void kprobe_thumb32_test_cases(void) +{ + kprobe_test_flags = 0; + + TEST_GROUP("Load/store multiple") + + TEST_UNSUPPORTED("rfedb sp") + TEST_UNSUPPORTED("rfeia sp") + TEST_UNSUPPORTED("rfedb sp!") + TEST_UNSUPPORTED("rfeia sp!") + + TEST_P( "stmia r",0, 16*4,", {r0,r8}") + TEST_P( "stmia r",4, 16*4,", {r0-r12,r14}") + TEST_P( "stmia r",7, 16*4,"!, {r8-r12,r14}") + TEST_P( "stmia r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + + TEST_P( "ldmia r",0, 16*4,", {r0,r8}") + TEST_P( "ldmia r",4, 0, ", {r0-r12,r14}") + TEST_BF_P("ldmia r",5, 8*4, "!, {r6-r12,r15}") + TEST_P( "ldmia r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_BF_P("ldmia r",14,14*4,"!, {r4,pc}") + + TEST_P( "stmdb r",0, 16*4,", {r0,r8}") + TEST_P( "stmdb r",4, 16*4,", {r0-r12,r14}") + TEST_P( "stmdb r",5, 16*4,"!, {r8-r12,r14}") + TEST_P( "stmdb r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + + TEST_P( "ldmdb r",0, 16*4,", {r0,r8}") + TEST_P( "ldmdb r",4, 16*4,", {r0-r12,r14}") + TEST_BF_P("ldmdb r",5, 16*4,"!, {r6-r12,r15}") + TEST_P( "ldmdb r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_BF_P("ldmdb r",14,16*4,"!, {r4,pc}") + + TEST_P( "stmdb r",13,16*4,"!, {r3-r12,lr}") + TEST_P( "stmdb r",13,16*4,"!, {r3-r12}") + TEST_P( "stmdb r",2, 16*4,", {r3-r12,lr}") + TEST_P( "stmdb r",13,16*4,"!, {r2-r12,lr}") + TEST_P( "stmdb r",0, 16*4,", {r0-r12}") + TEST_P( "stmdb r",0, 16*4,", {r0-r12,lr}") + + TEST_BF_P("ldmia r",13,5*4, "!, {r3-r12,pc}") + TEST_P( "ldmia r",13,5*4, "!, {r3-r12}") + TEST_BF_P("ldmia r",2, 5*4, "!, {r3-r12,pc}") + TEST_BF_P("ldmia r",13,4*4, "!, {r2-r12,pc}") + TEST_P( "ldmia r",0, 16*4,", {r0-r12}") + TEST_P( "ldmia r",0, 16*4,", {r0-r12,lr}") + + TEST_THUMB_TO_ARM_INTERWORK_P("ldmia r",0,14*4,", {r12,pc}") + TEST_THUMB_TO_ARM_INTERWORK_P("ldmia r",13,2*4,", {r0-r12,pc}") + + TEST_UNSUPPORTED(__inst_thumb32(0xe88f0101) " @ stmia pc, {r0,r8}") + TEST_UNSUPPORTED(__inst_thumb32(0xe92f5f00) " @ stmdb pc!, {r8-r12,r14}") + TEST_UNSUPPORTED(__inst_thumb32(0xe8bdc000) " @ ldmia r13!, {r14,pc}") + TEST_UNSUPPORTED(__inst_thumb32(0xe93ec000) " @ ldmdb r14!, {r14,pc}") + TEST_UNSUPPORTED(__inst_thumb32(0xe8a73f00) " @ stmia r7!, {r8-r12,sp}") + TEST_UNSUPPORTED(__inst_thumb32(0xe8a79f00) " @ stmia r7!, {r8-r12,pc}") + TEST_UNSUPPORTED(__inst_thumb32(0xe93e2010) " @ ldmdb r14!, {r4,sp}") + + TEST_GROUP("Load/store double or exclusive, table branch") + + TEST_P( "ldrd r0, r1, [r",1, 24,", #-16]") + TEST( "ldrd r12, r14, [sp, #16]") + TEST_P( "ldrd r1, r0, [r",7, 24,", #-16]!") + TEST( "ldrd r14, r12, [sp, #16]!") + TEST_P( "ldrd r1, r0, [r",7, 24,"], #16") + TEST( "ldrd r7, r8, [sp], #-16") + + TEST_X( "ldrd r12, r14, 3f", + ".align 3 \n\t" + "3: .word "__stringify(VAL1)" \n\t" + " .word "__stringify(VAL2)) + + TEST_UNSUPPORTED(__inst_thumb32(0xe9ffec04) " @ ldrd r14, r12, [pc, #16]!") + TEST_UNSUPPORTED(__inst_thumb32(0xe8ffec04) " @ ldrd r14, r12, [pc], #16") + TEST_UNSUPPORTED(__inst_thumb32(0xe9d4d800) " @ ldrd sp, r8, [r4]") + TEST_UNSUPPORTED(__inst_thumb32(0xe9d4f800) " @ ldrd pc, r8, [r4]") + TEST_UNSUPPORTED(__inst_thumb32(0xe9d47d00) " @ ldrd r7, sp, [r4]") + TEST_UNSUPPORTED(__inst_thumb32(0xe9d47f00) " @ ldrd r7, pc, [r4]") + + TEST_RRP("strd r",0, VAL1,", r",1, VAL2,", [r",1, 24,", #-16]") + TEST_RR( "strd r",12,VAL2,", r",14,VAL1,", [sp, #16]") + TEST_RRP("strd r",1, VAL1,", r",0, VAL2,", [r",7, 24,", #-16]!") + TEST_RR( "strd r",14,VAL2,", r",12,VAL1,", [sp, #16]!") + TEST_RRP("strd r",1, VAL1,", r",0, VAL2,", [r",7, 24,"], #16") + TEST_RR( "strd r",7, VAL2,", r",8, VAL1,", [sp], #-16") + TEST_UNSUPPORTED(__inst_thumb32(0xe9efec04) " @ strd r14, r12, [pc, #16]!") + TEST_UNSUPPORTED(__inst_thumb32(0xe8efec04) " @ strd r14, r12, [pc], #16") + + TEST_RX("tbb [pc, r",0, (9f-(1f+4)),"]", + "9: \n\t" + ".byte (2f-1b-4)>>1 \n\t" + ".byte (3f-1b-4)>>1 \n\t" + "3: mvn r0, r0 \n\t" + "2: nop \n\t") + + TEST_RX("tbb [pc, r",4, (9f-(1f+4)+1),"]", + "9: \n\t" + ".byte (2f-1b-4)>>1 \n\t" + ".byte (3f-1b-4)>>1 \n\t" + "3: mvn r0, r0 \n\t" + "2: nop \n\t") + + TEST_RRX("tbb [r",1,9f,", r",2,0,"]", + "9: \n\t" + ".byte (2f-1b-4)>>1 \n\t" + ".byte (3f-1b-4)>>1 \n\t" + "3: mvn r0, r0 \n\t" + "2: nop \n\t") + + TEST_RX("tbh [pc, r",7, (9f-(1f+4))>>1,"]", + "9: \n\t" + ".short (2f-1b-4)>>1 \n\t" + ".short (3f-1b-4)>>1 \n\t" + "3: mvn r0, r0 \n\t" + "2: nop \n\t") + + TEST_RX("tbh [pc, r",12, ((9f-(1f+4))>>1)+1,"]", + "9: \n\t" + ".short (2f-1b-4)>>1 \n\t" + ".short (3f-1b-4)>>1 \n\t" + "3: mvn r0, r0 \n\t" + "2: nop \n\t") + + TEST_RRX("tbh [r",1,9f, ", r",14,1,"]", + "9: \n\t" + ".short (2f-1b-4)>>1 \n\t" + ".short (3f-1b-4)>>1 \n\t" + "3: mvn r0, r0 \n\t" + "2: nop \n\t") + + TEST_UNSUPPORTED(__inst_thumb32(0xe8d1f01f) " @ tbh [r1, pc]") + TEST_UNSUPPORTED(__inst_thumb32(0xe8d1f01d) " @ tbh [r1, sp]") + TEST_UNSUPPORTED(__inst_thumb32(0xe8ddf012) " @ tbh [sp, r2]") + + TEST_UNSUPPORTED("strexb r0, r1, [r2]") + TEST_UNSUPPORTED("strexh r0, r1, [r2]") + TEST_UNSUPPORTED("strexd r0, r1, [r2]") + TEST_UNSUPPORTED("ldrexb r0, [r1]") + TEST_UNSUPPORTED("ldrexh r0, [r1]") + TEST_UNSUPPORTED("ldrexd r0, [r1]") + + TEST_GROUP("Data-processing (shifted register) and (modified immediate)") + +#define _DATA_PROCESSING32_DNM(op,s,val) \ + TEST_RR(op s".w r0, r",1, VAL1,", r",2, val, "") \ + TEST_RR(op s" r1, r",1, VAL1,", r",2, val, ", lsl #3") \ + TEST_RR(op s" r2, r",3, VAL1,", r",2, val, ", lsr #4") \ + TEST_RR(op s" r3, r",3, VAL1,", r",2, val, ", asr #5") \ + TEST_RR(op s" r4, r",5, VAL1,", r",2, N(val),", asr #6") \ + TEST_RR(op s" r5, r",5, VAL1,", r",2, val, ", ror #7") \ + TEST_RR(op s" r8, r",9, VAL1,", r",10,val, ", rrx") \ + TEST_R( op s" r0, r",11,VAL1,", #0x00010001") \ + TEST_R( op s" r11, r",0, VAL1,", #0xf5000000") \ + TEST_R( op s" r7, r",8, VAL2,", #0x000af000") + +#define DATA_PROCESSING32_DNM(op,val) \ + _DATA_PROCESSING32_DNM(op,"",val) \ + _DATA_PROCESSING32_DNM(op,"s",val) + +#define DATA_PROCESSING32_NM(op,val) \ + TEST_RR(op".w r",1, VAL1,", r",2, val, "") \ + TEST_RR(op" r",1, VAL1,", r",2, val, ", lsl #3") \ + TEST_RR(op" r",3, VAL1,", r",2, val, ", lsr #4") \ + TEST_RR(op" r",3, VAL1,", r",2, val, ", asr #5") \ + TEST_RR(op" r",5, VAL1,", r",2, N(val),", asr #6") \ + TEST_RR(op" r",5, VAL1,", r",2, val, ", ror #7") \ + TEST_RR(op" r",9, VAL1,", r",10,val, ", rrx") \ + TEST_R( op" r",11,VAL1,", #0x00010001") \ + TEST_R( op" r",0, VAL1,", #0xf5000000") \ + TEST_R( op" r",8, VAL2,", #0x000af000") + +#define _DATA_PROCESSING32_DM(op,s,val) \ + TEST_R( op s".w r0, r",14, val, "") \ + TEST_R( op s" r1, r",12, val, ", lsl #3") \ + TEST_R( op s" r2, r",11, val, ", lsr #4") \ + TEST_R( op s" r3, r",10, val, ", asr #5") \ + TEST_R( op s" r4, r",9, N(val),", asr #6") \ + TEST_R( op s" r5, r",8, val, ", ror #7") \ + TEST_R( op s" r8, r",7,val, ", rrx") \ + TEST( op s" r0, #0x00010001") \ + TEST( op s" r11, #0xf5000000") \ + TEST( op s" r7, #0x000af000") \ + TEST( op s" r4, #0x00005a00") + +#define DATA_PROCESSING32_DM(op,val) \ + _DATA_PROCESSING32_DM(op,"",val) \ + _DATA_PROCESSING32_DM(op,"s",val) + + DATA_PROCESSING32_DNM("and",0xf00f00ff) + DATA_PROCESSING32_NM("tst",0xf00f00ff) + DATA_PROCESSING32_DNM("bic",0xf00f00ff) + DATA_PROCESSING32_DNM("orr",0xf00f00ff) + DATA_PROCESSING32_DM("mov",VAL2) + DATA_PROCESSING32_DNM("orn",0xf00f00ff) + DATA_PROCESSING32_DM("mvn",VAL2) + DATA_PROCESSING32_DNM("eor",0xf00f00ff) + DATA_PROCESSING32_NM("teq",0xf00f00ff) + DATA_PROCESSING32_DNM("add",VAL2) + DATA_PROCESSING32_NM("cmn",VAL2) + DATA_PROCESSING32_DNM("adc",VAL2) + DATA_PROCESSING32_DNM("sbc",VAL2) + DATA_PROCESSING32_DNM("sub",VAL2) + DATA_PROCESSING32_NM("cmp",VAL2) + DATA_PROCESSING32_DNM("rsb",VAL2) + + TEST_RR("pkhbt r0, r",0, HH1,", r",1, HH2,"") + TEST_RR("pkhbt r14,r",12, HH1,", r",10,HH2,", lsl #2") + TEST_RR("pkhtb r0, r",0, HH1,", r",1, HH2,"") + TEST_RR("pkhtb r14,r",12, HH1,", r",10,HH2,", asr #2") + + TEST_UNSUPPORTED(__inst_thumb32(0xea170f0d) " @ tst.w r7, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xea170f0f) " @ tst.w r7, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xea1d0f07) " @ tst.w sp, r7") + TEST_UNSUPPORTED(__inst_thumb32(0xea1f0f07) " @ tst.w pc, r7") + TEST_UNSUPPORTED(__inst_thumb32(0xf01d1f08) " @ tst sp, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf01f1f08) " @ tst pc, #0x00080008") + + TEST_UNSUPPORTED(__inst_thumb32(0xea970f0d) " @ teq.w r7, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xea970f0f) " @ teq.w r7, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xea9d0f07) " @ teq.w sp, r7") + TEST_UNSUPPORTED(__inst_thumb32(0xea9f0f07) " @ teq.w pc, r7") + TEST_UNSUPPORTED(__inst_thumb32(0xf09d1f08) " @ tst sp, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf09f1f08) " @ tst pc, #0x00080008") + + TEST_UNSUPPORTED(__inst_thumb32(0xeb170f0d) " @ cmn.w r7, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xeb170f0f) " @ cmn.w r7, pc") + TEST_P("cmn.w sp, r",7,0,"") + TEST_UNSUPPORTED(__inst_thumb32(0xeb1f0f07) " @ cmn.w pc, r7") + TEST( "cmn sp, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf11f1f08) " @ cmn pc, #0x00080008") + + TEST_UNSUPPORTED(__inst_thumb32(0xebb70f0d) " @ cmp.w r7, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xebb70f0f) " @ cmp.w r7, pc") + TEST_P("cmp.w sp, r",7,0,"") + TEST_UNSUPPORTED(__inst_thumb32(0xebbf0f07) " @ cmp.w pc, r7") + TEST( "cmp sp, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf1bf1f08) " @ cmp pc, #0x00080008") + + TEST_UNSUPPORTED(__inst_thumb32(0xea5f070d) " @ movs.w r7, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xea5f070f) " @ movs.w r7, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xea5f0d07) " @ movs.w sp, r7") + TEST_UNSUPPORTED(__inst_thumb32(0xea4f0f07) " @ mov.w pc, r7") + TEST_UNSUPPORTED(__inst_thumb32(0xf04f1d08) " @ mov sp, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf04f1f08) " @ mov pc, #0x00080008") + + TEST_R("add.w r0, sp, r",1, 4,"") + TEST_R("adds r0, sp, r",1, 4,", asl #3") + TEST_R("add r0, sp, r",1, 4,", asl #4") + TEST_R("add r0, sp, r",1, 16,", ror #1") + TEST_R("add.w sp, sp, r",1, 4,"") + TEST_R("add sp, sp, r",1, 4,", asl #3") + TEST_UNSUPPORTED(__inst_thumb32(0xeb0d1d01) " @ add sp, sp, r1, asl #4") + TEST_UNSUPPORTED(__inst_thumb32(0xeb0d0d71) " @ add sp, sp, r1, ror #1") + TEST( "add.w r0, sp, #24") + TEST( "add.w sp, sp, #24") + TEST_UNSUPPORTED(__inst_thumb32(0xeb0d0f01) " @ add pc, sp, r1") + TEST_UNSUPPORTED(__inst_thumb32(0xeb0d000f) " @ add r0, sp, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xeb0d000d) " @ add r0, sp, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xeb0d0d0f) " @ add sp, sp, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xeb0d0d0d) " @ add sp, sp, sp") + + TEST_R("sub.w r0, sp, r",1, 4,"") + TEST_R("subs r0, sp, r",1, 4,", asl #3") + TEST_R("sub r0, sp, r",1, 4,", asl #4") + TEST_R("sub r0, sp, r",1, 16,", ror #1") + TEST_R("sub.w sp, sp, r",1, 4,"") + TEST_R("sub sp, sp, r",1, 4,", asl #3") + TEST_UNSUPPORTED(__inst_thumb32(0xebad1d01) " @ sub sp, sp, r1, asl #4") + TEST_UNSUPPORTED(__inst_thumb32(0xebad0d71) " @ sub sp, sp, r1, ror #1") + TEST_UNSUPPORTED(__inst_thumb32(0xebad0f01) " @ sub pc, sp, r1") + TEST( "sub.w r0, sp, #24") + TEST( "sub.w sp, sp, #24") + + TEST_UNSUPPORTED(__inst_thumb32(0xea02010f) " @ and r1, r2, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xea0f0103) " @ and r1, pc, r3") + TEST_UNSUPPORTED(__inst_thumb32(0xea020f03) " @ and pc, r2, r3") + TEST_UNSUPPORTED(__inst_thumb32(0xea02010d) " @ and r1, r2, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xea0d0103) " @ and r1, sp, r3") + TEST_UNSUPPORTED(__inst_thumb32(0xea020d03) " @ and sp, r2, r3") + TEST_UNSUPPORTED(__inst_thumb32(0xf00d1108) " @ and r1, sp, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf00f1108) " @ and r1, pc, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf0021d08) " @ and sp, r8, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf0021f08) " @ and pc, r8, #0x00080008") + + TEST_UNSUPPORTED(__inst_thumb32(0xeb02010f) " @ add r1, r2, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xeb0f0103) " @ add r1, pc, r3") + TEST_UNSUPPORTED(__inst_thumb32(0xeb020f03) " @ add pc, r2, r3") + TEST_UNSUPPORTED(__inst_thumb32(0xeb02010d) " @ add r1, r2, sp") + TEST_SUPPORTED( __inst_thumb32(0xeb0d0103) " @ add r1, sp, r3") + TEST_UNSUPPORTED(__inst_thumb32(0xeb020d03) " @ add sp, r2, r3") + TEST_SUPPORTED( __inst_thumb32(0xf10d1108) " @ add r1, sp, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf10d1f08) " @ add pc, sp, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf10f1108) " @ add r1, pc, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf1021d08) " @ add sp, r8, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf1021f08) " @ add pc, r8, #0x00080008") + + TEST_UNSUPPORTED(__inst_thumb32(0xeaa00000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xeaf00000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xeb200000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xeb800000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xebe00000) "") + + TEST_UNSUPPORTED(__inst_thumb32(0xf0a00000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xf0c00000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xf0f00000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xf1200000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xf1800000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xf1e00000) "") + + TEST_GROUP("Coprocessor instructions") + + TEST_UNSUPPORTED(__inst_thumb32(0xec000000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xeff00000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xfc000000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xfff00000) "") + + TEST_GROUP("Data-processing (plain binary immediate)") + + TEST_R("addw r0, r",1, VAL1,", #0x123") + TEST( "addw r14, sp, #0xf5a") + TEST( "addw sp, sp, #0x20") + TEST( "addw r7, pc, #0x888") + TEST_UNSUPPORTED(__inst_thumb32(0xf20f1f20) " @ addw pc, pc, #0x120") + TEST_UNSUPPORTED(__inst_thumb32(0xf20d1f20) " @ addw pc, sp, #0x120") + TEST_UNSUPPORTED(__inst_thumb32(0xf20f1d20) " @ addw sp, pc, #0x120") + TEST_UNSUPPORTED(__inst_thumb32(0xf2001d20) " @ addw sp, r0, #0x120") + + TEST_R("subw r0, r",1, VAL1,", #0x123") + TEST( "subw r14, sp, #0xf5a") + TEST( "subw sp, sp, #0x20") + TEST( "subw r7, pc, #0x888") + TEST_UNSUPPORTED(__inst_thumb32(0xf2af1f20) " @ subw pc, pc, #0x120") + TEST_UNSUPPORTED(__inst_thumb32(0xf2ad1f20) " @ subw pc, sp, #0x120") + TEST_UNSUPPORTED(__inst_thumb32(0xf2af1d20) " @ subw sp, pc, #0x120") + TEST_UNSUPPORTED(__inst_thumb32(0xf2a01d20) " @ subw sp, r0, #0x120") + + TEST("movw r0, #0") + TEST("movw r0, #0xffff") + TEST("movw lr, #0xffff") + TEST_UNSUPPORTED(__inst_thumb32(0xf2400d00) " @ movw sp, #0") + TEST_UNSUPPORTED(__inst_thumb32(0xf2400f00) " @ movw pc, #0") + + TEST_R("movt r",0, VAL1,", #0") + TEST_R("movt r",0, VAL2,", #0xffff") + TEST_R("movt r",14,VAL1,", #0xffff") + TEST_UNSUPPORTED(__inst_thumb32(0xf2c00d00) " @ movt sp, #0") + TEST_UNSUPPORTED(__inst_thumb32(0xf2c00f00) " @ movt pc, #0") + + TEST_R( "ssat r0, #24, r",0, VAL1,"") + TEST_R( "ssat r14, #24, r",12, VAL2,"") + TEST_R( "ssat r0, #24, r",0, VAL1,", lsl #8") + TEST_R( "ssat r14, #24, r",12, VAL2,", asr #8") + TEST_UNSUPPORTED(__inst_thumb32(0xf30c0d17) " @ ssat sp, #24, r12") + TEST_UNSUPPORTED(__inst_thumb32(0xf30c0f17) " @ ssat pc, #24, r12") + TEST_UNSUPPORTED(__inst_thumb32(0xf30d0c17) " @ ssat r12, #24, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xf30f0c17) " @ ssat r12, #24, pc") + + TEST_R( "usat r0, #24, r",0, VAL1,"") + TEST_R( "usat r14, #24, r",12, VAL2,"") + TEST_R( "usat r0, #24, r",0, VAL1,", lsl #8") + TEST_R( "usat r14, #24, r",12, VAL2,", asr #8") + TEST_UNSUPPORTED(__inst_thumb32(0xf38c0d17) " @ usat sp, #24, r12") + TEST_UNSUPPORTED(__inst_thumb32(0xf38c0f17) " @ usat pc, #24, r12") + TEST_UNSUPPORTED(__inst_thumb32(0xf38d0c17) " @ usat r12, #24, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xf38f0c17) " @ usat r12, #24, pc") + + TEST_R( "ssat16 r0, #12, r",0, HH1,"") + TEST_R( "ssat16 r14, #12, r",12, HH2,"") + TEST_UNSUPPORTED(__inst_thumb32(0xf32c0d0b) " @ ssat16 sp, #12, r12") + TEST_UNSUPPORTED(__inst_thumb32(0xf32c0f0b) " @ ssat16 pc, #12, r12") + TEST_UNSUPPORTED(__inst_thumb32(0xf32d0c0b) " @ ssat16 r12, #12, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xf32f0c0b) " @ ssat16 r12, #12, pc") + + TEST_R( "usat16 r0, #12, r",0, HH1,"") + TEST_R( "usat16 r14, #12, r",12, HH2,"") + TEST_UNSUPPORTED(__inst_thumb32(0xf3ac0d0b) " @ usat16 sp, #12, r12") + TEST_UNSUPPORTED(__inst_thumb32(0xf3ac0f0b) " @ usat16 pc, #12, r12") + TEST_UNSUPPORTED(__inst_thumb32(0xf3ad0c0b) " @ usat16 r12, #12, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xf3af0c0b) " @ usat16 r12, #12, pc") + + TEST_R( "sbfx r0, r",0 , VAL1,", #0, #31") + TEST_R( "sbfx r14, r",12, VAL2,", #8, #16") + TEST_R( "sbfx r4, r",10, VAL1,", #16, #15") + TEST_UNSUPPORTED(__inst_thumb32(0xf34c2d0f) " @ sbfx sp, r12, #8, #16") + TEST_UNSUPPORTED(__inst_thumb32(0xf34c2f0f) " @ sbfx pc, r12, #8, #16") + TEST_UNSUPPORTED(__inst_thumb32(0xf34d2c0f) " @ sbfx r12, sp, #8, #16") + TEST_UNSUPPORTED(__inst_thumb32(0xf34f2c0f) " @ sbfx r12, pc, #8, #16") + + TEST_R( "ubfx r0, r",0 , VAL1,", #0, #31") + TEST_R( "ubfx r14, r",12, VAL2,", #8, #16") + TEST_R( "ubfx r4, r",10, VAL1,", #16, #15") + TEST_UNSUPPORTED(__inst_thumb32(0xf3cc2d0f) " @ ubfx sp, r12, #8, #16") + TEST_UNSUPPORTED(__inst_thumb32(0xf3cc2f0f) " @ ubfx pc, r12, #8, #16") + TEST_UNSUPPORTED(__inst_thumb32(0xf3cd2c0f) " @ ubfx r12, sp, #8, #16") + TEST_UNSUPPORTED(__inst_thumb32(0xf3cf2c0f) " @ ubfx r12, pc, #8, #16") + + TEST_R( "bfc r",0, VAL1,", #4, #20") + TEST_R( "bfc r",14,VAL2,", #4, #20") + TEST_R( "bfc r",7, VAL1,", #0, #31") + TEST_R( "bfc r",8, VAL2,", #0, #31") + TEST_UNSUPPORTED(__inst_thumb32(0xf36f0d1e) " @ bfc sp, #0, #31") + TEST_UNSUPPORTED(__inst_thumb32(0xf36f0f1e) " @ bfc pc, #0, #31") + + TEST_RR( "bfi r",0, VAL1,", r",0 , VAL2,", #0, #31") + TEST_RR( "bfi r",12,VAL1,", r",14 , VAL2,", #4, #20") + TEST_UNSUPPORTED(__inst_thumb32(0xf36e1d17) " @ bfi sp, r14, #4, #20") + TEST_UNSUPPORTED(__inst_thumb32(0xf36e1f17) " @ bfi pc, r14, #4, #20") + TEST_UNSUPPORTED(__inst_thumb32(0xf36d1e17) " @ bfi r14, sp, #4, #20") + + TEST_GROUP("Branches and miscellaneous control") + +CONDITION_INSTRUCTIONS(22, + TEST_BF("beq.w 2f") + TEST_BB("bne.w 2b") + TEST_BF("bgt.w 2f") + TEST_BB("blt.w 2b") + TEST_BF_X("bpl.w 2f", SPACE_0x1000) +) + + TEST_UNSUPPORTED("msr cpsr, r0") + TEST_UNSUPPORTED("msr cpsr_f, r1") + TEST_UNSUPPORTED("msr spsr, r2") + + TEST_UNSUPPORTED("cpsie.w i") + TEST_UNSUPPORTED("cpsid.w i") + TEST_UNSUPPORTED("cps 0x13") + + TEST_SUPPORTED("yield.w") + TEST("sev.w") + TEST("nop.w") + TEST("wfi.w") + TEST_SUPPORTED("wfe.w") + TEST_UNSUPPORTED("dbg.w #0") + + TEST_UNSUPPORTED("clrex") + TEST_UNSUPPORTED("dsb") + TEST_UNSUPPORTED("dmb") + TEST_UNSUPPORTED("isb") + + TEST_UNSUPPORTED("bxj r0") + + TEST_UNSUPPORTED("subs pc, lr, #4") + + TEST("mrs r0, cpsr") + TEST("mrs r14, cpsr") + TEST_UNSUPPORTED(__inst_thumb32(0xf3ef8d00) " @ mrs sp, spsr") + TEST_UNSUPPORTED(__inst_thumb32(0xf3ef8f00) " @ mrs pc, spsr") + TEST_UNSUPPORTED("mrs r0, spsr") + TEST_UNSUPPORTED("mrs lr, spsr") + + TEST_UNSUPPORTED(__inst_thumb32(0xf7f08000) " @ smc #0") + + TEST_UNSUPPORTED(__inst_thumb32(0xf7f0a000) " @ undefeined") + + TEST_BF( "b.w 2f") + TEST_BB( "b.w 2b") + TEST_BF_X("b.w 2f", SPACE_0x1000) + + TEST_BF( "bl.w 2f") + TEST_BB( "bl.w 2b") + TEST_BB_X("bl.w 2b", SPACE_0x1000) + + TEST_X( "blx __dummy_arm_subroutine", + ".arm \n\t" + ".align \n\t" + ".type __dummy_arm_subroutine, %%function \n\t" + "__dummy_arm_subroutine: \n\t" + "mov r0, pc \n\t" + "bx lr \n\t" + ".thumb \n\t" + ) + TEST( "blx __dummy_arm_subroutine") + + TEST_GROUP("Store single data item") + +#define SINGLE_STORE(size) \ + TEST_RP( "str"size" r",0, VAL1,", [r",11,-1024,", #1024]") \ + TEST_RP( "str"size" r",14,VAL2,", [r",1, -1024,", #1080]") \ + TEST_RP( "str"size" r",0, VAL1,", [r",11,256, ", #-120]") \ + TEST_RP( "str"size" r",14,VAL2,", [r",1, 256, ", #-128]") \ + TEST_RP( "str"size" r",0, VAL1,", [r",11,24, "], #120") \ + TEST_RP( "str"size" r",14,VAL2,", [r",1, 24, "], #128") \ + TEST_RP( "str"size" r",0, VAL1,", [r",11,24, "], #-120") \ + TEST_RP( "str"size" r",14,VAL2,", [r",1, 24, "], #-128") \ + TEST_RP( "str"size" r",0, VAL1,", [r",11,24, ", #120]!") \ + TEST_RP( "str"size" r",14,VAL2,", [r",1, 24, ", #128]!") \ + TEST_RP( "str"size" r",0, VAL1,", [r",11,256, ", #-120]!") \ + TEST_RP( "str"size" r",14,VAL2,", [r",1, 256, ", #-128]!") \ + TEST_RPR("str"size".w r",0, VAL1,", [r",1, 0,", r",2, 4,"]") \ + TEST_RPR("str"size" r",14,VAL2,", [r",10,0,", r",11,4,", lsl #1]") \ + TEST_R( "str"size".w r",7, VAL1,", [sp, #24]") \ + TEST_RP( "str"size".w r",0, VAL2,", [r",0,0, "]") \ + TEST_UNSUPPORTED("str"size"t r0, [r1, #4]") + + SINGLE_STORE("b") + SINGLE_STORE("h") + SINGLE_STORE("") + + TEST("str sp, [sp]") + TEST_UNSUPPORTED(__inst_thumb32(0xf8cfe000) " @ str r14, [pc]") + TEST_UNSUPPORTED(__inst_thumb32(0xf8cef000) " @ str pc, [r14]") + + TEST_GROUP("Advanced SIMD element or structure load/store instructions") + + TEST_UNSUPPORTED(__inst_thumb32(0xf9000000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xf92fffff) "") + TEST_UNSUPPORTED(__inst_thumb32(0xf9800000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xf9efffff) "") + + TEST_GROUP("Load single data item and memory hints") + +#define SINGLE_LOAD(size) \ + TEST_P( "ldr"size" r0, [r",11,-1024, ", #1024]") \ + TEST_P( "ldr"size" r14, [r",1, -1024,", #1080]") \ + TEST_P( "ldr"size" r0, [r",11,256, ", #-120]") \ + TEST_P( "ldr"size" r14, [r",1, 256, ", #-128]") \ + TEST_P( "ldr"size" r0, [r",11,24, "], #120") \ + TEST_P( "ldr"size" r14, [r",1, 24, "], #128") \ + TEST_P( "ldr"size" r0, [r",11,24, "], #-120") \ + TEST_P( "ldr"size" r14, [r",1,24, "], #-128") \ + TEST_P( "ldr"size" r0, [r",11,24, ", #120]!") \ + TEST_P( "ldr"size" r14, [r",1, 24, ", #128]!") \ + TEST_P( "ldr"size" r0, [r",11,256, ", #-120]!") \ + TEST_P( "ldr"size" r14, [r",1, 256, ", #-128]!") \ + TEST_PR("ldr"size".w r0, [r",1, 0,", r",2, 4,"]") \ + TEST_PR("ldr"size" r14, [r",10,0,", r",11,4,", lsl #1]") \ + TEST_X( "ldr"size".w r0, 3f", \ + ".align 3 \n\t" \ + "3: .word "__stringify(VAL1)) \ + TEST_X( "ldr"size".w r14, 3f", \ + ".align 3 \n\t" \ + "3: .word "__stringify(VAL2)) \ + TEST( "ldr"size".w r7, 3b") \ + TEST( "ldr"size".w r7, [sp, #24]") \ + TEST_P( "ldr"size".w r0, [r",0,0, "]") \ + TEST_UNSUPPORTED("ldr"size"t r0, [r1, #4]") + + SINGLE_LOAD("b") + SINGLE_LOAD("sb") + SINGLE_LOAD("h") + SINGLE_LOAD("sh") + SINGLE_LOAD("") + + TEST_BF_P("ldr pc, [r",14, 15*4,"]") + TEST_P( "ldr sp, [r",14, 13*4,"]") + TEST_BF_R("ldr pc, [sp, r",14, 15*4,"]") + TEST_R( "ldr sp, [sp, r",14, 13*4,"]") + TEST_THUMB_TO_ARM_INTERWORK_P("ldr pc, [r",0,0,", #15*4]") + TEST_SUPPORTED("ldr sp, 99f") + TEST_SUPPORTED("ldr pc, 99f") + + TEST_UNSUPPORTED(__inst_thumb32(0xf854700d) " @ ldr r7, [r4, sp]") + TEST_UNSUPPORTED(__inst_thumb32(0xf854700f) " @ ldr r7, [r4, pc]") + TEST_UNSUPPORTED(__inst_thumb32(0xf814700d) " @ ldrb r7, [r4, sp]") + TEST_UNSUPPORTED(__inst_thumb32(0xf814700f) " @ ldrb r7, [r4, pc]") + TEST_UNSUPPORTED(__inst_thumb32(0xf89fd004) " @ ldrb sp, 99f") + TEST_UNSUPPORTED(__inst_thumb32(0xf814d008) " @ ldrb sp, [r4, r8]") + TEST_UNSUPPORTED(__inst_thumb32(0xf894d000) " @ ldrb sp, [r4]") + + TEST_UNSUPPORTED(__inst_thumb32(0xf8600000) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xf9ffffff) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xf9500000) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xf95fffff) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xf8000800) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xf97ffaff) "") /* Unallocated space */ + + TEST( "pli [pc, #4]") + TEST( "pli [pc, #-4]") + TEST( "pld [pc, #4]") + TEST( "pld [pc, #-4]") + + TEST_P( "pld [r",0,-1024,", #1024]") + TEST( __inst_thumb32(0xf8b0f400) " @ pldw [r0, #1024]") + TEST_P( "pli [r",4, 0b,", #1024]") + TEST_P( "pld [r",7, 120,", #-120]") + TEST( __inst_thumb32(0xf837fc78) " @ pldw [r7, #-120]") + TEST_P( "pli [r",11,120,", #-120]") + TEST( "pld [sp, #0]") + + TEST_PR("pld [r",7, 24, ", r",0, 16,"]") + TEST_PR("pld [r",8, 24, ", r",12,16,", lsl #3]") + TEST_SUPPORTED(__inst_thumb32(0xf837f000) " @ pldw [r7, r0]") + TEST_SUPPORTED(__inst_thumb32(0xf838f03c) " @ pldw [r8, r12, lsl #3]"); + TEST_RR("pli [r",12,0b,", r",0, 16,"]") + TEST_RR("pli [r",0, 0b,", r",12,16,", lsl #3]") + TEST_R( "pld [sp, r",1, 16,"]") + TEST_UNSUPPORTED(__inst_thumb32(0xf817f00d) " @pld [r7, sp]") + TEST_UNSUPPORTED(__inst_thumb32(0xf817f00f) " @pld [r7, pc]") + + TEST_GROUP("Data-processing (register)") + +#define SHIFTS32(op) \ + TEST_RR(op" r0, r",1, VAL1,", r",2, 3, "") \ + TEST_RR(op" r14, r",12,VAL2,", r",11,10,"") + + SHIFTS32("lsl") + SHIFTS32("lsls") + SHIFTS32("lsr") + SHIFTS32("lsrs") + SHIFTS32("asr") + SHIFTS32("asrs") + SHIFTS32("ror") + SHIFTS32("rors") + + TEST_UNSUPPORTED(__inst_thumb32(0xfa01ff02) " @ lsl pc, r1, r2") + TEST_UNSUPPORTED(__inst_thumb32(0xfa01fd02) " @ lsl sp, r1, r2") + TEST_UNSUPPORTED(__inst_thumb32(0xfa0ff002) " @ lsl r0, pc, r2") + TEST_UNSUPPORTED(__inst_thumb32(0xfa0df002) " @ lsl r0, sp, r2") + TEST_UNSUPPORTED(__inst_thumb32(0xfa01f00f) " @ lsl r0, r1, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xfa01f00d) " @ lsl r0, r1, sp") + + TEST_RR( "sxtah r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sxtah r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "sxth r8, r",7, HH1,"") + + TEST_UNSUPPORTED(__inst_thumb32(0xfa0fff87) " @ sxth pc, r7"); + TEST_UNSUPPORTED(__inst_thumb32(0xfa0ffd87) " @ sxth sp, r7"); + TEST_UNSUPPORTED(__inst_thumb32(0xfa0ff88f) " @ sxth r8, pc"); + TEST_UNSUPPORTED(__inst_thumb32(0xfa0ff88d) " @ sxth r8, sp"); + + TEST_RR( "uxtah r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uxtah r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "uxth r8, r",7, HH1,"") + + TEST_RR( "sxtab16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sxtab16 r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "sxtb16 r8, r",7, HH1,"") + + TEST_RR( "uxtab16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uxtab16 r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "uxtb16 r8, r",7, HH1,"") + + TEST_RR( "sxtab r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sxtab r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "sxtb r8, r",7, HH1,"") + + TEST_RR( "uxtab r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uxtab r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "uxtb r8, r",7, HH1,"") + + TEST_UNSUPPORTED(__inst_thumb32(0xfa6000f0) "") + TEST_UNSUPPORTED(__inst_thumb32(0xfa7fffff) "") + +#define PARALLEL_ADD_SUB(op) \ + TEST_RR( op"add16 r0, r",0, HH1,", r",1, HH2,"") \ + TEST_RR( op"add16 r14, r",12,HH2,", r",10,HH1,"") \ + TEST_RR( op"asx r0, r",0, HH1,", r",1, HH2,"") \ + TEST_RR( op"asx r14, r",12,HH2,", r",10,HH1,"") \ + TEST_RR( op"sax r0, r",0, HH1,", r",1, HH2,"") \ + TEST_RR( op"sax r14, r",12,HH2,", r",10,HH1,"") \ + TEST_RR( op"sub16 r0, r",0, HH1,", r",1, HH2,"") \ + TEST_RR( op"sub16 r14, r",12,HH2,", r",10,HH1,"") \ + TEST_RR( op"add8 r0, r",0, HH1,", r",1, HH2,"") \ + TEST_RR( op"add8 r14, r",12,HH2,", r",10,HH1,"") \ + TEST_RR( op"sub8 r0, r",0, HH1,", r",1, HH2,"") \ + TEST_RR( op"sub8 r14, r",12,HH2,", r",10,HH1,"") + + TEST_GROUP("Parallel addition and subtraction, signed") + + PARALLEL_ADD_SUB("s") + PARALLEL_ADD_SUB("q") + PARALLEL_ADD_SUB("sh") + + TEST_GROUP("Parallel addition and subtraction, unsigned") + + PARALLEL_ADD_SUB("u") + PARALLEL_ADD_SUB("uq") + PARALLEL_ADD_SUB("uh") + + TEST_GROUP("Miscellaneous operations") + + TEST_RR("qadd r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR("qadd lr, r",9, VAL2,", r",8, VAL1,"") + TEST_RR("qsub r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR("qsub lr, r",9, VAL2,", r",8, VAL1,"") + TEST_RR("qdadd r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR("qdadd lr, r",9, VAL2,", r",8, VAL1,"") + TEST_RR("qdsub r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR("qdsub lr, r",9, VAL2,", r",8, VAL1,"") + + TEST_R("rev.w r0, r",0, VAL1,"") + TEST_R("rev r14, r",12, VAL2,"") + TEST_R("rev16.w r0, r",0, VAL1,"") + TEST_R("rev16 r14, r",12, VAL2,"") + TEST_R("rbit r0, r",0, VAL1,"") + TEST_R("rbit r14, r",12, VAL2,"") + TEST_R("revsh.w r0, r",0, VAL1,"") + TEST_R("revsh r14, r",12, VAL2,"") + + TEST_UNSUPPORTED(__inst_thumb32(0xfa9cff8c) " @ rev pc, r12"); + TEST_UNSUPPORTED(__inst_thumb32(0xfa9cfd8c) " @ rev sp, r12"); + TEST_UNSUPPORTED(__inst_thumb32(0xfa9ffe8f) " @ rev r14, pc"); + TEST_UNSUPPORTED(__inst_thumb32(0xfa9dfe8d) " @ rev r14, sp"); + + TEST_RR("sel r0, r",0, VAL1,", r",1, VAL2,"") + TEST_RR("sel r14, r",12,VAL1,", r",10, VAL2,"") + + TEST_R("clz r0, r",0, 0x0,"") + TEST_R("clz r7, r",14,0x1,"") + TEST_R("clz lr, r",7, 0xffffffff,"") + + TEST_UNSUPPORTED(__inst_thumb32(0xfa80f030) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xfaffff7f) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xfab0f000) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xfaffff7f) "") /* Unallocated space */ + + TEST_GROUP("Multiply, multiply accumulate, and absolute difference operations") + + TEST_RR( "mul r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "mul r7, r",8, VAL2,", r",9, VAL2,"") + TEST_UNSUPPORTED(__inst_thumb32(0xfb08ff09) " @ mul pc, r8, r9") + TEST_UNSUPPORTED(__inst_thumb32(0xfb08fd09) " @ mul sp, r8, r9") + TEST_UNSUPPORTED(__inst_thumb32(0xfb0ff709) " @ mul r7, pc, r9") + TEST_UNSUPPORTED(__inst_thumb32(0xfb0df709) " @ mul r7, sp, r9") + TEST_UNSUPPORTED(__inst_thumb32(0xfb08f70f) " @ mul r7, r8, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xfb08f70d) " @ mul r7, r8, sp") + + TEST_RRR( "mla r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "mla r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_UNSUPPORTED(__inst_thumb32(0xfb08af09) " @ mla pc, r8, r9, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb08ad09) " @ mla sp, r8, r9, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb0fa709) " @ mla r7, pc, r9, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb0da709) " @ mla r7, sp, r9, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb08a70f) " @ mla r7, r8, pc, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb08a70d) " @ mla r7, r8, sp, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb08d709) " @ mla r7, r8, r9, sp"); + + TEST_RRR( "mls r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "mls r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + + TEST_RRR( "smlabb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlabb r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RRR( "smlatb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlatb r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RRR( "smlabt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlabt r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RRR( "smlatt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlatt r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "smulbb r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulbb r7, r",8, VAL3,", r",9, VAL1,"") + TEST_RR( "smultb r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smultb r7, r",8, VAL3,", r",9, VAL1,"") + TEST_RR( "smulbt r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulbt r7, r",8, VAL3,", r",9, VAL1,"") + TEST_RR( "smultt r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smultt r7, r",8, VAL3,", r",9, VAL1,"") + + TEST_RRR( "smlad r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") + TEST_RRR( "smlad r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") + TEST_RRR( "smladx r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") + TEST_RRR( "smladx r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") + TEST_RR( "smuad r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "smuad r14, r",12,HH2,", r",10,HH1,"") + TEST_RR( "smuadx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "smuadx r14, r",12,HH2,", r",10,HH1,"") + + TEST_RRR( "smlawb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlawb r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RRR( "smlawt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlawt r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "smulwb r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulwb r7, r",8, VAL3,", r",9, VAL1,"") + TEST_RR( "smulwt r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulwt r7, r",8, VAL3,", r",9, VAL1,"") + + TEST_RRR( "smlsd r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") + TEST_RRR( "smlsd r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") + TEST_RRR( "smlsdx r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") + TEST_RRR( "smlsdx r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") + TEST_RR( "smusd r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "smusd r14, r",12,HH2,", r",10,HH1,"") + TEST_RR( "smusdx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "smusdx r14, r",12,HH2,", r",10,HH1,"") + + TEST_RRR( "smmla r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") + TEST_RRR( "smmla r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") + TEST_RRR( "smmlar r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") + TEST_RRR( "smmlar r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") + TEST_RR( "smmul r0, r",0, VAL1,", r",1, VAL2,"") + TEST_RR( "smmul r14, r",12,VAL2,", r",10,VAL1,"") + TEST_RR( "smmulr r0, r",0, VAL1,", r",1, VAL2,"") + TEST_RR( "smmulr r14, r",12,VAL2,", r",10,VAL1,"") + + TEST_RRR( "smmls r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") + TEST_RRR( "smmls r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") + TEST_RRR( "smmlsr r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") + TEST_RRR( "smmlsr r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") + + TEST_RRR( "usada8 r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL3,"") + TEST_RRR( "usada8 r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL3,"") + TEST_RR( "usad8 r0, r",0, VAL1,", r",1, VAL2,"") + TEST_RR( "usad8 r14, r",12,VAL2,", r",10,VAL1,"") + + TEST_UNSUPPORTED(__inst_thumb32(0xfb00f010) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xfb0fff1f) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xfb70f010) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xfb7fff1f) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xfb700010) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xfb7fff1f) "") /* Unallocated space */ + + TEST_GROUP("Long multiply, long multiply accumulate, and divide") + + TEST_RR( "smull r0, r1, r",2, VAL1,", r",3, VAL2,"") + TEST_RR( "smull r7, r8, r",9, VAL2,", r",10, VAL1,"") + TEST_UNSUPPORTED(__inst_thumb32(0xfb89f80a) " @ smull pc, r8, r9, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb89d80a) " @ smull sp, r8, r9, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb897f0a) " @ smull r7, pc, r9, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb897d0a) " @ smull r7, sp, r9, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb8f780a) " @ smull r7, r8, pc, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb8d780a) " @ smull r7, r8, sp, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb89780f) " @ smull r7, r8, r9, pc"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb89780d) " @ smull r7, r8, r9, sp"); + + TEST_RR( "umull r0, r1, r",2, VAL1,", r",3, VAL2,"") + TEST_RR( "umull r7, r8, r",9, VAL2,", r",10, VAL1,"") + + TEST_RRRR( "smlal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlal r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + + TEST_RRRR( "smlalbb r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlalbb r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRRR( "smlalbt r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlalbt r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRRR( "smlaltb r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlaltb r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRRR( "smlaltt r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlaltt r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + + TEST_RRRR( "smlald r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) + TEST_RRRR( "smlald r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) + TEST_RRRR( "smlaldx r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) + TEST_RRRR( "smlaldx r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) + + TEST_RRRR( "smlsld r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) + TEST_RRRR( "smlsld r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) + TEST_RRRR( "smlsldx r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) + TEST_RRRR( "smlsldx r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) + + TEST_RRRR( "umlal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "umlal r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRRR( "umaal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "umaal r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + + TEST_GROUP("Coprocessor instructions") + + TEST_UNSUPPORTED(__inst_thumb32(0xfc000000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xffffffff) "") + + TEST_GROUP("Testing instructions in IT blocks") + + TEST_ITBLOCK("sub.w r0, r0") + + verbose("\n"); +} + diff --git a/arch/arm/probes/uprobes/Makefile b/arch/arm/probes/uprobes/Makefile new file mode 100644 index 0000000..e1dc3d0 --- /dev/null +++ b/arch/arm/probes/uprobes/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_UPROBES) += core.o actions-arm.o diff --git a/arch/arm/probes/uprobes/actions-arm.c b/arch/arm/probes/uprobes/actions-arm.c new file mode 100644 index 0000000..1dd4916 --- /dev/null +++ b/arch/arm/probes/uprobes/actions-arm.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2012 Rabin Vincent + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include "../decode.h" +#include "../decode-arm.h" +#include "core.h" + +static int uprobes_substitute_pc(unsigned long *pinsn, u32 oregs) +{ + probes_opcode_t insn = __mem_to_opcode_arm(*pinsn); + probes_opcode_t temp; + probes_opcode_t mask; + int freereg; + u32 free = 0xffff; + u32 regs; + + for (regs = oregs; regs; regs >>= 4, insn >>= 4) { + if ((regs & 0xf) == REG_TYPE_NONE) + continue; + + free &= ~(1 << (insn & 0xf)); + } + + /* No PC, no problem */ + if (free & (1 << 15)) + return 15; + + if (!free) + return -1; + + /* + * fls instead of ffs ensures that for "ldrd r0, r1, [pc]" we would + * pick LR instead of R1. + */ + freereg = free = fls(free) - 1; + + temp = __mem_to_opcode_arm(*pinsn); + insn = temp; + regs = oregs; + mask = 0xf; + + for (; regs; regs >>= 4, mask <<= 4, free <<= 4, temp >>= 4) { + if ((regs & 0xf) == REG_TYPE_NONE) + continue; + + if ((temp & 0xf) != 15) + continue; + + insn &= ~mask; + insn |= free & mask; + } + + *pinsn = __opcode_to_mem_arm(insn); + return freereg; +} + +static void uprobe_set_pc(struct arch_uprobe *auprobe, + struct arch_uprobe_task *autask, + struct pt_regs *regs) +{ + u32 pcreg = auprobe->pcreg; + + autask->backup = regs->uregs[pcreg]; + regs->uregs[pcreg] = regs->ARM_pc + 8; +} + +static void uprobe_unset_pc(struct arch_uprobe *auprobe, + struct arch_uprobe_task *autask, + struct pt_regs *regs) +{ + /* PC will be taken care of by common code */ + regs->uregs[auprobe->pcreg] = autask->backup; +} + +static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe, + struct arch_uprobe_task *autask, + struct pt_regs *regs) +{ + u32 pcreg = auprobe->pcreg; + + alu_write_pc(regs->uregs[pcreg], regs); + regs->uregs[pcreg] = autask->backup; +} + +static void uprobe_write_pc(struct arch_uprobe *auprobe, + struct arch_uprobe_task *autask, + struct pt_regs *regs) +{ + u32 pcreg = auprobe->pcreg; + + load_write_pc(regs->uregs[pcreg], regs); + regs->uregs[pcreg] = autask->backup; +} + +enum probes_insn +decode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe, + asi); + struct decode_emulate *decode = (struct decode_emulate *) d; + u32 regs = decode->header.type_regs.bits >> DECODE_TYPE_BITS; + int reg; + + reg = uprobes_substitute_pc(&auprobe->ixol[0], regs); + if (reg == 15) + return INSN_GOOD; + + if (reg == -1) + return INSN_REJECTED; + + auprobe->pcreg = reg; + auprobe->prehandler = uprobe_set_pc; + auprobe->posthandler = uprobe_unset_pc; + + return INSN_GOOD; +} + +enum probes_insn +decode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d, bool alu) +{ + struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe, + asi); + enum probes_insn ret = decode_pc_ro(insn, asi, d); + + if (((insn >> 12) & 0xf) == 15) + auprobe->posthandler = alu ? uprobe_aluwrite_pc + : uprobe_write_pc; + + return ret; +} + +enum probes_insn +decode_rd12rn16rm0rs8_rwflags(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *d) +{ + return decode_wb_pc(insn, asi, d, true); +} + +enum probes_insn +decode_ldr(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + return decode_wb_pc(insn, asi, d, false); +} + +enum probes_insn +uprobe_decode_ldmstm(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *d) +{ + struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe, + asi); + unsigned reglist = insn & 0xffff; + int rn = (insn >> 16) & 0xf; + int lbit = insn & (1 << 20); + unsigned used = reglist | (1 << rn); + + if (rn == 15) + return INSN_REJECTED; + + if (!(used & (1 << 15))) + return INSN_GOOD; + + if (used & (1 << 14)) + return INSN_REJECTED; + + /* Use LR instead of PC */ + insn ^= 0xc000; + + auprobe->pcreg = 14; + auprobe->ixol[0] = __opcode_to_mem_arm(insn); + + auprobe->prehandler = uprobe_set_pc; + if (lbit) + auprobe->posthandler = uprobe_write_pc; + else + auprobe->posthandler = uprobe_unset_pc; + + return INSN_GOOD; +} + +const union decode_action uprobes_probes_actions[] = { + [PROBES_EMULATE_NONE] = {.handler = probes_simulate_nop}, + [PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop}, + [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop}, + [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop}, + [PROBES_BRANCH_IMM] = {.handler = simulate_blx1}, + [PROBES_MRS] = {.handler = simulate_mrs}, + [PROBES_BRANCH_REG] = {.handler = simulate_blx2bx}, + [PROBES_CLZ] = {.handler = probes_simulate_nop}, + [PROBES_SATURATING_ARITHMETIC] = {.handler = probes_simulate_nop}, + [PROBES_MUL1] = {.handler = probes_simulate_nop}, + [PROBES_MUL2] = {.handler = probes_simulate_nop}, + [PROBES_SWP] = {.handler = probes_simulate_nop}, + [PROBES_LDRSTRD] = {.decoder = decode_pc_ro}, + [PROBES_LOAD_EXTRA] = {.decoder = decode_pc_ro}, + [PROBES_LOAD] = {.decoder = decode_ldr}, + [PROBES_STORE_EXTRA] = {.decoder = decode_pc_ro}, + [PROBES_STORE] = {.decoder = decode_pc_ro}, + [PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp}, + [PROBES_DATA_PROCESSING_REG] = { + .decoder = decode_rd12rn16rm0rs8_rwflags}, + [PROBES_DATA_PROCESSING_IMM] = { + .decoder = decode_rd12rn16rm0rs8_rwflags}, + [PROBES_MOV_HALFWORD] = {.handler = probes_simulate_nop}, + [PROBES_SEV] = {.handler = probes_simulate_nop}, + [PROBES_WFE] = {.handler = probes_simulate_nop}, + [PROBES_SATURATE] = {.handler = probes_simulate_nop}, + [PROBES_REV] = {.handler = probes_simulate_nop}, + [PROBES_MMI] = {.handler = probes_simulate_nop}, + [PROBES_PACK] = {.handler = probes_simulate_nop}, + [PROBES_EXTEND] = {.handler = probes_simulate_nop}, + [PROBES_EXTEND_ADD] = {.handler = probes_simulate_nop}, + [PROBES_MUL_ADD_LONG] = {.handler = probes_simulate_nop}, + [PROBES_MUL_ADD] = {.handler = probes_simulate_nop}, + [PROBES_BITFIELD] = {.handler = probes_simulate_nop}, + [PROBES_BRANCH] = {.handler = simulate_bbl}, + [PROBES_LDMSTM] = {.decoder = uprobe_decode_ldmstm} +}; diff --git a/arch/arm/probes/uprobes/core.c b/arch/arm/probes/uprobes/core.c new file mode 100644 index 0000000..b2954f6 --- /dev/null +++ b/arch/arm/probes/uprobes/core.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2012 Rabin Vincent + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../decode.h" +#include "../decode-arm.h" +#include "core.h" + +#define UPROBE_TRAP_NR UINT_MAX + +bool is_swbp_insn(uprobe_opcode_t *insn) +{ + return (__mem_to_opcode_arm(*insn) & 0x0fffffff) == + (UPROBE_SWBP_ARM_INSN & 0x0fffffff); +} + +int set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, + unsigned long vaddr) +{ + return uprobe_write_opcode(mm, vaddr, + __opcode_to_mem_arm(auprobe->bpinsn)); +} + +bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) { + regs->ARM_pc += 4; + return true; + } + + return false; +} + +bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + probes_opcode_t opcode; + + if (!auprobe->simulate) + return false; + + opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn); + + auprobe->asi.insn_singlestep(opcode, &auprobe->asi, regs); + + return true; +} + +unsigned long +arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, + struct pt_regs *regs) +{ + unsigned long orig_ret_vaddr; + + orig_ret_vaddr = regs->ARM_lr; + /* Replace the return addr with trampoline addr */ + regs->ARM_lr = trampoline_vaddr; + return orig_ret_vaddr; +} + +int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, + unsigned long addr) +{ + unsigned int insn; + unsigned int bpinsn; + enum probes_insn ret; + + /* Thumb not yet support */ + if (addr & 0x3) + return -EINVAL; + + insn = __mem_to_opcode_arm(*(unsigned int *)auprobe->insn); + auprobe->ixol[0] = __opcode_to_mem_arm(insn); + auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN); + + ret = arm_probes_decode_insn(insn, &auprobe->asi, false, + uprobes_probes_actions); + switch (ret) { + case INSN_REJECTED: + return -EINVAL; + + case INSN_GOOD_NO_SLOT: + auprobe->simulate = true; + break; + + case INSN_GOOD: + default: + break; + } + + bpinsn = UPROBE_SWBP_ARM_INSN & 0x0fffffff; + if (insn >= 0xe0000000) + bpinsn |= 0xe0000000; /* Unconditional instruction */ + else + bpinsn |= insn & 0xf0000000; /* Copy condition from insn */ + + auprobe->bpinsn = bpinsn; + + return 0; +} + +void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr, + void *src, unsigned long len) +{ + void *xol_page_kaddr = kmap_atomic(page); + void *dst = xol_page_kaddr + (vaddr & ~PAGE_MASK); + + preempt_disable(); + + /* Initialize the slot */ + memcpy(dst, src, len); + + /* flush caches (dcache/icache) */ + flush_uprobe_xol_access(page, vaddr, dst, len); + + preempt_enable(); + + kunmap_atomic(xol_page_kaddr); +} + + +int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + struct uprobe_task *utask = current->utask; + + if (auprobe->prehandler) + auprobe->prehandler(auprobe, &utask->autask, regs); + + utask->autask.saved_trap_no = current->thread.trap_no; + current->thread.trap_no = UPROBE_TRAP_NR; + regs->ARM_pc = utask->xol_vaddr; + + return 0; +} + +int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + struct uprobe_task *utask = current->utask; + + WARN_ON_ONCE(current->thread.trap_no != UPROBE_TRAP_NR); + + current->thread.trap_no = utask->autask.saved_trap_no; + regs->ARM_pc = utask->vaddr + 4; + + if (auprobe->posthandler) + auprobe->posthandler(auprobe, &utask->autask, regs); + + return 0; +} + +bool arch_uprobe_xol_was_trapped(struct task_struct *t) +{ + if (t->thread.trap_no != UPROBE_TRAP_NR) + return true; + + return false; +} + +void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + struct uprobe_task *utask = current->utask; + + current->thread.trap_no = utask->autask.saved_trap_no; + instruction_pointer_set(regs, utask->vaddr); +} + +int arch_uprobe_exception_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + return NOTIFY_DONE; +} + +static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr) +{ + unsigned long flags; + + local_irq_save(flags); + instr &= 0x0fffffff; + if (instr == (UPROBE_SWBP_ARM_INSN & 0x0fffffff)) + uprobe_pre_sstep_notifier(regs); + else if (instr == (UPROBE_SS_ARM_INSN & 0x0fffffff)) + uprobe_post_sstep_notifier(regs); + local_irq_restore(flags); + + return 0; +} + +unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) +{ + return instruction_pointer(regs); +} + +static struct undef_hook uprobes_arm_break_hook = { + .instr_mask = 0x0fffffff, + .instr_val = (UPROBE_SWBP_ARM_INSN & 0x0fffffff), + .cpsr_mask = MODE_MASK, + .cpsr_val = USR_MODE, + .fn = uprobe_trap_handler, +}; + +static struct undef_hook uprobes_arm_ss_hook = { + .instr_mask = 0x0fffffff, + .instr_val = (UPROBE_SS_ARM_INSN & 0x0fffffff), + .cpsr_mask = MODE_MASK, + .cpsr_val = USR_MODE, + .fn = uprobe_trap_handler, +}; + +static int arch_uprobes_init(void) +{ + register_undef_hook(&uprobes_arm_break_hook); + register_undef_hook(&uprobes_arm_ss_hook); + + return 0; +} +device_initcall(arch_uprobes_init); diff --git a/arch/arm/probes/uprobes/core.h b/arch/arm/probes/uprobes/core.h new file mode 100644 index 0000000..1d0c12d --- /dev/null +++ b/arch/arm/probes/uprobes/core.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2012 Rabin Vincent + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ARM_KERNEL_UPROBES_H +#define __ARM_KERNEL_UPROBES_H + +enum probes_insn uprobe_decode_ldmstm(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *d); + +enum probes_insn decode_ldr(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *d); + +enum probes_insn +decode_rd12rn16rm0rs8_rwflags(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *d); + +enum probes_insn +decode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d, bool alu); + +enum probes_insn +decode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d); + +extern const union decode_action uprobes_probes_actions[]; + +#endif -- cgit v0.10.2 From 832607e79d7423c67ef8a3de8dfa3d25b5b0bf86 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Wed, 7 Jan 2015 11:42:30 +0000 Subject: ARM: probes: Use correct action types for MOVW, SEV and WFI This doesn't correct any bugs when probing these instructions but makes MOVW slightly faster and makes everything more symmetric with the Thumb instruction cases. We can also remove the now redundant PROBES_EMULATE_NONE and PROBES_SIMULATE_NOP actions. Signed-off-by: Jon Medhurst diff --git a/arch/arm/probes/decode-arm.c b/arch/arm/probes/decode-arm.c index e39cc75..04114f7 100644 --- a/arch/arm/probes/decode-arm.c +++ b/arch/arm/probes/decode-arm.c @@ -370,17 +370,17 @@ static const union decode_item arm_cccc_001x_table[] = { /* MOVW cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */ /* MOVT cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0fb00000, 0x03000000, PROBES_DATA_PROCESSING_IMM, + DECODE_EMULATEX (0x0fb00000, 0x03000000, PROBES_MOV_HALFWORD, REGS(0, NOPC, 0, 0, 0)), /* YIELD cccc 0011 0010 0000 xxxx xxxx 0000 0001 */ DECODE_OR (0x0fff00ff, 0x03200001), /* SEV cccc 0011 0010 0000 xxxx xxxx 0000 0100 */ - DECODE_EMULATE (0x0fff00ff, 0x03200004, PROBES_EMULATE_NONE), + DECODE_EMULATE (0x0fff00ff, 0x03200004, PROBES_SEV), /* NOP cccc 0011 0010 0000 xxxx xxxx 0000 0000 */ /* WFE cccc 0011 0010 0000 xxxx xxxx 0000 0010 */ /* WFI cccc 0011 0010 0000 xxxx xxxx 0000 0011 */ - DECODE_SIMULATE (0x0fff00fc, 0x03200000, PROBES_SIMULATE_NOP), + DECODE_SIMULATE (0x0fff00fc, 0x03200000, PROBES_WFE), /* DBG cccc 0011 0010 0000 xxxx xxxx ffff xxxx */ /* unallocated hints cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */ /* MSR (immediate) cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */ diff --git a/arch/arm/probes/decode-arm.h b/arch/arm/probes/decode-arm.h index 9c56b40..cb0b263 100644 --- a/arch/arm/probes/decode-arm.h +++ b/arch/arm/probes/decode-arm.h @@ -18,8 +18,6 @@ #include "decode.h" enum probes_arm_action { - PROBES_EMULATE_NONE, - PROBES_SIMULATE_NOP, PROBES_PRELOAD_IMM, PROBES_PRELOAD_REG, PROBES_BRANCH_IMM, diff --git a/arch/arm/probes/kprobes/actions-arm.c b/arch/arm/probes/kprobes/actions-arm.c index 8797879..2206f2d 100644 --- a/arch/arm/probes/kprobes/actions-arm.c +++ b/arch/arm/probes/kprobes/actions-arm.c @@ -302,8 +302,6 @@ emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(probes_opcode_t insn, } const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = { - [PROBES_EMULATE_NONE] = {.handler = probes_emulate_none}, - [PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop}, [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop}, [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop}, [PROBES_BRANCH_IMM] = {.handler = simulate_blx1}, diff --git a/arch/arm/probes/uprobes/actions-arm.c b/arch/arm/probes/uprobes/actions-arm.c index 1dd4916..76eb449 100644 --- a/arch/arm/probes/uprobes/actions-arm.c +++ b/arch/arm/probes/uprobes/actions-arm.c @@ -195,8 +195,6 @@ uprobe_decode_ldmstm(probes_opcode_t insn, } const union decode_action uprobes_probes_actions[] = { - [PROBES_EMULATE_NONE] = {.handler = probes_simulate_nop}, - [PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop}, [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop}, [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop}, [PROBES_BRANCH_IMM] = {.handler = simulate_blx1}, -- cgit v0.10.2 From 83803d97dae1eaf6850a45ef8ee179cc66e147dc Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 5 Jan 2015 19:29:18 +0800 Subject: ARM: kprobes: introduces checker This patch introdces 'checker' to decoding phase, and calls checkers when instruction decoding. This allows further decoding for specific instructions. This patch introduces a stub call of checkers in kprobe arch_prepare_kprobe() as an example and for further expansion. Signed-off-by: Wang Nan Reviewed-by: Jon Medhurst Reviewed-by: Masami Hiramatsu Signed-off-by: Jon Medhurst diff --git a/arch/arm/probes/decode-arm.c b/arch/arm/probes/decode-arm.c index 04114f7..f72c33a 100644 --- a/arch/arm/probes/decode-arm.c +++ b/arch/arm/probes/decode-arm.c @@ -726,10 +726,11 @@ static void __kprobes arm_singlestep(probes_opcode_t insn, */ enum probes_insn __kprobes arm_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions) + bool emulate, const union decode_action *actions, + const struct decode_checker *checkers[]) { asi->insn_singlestep = arm_singlestep; asi->insn_check_cc = probes_condition_checks[insn>>28]; return probes_decode_insn(insn, asi, probes_decode_arm_table, false, - emulate, actions); + emulate, actions, checkers); } diff --git a/arch/arm/probes/decode-arm.h b/arch/arm/probes/decode-arm.h index cb0b263..b3b80f6 100644 --- a/arch/arm/probes/decode-arm.h +++ b/arch/arm/probes/decode-arm.h @@ -68,6 +68,7 @@ extern const union decode_item probes_decode_arm_table[]; enum probes_insn arm_probes_decode_insn(probes_opcode_t, struct arch_probes_insn *, bool emulate, - const union decode_action *actions); + const union decode_action *actions, + const struct decode_checker *checkers[]); #endif diff --git a/arch/arm/probes/decode-thumb.c b/arch/arm/probes/decode-thumb.c index 2f0453a..985e7dd 100644 --- a/arch/arm/probes/decode-thumb.c +++ b/arch/arm/probes/decode-thumb.c @@ -863,20 +863,22 @@ static void __kprobes thumb32_singlestep(probes_opcode_t opcode, enum probes_insn __kprobes thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions) + bool emulate, const union decode_action *actions, + const struct decode_checker *checkers[]) { asi->insn_singlestep = thumb16_singlestep; asi->insn_check_cc = thumb_check_cc; return probes_decode_insn(insn, asi, probes_decode_thumb16_table, true, - emulate, actions); + emulate, actions, checkers); } enum probes_insn __kprobes thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions) + bool emulate, const union decode_action *actions, + const struct decode_checker *checkers[]) { asi->insn_singlestep = thumb32_singlestep; asi->insn_check_cc = thumb_check_cc; return probes_decode_insn(insn, asi, probes_decode_thumb32_table, true, - emulate, actions); + emulate, actions, checkers); } diff --git a/arch/arm/probes/decode-thumb.h b/arch/arm/probes/decode-thumb.h index 039013c..8457add 100644 --- a/arch/arm/probes/decode-thumb.h +++ b/arch/arm/probes/decode-thumb.h @@ -91,9 +91,11 @@ extern const union decode_item probes_decode_thumb16_table[]; enum probes_insn __kprobes thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions); + bool emulate, const union decode_action *actions, + const struct decode_checker *checkers[]); enum probes_insn __kprobes thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions); + bool emulate, const union decode_action *actions, + const struct decode_checker *checkers[]); #endif diff --git a/arch/arm/probes/decode.c b/arch/arm/probes/decode.c index 3b05d57..c7d4420 100644 --- a/arch/arm/probes/decode.c +++ b/arch/arm/probes/decode.c @@ -342,6 +342,31 @@ static const int decode_struct_sizes[NUM_DECODE_TYPES] = { [DECODE_TYPE_REJECT] = sizeof(struct decode_reject) }; +static int run_checkers(const struct decode_checker *checkers[], + int action, probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *h) +{ + const struct decode_checker **p; + + if (!checkers) + return INSN_GOOD; + + p = checkers; + while (*p != NULL) { + int retval; + probes_check_t *checker_func = (*p)[action].checker; + + retval = INSN_GOOD; + if (checker_func) + retval = checker_func(insn, asi, h); + if (retval == INSN_REJECTED) + return retval; + p++; + } + return INSN_GOOD; +} + /* * probes_decode_insn operates on data tables in order to decode an ARM * architecture instruction onto which a kprobe has been placed. @@ -388,11 +413,17 @@ static const int decode_struct_sizes[NUM_DECODE_TYPES] = { int __kprobes probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, const union decode_item *table, bool thumb, - bool emulate, const union decode_action *actions) + bool emulate, const union decode_action *actions, + const struct decode_checker *checkers[]) { const struct decode_header *h = (struct decode_header *)table; const struct decode_header *next; bool matched = false; + /* + * @insn can be modified by decode_regs. Save its original + * value for checkers. + */ + probes_opcode_t origin_insn = insn; if (emulate) insn = prepare_emulated_insn(insn, asi, thumb); @@ -422,24 +453,41 @@ probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, } case DECODE_TYPE_CUSTOM: { + int err; struct decode_custom *d = (struct decode_custom *)h; - return actions[d->decoder.action].decoder(insn, asi, h); + int action = d->decoder.action; + + err = run_checkers(checkers, action, origin_insn, asi, h); + if (err == INSN_REJECTED) + return INSN_REJECTED; + return actions[action].decoder(insn, asi, h); } case DECODE_TYPE_SIMULATE: { + int err; struct decode_simulate *d = (struct decode_simulate *)h; - asi->insn_handler = actions[d->handler.action].handler; + int action = d->handler.action; + + err = run_checkers(checkers, action, origin_insn, asi, h); + if (err == INSN_REJECTED) + return INSN_REJECTED; + asi->insn_handler = actions[action].handler; return INSN_GOOD_NO_SLOT; } case DECODE_TYPE_EMULATE: { + int err; struct decode_emulate *d = (struct decode_emulate *)h; + int action = d->handler.action; + + err = run_checkers(checkers, action, origin_insn, asi, h); + if (err == INSN_REJECTED) + return INSN_REJECTED; if (!emulate) - return actions[d->handler.action].decoder(insn, - asi, h); + return actions[action].decoder(insn, asi, h); - asi->insn_handler = actions[d->handler.action].handler; + asi->insn_handler = actions[action].handler; set_emulated_insn(insn, asi, thumb); return INSN_GOOD; } diff --git a/arch/arm/probes/decode.h b/arch/arm/probes/decode.h index 1d0b531..f9b08ba 100644 --- a/arch/arm/probes/decode.h +++ b/arch/arm/probes/decode.h @@ -314,6 +314,14 @@ union decode_action { probes_custom_decode_t *decoder; }; +typedef enum probes_insn (probes_check_t)(probes_opcode_t, + struct arch_probes_insn *, + const struct decode_header *); + +struct decode_checker { + probes_check_t *checker; +}; + #define DECODE_END \ {.bits = DECODE_TYPE_END} @@ -402,6 +410,7 @@ probes_insn_handler_t probes_emulate_none; int __kprobes probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, const union decode_item *table, bool thumb, bool emulate, - const union decode_action *actions); + const union decode_action *actions, + const struct decode_checker **checkers); #endif diff --git a/arch/arm/probes/kprobes/actions-arm.c b/arch/arm/probes/kprobes/actions-arm.c index 2206f2d..fbd93a9 100644 --- a/arch/arm/probes/kprobes/actions-arm.c +++ b/arch/arm/probes/kprobes/actions-arm.c @@ -339,3 +339,5 @@ const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = { [PROBES_BRANCH] = {.handler = simulate_bbl}, [PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm} }; + +const struct decode_checker *kprobes_arm_checkers[] = {NULL}; diff --git a/arch/arm/probes/kprobes/actions-thumb.c b/arch/arm/probes/kprobes/actions-thumb.c index 6c4e60b..2796121 100644 --- a/arch/arm/probes/kprobes/actions-thumb.c +++ b/arch/arm/probes/kprobes/actions-thumb.c @@ -664,3 +664,6 @@ const union decode_action kprobes_t32_actions[NUM_PROBES_T32_ACTIONS] = { [PROBES_T32_MUL_ADD_LONG] = { .handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags}, }; + +const struct decode_checker *kprobes_t32_checkers[] = {NULL}; +const struct decode_checker *kprobes_t16_checkers[] = {NULL}; diff --git a/arch/arm/probes/kprobes/core.c b/arch/arm/probes/kprobes/core.c index 701f49d..74f3dc3 100644 --- a/arch/arm/probes/kprobes/core.c +++ b/arch/arm/probes/kprobes/core.c @@ -61,6 +61,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) kprobe_decode_insn_t *decode_insn; const union decode_action *actions; int is; + const struct decode_checker **checkers; if (in_exception_text(addr)) return -EINVAL; @@ -74,9 +75,11 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) insn = __opcode_thumb32_compose(insn, inst2); decode_insn = thumb32_probes_decode_insn; actions = kprobes_t32_actions; + checkers = kprobes_t32_checkers; } else { decode_insn = thumb16_probes_decode_insn; actions = kprobes_t16_actions; + checkers = kprobes_t16_checkers; } #else /* !CONFIG_THUMB2_KERNEL */ thumb = false; @@ -85,12 +88,13 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) insn = __mem_to_opcode_arm(*p->addr); decode_insn = arm_probes_decode_insn; actions = kprobes_arm_actions; + checkers = kprobes_arm_checkers; #endif p->opcode = insn; p->ainsn.insn = tmp_insn; - switch ((*decode_insn)(insn, &p->ainsn, true, actions)) { + switch ((*decode_insn)(insn, &p->ainsn, true, actions, checkers)) { case INSN_REJECTED: /* not supported */ return -EINVAL; diff --git a/arch/arm/probes/kprobes/core.h b/arch/arm/probes/kprobes/core.h index 2e1e5a3..f88c79f 100644 --- a/arch/arm/probes/kprobes/core.h +++ b/arch/arm/probes/kprobes/core.h @@ -37,16 +37,19 @@ kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_probes_insn *asi, typedef enum probes_insn (kprobe_decode_insn_t)(probes_opcode_t, struct arch_probes_insn *, bool, - const union decode_action *); + const union decode_action *, + const struct decode_checker *[*]); #ifdef CONFIG_THUMB2_KERNEL extern const union decode_action kprobes_t32_actions[]; extern const union decode_action kprobes_t16_actions[]; - +extern const struct decode_checker *kprobes_t32_checkers[]; +extern const struct decode_checker *kprobes_t16_checkers[]; #else /* !CONFIG_THUMB2_KERNEL */ extern const union decode_action kprobes_arm_actions[]; +extern const struct decode_checker *kprobes_arm_checkers[]; #endif diff --git a/arch/arm/probes/uprobes/core.c b/arch/arm/probes/uprobes/core.c index b2954f6..d1329f1 100644 --- a/arch/arm/probes/uprobes/core.c +++ b/arch/arm/probes/uprobes/core.c @@ -88,7 +88,7 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN); ret = arm_probes_decode_insn(insn, &auprobe->asi, false, - uprobes_probes_actions); + uprobes_probes_actions, NULL); switch (ret) { case INSN_REJECTED: return -EINVAL; -- cgit v0.10.2 From b9dfe0bed999d23ee8838d389637dd8aef83fafa Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Fri, 9 Jan 2015 10:53:21 +0100 Subject: livepatch: handle ancient compilers with more grace We are aborting a build in case when gcc doesn't support fentry on x86_64 (regs->ip modification can't really reliably work with mcount). This however breaks allmodconfig for people with older gccs that don't support -mfentry. Turn the build-time failure into runtime failure, resulting in the whole infrastructure not being initialized if CC_USING_FENTRY is unset. Reported-by: Andrew Morton Signed-off-by: Jiri Kosina Signed-off-by: Andrew Morton Acked-by: Josh Poimboeuf diff --git a/arch/x86/include/asm/livepatch.h b/arch/x86/include/asm/livepatch.h index b5608d7..26e5813 100644 --- a/arch/x86/include/asm/livepatch.h +++ b/arch/x86/include/asm/livepatch.h @@ -25,9 +25,13 @@ #include #ifdef CONFIG_LIVE_PATCHING +static inline int klp_check_compiler_support(void) +{ #ifndef CC_USING_FENTRY -#error Your compiler must support -mfentry for live patching to work + return 1; #endif + return 0; +} extern int klp_write_module_reloc(struct module *mod, unsigned long type, unsigned long loc, unsigned long value); diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index 6f63879..ce42d3b 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c @@ -911,6 +911,12 @@ static int klp_init(void) { int ret; + ret = klp_check_compiler_support(); + if (ret) { + pr_info("Your compiler is too old; turning off.\n"); + return -EINVAL; + } + ret = register_module_notifier(&klp_module_nb); if (ret) return ret; -- cgit v0.10.2 From 005b3f574a0a53f476a62da5c861c1c9bf8177b7 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 8 Jan 2015 14:37:12 -0500 Subject: HID: logitech-hidpp: store the name of the device in struct hidpp If a disconnect occurs while getting the actual name of the device (which can take several HID transactions), the name of the device will be the hid name, provided by the Unifying Receiver. This means that in some cases, the user space will see a different name that what it usually sees when there is no disconnect. We should store the name of the device in the struct hidpp. That way, if a disconnect occurs while we are accessing the name, hidpp_connect_event() can fail, and the input node is not created. The input node will be created only if we have a connection which lasts long enough to retrieve all the requested information: name, protocol, and specific configuration. Reviewed-by: Peter Wu Tested-by: Peter Wu Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index cf57955..acb632d 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -89,6 +89,7 @@ struct hidpp_device { struct hid_device *hid_dev; struct mutex send_mutex; void *send_receive_buf; + char *name; /* will never be NULL and should not be freed */ wait_queue_head_t wait; bool answer_available; u8 protocol_major; @@ -1080,6 +1081,7 @@ static void hidpp_input_close(struct input_dev *dev) static struct input_dev *hidpp_allocate_input(struct hid_device *hdev) { struct input_dev *input_dev = devm_input_allocate_device(&hdev->dev); + struct hidpp_device *hidpp = hid_get_drvdata(hdev); if (!input_dev) return NULL; @@ -1088,7 +1090,7 @@ static struct input_dev *hidpp_allocate_input(struct hid_device *hdev) input_dev->open = hidpp_input_open; input_dev->close = hidpp_input_close; - input_dev->name = hdev->name; + input_dev->name = hidpp->name; input_dev->phys = hdev->phys; input_dev->uniq = hdev->uniq; input_dev->id.bustype = hdev->bus; @@ -1130,22 +1132,28 @@ static void hidpp_connect_event(struct hidpp_device *hidpp) hid_info(hdev, "HID++ %u.%u device connected.\n", hidpp->protocol_major, hidpp->protocol_minor); + if (!hidpp->name || hidpp->name == hdev->name) { + name = hidpp_get_device_name(hidpp); + if (!name) { + hid_err(hdev, + "unable to retrieve the name of the device"); + return; + } + + devm_name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s", name); + kfree(name); + if (!devm_name) + return; + + hidpp->name = devm_name; + } + input = hidpp_allocate_input(hdev); if (!input) { hid_err(hdev, "cannot allocate new input device: %d\n", ret); return; } - name = hidpp_get_device_name(hidpp); - if (!name) { - hid_err(hdev, "unable to retrieve the name of the device"); - } else { - devm_name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s", name); - if (devm_name) - input->name = devm_name; - kfree(name); - } - hidpp_populate_input(hidpp, input, false); ret = input_register_device(input); @@ -1168,6 +1176,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) return -ENOMEM; hidpp->hid_dev = hdev; + hidpp->name = hdev->name; hid_set_drvdata(hdev, hidpp); hidpp->quirks = id->driver_data; -- cgit v0.10.2 From a809ca5e03c6dd3b324d148564715ca5ea2b8103 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 9 Jan 2015 11:29:50 +0100 Subject: mac80211_hwsim: fix PS debugfs file locking The functions called within the iterators must be called with tasklets disabled, so use atomic iteration like the rest of the code and disable tasklets around the whole operation. Signed-off-by: Johannes Berg diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 057a99e..74f4e1c 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -626,22 +626,22 @@ static int hwsim_fops_ps_write(void *dat, u64 val) old_ps = data->ps; data->ps = val; + local_bh_disable(); if (val == PS_MANUAL_POLL) { - ieee80211_iterate_active_interfaces(data->hw, - IEEE80211_IFACE_ITER_NORMAL, - hwsim_send_ps_poll, data); + ieee80211_iterate_active_interfaces_atomic( + data->hw, IEEE80211_IFACE_ITER_NORMAL, + hwsim_send_ps_poll, data); data->ps_poll_pending = true; } else if (old_ps == PS_DISABLED && val != PS_DISABLED) { - ieee80211_iterate_active_interfaces(data->hw, - IEEE80211_IFACE_ITER_NORMAL, - hwsim_send_nullfunc_ps, - data); + ieee80211_iterate_active_interfaces_atomic( + data->hw, IEEE80211_IFACE_ITER_NORMAL, + hwsim_send_nullfunc_ps, data); } else if (old_ps != PS_DISABLED && val == PS_DISABLED) { - ieee80211_iterate_active_interfaces(data->hw, - IEEE80211_IFACE_ITER_NORMAL, - hwsim_send_nullfunc_no_ps, - data); + ieee80211_iterate_active_interfaces_atomic( + data->hw, IEEE80211_IFACE_ITER_NORMAL, + hwsim_send_nullfunc_no_ps, data); } + local_bh_enable(); return 0; } -- cgit v0.10.2 From 9b7a86f3514dd5cb4a6787292781daae65a29a10 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 9 Jan 2015 11:40:39 +0100 Subject: mac80211: fix handling TIM IE when stations disconnect When a station disconnects with frames still pending, we clear the TIM bit, but too late - it's only cleared when the station is already removed from the driver, and thus the driver can get confused (and hwsim will loudly complain.) Fix this by clearing the TIM bit earlier, when the station has been unlinked but not removed from the driver yet. To do this, refactor the TIM recalculation to in that case ignore traffic and simply assume no pending traffic - this is correct for the disconnected station even though the frames haven't been freed yet at that point. This patch isn't needed for current drivers though as they don't check the station argument to the set_tim() operation and thus don't really run into the possible confusion. Reported-by: Jouni Malinen Signed-off-by: Johannes Berg diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index dc352fc..79383ef 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -116,7 +116,6 @@ static void __cleanup_single_sta(struct sta_info *sta) clear_sta_flag(sta, WLAN_STA_PS_DELIVER); atomic_dec(&ps->num_sta_ps); - sta_info_recalc_tim(sta); } for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { @@ -625,7 +624,7 @@ static unsigned long ieee80211_tids_for_ac(int ac) } } -void sta_info_recalc_tim(struct sta_info *sta) +static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending) { struct ieee80211_local *local = sta->local; struct ps_data *ps; @@ -667,6 +666,9 @@ void sta_info_recalc_tim(struct sta_info *sta) if (ignore_for_tim == BIT(IEEE80211_NUM_ACS) - 1) ignore_for_tim = 0; + if (ignore_pending) + ignore_for_tim = BIT(IEEE80211_NUM_ACS) - 1; + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { unsigned long tids; @@ -695,7 +697,7 @@ void sta_info_recalc_tim(struct sta_info *sta) else __bss_tim_clear(ps->tim, id); - if (local->ops->set_tim) { + if (local->ops->set_tim && !WARN_ON(sta->dead)) { local->tim_in_locked_section = true; drv_set_tim(local, &sta->sta, indicate_tim); local->tim_in_locked_section = false; @@ -705,6 +707,11 @@ out_unlock: spin_unlock_bh(&local->tim_lock); } +void sta_info_recalc_tim(struct sta_info *sta) +{ + __sta_info_recalc_tim(sta, false); +} + static bool sta_info_buffer_expired(struct sta_info *sta, struct sk_buff *skb) { struct ieee80211_tx_info *info; @@ -888,6 +895,9 @@ static void __sta_info_destroy_part2(struct sta_info *sta) /* now keys can no longer be reached */ ieee80211_free_sta_keys(local, sta); + /* disable TIM bit - last chance to tell driver */ + __sta_info_recalc_tim(sta, true); + sta->dead = true; local->num_sta--; -- cgit v0.10.2 From 8f6cb409f016e52b753a93e393622770556d1ab9 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Mon, 5 Jan 2015 13:25:10 -0500 Subject: GFS2: Eliminate __gfs2_glock_remove_from_lru Since the only caller of function __gfs2_glock_remove_from_lru locks the same spin_lock as gfs2_glock_remove_from_lru, the functions can be combined. Signed-off-by: Bob Peterson Signed-off-by: Steven Whitehouse diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index a23524a..aeb7bc9 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -173,19 +173,14 @@ void gfs2_glock_add_to_lru(struct gfs2_glock *gl) spin_unlock(&lru_lock); } -static void __gfs2_glock_remove_from_lru(struct gfs2_glock *gl) +static void gfs2_glock_remove_from_lru(struct gfs2_glock *gl) { + spin_lock(&lru_lock); if (!list_empty(&gl->gl_lru)) { list_del_init(&gl->gl_lru); atomic_dec(&lru_count); clear_bit(GLF_LRU, &gl->gl_flags); } -} - -static void gfs2_glock_remove_from_lru(struct gfs2_glock *gl) -{ - spin_lock(&lru_lock); - __gfs2_glock_remove_from_lru(gl); spin_unlock(&lru_lock); } @@ -205,9 +200,7 @@ void gfs2_glock_put(struct gfs2_glock *gl) lockref_mark_dead(&gl->gl_lockref); - spin_lock(&lru_lock); - __gfs2_glock_remove_from_lru(gl); - spin_unlock(&lru_lock); + gfs2_glock_remove_from_lru(gl); spin_unlock(&gl->gl_lockref.lock); spin_lock_bucket(gl->gl_hash); hlist_bl_del_rcu(&gl->gl_list); -- cgit v0.10.2 From e5a7a72cd51a585b8f1a1e299bf88fff44b94440 Mon Sep 17 00:00:00 2001 From: Robin Gong Date: Fri, 9 Jan 2015 09:57:33 +0800 Subject: regulator: pfuze100-regulator: add pfuze3000 support Add pfuze3000 chip support. Signed-off-by: Robin Gong Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/regulator/pfuze100.txt b/Documentation/devicetree/bindings/regulator/pfuze100.txt index 34ef5d1..9b40db8 100644 --- a/Documentation/devicetree/bindings/regulator/pfuze100.txt +++ b/Documentation/devicetree/bindings/regulator/pfuze100.txt @@ -1,7 +1,7 @@ PFUZE100 family of regulators Required properties: -- compatible: "fsl,pfuze100" or "fsl,pfuze200" +- compatible: "fsl,pfuze100", "fsl,pfuze200", "fsl,pfuze3000" - reg: I2C slave address Required child node: @@ -14,6 +14,8 @@ Required child node: sw1ab,sw1c,sw2,sw3a,sw3b,sw4,swbst,vsnvs,vrefddr,vgen1~vgen6 --PFUZE200 sw1ab,sw2,sw3a,sw3b,swbst,vsnvs,vrefddr,vgen1~vgen6 + --PFUZE3000 + sw1a,sw1b,sw2,sw3,swbst,vsnvs,vrefddr,vldo1,vldo2,vccsd,v33,vldo3,vldo4 Each regulator is defined using the standard binding for regulators. @@ -205,3 +207,93 @@ Example 2: PFUZE200 }; }; }; + +Example 3: PFUZE3000 + + pmic: pfuze3000@08 { + compatible = "fsl,pfuze3000"; + reg = <0x08>; + + regulators { + sw1a_reg: sw1a { + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1475000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <6250>; + }; + /* use sw1c_reg to align with pfuze100/pfuze200 */ + sw1c_reg: sw1b { + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1475000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <6250>; + }; + + sw2_reg: sw2 { + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3a_reg: sw3 { + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1650000>; + regulator-boot-on; + regulator-always-on; + }; + + swbst_reg: swbst { + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5150000>; + }; + + snvs_reg: vsnvs { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3000000>; + regulator-boot-on; + regulator-always-on; + }; + + vref_reg: vrefddr { + regulator-boot-on; + regulator-always-on; + }; + + vgen1_reg: vldo1 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen2_reg: vldo2 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1550000>; + }; + + vgen3_reg: vccsd { + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen4_reg: v33 { + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <3300000>; + }; + + vgen5_reg: vldo3 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen6_reg: vldo4 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; + }; diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c index c879dff..8cc8d18 100644 --- a/drivers/regulator/pfuze100-regulator.c +++ b/drivers/regulator/pfuze100-regulator.c @@ -56,7 +56,7 @@ #define PFUZE100_VGEN5VOL 0x70 #define PFUZE100_VGEN6VOL 0x71 -enum chips { PFUZE100, PFUZE200 }; +enum chips { PFUZE100, PFUZE200, PFUZE3000 = 3 }; struct pfuze_regulator { struct regulator_desc desc; @@ -80,9 +80,18 @@ static const int pfuze100_vsnvs[] = { 1000000, 1100000, 1200000, 1300000, 1500000, 1800000, 3000000, }; +static const int pfuze3000_sw2lo[] = { + 1500000, 1550000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, +}; + +static const int pfuze3000_sw2hi[] = { + 2500000, 2800000, 2850000, 3000000, 3100000, 3150000, 3200000, 3300000, +}; + static const struct i2c_device_id pfuze_device_id[] = { {.name = "pfuze100", .driver_data = PFUZE100}, {.name = "pfuze200", .driver_data = PFUZE200}, + {.name = "pfuze3000", .driver_data = PFUZE3000}, { } }; MODULE_DEVICE_TABLE(i2c, pfuze_device_id); @@ -90,6 +99,7 @@ MODULE_DEVICE_TABLE(i2c, pfuze_device_id); static const struct of_device_id pfuze_dt_ids[] = { { .compatible = "fsl,pfuze100", .data = (void *)PFUZE100}, { .compatible = "fsl,pfuze200", .data = (void *)PFUZE200}, + { .compatible = "fsl,pfuze3000", .data = (void *)PFUZE3000}, { } }; MODULE_DEVICE_TABLE(of, pfuze_dt_ids); @@ -219,6 +229,60 @@ static struct regulator_ops pfuze100_swb_regulator_ops = { .stby_mask = 0x20, \ } +#define PFUZE3000_VCC_REG(_chip, _name, base, min, max, step) { \ + .desc = { \ + .name = #_name, \ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &pfuze100_ldo_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = _chip ## _ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (base), \ + .vsel_mask = 0x3, \ + .enable_reg = (base), \ + .enable_mask = 0x10, \ + }, \ + .stby_reg = (base), \ + .stby_mask = 0x20, \ +} + + +#define PFUZE3000_SW2_REG(_chip, _name, base, min, max, step) { \ + .desc = { \ + .name = #_name,\ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &pfuze100_sw_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = _chip ## _ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \ + .vsel_mask = 0x7, \ + }, \ + .stby_reg = (base) + PFUZE100_STANDBY_OFFSET, \ + .stby_mask = 0x7, \ +} + +#define PFUZE3000_SW3_REG(_chip, _name, base, min, max, step) { \ + .desc = { \ + .name = #_name,\ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &pfuze100_sw_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = _chip ## _ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \ + .vsel_mask = 0xf, \ + }, \ + .stby_reg = (base) + PFUZE100_STANDBY_OFFSET, \ + .stby_mask = 0xf, \ +} + /* PFUZE100 */ static struct pfuze_regulator pfuze100_regulators[] = { PFUZE100_SW_REG(PFUZE100, SW1AB, PFUZE100_SW1ABVOL, 300000, 1875000, 25000), @@ -254,6 +318,22 @@ static struct pfuze_regulator pfuze200_regulators[] = { PFUZE100_VGEN_REG(PFUZE200, VGEN6, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), }; +static struct pfuze_regulator pfuze3000_regulators[] = { + PFUZE100_SW_REG(PFUZE3000, SW1A, PFUZE100_SW1ABVOL, 700000, 1475000, 25000), + PFUZE100_SW_REG(PFUZE3000, SW1B, PFUZE100_SW1CVOL, 700000, 1475000, 25000), + PFUZE100_SWB_REG(PFUZE3000, SW2, PFUZE100_SW2VOL, 0x7, pfuze3000_sw2lo), + PFUZE3000_SW3_REG(PFUZE3000, SW3, PFUZE100_SW3AVOL, 900000, 1650000, 50000), + PFUZE100_SWB_REG(PFUZE3000, SWBST, PFUZE100_SWBSTCON1, 0x3, pfuze100_swbst), + PFUZE100_SWB_REG(PFUZE3000, VSNVS, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs), + PFUZE100_FIXED_REG(PFUZE3000, VREFDDR, PFUZE100_VREFDDRCON, 750000), + PFUZE100_VGEN_REG(PFUZE3000, VLDO1, PFUZE100_VGEN1VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(PFUZE3000, VLDO2, PFUZE100_VGEN2VOL, 800000, 1550000, 50000), + PFUZE3000_VCC_REG(PFUZE3000, VCCSD, PFUZE100_VGEN3VOL, 2850000, 3300000, 150000), + PFUZE3000_VCC_REG(PFUZE3000, V33, PFUZE100_VGEN4VOL, 2850000, 3300000, 150000), + PFUZE100_VGEN_REG(PFUZE3000, VLDO3, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(PFUZE3000, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), +}; + static struct pfuze_regulator *pfuze_regulators; #ifdef CONFIG_OF @@ -294,6 +374,24 @@ static struct of_regulator_match pfuze200_matches[] = { { .name = "vgen6", }, }; +/* PFUZE3000 */ +static struct of_regulator_match pfuze3000_matches[] = { + + { .name = "sw1a", }, + { .name = "sw1b", }, + { .name = "sw2", }, + { .name = "sw3", }, + { .name = "swbst", }, + { .name = "vsnvs", }, + { .name = "vrefddr", }, + { .name = "vldo1", }, + { .name = "vldo2", }, + { .name = "vccsd", }, + { .name = "v33", }, + { .name = "vldo3", }, + { .name = "vldo4", }, +}; + static struct of_regulator_match *pfuze_matches; static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) @@ -313,6 +411,11 @@ static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) } switch (chip->chip_id) { + case PFUZE3000: + pfuze_matches = pfuze3000_matches; + ret = of_regulator_match(dev, parent, pfuze3000_matches, + ARRAY_SIZE(pfuze3000_matches)); + break; case PFUZE200: pfuze_matches = pfuze200_matches; ret = of_regulator_match(dev, parent, pfuze200_matches, @@ -378,7 +481,8 @@ static int pfuze_identify(struct pfuze_chip *pfuze_chip) * as ID=8 in PFUZE100 */ dev_info(pfuze_chip->dev, "Assuming misprogrammed ID=0x8"); - } else if ((value & 0x0f) != pfuze_chip->chip_id) { + } else if ((value & 0x0f) != pfuze_chip->chip_id && + (value & 0xf0) >> 4 != pfuze_chip->chip_id) { /* device id NOT match with your setting */ dev_warn(pfuze_chip->dev, "Illegal ID: %x\n", value); return -ENODEV; @@ -417,7 +521,7 @@ static int pfuze100_regulator_probe(struct i2c_client *client, int i, ret; const struct of_device_id *match; u32 regulator_num; - u32 sw_check_start, sw_check_end; + u32 sw_check_start, sw_check_end, sw_hi = 0x40; pfuze_chip = devm_kzalloc(&client->dev, sizeof(*pfuze_chip), GFP_KERNEL); @@ -458,13 +562,19 @@ static int pfuze100_regulator_probe(struct i2c_client *client, /* use the right regulators after identify the right device */ switch (pfuze_chip->chip_id) { + case PFUZE3000: + pfuze_regulators = pfuze3000_regulators; + regulator_num = ARRAY_SIZE(pfuze3000_regulators); + sw_check_start = PFUZE3000_SW2; + sw_check_end = PFUZE3000_SW2; + sw_hi = 1 << 3; + break; case PFUZE200: pfuze_regulators = pfuze200_regulators; regulator_num = ARRAY_SIZE(pfuze200_regulators); sw_check_start = PFUZE200_SW2; sw_check_end = PFUZE200_SW3B; break; - case PFUZE100: default: pfuze_regulators = pfuze100_regulators; @@ -474,7 +584,8 @@ static int pfuze100_regulator_probe(struct i2c_client *client, break; } dev_info(&client->dev, "pfuze%s found.\n", - (pfuze_chip->chip_id == PFUZE100) ? "100" : "200"); + (pfuze_chip->chip_id == PFUZE100) ? "100" : + ((pfuze_chip->chip_id == PFUZE200) ? "200" : "3000")); memcpy(pfuze_chip->regulator_descs, pfuze_regulators, sizeof(pfuze_chip->regulator_descs)); @@ -498,10 +609,15 @@ static int pfuze100_regulator_probe(struct i2c_client *client, /* SW2~SW4 high bit check and modify the voltage value table */ if (i >= sw_check_start && i <= sw_check_end) { regmap_read(pfuze_chip->regmap, desc->vsel_reg, &val); - if (val & 0x40) { - desc->min_uV = 800000; - desc->uV_step = 50000; - desc->n_voltages = 51; + if (val & sw_hi) { + if (pfuze_chip->chip_id == PFUZE3000) { + desc->volt_table = pfuze3000_sw2hi; + desc->n_voltages = ARRAY_SIZE(pfuze3000_sw2hi); + } else { + desc->min_uV = 800000; + desc->uV_step = 50000; + desc->n_voltages = 51; + } } } diff --git a/include/linux/regulator/pfuze100.h b/include/linux/regulator/pfuze100.h index 364f7a7..70c6c66 100644 --- a/include/linux/regulator/pfuze100.h +++ b/include/linux/regulator/pfuze100.h @@ -49,6 +49,20 @@ #define PFUZE200_VGEN5 11 #define PFUZE200_VGEN6 12 +#define PFUZE3000_SW1A 0 +#define PFUZE3000_SW1B 1 +#define PFUZE3000_SW2 2 +#define PFUZE3000_SW3 3 +#define PFUZE3000_SWBST 4 +#define PFUZE3000_VSNVS 5 +#define PFUZE3000_VREFDDR 6 +#define PFUZE3000_VLDO1 7 +#define PFUZE3000_VLDO2 8 +#define PFUZE3000_VCCSD 9 +#define PFUZE3000_V33 10 +#define PFUZE3000_VLDO3 11 +#define PFUZE3000_VLDO4 12 + struct regulator_init_data; struct pfuze_regulator_platform_data { -- cgit v0.10.2 From d6b984816b902c73b2273c6088f52660a54c2034 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 29 Dec 2014 08:24:01 +0200 Subject: wlcore: fix WLCORE_VENDOR_ATTR_GROUP_KEY policy The attribute type is binary data (with max length). Signed-off-by: Eliad Peller Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ti/wlcore/vendor_cmd.c b/drivers/net/wireless/ti/wlcore/vendor_cmd.c index ad86a48..fd4e9ba 100644 --- a/drivers/net/wireless/ti/wlcore/vendor_cmd.c +++ b/drivers/net/wireless/ti/wlcore/vendor_cmd.c @@ -21,7 +21,7 @@ static const struct nla_policy wlcore_vendor_attr_policy[NUM_WLCORE_VENDOR_ATTR] = { [WLCORE_VENDOR_ATTR_FREQ] = { .type = NLA_U32 }, [WLCORE_VENDOR_ATTR_GROUP_ID] = { .type = NLA_U32 }, - [WLCORE_VENDOR_ATTR_GROUP_KEY] = { .type = NLA_U32, + [WLCORE_VENDOR_ATTR_GROUP_KEY] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, }; -- cgit v0.10.2 From 16129d1d59be0f330f80b33fef8a7a7d7b18394d Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 29 Dec 2014 08:24:02 +0200 Subject: wlcore: fix sparse warning Use kstrtoul_from_user() for reading the user value, and fix the following sparse warning: drivers/net/wireless/ti/wlcore/debugfs.c:937:15: error: incompatible types in comparison expression (different type sizes) Signed-off-by: Eliad Peller Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index 0be21f6..68f3bf2 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -929,17 +929,10 @@ static ssize_t beacon_filtering_write(struct file *file, { struct wl1271 *wl = file->private_data; struct wl12xx_vif *wlvif; - char buf[10]; - size_t len; unsigned long value; int ret; - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - buf[len] = '\0'; - - ret = kstrtoul(buf, 0, &value); + ret = kstrtoul_from_user(user_buf, count, 0, &value); if (ret < 0) { wl1271_warning("illegal value for beacon_filtering!"); return -EINVAL; -- cgit v0.10.2 From 7d3b29e5c86e0da38052d33fdd1f195d4591c6b2 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 29 Dec 2014 08:24:03 +0200 Subject: wlcore/wl18xx: handle rc updates in a separate work sta_rc_update runs in atomic context. thus, a new work should be scheduled in order to configure the fw with the required configuration. Signed-off-by: Eliad Peller Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 8e56261..96dbe71 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1559,26 +1559,19 @@ static u32 wl18xx_pre_pkt_send(struct wl1271 *wl, } static void wl18xx_sta_rc_update(struct wl1271 *wl, - struct wl12xx_vif *wlvif, - struct ieee80211_sta *sta, - u32 changed) + struct wl12xx_vif *wlvif) { - bool wide = sta->bandwidth >= IEEE80211_STA_RX_BW_40; + bool wide = wlvif->rc_update_bw >= IEEE80211_STA_RX_BW_40; wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update wide %d", wide); - if (!(changed & IEEE80211_RC_BW_CHANGED)) - return; - - mutex_lock(&wl->mutex); - /* sanity */ if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS)) - goto out; + return; /* ignore the change before association */ if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) - goto out; + return; /* * If we started out as wide, we can change the operation mode. If we @@ -1589,9 +1582,6 @@ static void wl18xx_sta_rc_update(struct wl1271 *wl, wl18xx_acx_peer_ht_operation_mode(wl, wlvif->sta.hlid, wide); else ieee80211_connection_loss(wl12xx_wlvif_to_vif(wlvif)); - -out: - mutex_unlock(&wl->mutex); } static int wl18xx_set_peer_cap(struct wl1271 *wl, diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index aa9f82c..29ce55f 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -211,11 +211,10 @@ wlcore_hw_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len) } static inline void -wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif, - struct ieee80211_sta *sta, u32 changed) +wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif) { if (wl->ops->sta_rc_update) - wl->ops->sta_rc_update(wl, wlvif, sta, changed); + wl->ops->sta_rc_update(wl, wlvif); } static inline int diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 6ad3fce..7b32b45 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -226,6 +226,29 @@ void wl12xx_rearm_tx_watchdog_locked(struct wl1271 *wl) msecs_to_jiffies(wl->conf.tx.tx_watchdog_timeout)); } +static void wlcore_rc_update_work(struct work_struct *work) +{ + int ret; + struct wl12xx_vif *wlvif = container_of(work, struct wl12xx_vif, + rc_update_work); + struct wl1271 *wl = wlvif->wl; + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + wlcore_hw_sta_rc_update(wl, wlvif); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + static void wl12xx_tx_watchdog_work(struct work_struct *work) { struct delayed_work *dwork; @@ -2279,6 +2302,7 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) wl1271_rx_streaming_enable_work); INIT_WORK(&wlvif->rx_streaming_disable_work, wl1271_rx_streaming_disable_work); + INIT_WORK(&wlvif->rc_update_work, wlcore_rc_update_work); INIT_DELAYED_WORK(&wlvif->channel_switch_work, wlcore_channel_switch_work); INIT_DELAYED_WORK(&wlvif->connection_loss_work, @@ -2723,6 +2747,7 @@ unlock: del_timer_sync(&wlvif->rx_streaming_timer); cancel_work_sync(&wlvif->rx_streaming_enable_work); cancel_work_sync(&wlvif->rx_streaming_disable_work); + cancel_work_sync(&wlvif->rc_update_work); cancel_delayed_work_sync(&wlvif->connection_loss_work); cancel_delayed_work_sync(&wlvif->channel_switch_work); cancel_delayed_work_sync(&wlvif->pending_auth_complete_work); @@ -5370,9 +5395,15 @@ static void wlcore_op_sta_rc_update(struct ieee80211_hw *hw, u32 changed) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); - struct wl1271 *wl = hw->priv; - wlcore_hw_sta_rc_update(wl, wlvif, sta, changed); + wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update"); + + if (!(changed & IEEE80211_RC_BW_CHANGED)) + return; + + /* this callback is atomic, so schedule a new work */ + wlvif->rc_update_bw = sta->bandwidth; + ieee80211_queue_work(hw, &wlvif->rc_update_work); } static int wlcore_op_get_rssi(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index df78cf1..2440ebe 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -106,8 +106,7 @@ struct wlcore_ops { struct wl12xx_vif *wlvif, struct ieee80211_channel_switch *ch_switch); u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len); - void (*sta_rc_update)(struct wl1271 *wl, struct wl12xx_vif *wlvif, - struct ieee80211_sta *sta, u32 changed); + void (*sta_rc_update)(struct wl1271 *wl, struct wl12xx_vif *wlvif); int (*set_peer_cap)(struct wl1271 *wl, struct ieee80211_sta_ht_cap *ht_cap, bool allow_ht_operation, diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 0e52556..811851d 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -463,6 +463,10 @@ struct wl12xx_vif { /* work for canceling ROC after pending auth reply */ struct delayed_work pending_auth_complete_work; + /* update rate conrol */ + enum ieee80211_sta_rx_bandwidth rc_update_bw; + struct work_struct rc_update_work; + /* * total freed FW packets on the link. * For STA this holds the PN of the link to the AP. -- cgit v0.10.2 From 6d5a748d4836ddd0ca626fe4870942a0e90a5c3d Mon Sep 17 00:00:00 2001 From: Ram Amrani Date: Mon, 29 Dec 2014 08:24:04 +0200 Subject: wlcore: add ability to reduce FW interrupts during suspend Add the ability to mask FW interrupts on RX BA activity, PSM entry/exit and fast-link notifications. This is used when the host is suspended in order to decrease redundant wake ups. Signed-off-by: Ram Amrani Signed-off-by: Arik Nemtsov Signed-off-by: Eliad Peller Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index d6d0d6d..f3cee5a 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -250,6 +250,7 @@ static struct wlcore_conf wl12xx_conf = { .keep_alive_interval = 55000, .max_listen_interval = 20, .sta_sleep_auth = WL1271_PSM_ILLEGAL, + .suspend_rx_ba_activity = 0, }, .itrim = { .enable = false, @@ -1728,6 +1729,8 @@ static struct wlcore_ops wl12xx_ops = { .convert_hwaddr = wl12xx_convert_hwaddr, .lnk_high_prio = wl12xx_lnk_high_prio, .lnk_low_prio = wl12xx_lnk_low_prio, + .interrupt_notify = NULL, + .rx_ba_filter = NULL, }; static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { diff --git a/drivers/net/wireless/ti/wl18xx/acx.c b/drivers/net/wireless/ti/wl18xx/acx.c index a169bb5..9d4b9aa 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.c +++ b/drivers/net/wireless/ti/wl18xx/acx.c @@ -194,3 +194,59 @@ out: kfree(acx); return ret; } + +/* + * When the host is suspended, we don't want to get any fast-link/PSM + * notifications + */ +int wl18xx_acx_interrupt_notify_config(struct wl1271 *wl, + bool action) +{ + struct wl18xx_acx_interrupt_notify *acx; + int ret = 0; + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->enable = action; + ret = wl1271_cmd_configure(wl, ACX_INTERRUPT_NOTIFY, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx interrupt notify setting failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +/* + * When the host is suspended, we can configure the FW to disable RX BA + * notifications. + */ +int wl18xx_acx_rx_ba_filter(struct wl1271 *wl, bool action) +{ + struct wl18xx_acx_rx_ba_filter *acx; + int ret = 0; + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->enable = (u32)action; + ret = wl1271_cmd_configure(wl, ACX_RX_BA_FILTER, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx rx ba activity filter setting failed: %d", + ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/ti/wl18xx/acx.h b/drivers/net/wireless/ti/wl18xx/acx.h index 0e636de..1234bdc 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.h +++ b/drivers/net/wireless/ti/wl18xx/acx.h @@ -32,7 +32,10 @@ enum { ACX_SIM_CONFIG = 0x0053, ACX_CLEAR_STATISTICS = 0x0054, ACX_AUTO_RX_STREAMING = 0x0055, - ACX_PEER_CAP = 0x0056 + ACX_PEER_CAP = 0x0056, + ACX_INTERRUPT_NOTIFY = 0x0057, + ACX_RX_BA_FILTER = 0x0058 + }; /* numbers of bits the length field takes (add 1 for the actual number) */ @@ -326,6 +329,24 @@ struct wlcore_acx_peer_cap { u8 padding; } __packed; +/* + * ACX_INTERRUPT_NOTIFY + * enable/disable fast-link/PSM notification from FW + */ +struct wl18xx_acx_interrupt_notify { + struct acx_header header; + u32 enable; +}; + +/* + * ACX_RX_BA_FILTER + * enable/disable RX BA filtering in FW + */ +struct wl18xx_acx_rx_ba_filter { + struct acx_header header; + u32 enable; +}; + int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, u32 sdio_blk_size, u32 extra_mem_blks, u32 len_field_size); @@ -336,5 +357,7 @@ int wl18xx_acx_set_peer_cap(struct wl1271 *wl, struct ieee80211_sta_ht_cap *ht_cap, bool allow_ht_operation, u32 rate_set, u8 hlid); +int wl18xx_acx_interrupt_notify_config(struct wl1271 *wl, bool action); +int wl18xx_acx_rx_ba_filter(struct wl1271 *wl, bool action); #endif /* __WL18XX_ACX_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 96dbe71..6c1000d 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -378,6 +378,7 @@ static struct wlcore_conf wl18xx_conf = { .keep_alive_interval = 55000, .max_listen_interval = 20, .sta_sleep_auth = WL1271_PSM_ILLEGAL, + .suspend_rx_ba_activity = 0, }, .itrim = { .enable = false, @@ -1693,6 +1694,8 @@ static struct wlcore_ops wl18xx_ops = { .smart_config_start = wl18xx_cmd_smart_config_start, .smart_config_stop = wl18xx_cmd_smart_config_stop, .smart_config_set_group_key = wl18xx_cmd_smart_config_set_group_key, + .interrupt_notify = wl18xx_acx_interrupt_notify_config, + .rx_ba_filter = wl18xx_acx_rx_ba_filter, }; /* HT cap appropriate for wide channels in 2Ghz */ diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index 40995c4..166add0 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -997,6 +997,11 @@ struct conf_conn_settings { * whether we can go to ELP. */ u8 sta_sleep_auth; + + /* + * Default RX BA Activity filter configuration + */ + u8 suspend_rx_ba_activity; } __packed; enum { @@ -1347,7 +1352,7 @@ struct conf_recovery_settings { * version, the two LSB are the lower driver's private conf * version. */ -#define WLCORE_CONF_VERSION (0x0005 << 16) +#define WLCORE_CONF_VERSION (0x0006 << 16) #define WLCORE_CONF_MASK 0xffff0000 #define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \ sizeof(struct wlcore_conf)) diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 29ce55f..c2545ce 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -218,6 +218,22 @@ wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif) } static inline int +wlcore_hw_interrupt_notify(struct wl1271 *wl, bool action) +{ + if (wl->ops->interrupt_notify) + return wl->ops->interrupt_notify(wl, action); + return 0; +} + +static inline int +wlcore_hw_rx_ba_filter(struct wl1271 *wl, bool action) +{ + if (wl->ops->rx_ba_filter) + return wl->ops->rx_ba_filter(wl, action); + return 0; +} + +static inline int wlcore_hw_set_peer_cap(struct wl1271 *wl, struct ieee80211_sta_ht_cap *ht_cap, bool allow_ht_operation, diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 7b32b45..de3bf78 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1685,19 +1685,15 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl, if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) goto out; - ret = wl1271_ps_elp_wakeup(wl); - if (ret < 0) - goto out; - ret = wl1271_configure_wowlan(wl, wow); if (ret < 0) - goto out_sleep; + goto out; if ((wl->conf.conn.suspend_wake_up_event == wl->conf.conn.wake_up_event) && (wl->conf.conn.suspend_listen_interval == wl->conf.conn.listen_interval)) - goto out_sleep; + goto out; ret = wl1271_acx_wake_up_conditions(wl, wlvif, wl->conf.conn.suspend_wake_up_event, @@ -1705,9 +1701,6 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl, if (ret < 0) wl1271_error("suspend: set wake up conditions failed: %d", ret); - -out_sleep: - wl1271_ps_elp_sleep(wl); out: return ret; @@ -1721,13 +1714,8 @@ static int wl1271_configure_suspend_ap(struct wl1271 *wl, if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) goto out; - ret = wl1271_ps_elp_wakeup(wl); - if (ret < 0) - goto out; - ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true); - wl1271_ps_elp_sleep(wl); out: return ret; @@ -1756,10 +1744,6 @@ static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif) if (is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) return; - ret = wl1271_ps_elp_wakeup(wl); - if (ret < 0) - return; - if (is_sta) { wl1271_configure_wowlan(wl, NULL); @@ -1767,7 +1751,7 @@ static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif) wl->conf.conn.wake_up_event) && (wl->conf.conn.suspend_listen_interval == wl->conf.conn.listen_interval)) - goto out_sleep; + return; ret = wl1271_acx_wake_up_conditions(wl, wlvif, wl->conf.conn.wake_up_event, @@ -1780,9 +1764,6 @@ static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif) } else if (is_ap) { ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false); } - -out_sleep: - wl1271_ps_elp_sleep(wl); } static int wl1271_op_suspend(struct ieee80211_hw *hw, @@ -1804,6 +1785,11 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw, wl1271_tx_flush(wl); mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + return ret; + wl->wow_enabled = true; wl12xx_for_each_wlvif(wl, wlvif) { ret = wl1271_configure_suspend(wl, wlvif, wow); @@ -1813,7 +1799,27 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw, return ret; } } + + /* disable fast link flow control notifications from FW */ + ret = wlcore_hw_interrupt_notify(wl, false); + if (ret < 0) + goto out_sleep; + + /* if filtering is enabled, configure the FW to drop all RX BA frames */ + ret = wlcore_hw_rx_ba_filter(wl, + !!wl->conf.conn.suspend_rx_ba_activity); + if (ret < 0) + goto out_sleep; + +out_sleep: + wl1271_ps_elp_sleep(wl); mutex_unlock(&wl->mutex); + + if (ret < 0) { + wl1271_warning("couldn't prepare device to suspend"); + return ret; + } + /* flush any remaining work */ wl1271_debug(DEBUG_MAC80211, "flushing remaining works"); @@ -1887,13 +1893,29 @@ static int wl1271_op_resume(struct ieee80211_hw *hw) if (pending_recovery) { wl1271_warning("queuing forgotten recovery on resume"); ieee80211_queue_work(wl->hw, &wl->recovery_work); - goto out; + goto out_sleep; } + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + wl12xx_for_each_wlvif(wl, wlvif) { wl1271_configure_resume(wl, wlvif); } + ret = wlcore_hw_interrupt_notify(wl, true); + if (ret < 0) + goto out_sleep; + + /* if filtering is enabled, configure the FW to drop all RX BA frames */ + ret = wlcore_hw_rx_ba_filter(wl, false); + if (ret < 0) + goto out_sleep; + +out_sleep: + wl1271_ps_elp_sleep(wl); + out: wl->wow_enabled = false; diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 2440ebe..7860a4e 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -116,6 +116,8 @@ struct wlcore_ops { struct wl1271_link *lnk); bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid, struct wl1271_link *lnk); + int (*interrupt_notify)(struct wl1271 *wl, bool action); + int (*rx_ba_filter)(struct wl1271 *wl, bool action); int (*smart_config_start)(struct wl1271 *wl, u32 group_bitmap); int (*smart_config_stop)(struct wl1271 *wl); int (*smart_config_set_group_key)(struct wl1271 *wl, u16 group_id, -- cgit v0.10.2 From b8714d1b6a7ee4c4e4730203a90e1db6485d9343 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 29 Dec 2014 08:24:05 +0200 Subject: wlcore: enable AP wowlan configure wowlan when host is suspended in AP mode, since the FW can now wake the host up on Rx. Signed-off-by: Kobi Leibovitch Signed-off-by: Arik Nemtsov Signed-off-by: Eliad Peller Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index de3bf78..caff699 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1707,7 +1707,8 @@ out: } static int wl1271_configure_suspend_ap(struct wl1271 *wl, - struct wl12xx_vif *wlvif) + struct wl12xx_vif *wlvif, + struct cfg80211_wowlan *wow) { int ret = 0; @@ -1715,6 +1716,12 @@ static int wl1271_configure_suspend_ap(struct wl1271 *wl, goto out; ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true); + if (ret < 0) + goto out; + + ret = wl1271_configure_wowlan(wl, wow); + if (ret < 0) + goto out; out: return ret; @@ -1728,7 +1735,7 @@ static int wl1271_configure_suspend(struct wl1271 *wl, if (wlvif->bss_type == BSS_TYPE_STA_BSS) return wl1271_configure_suspend_sta(wl, wlvif, wow); if (wlvif->bss_type == BSS_TYPE_AP_BSS) - return wl1271_configure_suspend_ap(wl, wlvif); + return wl1271_configure_suspend_ap(wl, wlvif, wow); return 0; } @@ -1741,12 +1748,13 @@ static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif) if ((!is_ap) && (!is_sta)) return; - if (is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + if ((is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) || + (is_ap && !test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))) return; - if (is_sta) { - wl1271_configure_wowlan(wl, NULL); + wl1271_configure_wowlan(wl, NULL); + if (is_sta) { if ((wl->conf.conn.suspend_wake_up_event == wl->conf.conn.wake_up_event) && (wl->conf.conn.suspend_listen_interval == -- cgit v0.10.2 From e2f1e50f62ae70c7ddde8420ed586c6a1aa1e28c Mon Sep 17 00:00:00 2001 From: Kobi L Date: Mon, 29 Dec 2014 08:24:06 +0200 Subject: wlcore: enable sleep during AP mode operation Enable ELP authorization in AP mode and enable the use of the wakeup bit in the ELP register. Introduce AP role sleep configuration which is disabled by default. When configured, it allows the AP to sleep when ELP is authorized for it. Signed-off-by: Kobi Leibovitch Signed-off-by: Arik Nemtsov Signed-off-by: Eliad Peller Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index f3cee5a..144d1f8 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1731,6 +1731,7 @@ static struct wlcore_ops wl12xx_ops = { .lnk_low_prio = wl12xx_lnk_low_prio, .interrupt_notify = NULL, .rx_ba_filter = NULL, + .ap_sleep = NULL, }; static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { diff --git a/drivers/net/wireless/ti/wl18xx/acx.c b/drivers/net/wireless/ti/wl18xx/acx.c index 9d4b9aa..67f2a0e 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.c +++ b/drivers/net/wireless/ti/wl18xx/acx.c @@ -24,6 +24,7 @@ #include "../wlcore/acx.h" #include "acx.h" +#include "wl18xx.h" int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, u32 sdio_blk_size, u32 extra_mem_blks, @@ -250,3 +251,34 @@ out: kfree(acx); return ret; } + +int wl18xx_acx_ap_sleep(struct wl1271 *wl) +{ + struct wl18xx_priv *priv = wl->priv; + struct acx_ap_sleep_cfg *acx; + struct conf_ap_sleep_settings *conf = &priv->conf.ap_sleep; + int ret; + + wl1271_debug(DEBUG_ACX, "acx config ap sleep"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->idle_duty_cycle = conf->idle_duty_cycle; + acx->connected_duty_cycle = conf->connected_duty_cycle; + acx->max_stations_thresh = conf->max_stations_thresh; + acx->idle_conn_thresh = conf->idle_conn_thresh; + + ret = wl1271_cmd_configure(wl, ACX_AP_SLEEP_CFG, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx config ap-sleep failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/ti/wl18xx/acx.h b/drivers/net/wireless/ti/wl18xx/acx.h index 1234bdc..4afccd4 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.h +++ b/drivers/net/wireless/ti/wl18xx/acx.h @@ -34,8 +34,8 @@ enum { ACX_AUTO_RX_STREAMING = 0x0055, ACX_PEER_CAP = 0x0056, ACX_INTERRUPT_NOTIFY = 0x0057, - ACX_RX_BA_FILTER = 0x0058 - + ACX_RX_BA_FILTER = 0x0058, + ACX_AP_SLEEP_CFG = 0x0059 }; /* numbers of bits the length field takes (add 1 for the actual number) */ @@ -347,6 +347,26 @@ struct wl18xx_acx_rx_ba_filter { u32 enable; }; +struct acx_ap_sleep_cfg { + struct acx_header header; + /* Duty Cycle (20-80% of staying Awake) for IDLE AP + * (0: disable) + */ + u8 idle_duty_cycle; + /* Duty Cycle (20-80% of staying Awake) for Connected AP + * (0: disable) + */ + u8 connected_duty_cycle; + /* Maximum stations that are allowed to be connected to AP + * (255: no limit) + */ + u8 max_stations_thresh; + /* Timeout till enabling the Sleep Mechanism after data stops + * [unit: 100 msec] + */ + u8 idle_conn_thresh; +} __packed; + int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, u32 sdio_blk_size, u32 extra_mem_blks, u32 len_field_size); @@ -359,5 +379,6 @@ int wl18xx_acx_set_peer_cap(struct wl1271 *wl, u32 rate_set, u8 hlid); int wl18xx_acx_interrupt_notify_config(struct wl1271 *wl, bool action); int wl18xx_acx_rx_ba_filter(struct wl1271 *wl, bool action); +int wl18xx_acx_ap_sleep(struct wl1271 *wl); #endif /* __WL18XX_ACX_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h index e34302e..71f1ec4 100644 --- a/drivers/net/wireless/ti/wl18xx/conf.h +++ b/drivers/net/wireless/ti/wl18xx/conf.h @@ -23,7 +23,7 @@ #define __WL18XX_CONF_H__ #define WL18XX_CONF_MAGIC 0x10e100ca -#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0006) +#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0007) #define WL18XX_CONF_MASK 0x0000ffff #define WL18XX_CONF_SIZE (WLCORE_CONF_SIZE + \ sizeof(struct wl18xx_priv_conf)) @@ -110,12 +110,33 @@ struct wl18xx_ht_settings { u8 mode; } __packed; +struct conf_ap_sleep_settings { + /* Duty Cycle (20-80% of staying Awake) for IDLE AP + * (0: disable) + */ + u8 idle_duty_cycle; + /* Duty Cycle (20-80% of staying Awake) for Connected AP + * (0: disable) + */ + u8 connected_duty_cycle; + /* Maximum stations that are allowed to be connected to AP + * (255: no limit) + */ + u8 max_stations_thresh; + /* Timeout till enabling the Sleep Mechanism after data stops + * [unit: 100 msec] + */ + u8 idle_conn_thresh; +} __packed; + struct wl18xx_priv_conf { /* Module params structures */ struct wl18xx_ht_settings ht; /* this structure is copied wholesale to FW */ struct wl18xx_mac_and_phy_params phy; + + struct conf_ap_sleep_settings ap_sleep; } __packed; #endif /* __WL18XX_CONF_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 6c1000d..04db941 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -568,6 +568,12 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = { .high_power_val_2nd = 0xff, .tx_rf_margin = 1, }, + .ap_sleep = { /* disabled by default */ + .idle_duty_cycle = 0, + .connected_duty_cycle = 0, + .max_stations_thresh = 0, + .idle_conn_thresh = 0, + }, }; static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = { @@ -1696,6 +1702,7 @@ static struct wlcore_ops wl18xx_ops = { .smart_config_set_group_key = wl18xx_cmd_smart_config_set_group_key, .interrupt_notify = wl18xx_acx_interrupt_notify_config, .rx_ba_filter = wl18xx_acx_rx_ba_filter, + .ap_sleep = wl18xx_acx_ap_sleep, }; /* HT cap appropriate for wide channels in 2Ghz */ diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index c2545ce..449050b 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -234,6 +234,15 @@ wlcore_hw_rx_ba_filter(struct wl1271 *wl, bool action) } static inline int +wlcore_hw_ap_sleep(struct wl1271 *wl) +{ + if (wl->ops->ap_sleep) + return wl->ops->ap_sleep(wl); + + return 0; +} + +static inline int wlcore_hw_set_peer_cap(struct wl1271 *wl, struct ieee80211_sta_ht_cap *ht_cap, bool allow_ht_operation, diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c index 199e941..5ca1fb1 100644 --- a/drivers/net/wireless/ti/wlcore/init.c +++ b/drivers/net/wireless/ti/wlcore/init.c @@ -392,6 +392,11 @@ static int wl1271_ap_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif) if (ret < 0) return ret; + /* configure AP sleep, if enabled */ + ret = wlcore_hw_ap_sleep(wl); + if (ret < 0) + return ret; + return 0; } @@ -567,8 +572,7 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif) /* consider all existing roles before configuring psm. */ if (wl->ap_count == 0 && is_ap) { /* first AP */ - /* Configure for power always on */ - ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); + ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); if (ret < 0) return ret; diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c index b52516e..f3ed543 100644 --- a/drivers/net/wireless/ti/wlcore/ps.c +++ b/drivers/net/wireless/ti/wlcore/ps.c @@ -56,9 +56,6 @@ void wl1271_elp_work(struct work_struct *work) goto out; wl12xx_for_each_wlvif(wl, wlvif) { - if (wlvif->bss_type == BSS_TYPE_AP_BSS) - goto out; - if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) && test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) goto out; @@ -95,9 +92,6 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl) return; wl12xx_for_each_wlvif(wl, wlvif) { - if (wlvif->bss_type == BSS_TYPE_AP_BSS) - return; - if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) && test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) return; diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 7860a4e..c8fe2ae 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -118,6 +118,7 @@ struct wlcore_ops { struct wl1271_link *lnk); int (*interrupt_notify)(struct wl1271 *wl, bool action); int (*rx_ba_filter)(struct wl1271 *wl, bool action); + int (*ap_sleep)(struct wl1271 *wl); int (*smart_config_start)(struct wl1271 *wl, u32 group_bitmap); int (*smart_config_stop)(struct wl1271 *wl); int (*smart_config_set_group_key)(struct wl1271 *wl, u16 group_id, -- cgit v0.10.2 From 750e9d15e2fe93fec696893be7b120b2940378d0 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 29 Dec 2014 08:24:07 +0200 Subject: wl18xx: add radar detection implementation Add support for CAC start/stop commands, and pass radar detection events from the fw to mac80211. Bump fw name (to wl18xx-fw-4.bin) and min fw version (to 8.9.*.*.11), and align event mailbox accordingly. Signed-off-by: Guy Mishol Signed-off-by: Eliad Peller Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ti/wl18xx/cmd.c b/drivers/net/wireless/ti/wl18xx/cmd.c index 44f0b20..10f9d1c 100644 --- a/drivers/net/wireless/ti/wl18xx/cmd.c +++ b/drivers/net/wireless/ti/wl18xx/cmd.c @@ -167,3 +167,34 @@ out_free: out: return ret; } + +int wl18xx_cmd_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start) +{ + struct wlcore_cmd_cac_start *cmd; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd cac (channel %d) %s", + wlvif->channel, start ? "start" : "stop"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->role_id = wlvif->role_id; + cmd->channel = wlvif->channel; + if (wlvif->band == IEEE80211_BAND_5GHZ) + cmd->band = WLCORE_BAND_5GHZ; + cmd->bandwidth = wlcore_get_native_channel_type(wlvif->channel_type); + + ret = wl1271_cmd_send(wl, + start ? CMD_CAC_START : CMD_CAC_STOP, + cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to send cac command"); + goto out_free; + } + +out_free: + kfree(cmd); + return ret; +} diff --git a/drivers/net/wireless/ti/wl18xx/cmd.h b/drivers/net/wireless/ti/wl18xx/cmd.h index 92499e2..91b3e2f 100644 --- a/drivers/net/wireless/ti/wl18xx/cmd.h +++ b/drivers/net/wireless/ti/wl18xx/cmd.h @@ -59,6 +59,16 @@ struct wl18xx_cmd_smart_config_set_group_key { u8 key[16]; } __packed; +/* cac_start and cac_stop share the same params */ +struct wlcore_cmd_cac_start { + struct wl1271_cmd_header header; + + u8 role_id; + u8 channel; + u8 band; + u8 bandwidth; +} __packed; + int wl18xx_cmd_channel_switch(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_channel_switch *ch_switch); @@ -66,4 +76,5 @@ int wl18xx_cmd_smart_config_start(struct wl1271 *wl, u32 group_bitmap); int wl18xx_cmd_smart_config_stop(struct wl1271 *wl); int wl18xx_cmd_smart_config_set_group_key(struct wl1271 *wl, u16 group_id, u8 key_len, u8 *key); +int wl18xx_cmd_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start); #endif diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c index eb1848e..c28f068 100644 --- a/drivers/net/wireless/ti/wl18xx/event.c +++ b/drivers/net/wireless/ti/wl18xx/event.c @@ -47,6 +47,19 @@ int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event, return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout); } +static const char *wl18xx_radar_type_decode(u8 radar_type) +{ + switch (radar_type) { + case RADAR_TYPE_REGULAR: + return "REGULAR"; + case RADAR_TYPE_CHIRP: + return "CHIRP"; + case RADAR_TYPE_NONE: + default: + return "N/A"; + } +} + static int wlcore_smart_config_sync_event(struct wl1271 *wl, u8 sync_channel, u8 sync_band) { @@ -115,6 +128,14 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl) wl18xx_scan_completed(wl, wl->scan_wlvif); } + if (vector & RADAR_DETECTED_EVENT_ID) { + wl1271_info("radar event: channel %d type %s", + mbox->radar_channel, + wl18xx_radar_type_decode(mbox->radar_type)); + + ieee80211_radar_detected(wl->hw); + } + if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT (results %d)", diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h index 0680312..266ee87 100644 --- a/drivers/net/wireless/ti/wl18xx/event.h +++ b/drivers/net/wireless/ti/wl18xx/event.h @@ -42,6 +42,12 @@ enum { SMART_CONFIG_DECODE_EVENT_ID = BIT(23), }; +enum wl18xx_radar_types { + RADAR_TYPE_NONE, + RADAR_TYPE_REGULAR, + RADAR_TYPE_CHIRP +}; + struct wl18xx_event_mailbox { __le32 events_vector; @@ -83,13 +89,19 @@ struct wl18xx_event_mailbox { u8 sc_token_len; u8 padding1; u8 sc_ssid[32]; - u8 sc_pwd[32]; + u8 sc_pwd[64]; u8 sc_token[32]; /* smart config sync channel */ u8 sc_sync_channel; u8 sc_sync_band; u8 padding2[2]; + + /* radar detect */ + u8 radar_channel; + u8 radar_type; + + u8 padding3[2]; } __packed; int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event, diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 04db941..c36b1af 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -655,7 +655,7 @@ static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = { }; /* TODO: maybe move to a new header file? */ -#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-3.bin" +#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-4.bin" static int wl18xx_identify_chip(struct wl1271 *wl) { @@ -990,6 +990,7 @@ static int wl18xx_boot(struct wl1271 *wl) wl->event_mask = BSS_LOSS_EVENT_ID | SCAN_COMPLETE_EVENT_ID | + RADAR_DETECTED_EVENT_ID | RSSI_SNR_TRIGGER_0_EVENT_ID | PERIODIC_SCAN_COMPLETE_EVENT_ID | PERIODIC_SCAN_REPORT_EVENT_ID | @@ -1703,6 +1704,7 @@ static struct wlcore_ops wl18xx_ops = { .interrupt_notify = wl18xx_acx_interrupt_notify_config, .rx_ba_filter = wl18xx_acx_rx_ba_filter, .ap_sleep = wl18xx_acx_ap_sleep, + .set_cac = wl18xx_cmd_set_cac, }; /* HT cap appropriate for wide channels in 2Ghz */ diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h index 6a2b880..71e9e38 100644 --- a/drivers/net/wireless/ti/wl18xx/wl18xx.h +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -26,10 +26,10 @@ /* minimum FW required for driver */ #define WL18XX_CHIP_VER 8 -#define WL18XX_IFTYPE_VER 8 +#define WL18XX_IFTYPE_VER 9 #define WL18XX_MAJOR_VER WLCORE_FW_VER_IGNORE #define WL18XX_SUBTYPE_VER WLCORE_FW_VER_IGNORE -#define WL18XX_MINOR_VER 13 +#define WL18XX_MINOR_VER 11 #define WL18XX_CMD_MAX_SIZE 740 diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index b826619..aacad4e 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -403,7 +403,7 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) WARN_ON_ONCE(wl->active_link_count < 0); } -static u8 wlcore_get_native_channel_type(u8 nl_channel_type) +u8 wlcore_get_native_channel_type(u8 nl_channel_type) { switch (nl_channel_type) { case NL80211_CHAN_NO_HT: @@ -419,6 +419,7 @@ static u8 wlcore_get_native_channel_type(u8 nl_channel_type) return WLCORE_CHAN_NO_HT; } } +EXPORT_SYMBOL_GPL(wlcore_get_native_channel_type); static int wl12xx_cmd_role_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif, diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index 453684a..06bdee2 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -105,6 +105,7 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid); int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl, u32 mask, bool *timeout); +u8 wlcore_get_native_channel_type(u8 nl_channel_type); enum wl1271_commands { CMD_INTERROGATE = 1, /* use this to read information elements */ @@ -172,6 +173,11 @@ enum wl1271_commands { CMD_SMART_CONFIG_STOP = 62, CMD_SMART_CONFIG_SET_GROUP_KEY = 63, + CMD_CAC_START = 64, + CMD_CAC_STOP = 65, + CMD_DFS_MASTER_RESTART = 66, + CMD_DFS_RADAR_DETECTION_DEBUG = 67, + MAX_COMMAND_ID = 0xFFFF, }; diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 449050b..42fef84 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -311,4 +311,13 @@ wlcore_smart_config_set_group_key(struct wl1271 *wl, u16 group_id, return wl->ops->smart_config_set_group_key(wl, group_id, key_len, key); } + +static inline int +wlcore_hw_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start) +{ + if (!wl->ops->set_cac) + return -EINVAL; + + return wl->ops->set_cac(wl, wlvif, start); +} #endif diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index caff699..f46c919 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4629,10 +4629,46 @@ static void wlcore_op_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx, u32 changed) { + struct wl1271 *wl = hw->priv; + struct wl12xx_vif *wlvif; + int ret; + int channel = ieee80211_frequency_to_channel( + ctx->def.chan->center_freq); + wl1271_debug(DEBUG_MAC80211, "mac80211 change chanctx %d (type %d) changed 0x%x", - ieee80211_frequency_to_channel(ctx->def.chan->center_freq), - cfg80211_get_chandef_type(&ctx->def), changed); + channel, cfg80211_get_chandef_type(&ctx->def), changed); + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + wl12xx_for_each_wlvif(wl, wlvif) { + struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); + + rcu_read_lock(); + if (rcu_access_pointer(vif->chanctx_conf) != ctx) { + rcu_read_unlock(); + continue; + } + rcu_read_unlock(); + + /* start radar if needed */ + if (changed & IEEE80211_CHANCTX_CHANGE_RADAR && + wlvif->bss_type == BSS_TYPE_AP_BSS && + ctx->radar_enabled && !wlvif->radar_enabled && + ctx->def.chan->dfs_state == NL80211_DFS_USABLE) { + wl1271_debug(DEBUG_MAC80211, "Start radar detection"); + wlcore_hw_set_cac(wl, wlvif, true); + wlvif->radar_enabled = true; + } + } + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); } static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw, @@ -4643,13 +4679,26 @@ static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw, struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int channel = ieee80211_frequency_to_channel( ctx->def.chan->center_freq); + int ret = -EINVAL; wl1271_debug(DEBUG_MAC80211, - "mac80211 assign chanctx (role %d) %d (type %d)", - wlvif->role_id, channel, cfg80211_get_chandef_type(&ctx->def)); + "mac80211 assign chanctx (role %d) %d (type %d) (radar %d dfs_state %d)", + wlvif->role_id, channel, + cfg80211_get_chandef_type(&ctx->def), + ctx->radar_enabled, ctx->def.chan->dfs_state); mutex_lock(&wl->mutex); + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))) + goto out; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + wlvif->band = ctx->def.chan->band; wlvif->channel = channel; wlvif->channel_type = cfg80211_get_chandef_type(&ctx->def); @@ -4657,6 +4706,15 @@ static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw, /* update default rates according to the band */ wl1271_set_band_rate(wl, wlvif); + if (ctx->radar_enabled && + ctx->def.chan->dfs_state == NL80211_DFS_USABLE) { + wl1271_debug(DEBUG_MAC80211, "Start radar detection"); + wlcore_hw_set_cac(wl, wlvif, true); + wlvif->radar_enabled = true; + } + + wl1271_ps_elp_sleep(wl); +out: mutex_unlock(&wl->mutex); return 0; @@ -4668,6 +4726,7 @@ static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw, { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 unassign chanctx (role %d) %d (type %d)", @@ -4676,6 +4735,97 @@ static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw, cfg80211_get_chandef_type(&ctx->def)); wl1271_tx_flush(wl); + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))) + goto out; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + if (wlvif->radar_enabled) { + wl1271_debug(DEBUG_MAC80211, "Stop radar detection"); + wlcore_hw_set_cac(wl, wlvif, false); + wlvif->radar_enabled = false; + } + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + +static int __wlcore_switch_vif_chan(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + struct ieee80211_chanctx_conf *new_ctx) +{ + int channel = ieee80211_frequency_to_channel( + new_ctx->def.chan->center_freq); + + wl1271_debug(DEBUG_MAC80211, + "switch vif (role %d) %d -> %d chan_type: %d", + wlvif->role_id, wlvif->channel, channel, + cfg80211_get_chandef_type(&new_ctx->def)); + + if (WARN_ON_ONCE(wlvif->bss_type != BSS_TYPE_AP_BSS)) + return 0; + + if (wlvif->radar_enabled) { + wl1271_debug(DEBUG_MAC80211, "Stop radar detection"); + wlcore_hw_set_cac(wl, wlvif, false); + wlvif->radar_enabled = false; + } + + wlvif->band = new_ctx->def.chan->band; + wlvif->channel = channel; + wlvif->channel_type = cfg80211_get_chandef_type(&new_ctx->def); + + /* start radar if needed */ + if (new_ctx->radar_enabled) { + wl1271_debug(DEBUG_MAC80211, "Start radar detection"); + wlcore_hw_set_cac(wl, wlvif, true); + wlvif->radar_enabled = true; + } + + return 0; +} + +static int +wlcore_op_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode) +{ + struct wl1271 *wl = hw->priv; + int i, ret; + + wl1271_debug(DEBUG_MAC80211, + "mac80211 switch chanctx n_vifs %d mode %d", + n_vifs, mode); + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + for (i = 0; i < n_vifs; i++) { + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vifs[i].vif); + + ret = __wlcore_switch_vif_chan(wl, wlvif, vifs[i].new_ctx); + if (ret) + goto out_sleep; + } +out_sleep: + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + + return 0; } static int wl1271_op_conf_tx(struct ieee80211_hw *hw, @@ -5665,6 +5815,7 @@ static const struct ieee80211_ops wl1271_ops = { .change_chanctx = wlcore_op_change_chanctx, .assign_vif_chanctx = wlcore_op_assign_vif_chanctx, .unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx, + .switch_vif_chanctx = wlcore_op_switch_vif_chanctx, .sta_rc_update = wlcore_op_sta_rc_update, .get_rssi = wlcore_op_get_rssi, CFG80211_TESTMODE_CMD(wl1271_tm_cmd) diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index c8fe2ae..caee58f 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -123,6 +123,8 @@ struct wlcore_ops { int (*smart_config_stop)(struct wl1271 *wl); int (*smart_config_set_group_key)(struct wl1271 *wl, u16 group_id, u8 key_len, u8 *key); + int (*set_cac)(struct wl1271 *wl, struct wl12xx_vif *wlvif, + bool start); }; enum wlcore_partitions { diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 811851d..b2bdb13 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -434,6 +434,8 @@ struct wl12xx_vif { bool wmm_enabled; + bool radar_enabled; + /* Rx Streaming */ struct work_struct rx_streaming_enable_work; struct work_struct rx_streaming_disable_work; -- cgit v0.10.2 From e7d323243f567b8f3ff1324dc8a6f17dd36b4106 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 29 Dec 2014 08:24:08 +0200 Subject: wl18xx: add debugfs file to emulate radar event Add debugfs file to emulate radar detection through a special fw cmd (which in turn will generate radar event) Signed-off-by: Eliad Peller Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ti/wl18xx/cmd.c b/drivers/net/wireless/ti/wl18xx/cmd.c index 10f9d1c..68e12e5 100644 --- a/drivers/net/wireless/ti/wl18xx/cmd.c +++ b/drivers/net/wireless/ti/wl18xx/cmd.c @@ -198,3 +198,29 @@ out_free: kfree(cmd); return ret; } + +int wl18xx_cmd_radar_detection_debug(struct wl1271 *wl, u8 channel) +{ + struct wl18xx_cmd_dfs_radar_debug *cmd; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd radar detection debug (chan %d)", + channel); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->channel = channel; + + ret = wl1271_cmd_send(wl, CMD_DFS_RADAR_DETECTION_DEBUG, + cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to send radar detection debug command"); + goto out_free; + } + +out_free: + kfree(cmd); + return ret; +} diff --git a/drivers/net/wireless/ti/wl18xx/cmd.h b/drivers/net/wireless/ti/wl18xx/cmd.h index 91b3e2f..0809b92 100644 --- a/drivers/net/wireless/ti/wl18xx/cmd.h +++ b/drivers/net/wireless/ti/wl18xx/cmd.h @@ -59,6 +59,13 @@ struct wl18xx_cmd_smart_config_set_group_key { u8 key[16]; } __packed; +struct wl18xx_cmd_dfs_radar_debug { + struct wl1271_cmd_header header; + + u8 channel; + u8 padding[3]; +} __packed; + /* cac_start and cac_stop share the same params */ struct wlcore_cmd_cac_start { struct wl1271_cmd_header header; @@ -77,4 +84,5 @@ int wl18xx_cmd_smart_config_stop(struct wl1271 *wl); int wl18xx_cmd_smart_config_set_group_key(struct wl1271 *wl, u16 group_id, u8 key_len, u8 *key); int wl18xx_cmd_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start); +int wl18xx_cmd_radar_detection_debug(struct wl1271 *wl, u8 channel); #endif diff --git a/drivers/net/wireless/ti/wl18xx/debugfs.c b/drivers/net/wireless/ti/wl18xx/debugfs.c index 7f1669c..c93fae9 100644 --- a/drivers/net/wireless/ti/wl18xx/debugfs.c +++ b/drivers/net/wireless/ti/wl18xx/debugfs.c @@ -22,9 +22,12 @@ #include "../wlcore/debugfs.h" #include "../wlcore/wlcore.h" +#include "../wlcore/debug.h" +#include "../wlcore/ps.h" #include "wl18xx.h" #include "acx.h" +#include "cmd.h" #include "debugfs.h" #define WL18XX_DEBUGFS_FWSTATS_FILE(a, b, c) \ @@ -239,6 +242,45 @@ static const struct file_operations clear_fw_stats_ops = { .llseek = default_llseek, }; +static ssize_t radar_detection_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + int ret; + u8 channel; + + ret = kstrtou8_from_user(user_buf, count, 10, &channel); + if (ret < 0) { + wl1271_warning("illegal channel"); + return -EINVAL; + } + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wl18xx_cmd_radar_detection_debug(wl, channel); + if (ret < 0) + count = ret; + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + return count; +} + +static const struct file_operations radar_detection_ops = { + .write = radar_detection_write, + .open = simple_open, + .llseek = default_llseek, +}; + int wl18xx_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) { @@ -390,6 +432,7 @@ int wl18xx_debugfs_add_files(struct wl1271 *wl, DEBUGFS_FWSTATS_ADD(mem, fw_gen_free_mem_blks); DEBUGFS_ADD(conf, moddir); + DEBUGFS_ADD(radar_detection, moddir); return 0; diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c index f3ed543..4cd316e 100644 --- a/drivers/net/wireless/ti/wlcore/ps.c +++ b/drivers/net/wireless/ti/wlcore/ps.c @@ -102,6 +102,7 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl) ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, msecs_to_jiffies(timeout)); } +EXPORT_SYMBOL_GPL(wl1271_ps_elp_sleep); int wl1271_ps_elp_wakeup(struct wl1271 *wl) { @@ -169,6 +170,7 @@ err: out: return 0; } +EXPORT_SYMBOL_GPL(wl1271_ps_elp_wakeup); int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, enum wl1271_cmd_ps_mode mode) -- cgit v0.10.2 From 534719f445c6f8bf5218adaf4bec36f118ccc1e9 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 29 Dec 2014 08:24:09 +0200 Subject: wlcore: add support for ap csa Support ap csa support by implementing the channel_switch_beacon() mac80211 op. Signed-off-by: Eliad Peller Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ti/wl18xx/cmd.c b/drivers/net/wireless/ti/wl18xx/cmd.c index 68e12e5..5731950 100644 --- a/drivers/net/wireless/ti/wl18xx/cmd.c +++ b/drivers/net/wireless/ti/wl18xx/cmd.c @@ -33,7 +33,8 @@ int wl18xx_cmd_channel_switch(struct wl1271 *wl, u32 supported_rates; int ret; - wl1271_debug(DEBUG_ACX, "cmd channel switch"); + wl1271_debug(DEBUG_ACX, "cmd channel switch (count=%d)", + ch_switch->count); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { @@ -60,8 +61,12 @@ int wl18xx_cmd_channel_switch(struct wl1271 *wl, goto out_free; } - supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES | - wlcore_hw_sta_get_ap_rate_mask(wl, wlvif); + supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES; + if (wlvif->bss_type == BSS_TYPE_STA_BSS) + supported_rates |= wlcore_hw_sta_get_ap_rate_mask(wl, wlvif); + else + supported_rates |= + wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif); if (wlvif->p2p) supported_rates &= ~CONF_TX_CCK_RATES; cmd->local_supported_rates = cpu_to_le32(supported_rates); diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index 5153640..69a2661 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -139,7 +139,7 @@ void wlcore_event_channel_switch(struct wl1271 *wl, wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx success=%d", __func__, roles_bitmap, success); - wl12xx_for_each_wlvif_sta(wl, wlvif) { + wl12xx_for_each_wlvif(wl, wlvif) { if (wlvif->role_id == WL12XX_INVALID_ROLE_ID || !test_bit(wlvif->role_id , &roles_bitmap)) continue; @@ -150,8 +150,12 @@ void wlcore_event_channel_switch(struct wl1271 *wl, vif = wl12xx_wlvif_to_vif(wlvif); - ieee80211_chswitch_done(vif, success); - cancel_delayed_work(&wlvif->channel_switch_work); + if (wlvif->bss_type == BSS_TYPE_STA_BSS) { + ieee80211_chswitch_done(vif, success); + cancel_delayed_work(&wlvif->channel_switch_work); + } else { + ieee80211_csa_finish(vif); + } } } EXPORT_SYMBOL_GPL(wlcore_event_channel_switch); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index f46c919..68da50e 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5433,6 +5433,83 @@ out: mutex_unlock(&wl->mutex); } +static const void *wlcore_get_beacon_ie(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + u8 eid) +{ + int ieoffset = offsetof(struct ieee80211_mgmt, u.beacon.variable); + struct sk_buff *beacon = + ieee80211_beacon_get(wl->hw, wl12xx_wlvif_to_vif(wlvif)); + + if (!beacon) + return NULL; + + return cfg80211_find_ie(eid, + beacon->data + ieoffset, + beacon->len - ieoffset); +} + +static int wlcore_get_csa_count(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 *csa_count) +{ + const u8 *ie; + const struct ieee80211_channel_sw_ie *ie_csa; + + ie = wlcore_get_beacon_ie(wl, wlvif, WLAN_EID_CHANNEL_SWITCH); + if (!ie) + return -EINVAL; + + ie_csa = (struct ieee80211_channel_sw_ie *)&ie[2]; + *csa_count = ie_csa->count; + + return 0; +} + +static void wlcore_op_channel_switch_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_chan_def *chandef) +{ + struct wl1271 *wl = hw->priv; + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + struct ieee80211_channel_switch ch_switch = { + .block_tx = true, + .chandef = *chandef, + }; + int ret; + + wl1271_debug(DEBUG_MAC80211, + "mac80211 channel switch beacon (role %d)", + wlvif->role_id); + + ret = wlcore_get_csa_count(wl, wlvif, &ch_switch.count); + if (ret < 0) { + wl1271_error("error getting beacon (for CSA counter)"); + return; + } + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) { + ret = -EBUSY; + goto out; + } + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wl->ops->channel_switch(wl, wlvif, &ch_switch); + if (ret) + goto out_sleep; + + set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags); + +out_sleep: + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + static void wlcore_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop) { @@ -5807,6 +5884,7 @@ static const struct ieee80211_ops wl1271_ops = { .set_bitrate_mask = wl12xx_set_bitrate_mask, .set_default_unicast_key = wl1271_op_set_default_key_idx, .channel_switch = wl12xx_op_channel_switch, + .channel_switch_beacon = wlcore_op_channel_switch_beacon, .flush = wlcore_op_flush, .remain_on_channel = wlcore_op_remain_on_channel, .cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel, @@ -6023,7 +6101,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | - WIPHY_FLAG_SUPPORTS_SCHED_SCAN; + WIPHY_FLAG_SUPPORTS_SCHED_SCAN | + WIPHY_FLAG_HAS_CHANNEL_SWITCH; /* make sure all our channels fit in the scanned_ch bitmask */ BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) + -- cgit v0.10.2 From 830513abc6ea2b1828b83b37300711984bae89f6 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 29 Dec 2014 08:24:10 +0200 Subject: wlcore: add dfs master restart calls call wlcore_cmd_dfs_master_restart when starting the ap on a new channel (after csa is done). Add a new WLVIF_FLAG_BEACON_DISABLED flag to indicate that dfs_master_restart command is required. Signed-off-by: Eliad Peller Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ti/wl18xx/cmd.c b/drivers/net/wireless/ti/wl18xx/cmd.c index 5731950..a8d176d 100644 --- a/drivers/net/wireless/ti/wl18xx/cmd.c +++ b/drivers/net/wireless/ti/wl18xx/cmd.c @@ -229,3 +229,28 @@ out_free: kfree(cmd); return ret; } + +int wl18xx_cmd_dfs_master_restart(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ + struct wl18xx_cmd_dfs_master_restart *cmd; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd dfs master restart (role %d)", + wlvif->role_id); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->role_id = wlvif->role_id; + + ret = wl1271_cmd_send(wl, CMD_DFS_MASTER_RESTART, + cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to send dfs master restart command"); + goto out_free; + } +out_free: + kfree(cmd); + return ret; +} diff --git a/drivers/net/wireless/ti/wl18xx/cmd.h b/drivers/net/wireless/ti/wl18xx/cmd.h index 0809b92..7f9440a 100644 --- a/drivers/net/wireless/ti/wl18xx/cmd.h +++ b/drivers/net/wireless/ti/wl18xx/cmd.h @@ -66,6 +66,13 @@ struct wl18xx_cmd_dfs_radar_debug { u8 padding[3]; } __packed; +struct wl18xx_cmd_dfs_master_restart { + struct wl1271_cmd_header header; + + u8 role_id; + u8 padding[3]; +} __packed; + /* cac_start and cac_stop share the same params */ struct wlcore_cmd_cac_start { struct wl1271_cmd_header header; @@ -85,4 +92,5 @@ int wl18xx_cmd_smart_config_set_group_key(struct wl1271 *wl, u16 group_id, u8 key_len, u8 *key); int wl18xx_cmd_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start); int wl18xx_cmd_radar_detection_debug(struct wl1271 *wl, u8 channel); +int wl18xx_cmd_dfs_master_restart(struct wl1271 *wl, struct wl12xx_vif *wlvif); #endif diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index c36b1af..0e96b38 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1705,6 +1705,7 @@ static struct wlcore_ops wl18xx_ops = { .rx_ba_filter = wl18xx_acx_rx_ba_filter, .ap_sleep = wl18xx_acx_ap_sleep, .set_cac = wl18xx_cmd_set_cac, + .dfs_master_restart = wl18xx_cmd_dfs_master_restart, }; /* HT cap appropriate for wide channels in 2Ghz */ diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index 69a2661..c42e789 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -154,6 +154,7 @@ void wlcore_event_channel_switch(struct wl1271 *wl, ieee80211_chswitch_done(vif, success); cancel_delayed_work(&wlvif->channel_switch_work); } else { + set_bit(WLVIF_FLAG_BEACON_DISABLED, &wlvif->flags); ieee80211_csa_finish(vif); } } diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 42fef84..eec5693 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -320,4 +320,13 @@ wlcore_hw_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start) return wl->ops->set_cac(wl, wlvif, start); } + +static inline int +wlcore_hw_dfs_master_restart(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ + if (!wl->ops->dfs_master_restart) + return -EINVAL; + + return wl->ops->dfs_master_restart(wl, wlvif); +} #endif diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 68da50e..2cbec03 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4127,8 +4127,14 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl, ret = wlcore_set_beacon_template(wl, vif, is_ap); if (ret < 0) goto out; - } + if (test_and_clear_bit(WLVIF_FLAG_BEACON_DISABLED, + &wlvif->flags)) { + ret = wlcore_hw_dfs_master_restart(wl, wlvif); + if (ret < 0) + goto out; + } + } out: if (ret != 0) wl1271_error("beacon info change failed: %d", ret); @@ -4774,6 +4780,8 @@ static int __wlcore_switch_vif_chan(struct wl1271 *wl, if (WARN_ON_ONCE(wlvif->bss_type != BSS_TYPE_AP_BSS)) return 0; + WARN_ON(!test_bit(WLVIF_FLAG_BEACON_DISABLED, &wlvif->flags)); + if (wlvif->radar_enabled) { wl1271_debug(DEBUG_MAC80211, "Stop radar detection"); wlcore_hw_set_cac(wl, wlvif, false); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index caee58f..581c479 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -125,6 +125,7 @@ struct wlcore_ops { u8 key_len, u8 *key); int (*set_cac)(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start); + int (*dfs_master_restart)(struct wl1271 *wl, struct wl12xx_vif *wlvif); }; enum wlcore_partitions { diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index b2bdb13..3396ce5 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -251,6 +251,7 @@ enum wl12xx_vif_flags { WLVIF_FLAG_AP_PROBE_RESP_SET, WLVIF_FLAG_IN_USE, WLVIF_FLAG_ACTIVE, + WLVIF_FLAG_BEACON_DISABLED, }; struct wl12xx_vif; -- cgit v0.10.2 From 4ce9fad35a4ae21c393a89001b8ef33fa8c2033b Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 29 Dec 2014 08:24:11 +0200 Subject: wlcore: allow using dfs channels Since we are going to support dfs channels, there is no reason to mark them as NO_IR (having the DFS flag is enough anyway). Additionally, when setting the regdomain configuration, enable usable dfs channels. Signed-off-by: Eliad Peller Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index aacad4e..50ca10c 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -1687,9 +1687,7 @@ int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl) { struct wl12xx_cmd_regdomain_dfs_config *cmd = NULL; int ret = 0, i, b, ch_bit_idx; - struct ieee80211_channel *channel; u32 tmp_ch_bitmap[2]; - u16 ch; struct wiphy *wiphy = wl->hw->wiphy; struct ieee80211_supported_band *band; bool timeout = false; @@ -1704,12 +1702,16 @@ int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl) for (b = IEEE80211_BAND_2GHZ; b <= IEEE80211_BAND_5GHZ; b++) { band = wiphy->bands[b]; for (i = 0; i < band->n_channels; i++) { - channel = &band->channels[i]; - ch = channel->hw_value; + struct ieee80211_channel *channel = &band->channels[i]; + u16 ch = channel->hw_value; + u32 flags = channel->flags; - if (channel->flags & (IEEE80211_CHAN_DISABLED | - IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR)) + if (flags & (IEEE80211_CHAN_DISABLED | + IEEE80211_CHAN_NO_IR)) + continue; + + if ((flags & IEEE80211_CHAN_RADAR) && + channel->dfs_state != NL80211_DFS_AVAILABLE) continue; ch_bit_idx = wlcore_get_reg_conf_ch_idx(b, ch); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 2cbec03..a393ae8fa 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -79,23 +79,9 @@ static int wl12xx_set_authorized(struct wl1271 *wl, struct wl12xx_vif *wlvif) static void wl1271_reg_notify(struct wiphy *wiphy, struct regulatory_request *request) { - struct ieee80211_supported_band *band; - struct ieee80211_channel *ch; - int i; struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct wl1271 *wl = hw->priv; - band = wiphy->bands[IEEE80211_BAND_5GHZ]; - for (i = 0; i < band->n_channels; i++) { - ch = &band->channels[i]; - if (ch->flags & IEEE80211_CHAN_DISABLED) - continue; - - if (ch->flags & IEEE80211_CHAN_RADAR) - ch->flags |= IEEE80211_CHAN_NO_IR; - - } - wlcore_regdomain_config(wl); } -- cgit v0.10.2 From 1cd91b2c4d8a5ebe9ba7874fcd45ee2b9b444b04 Mon Sep 17 00:00:00 2001 From: Guy Mishol Date: Mon, 29 Dec 2014 08:24:12 +0200 Subject: wlcore: add dfs region to reg domain update cmd Add dfs region to the reg domain channel update command. Signed-off-by: Guy Mishol Signed-off-by: Eliad Peller Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 50ca10c..c26fc21 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -1736,6 +1736,7 @@ int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl) cmd->ch_bit_map1 = cpu_to_le32(tmp_ch_bitmap[0]); cmd->ch_bit_map2 = cpu_to_le32(tmp_ch_bitmap[1]); + cmd->dfs_region = wl->dfs_region; wl1271_debug(DEBUG_CMD, "cmd reg domain bitmap1: 0x%08x, bitmap2: 0x%08x", diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index 06bdee2..e14cd40 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -648,6 +648,8 @@ struct wl12xx_cmd_regdomain_dfs_config { __le32 ch_bit_map1; __le32 ch_bit_map2; + u8 dfs_region; + u8 padding[3]; } __packed; struct wl12xx_cmd_config_fwlog { diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index a393ae8fa..e90fb78 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -82,6 +82,10 @@ static void wl1271_reg_notify(struct wiphy *wiphy, struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct wl1271 *wl = hw->priv; + /* copy the current dfs region */ + if (request) + wl->dfs_region = request->dfs_region; + wlcore_regdomain_config(wl); } diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 581c479..d599c86 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -465,6 +465,9 @@ struct wl1271 { /* HW HT (11n) capabilities */ struct ieee80211_sta_ht_cap ht_cap[WLCORE_NUM_BANDS]; + /* the current dfs region */ + enum nl80211_dfs_regions dfs_region; + /* size of the private FW status data */ size_t fw_status_len; size_t fw_status_priv_len; -- cgit v0.10.2 From 86f2db86d410f5dc3e89cc7d9c31607f0c7763bd Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 29 Dec 2014 08:24:13 +0200 Subject: wl18xx: declare radar_detect_widths support for ap interfaces After having all the dfs infrastructure in place, declare radar_detect_widths support for the ap interfaces combination. Signed-off-by: Eliad Peller Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 0e96b38..717c4f5 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1799,6 +1799,10 @@ wl18xx_iface_combinations[] = { .limits = wl18xx_iface_ap_limits, .n_limits = ARRAY_SIZE(wl18xx_iface_ap_limits), .num_different_channels = 1, + .radar_detect_widths = BIT(NL80211_CHAN_NO_HT) | + BIT(NL80211_CHAN_HT20) | + BIT(NL80211_CHAN_HT40MINUS) | + BIT(NL80211_CHAN_HT40PLUS), } }; -- cgit v0.10.2 From 8bd61f8d8790c5502f7fdbf49861bbea1339054f Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 6 Jan 2015 23:02:54 +0100 Subject: brcmfmac: get rid of duplicate SDIO device identifiers Instead of defining SDIO device identifier in brcm80211 code use the defintions in linux/mmc/sdio_ids.h directly. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index dffd9e4..00ba90b8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -996,20 +996,20 @@ out: } #define BRCMF_SDIO_DEVICE(dev_id) \ - {SDIO_DEVICE(BRCM_SDIO_VENDOR_ID_BROADCOM, dev_id)} + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id)} /* devices we support, null terminated */ static const struct sdio_device_id brcmf_sdmmc_ids[] = { - BRCMF_SDIO_DEVICE(BRCM_SDIO_43143_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_43241_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_4329_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_4330_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_4334_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_43340_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_43341_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_43362_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_4335_4339_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_4354_DEVICE_ID), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43143), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43241), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4329), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4330), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4334), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43340), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 551da35..99a3776 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -3816,7 +3816,7 @@ static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr) u32 val, rev; val = brcmf_sdiod_regrl(sdiodev, addr, NULL); - if (sdiodev->func[0]->device == BRCM_SDIO_4335_4339_DEVICE_ID && + if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) { rev = (val & CID_REV_MASK) >> CID_REV_SHIFT; if (rev >= 2) { diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index 00215ef..2124a17 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -22,7 +22,6 @@ #define BRCM_USB_VENDOR_ID_BROADCOM 0x0a5c #define BRCM_PCIE_VENDOR_ID_BROADCOM PCI_VENDOR_ID_BROADCOM -#define BRCM_SDIO_VENDOR_ID_BROADCOM SDIO_VENDOR_ID_BROADCOM /* Chipcommon Core Chip IDs */ #define BRCM_CC_43143_CHIP_ID 43143 @@ -46,18 +45,6 @@ #define BRCM_CC_43570_CHIP_ID 43570 #define BRCM_CC_43602_CHIP_ID 43602 -/* SDIO Device IDs */ -#define BRCM_SDIO_43143_DEVICE_ID BRCM_CC_43143_CHIP_ID -#define BRCM_SDIO_43241_DEVICE_ID BRCM_CC_43241_CHIP_ID -#define BRCM_SDIO_4329_DEVICE_ID BRCM_CC_4329_CHIP_ID -#define BRCM_SDIO_4330_DEVICE_ID BRCM_CC_4330_CHIP_ID -#define BRCM_SDIO_4334_DEVICE_ID BRCM_CC_4334_CHIP_ID -#define BRCM_SDIO_43340_DEVICE_ID BRCM_CC_43340_CHIP_ID -#define BRCM_SDIO_43341_DEVICE_ID 43341 -#define BRCM_SDIO_43362_DEVICE_ID BRCM_CC_43362_CHIP_ID -#define BRCM_SDIO_4335_4339_DEVICE_ID BRCM_CC_4335_CHIP_ID -#define BRCM_SDIO_4354_DEVICE_ID BRCM_CC_4354_CHIP_ID - /* USB Device IDs */ #define BRCM_USB_43143_DEVICE_ID 0xbd1e #define BRCM_USB_43236_DEVICE_ID 0xbd17 -- cgit v0.10.2 From 1f6969fb230b79debcdf8ec3614162b553664396 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 6 Jan 2015 09:58:02 -0600 Subject: rtlwifi: Unify variable naming for all drivers Some drivers refer to a particular quantity in the driver's private are by one name, while others use a different name. These differences are removed. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c index f6cb5ae..c3aad76 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c @@ -221,7 +221,7 @@ static void rtl92c_dm_diginit(struct ieee80211_hw *hw) dm_digtable->forbidden_igi = DM_DIG_MIN; dm_digtable->large_fa_hit = 0; dm_digtable->recover_cnt = 0; - dm_digtable->dig_dynamic_min = 0x25; + dm_digtable->dig_min_0 = 0x25; } static u8 rtl92c_dm_initial_gain_min_pwdb(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c index 77deedf..2fa4c45 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c @@ -172,8 +172,8 @@ static void rtl92ee_dm_diginit(struct ieee80211_hw *hw) dm_dig->forbidden_igi = DM_DIG_MIN; dm_dig->large_fa_hit = 0; dm_dig->recover_cnt = 0; - dm_dig->dig_dynamic_min = DM_DIG_MIN; - dm_dig->dig_dynamic_min_1 = DM_DIG_MIN; + dm_dig->dig_min_0 = DM_DIG_MIN; + dm_dig->dig_min_1 = DM_DIG_MIN; dm_dig->media_connect_0 = false; dm_dig->media_connect_1 = false; rtlpriv->dm.dm_initialgain_enable = true; @@ -298,7 +298,7 @@ static void rtl92ee_dm_dig(struct ieee80211_hw *hw) struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct dig_t *dm_dig = &rtlpriv->dm_digtable; - u8 dig_dynamic_min , dig_maxofmin; + u8 dig_min_0, dig_maxofmin; bool bfirstconnect , bfirstdisconnect; u8 dm_dig_max, dm_dig_min; u8 current_igi = dm_dig->cur_igvalue; @@ -308,7 +308,7 @@ static void rtl92ee_dm_dig(struct ieee80211_hw *hw) if (mac->act_scanning) return; - dig_dynamic_min = dm_dig->dig_dynamic_min; + dig_min_0 = dm_dig->dig_min_0; bfirstconnect = (mac->link_state >= MAC80211_LINKED) && !dm_dig->media_connect_0; bfirstdisconnect = (mac->link_state < MAC80211_LINKED) && @@ -329,19 +329,19 @@ static void rtl92ee_dm_dig(struct ieee80211_hw *hw) if (rtlpriv->dm.one_entry_only) { offset = 0; if (dm_dig->rssi_val_min - offset < dm_dig_min) - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; else if (dm_dig->rssi_val_min - offset > dig_maxofmin) - dig_dynamic_min = dig_maxofmin; + dig_min_0 = dig_maxofmin; else - dig_dynamic_min = dm_dig->rssi_val_min - offset; + dig_min_0 = dm_dig->rssi_val_min - offset; } else { - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; } } else { dm_dig->rx_gain_max = dm_dig_max; - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "no link\n"); } @@ -368,10 +368,10 @@ static void rtl92ee_dm_dig(struct ieee80211_hw *hw) } else { if (dm_dig->large_fa_hit < 3) { if ((dm_dig->forbidden_igi - 1) < - dig_dynamic_min) { - dm_dig->forbidden_igi = dig_dynamic_min; + dig_min_0) { + dm_dig->forbidden_igi = dig_min_0; dm_dig->rx_gain_min = - dig_dynamic_min; + dig_min_0; } else { dm_dig->forbidden_igi--; dm_dig->rx_gain_min = @@ -430,7 +430,7 @@ static void rtl92ee_dm_dig(struct ieee80211_hw *hw) rtl92ee_dm_write_dig(hw , current_igi); dm_dig->media_connect_0 = ((mac->link_state >= MAC80211_LINKED) ? true : false); - dm_dig->dig_dynamic_min = dig_dynamic_min; + dm_dig->dig_min_0 = dig_min_0; } void rtl92ee_dm_write_cck_cca_thres(struct ieee80211_hw *hw, u8 cur_thres) diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/dm.c b/drivers/net/wireless/rtlwifi/rtl8723be/dm.c index dd7eb43..b92c521e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/dm.c @@ -232,8 +232,8 @@ static void rtl8723be_dm_diginit(struct ieee80211_hw *hw) dm_digtable->forbidden_igi = DM_DIG_MIN; dm_digtable->large_fa_hit = 0; dm_digtable->recover_cnt = 0; - dm_digtable->dig_dynamic_min = DM_DIG_MIN; - dm_digtable->dig_dynamic_min_1 = DM_DIG_MIN; + dm_digtable->dig_min_0 = DM_DIG_MIN; + dm_digtable->dig_min_1 = DM_DIG_MIN; dm_digtable->media_connect_0 = false; dm_digtable->media_connect_1 = false; rtlpriv->dm.dm_initialgain_enable = true; @@ -424,7 +424,7 @@ static void rtl8723be_dm_dig(struct ieee80211_hw *hw) struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *dm_digtable = &rtlpriv->dm_digtable; struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); - u8 dig_dynamic_min, dig_maxofmin; + u8 dig_min_0, dig_maxofmin; bool bfirstconnect, bfirstdisconnect; u8 dm_dig_max, dm_dig_min; u8 current_igi = dm_digtable->cur_igvalue; @@ -434,7 +434,7 @@ static void rtl8723be_dm_dig(struct ieee80211_hw *hw) if (mac->act_scanning) return; - dig_dynamic_min = dm_digtable->dig_dynamic_min; + dig_min_0 = dm_digtable->dig_min_0; bfirstconnect = (mac->link_state >= MAC80211_LINKED) && !dm_digtable->media_connect_0; bfirstdisconnect = (mac->link_state < MAC80211_LINKED) && @@ -456,20 +456,20 @@ static void rtl8723be_dm_dig(struct ieee80211_hw *hw) if (rtlpriv->dm.one_entry_only) { offset = 12; if (dm_digtable->rssi_val_min - offset < dm_dig_min) - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; else if (dm_digtable->rssi_val_min - offset > dig_maxofmin) - dig_dynamic_min = dig_maxofmin; + dig_min_0 = dig_maxofmin; else - dig_dynamic_min = + dig_min_0 = dm_digtable->rssi_val_min - offset; } else { - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; } } else { dm_digtable->rx_gain_max = dm_dig_max; - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "no link\n"); } @@ -497,11 +497,11 @@ static void rtl8723be_dm_dig(struct ieee80211_hw *hw) } else { if (dm_digtable->large_fa_hit < 3) { if ((dm_digtable->forbidden_igi - 1) < - dig_dynamic_min) { + dig_min_0) { dm_digtable->forbidden_igi = - dig_dynamic_min; + dig_min_0; dm_digtable->rx_gain_min = - dig_dynamic_min; + dig_min_0; } else { dm_digtable->forbidden_igi--; dm_digtable->rx_gain_min = @@ -552,7 +552,7 @@ static void rtl8723be_dm_dig(struct ieee80211_hw *hw) rtl8723be_dm_write_dig(hw, current_igi); dm_digtable->media_connect_0 = ((mac->link_state >= MAC80211_LINKED) ? true : false); - dm_digtable->dig_dynamic_min = dig_dynamic_min; + dm_digtable->dig_min_0 = dig_min_0; } static void rtl8723be_dm_false_alarm_counter_statistics( diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c index ba30b0d..8b0bfb2 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c @@ -539,8 +539,8 @@ static void rtl8821ae_dm_diginit(struct ieee80211_hw *hw) dm_digtable->forbidden_igi = DM_DIG_MIN; dm_digtable->large_fa_hit = 0; dm_digtable->recover_cnt = 0; - dm_digtable->dig_dynamic_min = DM_DIG_MIN; - dm_digtable->dig_dynamic_min_1 = DM_DIG_MIN; + dm_digtable->dig_min_0 = DM_DIG_MIN; + dm_digtable->dig_min_1 = DM_DIG_MIN; dm_digtable->media_connect_0 = false; dm_digtable->media_connect_1 = false; rtlpriv->dm.dm_initialgain_enable = true; @@ -822,7 +822,7 @@ static void rtl8821ae_dm_dig(struct ieee80211_hw *hw) struct dig_t *dm_digtable = &rtlpriv->dm_digtable; struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); - u8 dig_dynamic_min; + u8 dig_min_0; u8 dig_max_of_min; bool first_connect, first_disconnect; u8 dm_dig_max, dm_dig_min, offset; @@ -837,7 +837,7 @@ static void rtl8821ae_dm_dig(struct ieee80211_hw *hw) } /*add by Neil Chen to avoid PSD is processing*/ - dig_dynamic_min = dm_digtable->dig_dynamic_min; + dig_min_0 = dm_digtable->dig_min_0; first_connect = (mac->link_state >= MAC80211_LINKED) && (!dm_digtable->media_connect_0); first_disconnect = (mac->link_state < MAC80211_LINKED) && @@ -876,23 +876,23 @@ static void rtl8821ae_dm_dig(struct ieee80211_hw *hw) offset = 0; if (dm_digtable->rssi_val_min - offset < dm_dig_min) - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; else if (dm_digtable->rssi_val_min - offset > dig_max_of_min) - dig_dynamic_min = dig_max_of_min; + dig_min_0 = dig_max_of_min; else - dig_dynamic_min = + dig_min_0 = dm_digtable->rssi_val_min - offset; RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, - "bOneEntryOnly=TRUE, dig_dynamic_min=0x%x\n", - dig_dynamic_min); + "bOneEntryOnly=TRUE, dig_min_0=0x%x\n", + dig_min_0); } else { - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; } } else { dm_digtable->rx_gain_max = dm_dig_max; - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "No Link\n"); } @@ -925,11 +925,11 @@ static void rtl8821ae_dm_dig(struct ieee80211_hw *hw) } else { if (dm_digtable->large_fa_hit < 3) { if ((dm_digtable->forbidden_igi - 1) < - dig_dynamic_min) { + dig_min_0) { dm_digtable->forbidden_igi = - dig_dynamic_min; + dig_min_0; dm_digtable->rx_gain_min = - dig_dynamic_min; + dig_min_0; RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "Normal Case: At Lower Bound\n"); } else { @@ -1024,7 +1024,7 @@ static void rtl8821ae_dm_dig(struct ieee80211_hw *hw) rtl8821ae_dm_write_dig(hw, current_igi); dm_digtable->media_connect_0 = ((mac->link_state >= MAC80211_LINKED) ? true : false); - dm_digtable->dig_dynamic_min = dig_dynamic_min; + dm_digtable->dig_min_0 = dig_min_0; } static void rtl8821ae_dm_common_info_self_update(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index 7a718fd..b53d9dd 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -2412,8 +2412,6 @@ struct dig_t { u8 pre_ccastate; u8 cur_ccasate; u8 large_fa_hit; - u8 dig_dynamic_min; - u8 dig_dynamic_min_1; u8 forbidden_igi; u8 dig_state; u8 dig_highpwrstate; -- cgit v0.10.2 From 3f0c1cfa735a12dbe24c583d2ab833d348745856 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 6 Jan 2015 09:58:03 -0600 Subject: rtlwifi: rtl8723be: Improve modinfo output The description of the power-save variables for this driver is not as clear as for the others. The wording is changed to match the others. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/rtlwifi/rtl8723be/sw.c index 223eb42..1017f02 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/sw.c @@ -387,12 +387,14 @@ module_param_named(swlps, rtl8723be_mod_params.swctrl_lps, bool, 0444); module_param_named(fwlps, rtl8723be_mod_params.fwctrl_lps, bool, 0444); module_param_named(disable_watchdog, rtl8723be_mod_params.disable_watchdog, bool, 0444); -MODULE_PARM_DESC(swenc, "using hardware crypto (default 0 [hardware])\n"); -MODULE_PARM_DESC(ips, "using no link power save (default 1 is open)\n"); -MODULE_PARM_DESC(fwlps, "using linked fw control power save (default 1 is open)\n"); +MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); +MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); +MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); +MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); -MODULE_PARM_DESC(disable_watchdog, "Set to 1 to disable the watchdog (default 0)\n"); +MODULE_PARM_DESC(disable_watchdog, + "Set to 1 to disable the watchdog (default 0)\n"); static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); -- cgit v0.10.2 From 6f8214b6905741ac5c58958a35257e8103645c90 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 6 Jan 2015 09:58:04 -0600 Subject: rtlwifi: Create new routine to initialize the DM tables Each of the drivers contains a routine that initializes the dm_digtable member of the driver's private area. As a first step toward reducing the size of the drivers, a copy of this driver is created in rtlwifi, and the definitions of the parameters are moved there. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index deab852..5aa3ab3 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -1871,3 +1871,37 @@ bool rtl_btc_status_false(void) return false; } EXPORT_SYMBOL_GPL(rtl_btc_status_false); + +void rtl_dm_diginit(struct ieee80211_hw *hw, u32 cur_igvalue) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct dig_t *dm_digtable = &rtlpriv->dm_digtable; + + dm_digtable->dig_enable_flag = true; + dm_digtable->cur_igvalue = cur_igvalue; + dm_digtable->pre_igvalue = 0; + dm_digtable->cur_sta_cstate = DIG_STA_DISCONNECT; + dm_digtable->presta_cstate = DIG_STA_DISCONNECT; + dm_digtable->curmultista_cstate = DIG_MULTISTA_DISCONNECT; + dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW; + dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH; + dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; + dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; + dm_digtable->rx_gain_max = DM_DIG_MAX; + dm_digtable->rx_gain_min = DM_DIG_MIN; + dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT; + dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX; + dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN; + dm_digtable->pre_cck_cca_thres = 0xff; + dm_digtable->cur_cck_cca_thres = 0x83; + dm_digtable->forbidden_igi = DM_DIG_MIN; + dm_digtable->large_fa_hit = 0; + dm_digtable->recover_cnt = 0; + dm_digtable->dig_min_0 = 0x25; + dm_digtable->dig_min_1 = 0x25; + dm_digtable->media_connect_0 = false; + dm_digtable->media_connect_1 = false; + rtlpriv->dm.dm_initialgain_enable = true; + dm_digtable->bt30_cur_igi = 0x32; +} +EXPORT_SYMBOL(rtl_dm_diginit); diff --git a/drivers/net/wireless/rtlwifi/core.h b/drivers/net/wireless/rtlwifi/core.h index 8c87eb5..c0a0317 100644 --- a/drivers/net/wireless/rtlwifi/core.h +++ b/drivers/net/wireless/rtlwifi/core.h @@ -35,6 +35,29 @@ #define RTL_SUPPORTED_CTRL_FILTER 0xFF +#define DM_DIG_THRESH_HIGH 40 +#define DM_DIG_THRESH_LOW 35 +#define DM_FALSEALARM_THRESH_LOW 400 +#define DM_FALSEALARM_THRESH_HIGH 1000 + +#define DM_DIG_MAX 0x3e +#define DM_DIG_MIN 0x1e +#define DM_DIG_BACKOFF_MAX 12 +#define DM_DIG_BACKOFF_MIN -4 +#define DM_DIG_BACKOFF_DEFAULT 10 + +enum dm_dig_connect_e { + DIG_STA_DISCONNECT, + DIG_STA_CONNECT, + DIG_STA_BEFORE_CONNECT, + DIG_MULTISTA_DISCONNECT, + DIG_MULTISTA_CONNECT, + DIG_AP_DISCONNECT, + DIG_AP_CONNECT, + DIG_AP_ADD_STATION, + DIG_CONNECT_MAX +}; + extern const struct ieee80211_ops rtl_ops; void rtl_fw_cb(const struct firmware *firmware, void *context); void rtl_wowlan_fw_cb(const struct firmware *firmware, void *context); @@ -44,5 +67,6 @@ void rtl_rfreg_delay(struct ieee80211_hw *hw, enum radio_path rfpath, u32 addr, void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data); bool rtl_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb); bool rtl_btc_status_false(void); +void rtl_dm_diginit(struct ieee80211_hw *hw, u32 cur_igval); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c index 2aa34d9..7ebf6e0 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c @@ -26,6 +26,7 @@ #include "../wifi.h" #include "../base.h" #include "../pci.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h index 64f1f3ea..6217b7c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h @@ -186,15 +186,6 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 - -#define DM_FALSEALARM_THRESH_LOW 400 -#define DM_FALSEALARM_THRESH_HIGH 1000 - -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1e - #define DM_DIG_MAX_AP 0x32 #define DM_DIG_MIN_AP 0x20 @@ -204,10 +195,6 @@ #define DM_DIG_FA_TH1 0x300 #define DM_DIG_FA_TH2 0x400 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 -#define DM_DIG_BACKOFF_DEFAULT 10 - #define RXPATHSELECTION_SS_TH_W 30 #define RXPATHSELECTION_DIFF_TH 18 @@ -296,15 +283,6 @@ enum dm_dig_ext_port_alg_e { DIG_EXT_PORT_STAGE_MAX = 4, }; -enum dm_dig_connect_e { - DIG_STA_DISCONNECT = 0, - DIG_STA_CONNECT = 1, - DIG_STA_BEFORE_CONNECT = 2, - DIG_MULTISTA_DISCONNECT = 3, - DIG_MULTISTA_CONNECT = 4, - DIG_CONNECT_MAX -}; - enum pwr_track_control_method { BBSWING, TXAGC diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c index c3aad76..dabe18f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c @@ -32,6 +32,7 @@ #include "phy_common.h" #include "../pci.h" #include "../base.h" +#include "../core.h" #define BT_RSSI_STATE_NORMAL_POWER BIT_OFFSET_LEN_MASK_32(0, 1) #define BT_RSSI_STATE_AMDPU_OFF BIT_OFFSET_LEN_MASK_32(1, 1) diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h index 4f232a0..55ebffd 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h +++ b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h @@ -47,25 +47,12 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 - -#define DM_FALSEALARM_THRESH_LOW 400 -#define DM_FALSEALARM_THRESH_HIGH 1000 - -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1e - #define DM_DIG_FA_UPPER 0x32 #define DM_DIG_FA_LOWER 0x20 #define DM_DIG_FA_TH0 0x20 #define DM_DIG_FA_TH1 0x100 #define DM_DIG_FA_TH2 0x200 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 -#define DM_DIG_BACKOFF_DEFAULT 10 - #define RXPATHSELECTION_SS_TH_lOW 30 #define RXPATHSELECTION_DIFF_TH 18 @@ -157,15 +144,6 @@ enum dm_dig_ext_port_alg_e { DIG_EXT_PORT_STAGE_MAX = 4, }; -enum dm_dig_connect_e { - DIG_STA_DISCONNECT = 0, - DIG_STA_CONNECT = 1, - DIG_STA_BEFORE_CONNECT = 2, - DIG_MULTISTA_DISCONNECT = 3, - DIG_MULTISTA_CONNECT = 4, - DIG_CONNECT_MAX -}; - void rtl92c_dm_init(struct ieee80211_hw *hw); void rtl92c_dm_watchdog(struct ieee80211_hw *hw); void rtl92c_dm_write_dig(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/dm.c b/drivers/net/wireless/rtlwifi/rtl8192ce/dm.c index 74f9c08..09898cf 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/dm.c @@ -30,6 +30,7 @@ #include "../wifi.h" #include "../base.h" #include "../pci.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/dm.h b/drivers/net/wireless/rtlwifi/rtl8192ce/dm.h index 9c5311c..38ba707 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/dm.h @@ -42,25 +42,12 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 - -#define DM_FALSEALARM_THRESH_LOW 400 -#define DM_FALSEALARM_THRESH_HIGH 1000 - -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1e - #define DM_DIG_FA_UPPER 0x32 #define DM_DIG_FA_LOWER 0x20 #define DM_DIG_FA_TH0 0x20 #define DM_DIG_FA_TH1 0x100 #define DM_DIG_FA_TH2 0x200 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 -#define DM_DIG_BACKOFF_DEFAULT 10 - #define RXPATHSELECTION_SS_TH_lOW 30 #define RXPATHSELECTION_DIFF_TH 18 diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c index 304c443..75643ab 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c @@ -29,6 +29,7 @@ #include "../wifi.h" #include "../base.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.h b/drivers/net/wireless/rtlwifi/rtl8192de/dm.h index 3fea0c1..d21b3bc 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.h @@ -42,25 +42,12 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 - -#define DM_FALSEALARM_THRESH_LOW 400 -#define DM_FALSEALARM_THRESH_HIGH 1000 - -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1c - #define DM_DIG_FA_UPPER 0x32 #define DM_DIG_FA_LOWER 0x20 #define DM_DIG_FA_TH0 0x100 #define DM_DIG_FA_TH1 0x400 #define DM_DIG_FA_TH2 0x600 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 -#define DM_DIG_BACKOFF_DEFAULT 10 - #define RXPATHSELECTION_SS_TH_lOW 30 #define RXPATHSELECTION_DIFF_TH 18 @@ -142,15 +129,6 @@ enum dm_dig_ext_port_alg { DIG_EXT_PORT_STAGE_MAX = 4, }; -enum dm_dig_connect { - DIG_STA_DISCONNECT = 0, - DIG_STA_CONNECT = 1, - DIG_STA_BEFORE_CONNECT = 2, - DIG_MULTISTA_DISCONNECT = 3, - DIG_MULTISTA_CONNECT = 4, - DIG_CONNECT_MAX -}; - void rtl92d_dm_init(struct ieee80211_hw *hw); void rtl92d_dm_watchdog(struct ieee80211_hw *hw); void rtl92d_dm_init_edca_turbo(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c index 2fa4c45..70e58d1 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c @@ -26,6 +26,7 @@ #include "../wifi.h" #include "../base.h" #include "../pci.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.h b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.h index 881db7d..4880e19 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.h @@ -189,15 +189,6 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 - -#define DM_FALSEALARM_THRESH_LOW 400 -#define DM_FALSEALARM_THRESH_HIGH 1000 - -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1e - #define DM_DIG_MAX_AP 0x32 #define DM_DIG_MIN_AP 0x20 @@ -207,10 +198,6 @@ #define DM_DIG_FA_TH1 0x300 #define DM_DIG_FA_TH2 0x400 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 -#define DM_DIG_BACKOFF_DEFAULT 10 - #define RXPATHSELECTION_SS_TH_LOW 30 #define RXPATHSELECTION_DIFF_TH 18 diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/dm.c b/drivers/net/wireless/rtlwifi/rtl8192se/dm.c index b3a2d5e..6cac70b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/dm.c @@ -29,6 +29,7 @@ #include "../wifi.h" #include "../base.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/dm.h b/drivers/net/wireless/rtlwifi/rtl8192se/dm.h index 2e9052c..7d778d3 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8192se/dm.h @@ -54,16 +54,6 @@ enum dm_dig_sta { DM_STA_DIG_MAX }; -enum dm_dig_connect { - DIG_STA_DISCONNECT = 0, - DIG_STA_CONNECT = 1, - DIG_STA_BEFORE_CONNECT = 2, - DIG_AP_DISCONNECT = 3, - DIG_AP_CONNECT = 4, - DIG_AP_ADD_STATION = 5, - DIG_CONNECT_MAX -}; - enum dm_dig_ext_port_alg { DIG_EXT_PORT_STAGE_0 = 0, DIG_EXT_PORT_STAGE_1 = 1, @@ -99,22 +89,13 @@ enum dm_ratr_sta { #define TX_POWER_NEAR_FIELD_THRESH_LVL2 74 #define TX_POWER_NEAR_FIELD_THRESH_LVL1 67 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 -#define DM_FALSEALARM_THRESH_LOW 40 -#define DM_FALSEALARM_THRESH_HIGH 1000 #define DM_DIG_HIGH_PWR_THRESH_HIGH 75 #define DM_DIG_HIGH_PWR_THRESH_LOW 70 #define DM_DIG_BACKOFF 12 -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1c #define DM_DIG_MIN_Netcore 0x12 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 void rtl92s_dm_watchdog(struct ieee80211_hw *hw); void rtl92s_dm_init(struct ieee80211_hw *hw); void rtl92s_dm_init_edca_turbo(struct ieee80211_hw *hw); #endif - diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c index a0e8692..80f4c54 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c @@ -26,6 +26,7 @@ #include "../wifi.h" #include "../base.h" #include "../pci.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h index 6fa0feb..c54024e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h @@ -42,25 +42,12 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 - -#define DM_FALSEALARM_THRESH_LOW 400 -#define DM_FALSEALARM_THRESH_HIGH 1000 - -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1e - #define DM_DIG_FA_UPPER 0x32 #define DM_DIG_FA_LOWER 0x20 #define DM_DIG_FA_TH0 0x20 #define DM_DIG_FA_TH1 0x100 #define DM_DIG_FA_TH2 0x200 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 -#define DM_DIG_BACKOFF_DEFAULT 10 - #define RXPATHSELECTION_SS_TH_LOW 30 #define RXPATHSELECTION_DIFF_TH 18 @@ -142,15 +129,6 @@ enum dm_dig_ext_port_alg_e { DIG_EXT_PORT_STAGE_MAX = 4, }; -enum dm_dig_connect_e { - DIG_STA_DISCONNECT = 0, - DIG_STA_CONNECT = 1, - DIG_STA_BEFORE_CONNECT = 2, - DIG_MULTISTA_DISCONNECT = 3, - DIG_MULTISTA_CONNECT = 4, - DIG_CONNECT_MAX -}; - #define BT_RSSI_STATE_NORMAL_POWER BIT_OFFSET_LEN_MASK_32(0, 1) #define BT_RSSI_STATE_AMDPU_OFF BIT_OFFSET_LEN_MASK_32(1, 1) #define BT_RSSI_STATE_SPECIAL_LOW BIT_OFFSET_LEN_MASK_32(2, 1) diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/dm.c b/drivers/net/wireless/rtlwifi/rtl8723be/dm.c index b92c521e..ddf45d9 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/dm.c @@ -26,6 +26,7 @@ #include "../wifi.h" #include "../base.h" #include "../pci.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/dm.h b/drivers/net/wireless/rtlwifi/rtl8723be/dm.h index e4c0e8a..f3e47ab 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8723be/dm.h @@ -180,15 +180,6 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 - -#define DM_FALSEALARM_THRESH_LOW 400 -#define DM_FALSEALARM_THRESH_HIGH 1000 - -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1e - #define DM_DIG_MAX_AP 0x32 #define DM_DIG_MIN_AP 0x20 @@ -198,10 +189,6 @@ #define DM_DIG_FA_TH1 0x300 #define DM_DIG_FA_TH2 0x400 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 -#define DM_DIG_BACKOFF_DEFAULT 10 - #define RXPATHSELECTION_SS_TH_LOW 30 #define RXPATHSELECTION_DIFF_TH 18 @@ -260,15 +247,6 @@ enum dm_dig_ext_port_alg_e { DIG_EXT_PORT_STAGE_MAX = 4, }; -enum dm_dig_connect_e { - DIG_STA_DISCONNECT = 0, - DIG_STA_CONNECT = 1, - DIG_STA_BEFORE_CONNECT = 2, - DIG_MULTISTA_DISCONNECT = 3, - DIG_MULTISTA_CONNECT = 4, - DIG_CONNECT_MAX -}; - enum pwr_track_control_method { BBSWING, TXAGC diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c index 8b0bfb2..4af4613 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c @@ -26,6 +26,7 @@ #include "../wifi.h" #include "../base.h" #include "../pci.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h index 9dd40dd..f311887 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h @@ -187,9 +187,6 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 - #define DM_FALSEALARM_THRESH_LOW 400 #define DM_FALSEALARM_THRESH_HIGH 1000 @@ -205,10 +202,6 @@ #define DM_DIG_FA_TH1 0x300 #define DM_DIG_FA_TH2 0x400 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 -#define DM_DIG_BACKOFF_DEFAULT 10 - #define RXPATHSELECTION_SS_TH_LOW 30 #define RXPATHSELECTION_DIFF_TH 18 @@ -296,15 +289,6 @@ enum dm_dig_ext_port_alg_e { DIG_EXT_PORT_STAGE_MAX = 4, }; -enum dm_dig_connect_e { - DIG_STA_DISCONNECT = 0, - DIG_STA_CONNECT = 1, - DIG_STA_BEFORE_CONNECT = 2, - DIG_MULTISTA_DISCONNECT = 3, - DIG_MULTISTA_CONNECT = 4, - DIG_CONNECT_MAX -}; - enum pwr_track_control_method { BBSWING, TXAGC, -- cgit v0.10.2 From b5d4478dc35e0cd9ef482d22aba069a19fe04a04 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 6 Jan 2015 09:58:05 -0600 Subject: rtlwifi: rtl8188ee: Convert driver to use the common DM table init routine The previous patch created a routine in rtlwifi to initialize dm_digtable. Driver rtl8188ee is converted to use that routine. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c index 7ebf6e0..d930c1f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c @@ -342,38 +342,6 @@ static void dm_tx_pwr_track_set_pwr(struct ieee80211_hw *hw, } } -static void rtl88e_dm_diginit(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct dig_t *dm_dig = &rtlpriv->dm_digtable; - - dm_dig->dig_enable_flag = true; - dm_dig->cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f); - dm_dig->pre_igvalue = 0; - dm_dig->cur_sta_cstate = DIG_STA_DISCONNECT; - dm_dig->presta_cstate = DIG_STA_DISCONNECT; - dm_dig->curmultista_cstate = DIG_MULTISTA_DISCONNECT; - dm_dig->rssi_lowthresh = DM_DIG_THRESH_LOW; - dm_dig->rssi_highthresh = DM_DIG_THRESH_HIGH; - dm_dig->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; - dm_dig->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; - dm_dig->rx_gain_max = DM_DIG_MAX; - dm_dig->rx_gain_min = DM_DIG_MIN; - dm_dig->back_val = DM_DIG_BACKOFF_DEFAULT; - dm_dig->back_range_max = DM_DIG_BACKOFF_MAX; - dm_dig->back_range_min = DM_DIG_BACKOFF_MIN; - dm_dig->pre_cck_cca_thres = 0xff; - dm_dig->cur_cck_cca_thres = 0x83; - dm_dig->forbidden_igi = DM_DIG_MIN; - dm_dig->large_fa_hit = 0; - dm_dig->recover_cnt = 0; - dm_dig->dig_min_0 = 0x25; - dm_dig->dig_min_1 = 0x25; - dm_dig->media_connect_0 = false; - dm_dig->media_connect_1 = false; - rtlpriv->dm.dm_initialgain_enable = true; -} - static u8 rtl88e_dm_initial_gain_min_pwdb(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -1797,9 +1765,10 @@ static void rtl88e_dm_antenna_diversity(struct ieee80211_hw *hw) void rtl88e_dm_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f); rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; - rtl88e_dm_diginit(hw); + rtl_dm_diginit(hw, cur_igvalue); rtl88e_dm_init_dynamic_txpower(hw); rtl88e_dm_init_edca_turbo(hw); rtl88e_dm_init_rate_adaptive_mask(hw); -- cgit v0.10.2 From 3424a00fd559debf402559c226d9b1d895b94a5f Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 6 Jan 2015 09:58:06 -0600 Subject: rtlwifi: rtl8192c-common: Convert driver to use common DM table initialization These changes convert both rtl8192ce and rtl8192cu to use the new routine. Some additional definitions are needed in the core, thus several of the headers for other drivers are affected, but no other executable code is changed. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index 5aa3ab3..eb20316 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -1878,6 +1878,7 @@ void rtl_dm_diginit(struct ieee80211_hw *hw, u32 cur_igvalue) struct dig_t *dm_digtable = &rtlpriv->dm_digtable; dm_digtable->dig_enable_flag = true; + dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; dm_digtable->cur_igvalue = cur_igvalue; dm_digtable->pre_igvalue = 0; dm_digtable->cur_sta_cstate = DIG_STA_DISCONNECT; @@ -1903,5 +1904,7 @@ void rtl_dm_diginit(struct ieee80211_hw *hw, u32 cur_igvalue) dm_digtable->media_connect_1 = false; rtlpriv->dm.dm_initialgain_enable = true; dm_digtable->bt30_cur_igi = 0x32; + dm_digtable->pre_cck_pd_state = CCK_PD_STAGE_MAX; + dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_LOWRSSI; } EXPORT_SYMBOL(rtl_dm_diginit); diff --git a/drivers/net/wireless/rtlwifi/core.h b/drivers/net/wireless/rtlwifi/core.h index c0a0317..1cde356 100644 --- a/drivers/net/wireless/rtlwifi/core.h +++ b/drivers/net/wireless/rtlwifi/core.h @@ -46,6 +46,22 @@ #define DM_DIG_BACKOFF_MIN -4 #define DM_DIG_BACKOFF_DEFAULT 10 +enum cck_packet_detection_threshold { + CCK_PD_STAGE_LOWRSSI = 0, + CCK_PD_STAGE_HIGHRSSI = 1, + CCK_FA_STAGE_LOW = 2, + CCK_FA_STAGE_HIGH = 3, + CCK_PD_STAGE_MAX = 4, +}; + +enum dm_dig_ext_port_alg_e { + DIG_EXT_PORT_STAGE_0 = 0, + DIG_EXT_PORT_STAGE_1 = 1, + DIG_EXT_PORT_STAGE_2 = 2, + DIG_EXT_PORT_STAGE_3 = 3, + DIG_EXT_PORT_STAGE_MAX = 4, +}; + enum dm_dig_connect_e { DIG_STA_DISCONNECT, DIG_STA_CONNECT, diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h index 6217b7c..51ad3f9 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h @@ -249,14 +249,6 @@ enum tag_dynamic_init_gain_operation_type_definition { DIG_OP_TYPE_MAX }; -enum tag_cck_packet_detection_threshold_type_definition { - CCK_PD_STAGE_LOWRSSI = 0, - CCK_PD_STAGE_HIGHRSSI = 1, - CCK_FA_STAGE_LOW = 2, - CCK_FA_STAGE_HIGH = 3, - CCK_PD_STAGE_MAX = 4, -}; - enum dm_1r_cca_e { CCA_1R = 0, CCA_2R = 1, @@ -275,14 +267,6 @@ enum dm_sw_ant_switch_e { ANS_ANTENNA_MAX = 3, }; -enum dm_dig_ext_port_alg_e { - DIG_EXT_PORT_STAGE_0 = 0, - DIG_EXT_PORT_STAGE_1 = 1, - DIG_EXT_PORT_STAGE_2 = 2, - DIG_EXT_PORT_STAGE_3 = 3, - DIG_EXT_PORT_STAGE_MAX = 4, -}; - enum pwr_track_control_method { BBSWING, TXAGC diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c index dabe18f..f5ee67c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c @@ -195,36 +195,6 @@ void dm_savepowerindex(struct ieee80211_hw *hw) } EXPORT_SYMBOL_GPL(dm_savepowerindex); -static void rtl92c_dm_diginit(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct dig_t *dm_digtable = &rtlpriv->dm_digtable; - - dm_digtable->dig_enable_flag = true; - dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; - dm_digtable->cur_igvalue = 0x20; - dm_digtable->pre_igvalue = 0x0; - dm_digtable->cursta_cstate = DIG_STA_DISCONNECT; - dm_digtable->presta_cstate = DIG_STA_DISCONNECT; - dm_digtable->curmultista_cstate = DIG_MULTISTA_DISCONNECT; - dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW; - dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH; - dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; - dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; - dm_digtable->rx_gain_max = DM_DIG_MAX; - dm_digtable->rx_gain_min = DM_DIG_MIN; - dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT; - dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX; - dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN; - dm_digtable->pre_cck_pd_state = CCK_PD_STAGE_MAX; - dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_LowRssi; - - dm_digtable->forbidden_igi = DM_DIG_MIN; - dm_digtable->large_fa_hit = 0; - dm_digtable->recover_cnt = 0; - dm_digtable->dig_min_0 = 0x25; -} - static u8 rtl92c_dm_initial_gain_min_pwdb(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -508,27 +478,27 @@ static void rtl92c_dm_cck_packet_detection_thresh(struct ieee80211_hw *hw) if (dm_digtable->rssi_val_min > 100) dm_digtable->rssi_val_min = 100; - if (dm_digtable->pre_cck_pd_state == CCK_PD_STAGE_LowRssi) { + if (dm_digtable->pre_cck_pd_state == CCK_PD_STAGE_LOWRSSI) { if (dm_digtable->rssi_val_min <= 25) dm_digtable->cur_cck_pd_state = - CCK_PD_STAGE_LowRssi; + CCK_PD_STAGE_LOWRSSI; else dm_digtable->cur_cck_pd_state = - CCK_PD_STAGE_HighRssi; + CCK_PD_STAGE_HIGHRSSI; } else { if (dm_digtable->rssi_val_min <= 20) dm_digtable->cur_cck_pd_state = - CCK_PD_STAGE_LowRssi; + CCK_PD_STAGE_LOWRSSI; else dm_digtable->cur_cck_pd_state = - CCK_PD_STAGE_HighRssi; + CCK_PD_STAGE_HIGHRSSI; } } else { dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX; } if (dm_digtable->pre_cck_pd_state != dm_digtable->cur_cck_pd_state) { - if ((dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_LowRssi) || + if ((dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_LOWRSSI) || (dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_MAX)) rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0x83); else @@ -1375,7 +1345,7 @@ void rtl92c_dm_init(struct ieee80211_hw *hw) rtlpriv->dm.undec_sm_pwdb = -1; rtlpriv->dm.undec_sm_cck = -1; rtlpriv->dm.dm_initialgain_enable = true; - rtl92c_dm_diginit(hw); + rtl_dm_diginit(hw, 0x20); rtlpriv->dm.dm_flag |= HAL_DM_HIPWR_DISABLE; rtl92c_dm_init_dynamic_txpower(hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h index 55ebffd..4422e31 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h +++ b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h @@ -110,14 +110,6 @@ enum tag_dynamic_init_gain_operation_type_definition { DIG_OP_TYPE_MAX }; -enum tag_cck_packet_detection_threshold_type_definition { - CCK_PD_STAGE_LowRssi = 0, - CCK_PD_STAGE_HighRssi = 1, - CCK_FA_STAGE_Low = 2, - CCK_FA_STAGE_High = 3, - CCK_PD_STAGE_MAX = 4, -}; - enum dm_1r_cca_e { CCA_1R = 0, CCA_2R = 1, @@ -136,14 +128,6 @@ enum dm_sw_ant_switch_e { ANS_ANTENNA_MAX = 3, }; -enum dm_dig_ext_port_alg_e { - DIG_EXT_PORT_STAGE_0 = 0, - DIG_EXT_PORT_STAGE_1 = 1, - DIG_EXT_PORT_STAGE_2 = 2, - DIG_EXT_PORT_STAGE_3 = 3, - DIG_EXT_PORT_STAGE_MAX = 4, -}; - void rtl92c_dm_init(struct ieee80211_hw *hw); void rtl92c_dm_watchdog(struct ieee80211_hw *hw); void rtl92c_dm_write_dig(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.h b/drivers/net/wireless/rtlwifi/rtl8192de/dm.h index d21b3bc..f2d318c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.h @@ -95,14 +95,6 @@ enum tag_dynamic_init_gain_operation_type_definition { DIG_OP_TYPE_MAX }; -enum tag_cck_packet_detection_threshold_type_definition { - CCK_PD_STAGE_LOWRSSI = 0, - CCK_PD_STAGE_HIGHRSSI = 1, - CCK_FA_STAGE_LOW = 2, - CCK_FA_STAGE_HIGH = 3, - CCK_PD_STAGE_MAX = 4, -}; - enum dm_1r_cca { CCA_1R = 0, CCA_2R = 1, @@ -121,14 +113,6 @@ enum dm_sw_ant_switch { ANS_ANTENNA_MAX = 3, }; -enum dm_dig_ext_port_alg { - DIG_EXT_PORT_STAGE_0 = 0, - DIG_EXT_PORT_STAGE_1 = 1, - DIG_EXT_PORT_STAGE_2 = 2, - DIG_EXT_PORT_STAGE_3 = 3, - DIG_EXT_PORT_STAGE_MAX = 4, -}; - void rtl92d_dm_init(struct ieee80211_hw *hw); void rtl92d_dm_watchdog(struct ieee80211_hw *hw); void rtl92d_dm_init_edca_turbo(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/dm.h b/drivers/net/wireless/rtlwifi/rtl8192se/dm.h index 7d778d3..be07d81 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8192se/dm.h @@ -54,14 +54,6 @@ enum dm_dig_sta { DM_STA_DIG_MAX }; -enum dm_dig_ext_port_alg { - DIG_EXT_PORT_STAGE_0 = 0, - DIG_EXT_PORT_STAGE_1 = 1, - DIG_EXT_PORT_STAGE_2 = 2, - DIG_EXT_PORT_STAGE_3 = 3, - DIG_EXT_PORT_STAGE_MAX = 4, -}; - enum dm_ratr_sta { DM_RATR_STA_HIGH = 0, DM_RATR_STA_MIDDLEHIGH = 1, diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c index 80f4c54..8bd124c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c @@ -396,30 +396,30 @@ static void rtl8723e_dm_cck_packet_detection_thresh(struct ieee80211_hw *hw) if (dm_digtable->cursta_cstate == DIG_STA_CONNECT) { dm_digtable->rssi_val_min = rtl8723e_dm_initial_gain_min_pwdb(hw); - if (dm_digtable->pre_cck_pd_state == CCK_PD_STAGE_LowRssi) { + if (dm_digtable->pre_cck_pd_state == CCK_PD_STAGE_LOWRSSI) { if (dm_digtable->rssi_val_min <= 25) dm_digtable->cur_cck_pd_state = - CCK_PD_STAGE_LowRssi; + CCK_PD_STAGE_LOWRSSI; else dm_digtable->cur_cck_pd_state = - CCK_PD_STAGE_HighRssi; + CCK_PD_STAGE_HIGHRSSI; } else { if (dm_digtable->rssi_val_min <= 20) dm_digtable->cur_cck_pd_state = - CCK_PD_STAGE_LowRssi; + CCK_PD_STAGE_LOWRSSI; else dm_digtable->cur_cck_pd_state = - CCK_PD_STAGE_HighRssi; + CCK_PD_STAGE_HIGHRSSI; } } else { dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX; } if (dm_digtable->pre_cck_pd_state != dm_digtable->cur_cck_pd_state) { - if (dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_LowRssi) { + if (dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_LOWRSSI) { if (rtlpriv->falsealm_cnt.cnt_cck_fail > 800) dm_digtable->cur_cck_fa_state = - CCK_FA_STAGE_High; + CCK_FA_STAGE_HIGH; else dm_digtable->cur_cck_fa_state = CCK_FA_STAGE_LOW; diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h index c54024e..5711105 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h @@ -95,14 +95,6 @@ enum tag_dynamic_init_gain_operation_type_definition { DIG_OP_TYPE_MAX }; -enum tag_cck_packet_detection_threshold_type_definition { - CCK_PD_STAGE_LowRssi = 0, - CCK_PD_STAGE_HighRssi = 1, - CCK_FA_STAGE_LOW = 2, - CCK_FA_STAGE_High = 3, - CCK_PD_STAGE_MAX = 4, -}; - enum dm_1r_cca_e { CCA_1R = 0, CCA_2R = 1, @@ -121,14 +113,6 @@ enum dm_sw_ant_switch_e { ANS_ANTENNA_MAX = 3, }; -enum dm_dig_ext_port_alg_e { - DIG_EXT_PORT_STAGE_0 = 0, - DIG_EXT_PORT_STAGE_1 = 1, - DIG_EXT_PORT_STAGE_2 = 2, - DIG_EXT_PORT_STAGE_3 = 3, - DIG_EXT_PORT_STAGE_MAX = 4, -}; - #define BT_RSSI_STATE_NORMAL_POWER BIT_OFFSET_LEN_MASK_32(0, 1) #define BT_RSSI_STATE_AMDPU_OFF BIT_OFFSET_LEN_MASK_32(1, 1) #define BT_RSSI_STATE_SPECIAL_LOW BIT_OFFSET_LEN_MASK_32(2, 1) diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/dm.h b/drivers/net/wireless/rtlwifi/rtl8723be/dm.h index f3e47ab..533b4f2 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8723be/dm.h @@ -239,14 +239,6 @@ enum dm_sw_ant_switch_e { ANS_ANTENNA_MAX = 3, }; -enum dm_dig_ext_port_alg_e { - DIG_EXT_PORT_STAGE_0 = 0, - DIG_EXT_PORT_STAGE_1 = 1, - DIG_EXT_PORT_STAGE_2 = 2, - DIG_EXT_PORT_STAGE_3 = 3, - DIG_EXT_PORT_STAGE_MAX = 4, -}; - enum pwr_track_control_method { BBSWING, TXAGC diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h index f311887..5516557 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h @@ -255,14 +255,6 @@ enum tag_dynamic_init_gain_operation_type_definition { DIG_OP_TYPE_MAX }; -enum tag_cck_packet_detection_threshold_type_definition { - CCK_PD_STAGE_LOWRSSI = 0, - CCK_PD_STAGE_HIGHRSSI = 1, - CCK_FA_STAGE_LOW = 2, - CCK_FA_STAGE_HIGH = 3, - CCK_PD_STAGE_MAX = 4, -}; - enum dm_1r_cca_e { CCA_1R = 0, CCA_2R = 1, @@ -281,14 +273,6 @@ enum dm_sw_ant_switch_e { ANS_ANTENNA_MAX = 3, }; -enum dm_dig_ext_port_alg_e { - DIG_EXT_PORT_STAGE_0 = 0, - DIG_EXT_PORT_STAGE_1 = 1, - DIG_EXT_PORT_STAGE_2 = 2, - DIG_EXT_PORT_STAGE_3 = 3, - DIG_EXT_PORT_STAGE_MAX = 4, -}; - enum pwr_track_control_method { BBSWING, TXAGC, -- cgit v0.10.2 From 8b17c1f3a5e058952177339d0310f27a4fb42c13 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 6 Jan 2015 09:58:07 -0600 Subject: rtlwifi: rtl8192de: Convert driver to use common DM table initialization This patch converts driver rtl8192de to use the common routine to initialize dm_digtable. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c index 75643ab..a1be5a6 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c @@ -156,34 +156,6 @@ static const u8 cckswing_table_ch14[CCK_TABLE_SIZE][8] = { {0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00} /* 32, -16.0dB */ }; -static void rtl92d_dm_diginit(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct dig_t *de_digtable = &rtlpriv->dm_digtable; - - de_digtable->dig_enable_flag = true; - de_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; - de_digtable->cur_igvalue = 0x20; - de_digtable->pre_igvalue = 0x0; - de_digtable->cursta_cstate = DIG_STA_DISCONNECT; - de_digtable->presta_cstate = DIG_STA_DISCONNECT; - de_digtable->curmultista_cstate = DIG_MULTISTA_DISCONNECT; - de_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW; - de_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH; - de_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; - de_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; - de_digtable->rx_gain_max = DM_DIG_FA_UPPER; - de_digtable->rx_gain_min = DM_DIG_FA_LOWER; - de_digtable->back_val = DM_DIG_BACKOFF_DEFAULT; - de_digtable->back_range_max = DM_DIG_BACKOFF_MAX; - de_digtable->back_range_min = DM_DIG_BACKOFF_MIN; - de_digtable->pre_cck_pd_state = CCK_PD_STAGE_LOWRSSI; - de_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX; - de_digtable->large_fa_hit = 0; - de_digtable->recover_cnt = 0; - de_digtable->forbidden_igi = DM_DIG_FA_LOWER; -} - static void rtl92d_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw) { u32 ret_value; @@ -1306,7 +1278,9 @@ void rtl92d_dm_init(struct ieee80211_hw *hw) struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; - rtl92d_dm_diginit(hw); + rtl_dm_diginit(hw, 0x20); + rtlpriv->dm_digtable.rx_gain_max = DM_DIG_FA_UPPER; + rtlpriv->dm_digtable.rx_gain_min = DM_DIG_FA_LOWER; rtl92d_dm_init_dynamic_txpower(hw); rtl92d_dm_init_edca_turbo(hw); rtl92d_dm_init_rate_adaptive_mask(hw); -- cgit v0.10.2 From 153cb55731b68ddf7b0ebaa55536b56d766b47e6 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 6 Jan 2015 09:58:08 -0600 Subject: rtlwifi: rtl8192ee: Convert driver to use common DM table initialization Convert driver rtl8192ee to use the common routine to initialize dm_digtable. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c index 70e58d1..459f3d0 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c @@ -152,35 +152,6 @@ static const u8 cckswing_table_ch14[CCK_TABLE_SIZE][8] = { {0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00} /* 32, -16.0dB */ }; -static void rtl92ee_dm_diginit(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct dig_t *dm_dig = &rtlpriv->dm_digtable; - - dm_dig->cur_igvalue = rtl_get_bbreg(hw, DM_REG_IGI_A_11N, - DM_BIT_IGI_11N); - dm_dig->rssi_lowthresh = DM_DIG_THRESH_LOW; - dm_dig->rssi_highthresh = DM_DIG_THRESH_HIGH; - dm_dig->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; - dm_dig->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; - dm_dig->rx_gain_max = DM_DIG_MAX; - dm_dig->rx_gain_min = DM_DIG_MIN; - dm_dig->back_val = DM_DIG_BACKOFF_DEFAULT; - dm_dig->back_range_max = DM_DIG_BACKOFF_MAX; - dm_dig->back_range_min = DM_DIG_BACKOFF_MIN; - dm_dig->pre_cck_cca_thres = 0xff; - dm_dig->cur_cck_cca_thres = 0x83; - dm_dig->forbidden_igi = DM_DIG_MIN; - dm_dig->large_fa_hit = 0; - dm_dig->recover_cnt = 0; - dm_dig->dig_min_0 = DM_DIG_MIN; - dm_dig->dig_min_1 = DM_DIG_MIN; - dm_dig->media_connect_0 = false; - dm_dig->media_connect_1 = false; - rtlpriv->dm.dm_initialgain_enable = true; - dm_dig->bt30_cur_igi = 0x32; -} - static void rtl92ee_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw) { u32 ret_value; @@ -1089,10 +1060,11 @@ static void rtl92ee_dm_init_dynamic_atc_switch(struct ieee80211_hw *hw) void rtl92ee_dm_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 cur_igvalue = rtl_get_bbreg(hw, DM_REG_IGI_A_11N, DM_BIT_IGI_11N); rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; - rtl92ee_dm_diginit(hw); + rtl_dm_diginit(hw, cur_igvalue); rtl92ee_dm_init_rate_adaptive_mask(hw); rtl92ee_dm_init_primary_cca_check(hw); rtl92ee_dm_init_edca_turbo(hw); -- cgit v0.10.2 From 3592c54b6c8e85a1713515f463379c6a97bf8009 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 6 Jan 2015 09:58:09 -0600 Subject: rtlwifi: rtl8723ae: Convert driver to use common DM table initialization Convert driver rtl8723ae to use common routine rtl_dm_diginit(). Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c index 8bd124c..4c1c96c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c @@ -147,31 +147,6 @@ static const u8 cckswing_table_ch14[CCK_TABLE_SIZE][8] = { {0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00} }; -static void rtl8723e_dm_diginit(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct dig_t *dm_digtable = &rtlpriv->dm_digtable; - - dm_digtable->dig_enable_flag = true; - dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; - dm_digtable->cur_igvalue = 0x20; - dm_digtable->pre_igvalue = 0x0; - dm_digtable->cursta_cstate = DIG_STA_DISCONNECT; - dm_digtable->presta_cstate = DIG_STA_DISCONNECT; - dm_digtable->curmultista_cstate = DIG_MULTISTA_DISCONNECT; - dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW; - dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH; - dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; - dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; - dm_digtable->rx_gain_max = DM_DIG_MAX; - dm_digtable->rx_gain_min = DM_DIG_MIN; - dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT; - dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX; - dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN; - dm_digtable->pre_cck_pd_state = CCK_PD_STAGE_MAX; - dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX; -} - static u8 rtl8723e_dm_initial_gain_min_pwdb(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -819,7 +794,7 @@ void rtl8723e_dm_init(struct ieee80211_hw *hw) struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; - rtl8723e_dm_diginit(hw); + rtl_dm_diginit(hw, 0x20); rtl8723_dm_init_dynamic_txpower(hw); rtl8723_dm_init_edca_turbo(hw); rtl8723e_dm_init_rate_adaptive_mask(hw); -- cgit v0.10.2 From ac2f0baefab920a3f9117993eb1455b40b265ab5 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 6 Jan 2015 09:58:10 -0600 Subject: rtlwifi: rtl8723be: Convert driver to use common DM table initialization Convert driver rtl8723be to use routine rtl_dm_diginit(). Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/dm.c b/drivers/net/wireless/rtlwifi/rtl8723be/dm.c index ddf45d9..2367e8f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/dm.c @@ -212,35 +212,6 @@ void rtl8723be_dm_txpower_track_adjust(struct ieee80211_hw *hw, u8 type, (pwr_val << 16) | (pwr_val << 24); } -static void rtl8723be_dm_diginit(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct dig_t *dm_digtable = &rtlpriv->dm_digtable; - - dm_digtable->dig_enable_flag = true; - dm_digtable->cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f); - dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW; - dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH; - dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; - dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; - dm_digtable->rx_gain_max = DM_DIG_MAX; - dm_digtable->rx_gain_min = DM_DIG_MIN; - dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT; - dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX; - dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN; - dm_digtable->pre_cck_cca_thres = 0xff; - dm_digtable->cur_cck_cca_thres = 0x83; - dm_digtable->forbidden_igi = DM_DIG_MIN; - dm_digtable->large_fa_hit = 0; - dm_digtable->recover_cnt = 0; - dm_digtable->dig_min_0 = DM_DIG_MIN; - dm_digtable->dig_min_1 = DM_DIG_MIN; - dm_digtable->media_connect_0 = false; - dm_digtable->media_connect_1 = false; - rtlpriv->dm.dm_initialgain_enable = true; - dm_digtable->bt30_cur_igi = 0x32; -} - void rtl8723be_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -294,9 +265,10 @@ static void rtl8723be_dm_init_dynamic_atc_switch(struct ieee80211_hw *hw) void rtl8723be_dm_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f); rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; - rtl8723be_dm_diginit(hw); + rtl_dm_diginit(hw, cur_igvalue); rtl8723be_dm_init_rate_adaptive_mask(hw); rtl8723_dm_init_edca_turbo(hw); rtl8723_dm_init_dynamic_bb_powersaving(hw); -- cgit v0.10.2 From 9259ee79208a95aa20a9824717a9f22b0a52cccd Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 6 Jan 2015 09:58:11 -0600 Subject: rtlwifi: rtl8821ae: Convert driver to use common DM table initialization Convert driver to use routine rtl_dm_diginit(). Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c index 4af4613..0b2082d 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c @@ -520,34 +520,6 @@ void rtl8821ae_dm_initialize_txpower_tracking_thermalmeter( } } -static void rtl8821ae_dm_diginit(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct dig_t *dm_digtable = &rtlpriv->dm_digtable; - - dm_digtable->cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f); - dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW; - dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH; - dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; - dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; - dm_digtable->rx_gain_max = DM_DIG_MAX; - dm_digtable->rx_gain_min = DM_DIG_MIN; - dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT; - dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX; - dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN; - dm_digtable->pre_cck_cca_thres = 0xff; - dm_digtable->cur_cck_cca_thres = 0x83; - dm_digtable->forbidden_igi = DM_DIG_MIN; - dm_digtable->large_fa_hit = 0; - dm_digtable->recover_cnt = 0; - dm_digtable->dig_min_0 = DM_DIG_MIN; - dm_digtable->dig_min_1 = DM_DIG_MIN; - dm_digtable->media_connect_0 = false; - dm_digtable->media_connect_1 = false; - rtlpriv->dm.dm_initialgain_enable = true; - dm_digtable->bt30_cur_igi = 0x32; -} - void rtl8821ae_dm_init_edca_turbo(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -607,6 +579,7 @@ void rtl8821ae_dm_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &rtlpriv->phy; + u32 cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f); spin_lock(&rtlpriv->locks.iqk_lock); rtlphy->lck_inprogress = false; @@ -614,7 +587,7 @@ void rtl8821ae_dm_init(struct ieee80211_hw *hw) rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; rtl8821ae_dm_common_info_self_init(hw); - rtl8821ae_dm_diginit(hw); + rtl_dm_diginit(hw, cur_igvalue); rtl8821ae_dm_init_rate_adaptive_mask(hw); rtl8821ae_dm_init_edca_turbo(hw); rtl8821ae_dm_initialize_txpower_tracking_thermalmeter(hw); -- cgit v0.10.2 From ed0fb7eb2b2d27a07610b75d1f3a68cc22716347 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 6 Jan 2015 09:58:12 -0600 Subject: rtlwifi: Move macro definitions to core Several of the drivers still were defining their own copies of various macros. These are all moved into the core. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/rtlwifi/core.h b/drivers/net/wireless/rtlwifi/core.h index 1cde356..7b64e34 100644 --- a/drivers/net/wireless/rtlwifi/core.h +++ b/drivers/net/wireless/rtlwifi/core.h @@ -42,6 +42,7 @@ #define DM_DIG_MAX 0x3e #define DM_DIG_MIN 0x1e +#define DM_DIG_MAX_AP 0x32 #define DM_DIG_BACKOFF_MAX 12 #define DM_DIG_BACKOFF_MIN -4 #define DM_DIG_BACKOFF_DEFAULT 10 diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h index 51ad3f9..071ccee 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h @@ -186,9 +186,6 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_MAX_AP 0x32 -#define DM_DIG_MIN_AP 0x20 - #define DM_DIG_FA_UPPER 0x3e #define DM_DIG_FA_LOWER 0x1e #define DM_DIG_FA_TH0 0x200 diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.h b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.h index 4880e19..107d5a4 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.h @@ -189,9 +189,6 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_MAX_AP 0x32 -#define DM_DIG_MIN_AP 0x20 - #define DM_DIG_FA_UPPER 0x3e #define DM_DIG_FA_LOWER 0x1e #define DM_DIG_FA_TH0 0x200 diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/dm.c b/drivers/net/wireless/rtlwifi/rtl8192se/dm.c index 6cac70b..575980b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/dm.c @@ -470,7 +470,7 @@ static void _rtl92s_dm_initial_gain_sta_beforeconnect(struct ieee80211_hw *hw) if (digtable->backoff_enable_flag) rtl92s_backoff_enable_flag(hw); else - digtable->back_val = DM_DIG_BACKOFF; + digtable->back_val = DM_DIG_BACKOFF_MAX; if ((digtable->rssi_val + 10 - digtable->back_val) > digtable->rx_gain_max) @@ -504,7 +504,7 @@ static void _rtl92s_dm_initial_gain_sta_beforeconnect(struct ieee80211_hw *hw) digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; rtl92s_phy_set_fw_cmd(hw, FW_CMD_DIG_ENABLE); - digtable->back_val = DM_DIG_BACKOFF; + digtable->back_val = DM_DIG_BACKOFF_MAX; digtable->cur_igvalue = rtlpriv->phy.default_initialgain[0]; digtable->pre_igvalue = 0; return; @@ -692,7 +692,7 @@ static void _rtl92s_dm_init_dig(struct ieee80211_hw *hw) /* for dig debug rssi value */ digtable->rssi_val = 50; - digtable->back_val = DM_DIG_BACKOFF; + digtable->back_val = DM_DIG_BACKOFF_MAX; digtable->rx_gain_max = DM_DIG_MAX; digtable->rx_gain_min = DM_DIG_MIN; diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/dm.h b/drivers/net/wireless/rtlwifi/rtl8192se/dm.h index be07d81..de6ac79 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8192se/dm.h @@ -83,7 +83,6 @@ enum dm_ratr_sta { #define DM_DIG_HIGH_PWR_THRESH_HIGH 75 #define DM_DIG_HIGH_PWR_THRESH_LOW 70 -#define DM_DIG_BACKOFF 12 #define DM_DIG_MIN_Netcore 0x12 void rtl92s_dm_watchdog(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/dm.h b/drivers/net/wireless/rtlwifi/rtl8723be/dm.h index 533b4f2..f752a2c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8723be/dm.h @@ -180,9 +180,6 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_MAX_AP 0x32 -#define DM_DIG_MIN_AP 0x20 - #define DM_DIG_FA_UPPER 0x3e #define DM_DIG_FA_LOWER 0x1e #define DM_DIG_FA_TH0 0x200 diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h index 5516557..625a6bb 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h @@ -187,15 +187,6 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_FALSEALARM_THRESH_LOW 400 -#define DM_FALSEALARM_THRESH_HIGH 1000 - -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1e - -#define DM_DIG_MAX_AP 0x32 -#define DM_DIG_MIN_AP 0x20 - #define DM_DIG_FA_UPPER 0x3e #define DM_DIG_FA_LOWER 0x1e #define DM_DIG_FA_TH0 200 -- cgit v0.10.2 From 4e38bea60ebff6cc65abaf8c1c2c58e3c3c37dc0 Mon Sep 17 00:00:00 2001 From: Douglas Gilbert Date: Tue, 23 Dec 2014 11:29:08 -0500 Subject: scsi: asc/ascq codes, sync to T10 2014/12/21 The last time these SCSI asc/ascq codes were synced with T10 was 2013/06/05. Sync the SCSI ASC/ASCQ number to string tables with those found at http://www.t10.org/lists/asc-num.txt dated 2014/12/21. Signed-off-by: Douglas Gilbert Reviewed-by: Hannes Reinecke Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c index e2068a2..55a7157 100644 --- a/drivers/scsi/constants.c +++ b/drivers/scsi/constants.c @@ -392,7 +392,7 @@ struct error_info { /* * The canonical list of T10 Additional Sense Codes is available at: - * http://www.t10.org/lists/asc-num.txt [most recent: 20130605] + * http://www.t10.org/lists/asc-num.txt [most recent: 20141221] */ static const struct error_info additional[] = @@ -421,6 +421,7 @@ static const struct error_info additional[] = {0x001E, "Conflicting SA creation request"}, {0x001F, "Logical unit transitioning to another power condition"}, {0x0020, "Extended copy information available"}, + {0x0021, "Atomic command aborted due to ACA"}, {0x0100, "No index/sector signal"}, @@ -446,6 +447,7 @@ static const struct error_info additional[] = {0x040C, "Logical unit not accessible, target port in unavailable " "state"}, {0x040D, "Logical unit not ready, structure check required"}, + {0x040E, "Logical unit not ready, security session in progress"}, {0x0410, "Logical unit not ready, auxiliary memory not accessible"}, {0x0411, "Logical unit not ready, notify (enable spinup) required"}, {0x0412, "Logical unit not ready, offline"}, @@ -462,6 +464,11 @@ static const struct error_info additional[] = {0x041C, "Logical unit not ready, additional power use not yet " "granted"}, {0x041D, "Logical unit not ready, configuration in progress"}, + {0x041E, "Logical unit not ready, microcode activation required"}, + {0x041F, "Logical unit not ready, microcode download required"}, + {0x0420, "Logical unit not ready, logical unit reset required"}, + {0x0421, "Logical unit not ready, hard reset required"}, + {0x0422, "Logical unit not ready, power cycle required"}, {0x0500, "Logical unit does not respond to selection"}, @@ -480,6 +487,7 @@ static const struct error_info additional[] = {0x0902, "Focus servo failure"}, {0x0903, "Spindle servo failure"}, {0x0904, "Head select fault"}, + {0x0905, "Vibration induced tracking error"}, {0x0A00, "Error log overflow"}, @@ -510,6 +518,7 @@ static const struct error_info additional[] = {0x0C0D, "Write error - not enough unsolicited data"}, {0x0C0E, "Multiple write errors"}, {0x0C0F, "Defects in error window"}, + {0x0C10, "Incomplete multiple atomic write operations"}, {0x0D00, "Error detected by third party temporary initiator"}, {0x0D01, "Third party device failure"}, @@ -635,6 +644,10 @@ static const struct error_info additional[] = {0x2101, "Invalid element address"}, {0x2102, "Invalid address for write"}, {0x2103, "Invalid write crossing layer jump"}, + {0x2104, "Unaligned write command"}, + {0x2105, "Write boundary violation"}, + {0x2106, "Attempt to read invalid data"}, + {0x2107, "Read boundary violation"}, {0x2200, "Illegal function (use 20 00, 24 00, or 26 00)"}, @@ -691,6 +704,7 @@ static const struct error_info additional[] = {0x2705, "Permanent write protect"}, {0x2706, "Conditional write protect"}, {0x2707, "Space allocation failed write protect"}, + {0x2708, "Zone is read only"}, {0x2800, "Not ready to ready change, medium may have changed"}, {0x2801, "Import or export element accessed"}, @@ -743,10 +757,15 @@ static const struct error_info additional[] = {0x2C0A, "Partition or collection contains user objects"}, {0x2C0B, "Not reserved"}, {0x2C0C, "Orwrite generation does not match"}, + {0x2C0D, "Reset write pointer not allowed"}, + {0x2C0E, "Zone is offline"}, {0x2D00, "Overwrite error on update in place"}, {0x2E00, "Insufficient time for operation"}, + {0x2E01, "Command timeout before processing"}, + {0x2E02, "Command timeout during processing"}, + {0x2E03, "Command timeout during processing due to error recovery"}, {0x2F00, "Commands cleared by another initiator"}, {0x2F01, "Commands cleared by power loss notification"}, @@ -868,6 +887,7 @@ static const struct error_info additional[] = {0x3F13, "iSCSI IP address removed"}, {0x3F14, "iSCSI IP address changed"}, {0x3F15, "Inspect referrals sense descriptors"}, + {0x3F16, "Microcode has been changed without reset"}, /* * {0x40NN, "Ram failure"}, * {0x40NN, "Diagnostic failure on component nn"}, @@ -946,6 +966,11 @@ static const struct error_info additional[] = {0x5306, "Volume identifier missing"}, {0x5307, "Duplicate volume identifier"}, {0x5308, "Element status unknown"}, + {0x5309, "Data transfer device error - load failed"}, + {0x530a, "Data transfer device error - unload failed"}, + {0x530b, "Data transfer device error - unload missing"}, + {0x530c, "Data transfer device error - eject failed"}, + {0x530d, "Data transfer device error - library communication failed"}, {0x5400, "Scsi to host system interface failure"}, @@ -963,6 +988,7 @@ static const struct error_info additional[] = {0x550B, "Insufficient power for operation"}, {0x550C, "Insufficient resources to create rod"}, {0x550D, "Insufficient resources to create rod token"}, + {0x550E, "Insufficient zone resources"}, {0x5700, "Unable to recover table-of-contents"}, -- cgit v0.10.2 From 6583f6fb82296b737053dff154a1e7a9c174754e Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 29 Dec 2014 09:40:56 -0800 Subject: scsi: fix scsi_error.c kernel-doc warning Fix kernel-doc warning in scsi_error.c: Warning(..//drivers/scsi/scsi_error.c:887): No description found for parameter 'hostt' Fixes: 883a030f989a17b81167f3a181cf93d741fa98b4 (scsi: document scsi_try_to_abort_cmd) Signed-off-by: Randy Dunlap Reviewed-by: Hannes Reinecke Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 8afb016..9ac9eb1 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -868,6 +868,7 @@ static int scsi_try_bus_device_reset(struct scsi_cmnd *scmd) /** * scsi_try_to_abort_cmd - Ask host to abort a SCSI command + * @hostt: SCSI driver host template * @scmd: SCSI cmd used to send a target reset * * Return value: -- cgit v0.10.2 From eb9eea01d4d8ce86eeee52cdd40029fd1a549721 Mon Sep 17 00:00:00 2001 From: Rob Evers Date: Tue, 16 Dec 2014 11:01:18 -0500 Subject: scsi: avoid unnecessary GFP_ATOMIC allocation in scsi_report_lun_scan Signed-off-by: Rob Evers Reviewed-by: Ewan D. Milne Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 983aed1..9edae2f 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1416,7 +1416,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, * prevent us from finding any LUNs on this target. */ length = (max_scsi_report_luns + 1) * sizeof(struct scsi_lun); - lun_data = kmalloc(length, GFP_ATOMIC | + lun_data = kmalloc(length, GFP_KERNEL | (sdev->host->unchecked_isa_dma ? __GFP_DMA : 0)); if (!lun_data) { printk(ALLOC_FAILURE_MSG, __func__); -- cgit v0.10.2 From 2a904e5dd9b832fe70d86b70ce6e273d67e94389 Mon Sep 17 00:00:00 2001 From: Rob Evers Date: Tue, 16 Dec 2014 11:01:19 -0500 Subject: scsi: use set/get_unaligned_be32 in report_luns Signed-off-by: Rob Evers Reviewed-by: Ewan D. Milne Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 9edae2f..b7948d2 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -1367,7 +1368,6 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, unsigned int retries; int result; struct scsi_lun *lunp, *lun_data; - u8 *data; struct scsi_sense_hdr sshdr; struct scsi_device *sdev; struct Scsi_Host *shost = dev_to_shost(&starget->dev); @@ -1433,10 +1433,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, /* * bytes 6 - 9: length of the command. */ - scsi_cmd[6] = (unsigned char) (length >> 24) & 0xff; - scsi_cmd[7] = (unsigned char) (length >> 16) & 0xff; - scsi_cmd[8] = (unsigned char) (length >> 8) & 0xff; - scsi_cmd[9] = (unsigned char) length & 0xff; + put_unaligned_be32(length, &scsi_cmd[6]); scsi_cmd[10] = 0; /* reserved */ scsi_cmd[11] = 0; /* control */ @@ -1484,9 +1481,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, /* * Get the length from the first four bytes of lun_data. */ - data = (u8 *) lun_data->scsi_lun; - length = ((data[0] << 24) | (data[1] << 16) | - (data[2] << 8) | (data[3] << 0)); + length = get_unaligned_be32(lun_data->scsi_lun); num_luns = (length / sizeof(struct scsi_lun)); if (num_luns > max_scsi_report_luns) { -- cgit v0.10.2 From acd6d73826224c20c44f505f56b0503f022d695c Mon Sep 17 00:00:00 2001 From: Rob Evers Date: Tue, 16 Dec 2014 11:01:20 -0500 Subject: scsi: retry report-luns when reported LU count requres more memory Update scsi_report_lun_scan to initially always report up to 511 LUs, as the previous default max_report_luns did. Retry in a loop if not enough memory is available for the number of LUs reported. Parameter max_report_luns is removed as it is no longer used. Signed-off-by: Rob Evers Reviewed-by: Ewan D. Milne Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index b7948d2..0deb385 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -99,20 +99,6 @@ char scsi_scan_type[6] = SCSI_SCAN_TYPE_DEFAULT; module_param_string(scan, scsi_scan_type, sizeof(scsi_scan_type), S_IRUGO); MODULE_PARM_DESC(scan, "sync, async or none"); -/* - * max_scsi_report_luns: the maximum number of LUNS that will be - * returned from the REPORT LUNS command. 8 times this value must - * be allocated. In theory this could be up to an 8 byte value, but - * in practice, the maximum number of LUNs suppored by any device - * is about 16k. - */ -static unsigned int max_scsi_report_luns = 511; - -module_param_named(max_report_luns, max_scsi_report_luns, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(max_report_luns, - "REPORT LUNS maximum number of LUNS received (should be" - " between 1 and 16384)"); - static unsigned int scsi_inq_timeout = SCSI_TIMEOUT/HZ + 18; module_param_named(inq_timeout, scsi_inq_timeout, uint, S_IRUGO|S_IWUSR); @@ -1407,15 +1393,11 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, /* * Allocate enough to hold the header (the same size as one scsi_lun) - * plus the max number of luns we are requesting. - * - * Reallocating and trying again (with the exact amount we need) - * would be nice, but then we need to somehow limit the size - * allocated based on the available memory and the limits of - * kmalloc - we don't want a kmalloc() failure of a huge value to - * prevent us from finding any LUNs on this target. + * plus the number of luns we are requesting. 511 was the default + * value of the now removed max_report_luns parameter. */ - length = (max_scsi_report_luns + 1) * sizeof(struct scsi_lun); + length = (511 + 1) * sizeof(struct scsi_lun); +retry: lun_data = kmalloc(length, GFP_KERNEL | (sdev->host->unchecked_isa_dma ? __GFP_DMA : 0)); if (!lun_data) { @@ -1481,17 +1463,16 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, /* * Get the length from the first four bytes of lun_data. */ + if (get_unaligned_be32(lun_data->scsi_lun) + + sizeof(struct scsi_lun) > length) { + length = get_unaligned_be32(lun_data->scsi_lun) + + sizeof(struct scsi_lun); + kfree(lun_data); + goto retry; + } length = get_unaligned_be32(lun_data->scsi_lun); num_luns = (length / sizeof(struct scsi_lun)); - if (num_luns > max_scsi_report_luns) { - sdev_printk(KERN_WARNING, sdev, - "Only %d (max_scsi_report_luns)" - " of %d luns reported, try increasing" - " max_scsi_report_luns.\n", - max_scsi_report_luns, num_luns); - num_luns = max_scsi_report_luns; - } SCSI_LOG_SCAN_BUS(3, sdev_printk (KERN_INFO, sdev, "scsi scan: REPORT LUN scan\n")); -- cgit v0.10.2 From ed09dcc8bd7fe0991af7737e675996cbd022f38f Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 30 Dec 2014 14:46:14 -0800 Subject: ses: close potential registration race The slot and address fields have a small window of instability when userspace can read them before initialization. Separate enclosure_component allocation from registration. Signed-off-by: Dan Williams Signed-off-by: Song Liu Reviewed-by: Jens Axboe Cc: Hannes Reinecke Signed-off-by: Christoph Hellwig diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 180a544..e18278a 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -273,27 +273,26 @@ enclosure_component_find_by_name(struct enclosure_device *edev, static const struct attribute_group *enclosure_component_groups[]; /** - * enclosure_component_register - add a particular component to an enclosure + * enclosure_component_alloc - prepare a new enclosure component * @edev: the enclosure to add the component * @num: the device number * @type: the type of component being added * @name: an optional name to appear in sysfs (leave NULL if none) * - * Registers the component. The name is optional for enclosures that - * give their components a unique name. If not, leave the field NULL - * and a name will be assigned. + * The name is optional for enclosures that give their components a unique + * name. If not, leave the field NULL and a name will be assigned. * * Returns a pointer to the enclosure component or an error. */ struct enclosure_component * -enclosure_component_register(struct enclosure_device *edev, - unsigned int number, - enum enclosure_component_type type, - const char *name) +enclosure_component_alloc(struct enclosure_device *edev, + unsigned int number, + enum enclosure_component_type type, + const char *name) { struct enclosure_component *ecomp; struct device *cdev; - int err, i; + int i; char newname[COMPONENT_NAME_SIZE]; if (number >= edev->components) @@ -327,14 +326,30 @@ enclosure_component_register(struct enclosure_device *edev, cdev->release = enclosure_component_release; cdev->groups = enclosure_component_groups; + return ecomp; +} +EXPORT_SYMBOL_GPL(enclosure_component_alloc); + +/** + * enclosure_component_register - publishes an initialized enclosure component + * @ecomp: component to add + * + * Returns 0 on successful registration, releases the component otherwise + */ +int enclosure_component_register(struct enclosure_component *ecomp) +{ + struct device *cdev; + int err; + + cdev = &ecomp->cdev; err = device_register(cdev); if (err) { ecomp->number = -1; put_device(cdev); - return ERR_PTR(err); + return err; } - return ecomp; + return 0; } EXPORT_SYMBOL_GPL(enclosure_component_register); diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index b7e79e7..7dd9cf5 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -423,16 +423,23 @@ static void ses_enclosure_data_process(struct enclosure_device *edev, type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) { if (create) - ecomp = enclosure_component_register(edev, - components++, - type_ptr[0], - name); + ecomp = enclosure_component_alloc( + edev, + components++, + type_ptr[0], + name); else ecomp = &edev->component[components++]; - if (!IS_ERR(ecomp) && addl_desc_ptr) - ses_process_descriptor(ecomp, - addl_desc_ptr); + if (!IS_ERR(ecomp)) { + if (addl_desc_ptr) + ses_process_descriptor( + ecomp, + addl_desc_ptr); + if (create) + enclosure_component_register( + ecomp); + } } if (desc_ptr) desc_ptr += len; diff --git a/include/linux/enclosure.h b/include/linux/enclosure.h index 9a33c5f..a835d33 100644 --- a/include/linux/enclosure.h +++ b/include/linux/enclosure.h @@ -120,8 +120,9 @@ enclosure_register(struct device *, const char *, int, struct enclosure_component_callbacks *); void enclosure_unregister(struct enclosure_device *); struct enclosure_component * -enclosure_component_register(struct enclosure_device *, unsigned int, - enum enclosure_component_type, const char *); +enclosure_component_alloc(struct enclosure_device *, unsigned int, + enum enclosure_component_type, const char *); +int enclosure_component_register(struct enclosure_component *); int enclosure_add_device(struct enclosure_device *enclosure, int component, struct device *dev); int enclosure_remove_device(struct enclosure_device *, struct device *); -- cgit v0.10.2 From 15a0fbbc8e0d4c135b069c34258fbaebad471dd7 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 30 Dec 2014 14:46:15 -0800 Subject: ses: generate KOBJ_CHANGE on enclosure attach In support of a /dev/disk/by-slot populated with data from the enclosure and ses modules udev needs notification when the new interface files/links are available. Otherwise, any udev rules specified for the disk cannot assume that the enclosure topology has settled. Signed-off-by: Dan Williams Signed-off-by: Song Liu Reviewed-by: Jens Axboe Reviewed-by: Hannes Reinecke Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 7dd9cf5..6662b0c 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -349,7 +349,8 @@ static int ses_enclosure_find_by_addr(struct enclosure_device *edev, if (scomp->addr != efd->addr) continue; - enclosure_add_device(edev, i, efd->dev); + if (enclosure_add_device(edev, i, efd->dev) == 0) + kobject_uevent(&efd->dev->kobj, KOBJ_CHANGE); return 1; } return 0; -- cgit v0.10.2 From 967f7bab0eaaa74d7d01a56d45aa309f78fb87dd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 30 Dec 2014 14:46:16 -0800 Subject: ses: add enclosure logical id Export the NAA logical id for the enclosure. This is optionally available from the sas_transport_class, but it is really a property of the enclosure. Signed-off-by: Dan Williams Signed-off-by: Song Liu Reviewed-by: Jens Axboe Cc: Hannes Reinecke Signed-off-by: Christoph Hellwig diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index e18278a..958ee98 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -432,8 +432,21 @@ static ssize_t components_show(struct device *cdev, } static DEVICE_ATTR_RO(components); +static ssize_t id_show(struct device *cdev, + struct device_attribute *attr, + char *buf) +{ + struct enclosure_device *edev = to_enclosure_device(cdev); + + if (edev->cb->show_id) + return edev->cb->show_id(edev, buf); + return -EINVAL; +} +static DEVICE_ATTR_RO(id); + static struct attribute *enclosure_class_attrs[] = { &dev_attr_components.attr, + &dev_attr_id.attr, NULL, }; ATTRIBUTE_GROUPS(enclosure_class); diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 6662b0c..1041556 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -258,6 +258,14 @@ static int ses_set_active(struct enclosure_device *edev, return ses_set_page2_descriptor(edev, ecomp, desc); } +static int ses_show_id(struct enclosure_device *edev, char *buf) +{ + struct ses_device *ses_dev = edev->scratch; + unsigned long long id = get_unaligned_be64(ses_dev->page1+8+4); + + return sprintf(buf, "%#llx\n", id); +} + static struct enclosure_component_callbacks ses_enclosure_callbacks = { .get_fault = ses_get_fault, .set_fault = ses_set_fault, @@ -265,6 +273,7 @@ static struct enclosure_component_callbacks ses_enclosure_callbacks = { .get_locate = ses_get_locate, .set_locate = ses_set_locate, .set_active = ses_set_active, + .show_id = ses_show_id, }; struct ses_host_edev { diff --git a/include/linux/enclosure.h b/include/linux/enclosure.h index a835d33..807622b 100644 --- a/include/linux/enclosure.h +++ b/include/linux/enclosure.h @@ -79,6 +79,7 @@ struct enclosure_component_callbacks { int (*set_locate)(struct enclosure_device *, struct enclosure_component *, enum enclosure_component_setting); + int (*show_id)(struct enclosure_device *, char *buf); }; -- cgit v0.10.2 From 921ce7f5786052749a22a75780f5ce1a456bcdc6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 30 Dec 2014 14:46:17 -0800 Subject: ses: add reliable slot attribute The name provided by firmware is in a vendor specific format, publish the slot number to have a reliable mechanism for identifying slots across firmware implementations. If the enclosure does not provide a slot number fallback to the component number which is guaranteed unique, and usually mirrors the slot number. Cleaned up the unused ses_component.desc in the process. Signed-off-by: Dan Williams Signed-off-by: Song Liu Reviewed-by: Jens Axboe Reviewed-by: Hannes Reinecke Signed-off-by: Christoph Hellwig diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 958ee98..b62314d 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -145,8 +145,10 @@ enclosure_register(struct device *dev, const char *name, int components, if (err) goto err; - for (i = 0; i < components; i++) + for (i = 0; i < components; i++) { edev->component[i].number = -1; + edev->component[i].slot = -1; + } mutex_lock(&container_list_lock); list_add_tail(&edev->node, &container_list); @@ -589,6 +591,20 @@ static ssize_t get_component_type(struct device *cdev, return snprintf(buf, 40, "%s\n", enclosure_type[ecomp->type]); } +static ssize_t get_component_slot(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct enclosure_component *ecomp = to_enclosure_component(cdev); + int slot; + + /* if the enclosure does not override then use 'number' as a stand-in */ + if (ecomp->slot >= 0) + slot = ecomp->slot; + else + slot = ecomp->number; + + return snprintf(buf, 40, "%d\n", slot); +} static DEVICE_ATTR(fault, S_IRUGO | S_IWUSR, get_component_fault, set_component_fault); @@ -599,6 +615,7 @@ static DEVICE_ATTR(active, S_IRUGO | S_IWUSR, get_component_active, static DEVICE_ATTR(locate, S_IRUGO | S_IWUSR, get_component_locate, set_component_locate); static DEVICE_ATTR(type, S_IRUGO, get_component_type, NULL); +static DEVICE_ATTR(slot, S_IRUGO, get_component_slot, NULL); static struct attribute *enclosure_component_attrs[] = { &dev_attr_fault.attr, @@ -606,6 +623,7 @@ static struct attribute *enclosure_component_attrs[] = { &dev_attr_active.attr, &dev_attr_locate.attr, &dev_attr_type.attr, + &dev_attr_slot.attr, NULL }; ATTRIBUTE_GROUPS(enclosure_component); diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 1041556..433de8e 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -47,7 +47,6 @@ struct ses_device { struct ses_component { u64 addr; - unsigned char *desc; }; static int ses_probe(struct device *dev) @@ -307,19 +306,26 @@ static void ses_process_descriptor(struct enclosure_component *ecomp, int invalid = desc[0] & 0x80; enum scsi_protocol proto = desc[0] & 0x0f; u64 addr = 0; + int slot = -1; struct ses_component *scomp = ecomp->scratch; unsigned char *d; - scomp->desc = desc; - if (invalid) return; switch (proto) { + case SCSI_PROTOCOL_FCP: + if (eip) { + d = desc + 4; + slot = d[3]; + } + break; case SCSI_PROTOCOL_SAS: - if (eip) + if (eip) { + d = desc + 4; + slot = d[3]; d = desc + 8; - else + } else d = desc + 4; /* only take the phy0 addr */ addr = (u64)d[12] << 56 | @@ -335,6 +341,7 @@ static void ses_process_descriptor(struct enclosure_component *ecomp, /* FIXME: Need to add more protocols than just SAS */ break; } + ecomp->slot = slot; scomp->addr = addr; } diff --git a/include/linux/enclosure.h b/include/linux/enclosure.h index 807622b..0f826c1 100644 --- a/include/linux/enclosure.h +++ b/include/linux/enclosure.h @@ -92,6 +92,7 @@ struct enclosure_component { int fault; int active; int locate; + int slot; enum enclosure_status status; }; -- cgit v0.10.2 From 08024885a2a3ed432716e9d50046a620a5b2df05 Mon Sep 17 00:00:00 2001 From: Song Liu Date: Tue, 30 Dec 2014 14:46:18 -0800 Subject: ses: Add power_status to SES device slot Add power_status to SES device slot, so we can power on/off the HDDs behind the enclosure. Check firmware status in ses_set_* before sending control pages to firmware. Signed-off-by: Song Liu Acked-by: Dan Williams Reviewed-by: Jens Axboe Cc: Hannes Reinecke Signed-off-by: Christoph Hellwig diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index b62314d..38552a3 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -148,6 +148,7 @@ enclosure_register(struct device *dev, const char *name, int components, for (i = 0; i < components; i++) { edev->component[i].number = -1; edev->component[i].slot = -1; + edev->component[i].power_status = 1; } mutex_lock(&container_list_lock); @@ -583,6 +584,40 @@ static ssize_t set_component_locate(struct device *cdev, return count; } +static ssize_t get_component_power_status(struct device *cdev, + struct device_attribute *attr, + char *buf) +{ + struct enclosure_device *edev = to_enclosure_device(cdev->parent); + struct enclosure_component *ecomp = to_enclosure_component(cdev); + + if (edev->cb->get_power_status) + edev->cb->get_power_status(edev, ecomp); + return snprintf(buf, 40, "%s\n", ecomp->power_status ? "on" : "off"); +} + +static ssize_t set_component_power_status(struct device *cdev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct enclosure_device *edev = to_enclosure_device(cdev->parent); + struct enclosure_component *ecomp = to_enclosure_component(cdev); + int val; + + if (strncmp(buf, "on", 2) == 0 && + (buf[2] == '\n' || buf[2] == '\0')) + val = 1; + else if (strncmp(buf, "off", 3) == 0 && + (buf[3] == '\n' || buf[3] == '\0')) + val = 0; + else + return -EINVAL; + + if (edev->cb->set_power_status) + edev->cb->set_power_status(edev, ecomp, val); + return count; +} + static ssize_t get_component_type(struct device *cdev, struct device_attribute *attr, char *buf) { @@ -614,6 +649,8 @@ static DEVICE_ATTR(active, S_IRUGO | S_IWUSR, get_component_active, set_component_active); static DEVICE_ATTR(locate, S_IRUGO | S_IWUSR, get_component_locate, set_component_locate); +static DEVICE_ATTR(power_status, S_IRUGO | S_IWUSR, get_component_power_status, + set_component_power_status); static DEVICE_ATTR(type, S_IRUGO, get_component_type, NULL); static DEVICE_ATTR(slot, S_IRUGO, get_component_slot, NULL); @@ -622,6 +659,7 @@ static struct attribute *enclosure_component_attrs[] = { &dev_attr_status.attr, &dev_attr_active.attr, &dev_attr_locate.attr, + &dev_attr_power_status.attr, &dev_attr_type.attr, &dev_attr_slot.attr, NULL diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 433de8e..dcb0d76 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -67,6 +67,20 @@ static int ses_probe(struct device *dev) #define SES_TIMEOUT (30 * HZ) #define SES_RETRIES 3 +static void init_device_slot_control(unsigned char *dest_desc, + struct enclosure_component *ecomp, + unsigned char *status) +{ + memcpy(dest_desc, status, 4); + dest_desc[0] = 0; + /* only clear byte 1 for ENCLOSURE_COMPONENT_DEVICE */ + if (ecomp->type == ENCLOSURE_COMPONENT_DEVICE) + dest_desc[1] = 0; + dest_desc[2] &= 0xde; + dest_desc[3] &= 0x3c; +} + + static int ses_recv_diag(struct scsi_device *sdev, int page_code, void *buf, int bufflen) { @@ -178,14 +192,22 @@ static int ses_set_fault(struct enclosure_device *edev, struct enclosure_component *ecomp, enum enclosure_component_setting val) { - unsigned char desc[4] = {0 }; + unsigned char desc[4]; + unsigned char *desc_ptr; + + desc_ptr = ses_get_page2_descriptor(edev, ecomp); + + if (!desc_ptr) + return -EIO; + + init_device_slot_control(desc, ecomp, desc_ptr); switch (val) { case ENCLOSURE_SETTING_DISABLED: - /* zero is disabled */ + desc[3] &= 0xdf; break; case ENCLOSURE_SETTING_ENABLED: - desc[3] = 0x20; + desc[3] |= 0x20; break; default: /* SES doesn't do the SGPIO blink settings */ @@ -219,14 +241,22 @@ static int ses_set_locate(struct enclosure_device *edev, struct enclosure_component *ecomp, enum enclosure_component_setting val) { - unsigned char desc[4] = {0 }; + unsigned char desc[4]; + unsigned char *desc_ptr; + + desc_ptr = ses_get_page2_descriptor(edev, ecomp); + + if (!desc_ptr) + return -EIO; + + init_device_slot_control(desc, ecomp, desc_ptr); switch (val) { case ENCLOSURE_SETTING_DISABLED: - /* zero is disabled */ + desc[2] &= 0xfd; break; case ENCLOSURE_SETTING_ENABLED: - desc[2] = 0x02; + desc[2] |= 0x02; break; default: /* SES doesn't do the SGPIO blink settings */ @@ -239,15 +269,23 @@ static int ses_set_active(struct enclosure_device *edev, struct enclosure_component *ecomp, enum enclosure_component_setting val) { - unsigned char desc[4] = {0 }; + unsigned char desc[4]; + unsigned char *desc_ptr; + + desc_ptr = ses_get_page2_descriptor(edev, ecomp); + + if (!desc_ptr) + return -EIO; + + init_device_slot_control(desc, ecomp, desc_ptr); switch (val) { case ENCLOSURE_SETTING_DISABLED: - /* zero is disabled */ + desc[2] &= 0x7f; ecomp->active = 0; break; case ENCLOSURE_SETTING_ENABLED: - desc[2] = 0x80; + desc[2] |= 0x80; ecomp->active = 1; break; default: @@ -265,12 +303,53 @@ static int ses_show_id(struct enclosure_device *edev, char *buf) return sprintf(buf, "%#llx\n", id); } +static void ses_get_power_status(struct enclosure_device *edev, + struct enclosure_component *ecomp) +{ + unsigned char *desc; + + desc = ses_get_page2_descriptor(edev, ecomp); + if (desc) + ecomp->power_status = (desc[3] & 0x10) ? 0 : 1; +} + +static int ses_set_power_status(struct enclosure_device *edev, + struct enclosure_component *ecomp, + int val) +{ + unsigned char desc[4]; + unsigned char *desc_ptr; + + desc_ptr = ses_get_page2_descriptor(edev, ecomp); + + if (!desc_ptr) + return -EIO; + + init_device_slot_control(desc, ecomp, desc_ptr); + + switch (val) { + /* power = 1 is device_off = 0 and vice versa */ + case 0: + desc[3] |= 0x10; + break; + case 1: + desc[3] &= 0xef; + break; + default: + return -EINVAL; + } + ecomp->power_status = val; + return ses_set_page2_descriptor(edev, ecomp, desc); +} + static struct enclosure_component_callbacks ses_enclosure_callbacks = { .get_fault = ses_get_fault, .set_fault = ses_set_fault, .get_status = ses_get_status, .get_locate = ses_get_locate, .set_locate = ses_set_locate, + .get_power_status = ses_get_power_status, + .set_power_status = ses_set_power_status, .set_active = ses_set_active, .show_id = ses_show_id, }; @@ -449,6 +528,7 @@ static void ses_enclosure_data_process(struct enclosure_device *edev, ecomp = &edev->component[components++]; if (!IS_ERR(ecomp)) { + ses_get_power_status(edev, ecomp); if (addl_desc_ptr) ses_process_descriptor( ecomp, diff --git a/include/linux/enclosure.h b/include/linux/enclosure.h index 0f826c1..7be22da 100644 --- a/include/linux/enclosure.h +++ b/include/linux/enclosure.h @@ -79,6 +79,11 @@ struct enclosure_component_callbacks { int (*set_locate)(struct enclosure_device *, struct enclosure_component *, enum enclosure_component_setting); + void (*get_power_status)(struct enclosure_device *, + struct enclosure_component *); + int (*set_power_status)(struct enclosure_device *, + struct enclosure_component *, + int); int (*show_id)(struct enclosure_device *, char *buf); }; @@ -94,6 +99,7 @@ struct enclosure_component { int locate; int slot; enum enclosure_status status; + int power_status; }; struct enclosure_device { -- cgit v0.10.2 From 19c8ead7252bb66147011a361bb0c74dcb18e213 Mon Sep 17 00:00:00 2001 From: "Ewan D. Milne" Date: Thu, 4 Dec 2014 11:49:27 -0500 Subject: scsi_debug: Add REPORTED LUNS DATA HAS CHANGED Unit Attention Generate a REPORTED LUNS DATA HAS CHANGED Unit Attention if sysfs "max_luns" is used to change the number of scsi_debug LUNs. This is only done if scsi_debug_scsi_level is SPC-3 or above. Additionally, implement SPC-4 behavior which only generates this Unit Attention on the first LUN on the target to receive a command after the change. This condition is cleared when a REPORT LUNS command is received. Signed-off-by: Ewan D. Milne Acked-by: Douglas Gilbert Tested-by: Douglas Gilbert Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 7b8b51b..1435b15 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -80,6 +80,8 @@ static const char *scsi_debug_version_date = "20141022"; #define INVALID_FIELD_IN_PARAM_LIST 0x26 #define UA_RESET_ASC 0x29 #define UA_CHANGED_ASC 0x2a +#define TARGET_CHANGED_ASC 0x3f +#define LUNS_CHANGED_ASCQ 0x0e #define INSUFF_RES_ASC 0x55 #define INSUFF_RES_ASCQ 0x3 #define POWER_ON_RESET_ASCQ 0x0 @@ -180,7 +182,8 @@ static const char *scsi_debug_version_date = "20141022"; #define SDEBUG_UA_BUS_RESET 1 #define SDEBUG_UA_MODE_CHANGED 2 #define SDEBUG_UA_CAPACITY_CHANGED 3 -#define SDEBUG_NUM_UAS 4 +#define SDEBUG_UA_LUNS_CHANGED 4 +#define SDEBUG_NUM_UAS 5 /* for check_readiness() */ #define UAS_ONLY 1 /* check for UAs only */ @@ -782,6 +785,22 @@ static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg) /* return -ENOTTY; // correct return but upsets fdisk */ } +static void clear_luns_changed_on_target(struct sdebug_dev_info *devip) +{ + struct sdebug_host_info *sdhp; + struct sdebug_dev_info *dp; + + spin_lock(&sdebug_host_list_lock); + list_for_each_entry(sdhp, &sdebug_host_list, host_list) { + list_for_each_entry(dp, &sdhp->dev_info_list, dev_list) { + if ((devip->sdbg_host == dp->sdbg_host) && + (devip->target == dp->target)) + clear_bit(SDEBUG_UA_LUNS_CHANGED, dp->uas_bm); + } + } + spin_unlock(&sdebug_host_list_lock); +} + static int check_readiness(struct scsi_cmnd *SCpnt, int uas_only, struct sdebug_dev_info * devip) { @@ -817,6 +836,23 @@ static int check_readiness(struct scsi_cmnd *SCpnt, int uas_only, if (debug) cp = "capacity data changed"; break; + case SDEBUG_UA_LUNS_CHANGED: + /* + * SPC-3 behavior is to report a UNIT ATTENTION with + * ASC/ASCQ REPORTED LUNS DATA HAS CHANGED on every LUN + * on the target, until a REPORT LUNS command is + * received. SPC-4 behavior is to report it only once. + * NOTE: scsi_debug_scsi_level does not use the same + * values as struct scsi_device->scsi_level. + */ + if (scsi_debug_scsi_level >= 6) /* SPC-4 and above */ + clear_luns_changed_on_target(devip); + mk_sense_buffer(SCpnt, UNIT_ATTENTION, + TARGET_CHANGED_ASC, + LUNS_CHANGED_ASCQ); + if (debug) + cp = "reported luns data has changed"; + break; default: pr_warn("%s: unexpected unit attention code=%d\n", __func__, k); @@ -3229,6 +3265,7 @@ static int resp_report_luns(struct scsi_cmnd * scp, unsigned char arr[SDEBUG_RLUN_ARR_SZ]; unsigned char * max_addr; + clear_luns_changed_on_target(devip); alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24); shortish = (alloc_len < 4); if (shortish || (select_report > 2)) { @@ -4369,10 +4406,27 @@ static ssize_t max_luns_store(struct device_driver *ddp, const char *buf, size_t count) { int n; + bool changed; if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) { + changed = (scsi_debug_max_luns != n); scsi_debug_max_luns = n; sdebug_max_tgts_luns(); + if (changed && (scsi_debug_scsi_level >= 5)) { /* >= SPC-3 */ + struct sdebug_host_info *sdhp; + struct sdebug_dev_info *dp; + + spin_lock(&sdebug_host_list_lock); + list_for_each_entry(sdhp, &sdebug_host_list, + host_list) { + list_for_each_entry(dp, &sdhp->dev_info_list, + dev_list) { + set_bit(SDEBUG_UA_LUNS_CHANGED, + dp->uas_bm); + } + } + spin_unlock(&sdebug_host_list_lock); + } return count; } return -EINVAL; -- cgit v0.10.2 From acafd0b920e1a4a94a03f1911b4fb87e2081f235 Mon Sep 17 00:00:00 2001 From: "Ewan D. Milne" Date: Thu, 4 Dec 2014 11:49:28 -0500 Subject: scsi_debug: Implement WRITE BUFFER command Accept the WRITE BUFFER command and do nothing other than set the appropriate "microcode has been changed" UA on the LU. >From an earlier patch by Doug Gilbert. Signed-off-by: Ewan D. Milne Acked-by: Douglas Gilbert Tested-by: Douglas Gilbert Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 1435b15..8bcf6ad 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -93,6 +93,8 @@ static const char *scsi_debug_version_date = "20141022"; #define THRESHOLD_EXCEEDED 0x5d #define LOW_POWER_COND_ON 0x5e #define MISCOMPARE_VERIFY_ASC 0x1d +#define MICROCODE_CHANGED_ASCQ 0x1 /* with TARGET_CHANGED_ASC */ +#define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16 /* Additional Sense Code Qualifier (ASCQ) */ #define ACK_NAK_TO 0x3 @@ -183,7 +185,9 @@ static const char *scsi_debug_version_date = "20141022"; #define SDEBUG_UA_MODE_CHANGED 2 #define SDEBUG_UA_CAPACITY_CHANGED 3 #define SDEBUG_UA_LUNS_CHANGED 4 -#define SDEBUG_NUM_UAS 5 +#define SDEBUG_UA_MICROCODE_CHANGED 5 /* simulate firmware change */ +#define SDEBUG_UA_MICROCODE_CHANGED_WO_RESET 6 +#define SDEBUG_NUM_UAS 7 /* for check_readiness() */ #define UAS_ONLY 1 /* check for UAs only */ @@ -329,6 +333,7 @@ static int resp_write_same_10(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_write_same_16(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_xdwriteread_10(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_comp_write(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_write_buffer(struct scsi_cmnd *, struct sdebug_dev_info *); struct opcode_info_t { u8 num_attached; /* 0 if this is it (i.e. a leaf); use 0xff @@ -483,8 +488,9 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = { {0, 0x53, 0, F_D_IN | F_D_OUT | FF_DIRECT_IO, resp_xdwriteread_10, NULL, {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} }, - {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* WRITE_BUFFER */ - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0x3b, 0, F_D_OUT_MAYBE, resp_write_buffer, NULL, + {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0, + 0, 0, 0, 0} }, /* WRITE_BUFFER */ {1, 0x41, 0, F_D_OUT_MAYBE | FF_DIRECT_IO, resp_write_same_10, write_same_iarr, {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} }, @@ -836,6 +842,19 @@ static int check_readiness(struct scsi_cmnd *SCpnt, int uas_only, if (debug) cp = "capacity data changed"; break; + case SDEBUG_UA_MICROCODE_CHANGED: + mk_sense_buffer(SCpnt, UNIT_ATTENTION, + TARGET_CHANGED_ASC, MICROCODE_CHANGED_ASCQ); + if (debug) + cp = "microcode has been changed"; + break; + case SDEBUG_UA_MICROCODE_CHANGED_WO_RESET: + mk_sense_buffer(SCpnt, UNIT_ATTENTION, + TARGET_CHANGED_ASC, + MICROCODE_CHANGED_WO_RESET_ASCQ); + if (debug) + cp = "microcode has been changed without reset"; + break; case SDEBUG_UA_LUNS_CHANGED: /* * SPC-3 behavior is to report a UNIT ATTENTION with @@ -3069,6 +3088,55 @@ resp_write_same_16(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) return resp_write_same(scp, lba, num, ei_lba, unmap, ndob); } +/* Note the mode field is in the same position as the (lower) service action + * field. For the Report supported operation codes command, SPC-4 suggests + * each mode of this command should be reported separately; for future. */ +static int +resp_write_buffer(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) +{ + u8 *cmd = scp->cmnd; + struct scsi_device *sdp = scp->device; + struct sdebug_dev_info *dp; + u8 mode; + + mode = cmd[1] & 0x1f; + switch (mode) { + case 0x4: /* download microcode (MC) and activate (ACT) */ + /* set UAs on this device only */ + set_bit(SDEBUG_UA_BUS_RESET, devip->uas_bm); + set_bit(SDEBUG_UA_MICROCODE_CHANGED, devip->uas_bm); + break; + case 0x5: /* download MC, save and ACT */ + set_bit(SDEBUG_UA_MICROCODE_CHANGED_WO_RESET, devip->uas_bm); + break; + case 0x6: /* download MC with offsets and ACT */ + /* set UAs on most devices (LUs) in this target */ + list_for_each_entry(dp, + &devip->sdbg_host->dev_info_list, + dev_list) + if (dp->target == sdp->id) { + set_bit(SDEBUG_UA_BUS_RESET, dp->uas_bm); + if (devip != dp) + set_bit(SDEBUG_UA_MICROCODE_CHANGED, + dp->uas_bm); + } + break; + case 0x7: /* download MC with offsets, save, and ACT */ + /* set UA on all devices (LUs) in this target */ + list_for_each_entry(dp, + &devip->sdbg_host->dev_info_list, + dev_list) + if (dp->target == sdp->id) + set_bit(SDEBUG_UA_MICROCODE_CHANGED_WO_RESET, + dp->uas_bm); + break; + default: + /* do nothing for this command for other mode values */ + break; + } + return 0; +} + static int resp_comp_write(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) { -- cgit v0.10.2 From 2195d9690464445d0c30ee8170030a8e696f2053 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 27 Jul 2014 17:11:18 +0200 Subject: 3w-9xxx.c: Cleaning up missing null-terminate in conjunction with strncpy Replacing strncpy with strlcpy to avoid strings that lacks null terminate. And use the sizeof on the to string rather than strlen on the from string. Signed-off-by: Rickard Strandqvist Acked-by: Adam Radford Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c index cd4129f..7600639 100644 --- a/drivers/scsi/3w-9xxx.c +++ b/drivers/scsi/3w-9xxx.c @@ -608,7 +608,8 @@ static int twa_check_srl(TW_Device_Extension *tw_dev, int *flashed) } /* Load rest of compatibility struct */ - strncpy(tw_dev->tw_compat_info.driver_version, TW_DRIVER_VERSION, strlen(TW_DRIVER_VERSION)); + strlcpy(tw_dev->tw_compat_info.driver_version, TW_DRIVER_VERSION, + sizeof(tw_dev->tw_compat_info.driver_version)); tw_dev->tw_compat_info.driver_srl_high = TW_CURRENT_DRIVER_SRL; tw_dev->tw_compat_info.driver_branch_high = TW_CURRENT_DRIVER_BRANCH; tw_dev->tw_compat_info.driver_build_high = TW_CURRENT_DRIVER_BUILD; -- cgit v0.10.2 From c51e3a4fc45dab1b96159f02a610cccf58d1cfe3 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 4 Dec 2014 13:56:10 +0300 Subject: mpt2sas: issue_reset is uninitialized The "issue_reset" variable can be used uninitialized. It should be set to false at the start. Also I cleaned up the types by using bool. Signed-off-by: Dan Carpenter Acked-by: Sreekanth Reddy Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index 58e4521..3fb01d1 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -3236,7 +3236,7 @@ mpt2sas_base_sas_iounit_control(struct MPT2SAS_ADAPTER *ioc, u16 smid; u32 ioc_state; unsigned long timeleft; - u8 issue_reset; + bool issue_reset = false; int rc; void *request; u16 wait_state_count; @@ -3300,7 +3300,7 @@ mpt2sas_base_sas_iounit_control(struct MPT2SAS_ADAPTER *ioc, _debug_dump_mf(mpi_request, sizeof(Mpi2SasIoUnitControlRequest_t)/4); if (!(ioc->base_cmds.status & MPT2_CMD_RESET)) - issue_reset = 1; + issue_reset = true; goto issue_host_reset; } if (ioc->base_cmds.status & MPT2_CMD_REPLY_VALID) @@ -3341,7 +3341,7 @@ mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc, u16 smid; u32 ioc_state; unsigned long timeleft; - u8 issue_reset; + bool issue_reset = false; int rc; void *request; u16 wait_state_count; @@ -3398,7 +3398,7 @@ mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc, _debug_dump_mf(mpi_request, sizeof(Mpi2SepRequest_t)/4); if (!(ioc->base_cmds.status & MPT2_CMD_RESET)) - issue_reset = 1; + issue_reset = true; goto issue_host_reset; } if (ioc->base_cmds.status & MPT2_CMD_REPLY_VALID) -- cgit v0.10.2 From eb44552b98b414f00762ede69e5a8398bb50766f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 4 Dec 2014 13:57:05 +0300 Subject: mpt3sas: issue_reset is uninitialized The "issue_reset" can be used uninitialized. It should be set to false at the start. Also I cleaned up the types a little by using bool. Signed-off-by: Dan Carpenter Acked-by: Sreekanth Reddy Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 1560115..b9c2739 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -3419,7 +3419,7 @@ mpt3sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc, u16 smid; u32 ioc_state; unsigned long timeleft; - u8 issue_reset; + bool issue_reset = false; int rc; void *request; u16 wait_state_count; @@ -3483,7 +3483,7 @@ mpt3sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc, _debug_dump_mf(mpi_request, sizeof(Mpi2SasIoUnitControlRequest_t)/4); if (!(ioc->base_cmds.status & MPT3_CMD_RESET)) - issue_reset = 1; + issue_reset = true; goto issue_host_reset; } if (ioc->base_cmds.status & MPT3_CMD_REPLY_VALID) @@ -3523,7 +3523,7 @@ mpt3sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc, u16 smid; u32 ioc_state; unsigned long timeleft; - u8 issue_reset; + bool issue_reset = false; int rc; void *request; u16 wait_state_count; @@ -3581,7 +3581,7 @@ mpt3sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc, _debug_dump_mf(mpi_request, sizeof(Mpi2SepRequest_t)/4); if (!(ioc->base_cmds.status & MPT3_CMD_RESET)) - issue_reset = 1; + issue_reset = false; goto issue_host_reset; } if (ioc->base_cmds.status & MPT3_CMD_REPLY_VALID) -- cgit v0.10.2 From 37c0b105ae61113b98a81529cccc3a8404ba68b7 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 2 Jan 2015 15:18:13 +0100 Subject: esp_scsi: remove check for ESP_MAX_TAGS 'num_tags' is an unsigned char, so the check for 'ESP_MAX_TAGS' (which is set to 256) is pointless. Acked-by: Geert Uytterhoeven Signed-off-by: Hannes Reinecke Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c index ce5bd52..065b25d 100644 --- a/drivers/scsi/esp_scsi.c +++ b/drivers/scsi/esp_scsi.c @@ -2396,8 +2396,6 @@ int scsi_esp_register(struct esp *esp, struct device *dev) if (!esp->num_tags) esp->num_tags = ESP_DEFAULT_TAGS; - else if (esp->num_tags >= ESP_MAX_TAG) - esp->num_tags = ESP_MAX_TAG - 1; esp->host->transportt = esp_transport_template; esp->host->max_lun = ESP_MAX_LUN; esp->host->cmd_per_lun = 2; -- cgit v0.10.2 From d454c91f74fcefce5cd545cf98c565ed882bf81a Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Dec 2014 12:08:58 -0500 Subject: lpfc: correct device removal deadlock after link bounce This patch, applicable to 8G/4G/2G adapters, adds a call that resumes transmit operations after a link bounce. Without it, targets that tried to suspend exchanges after a link bounce (such as tape devices using sequence level error recovery) would never resume io operation, causing scan failures, and eventually deadlocks if a device removal request is made. Signed-off-by: James Smart Signed-off-by: Dick Kennedy Reviewed-by: Ewan D. Milne Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 4c25485..c66088d 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -2225,6 +2225,15 @@ lpfc_adisc_done(struct lpfc_vport *vport) if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) && !(vport->fc_flag & FC_RSCN_MODE) && (phba->sli_rev < LPFC_SLI_REV4)) { + /* The ADISCs are complete. Doesn't matter if they + * succeeded or failed because the ADISC completion + * routine guarantees to call the state machine and + * the RPI is either unregistered (failed ADISC response) + * or the RPI is still valid and the node is marked + * mapped for a target. The exchanges should be in the + * correct state. This code is specific to SLI3. + */ + lpfc_issue_clear_la(phba, vport); lpfc_issue_reg_vpi(phba, vport); return; } -- cgit v0.10.2 From ead3700d893654d440edcb66fb3767a0c0db54cf Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 29 Dec 2014 20:05:09 -0800 Subject: storvsc: use cmd_size to allocate per-command data STORVSC uses its own momory pool to manage device request data. However, the SCSI layer already has a mechanisim for allocating additional memory for each command issued to device driver. This patch removes the memory pool in STORVSC and makes it use SCSI layer to allocate memory for device request data. Reviewed-by: Long Li Signed-off-by: Christoph Hellwig Signed-off-by: K. Y. Srinivasan Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 4cff0dd..14ee98e 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -309,14 +308,6 @@ enum storvsc_request_type { * This is the end of Protocol specific defines. */ - -/* - * We setup a mempool to allocate request structures for this driver - * on a per-lun basis. The following define specifies the number of - * elements in the pool. - */ - -#define STORVSC_MIN_BUF_NR 64 static int storvsc_ringbuffer_size = (20 * PAGE_SIZE); module_param(storvsc_ringbuffer_size, int, S_IRUGO); @@ -346,7 +337,6 @@ static void storvsc_on_channel_callback(void *context); #define STORVSC_IDE_MAX_CHANNELS 1 struct storvsc_cmd_request { - struct list_head entry; struct scsi_cmnd *cmd; unsigned int bounce_sgl_count; @@ -357,7 +347,6 @@ struct storvsc_cmd_request { /* Synchronize the request/response if needed */ struct completion wait_event; - unsigned char *sense_buffer; struct hv_multipage_buffer data_buffer; struct vstor_packet vstor_packet; }; @@ -389,11 +378,6 @@ struct storvsc_device { struct storvsc_cmd_request reset_request; }; -struct stor_mem_pools { - struct kmem_cache *request_pool; - mempool_t *request_mempool; -}; - struct hv_host_device { struct hv_device *dev; unsigned int port; @@ -1070,10 +1054,8 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request) { struct scsi_cmnd *scmnd = cmd_request->cmd; struct hv_host_device *host_dev = shost_priv(scmnd->device->host); - void (*scsi_done_fn)(struct scsi_cmnd *); struct scsi_sense_hdr sense_hdr; struct vmscsi_request *vm_srb; - struct stor_mem_pools *memp = scmnd->device->hostdata; struct Scsi_Host *host; struct storvsc_device *stor_dev; struct hv_device *dev = host_dev->dev; @@ -1109,14 +1091,7 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request) cmd_request->data_buffer.len - vm_srb->data_transfer_length); - scsi_done_fn = scmnd->scsi_done; - - scmnd->host_scribble = NULL; - scmnd->scsi_done = NULL; - - scsi_done_fn(scmnd); - - mempool_free(cmd_request, memp->request_mempool); + scmnd->scsi_done(scmnd); } static void storvsc_on_io_completion(struct hv_device *device, @@ -1160,7 +1135,7 @@ static void storvsc_on_io_completion(struct hv_device *device, SRB_STATUS_AUTOSENSE_VALID) { /* autosense data available */ - memcpy(request->sense_buffer, + memcpy(request->cmd->sense_buffer, vstor_packet->vm_srb.sense_data, vstor_packet->vm_srb.sense_info_length); @@ -1378,55 +1353,6 @@ static int storvsc_do_io(struct hv_device *device, return ret; } -static int storvsc_device_alloc(struct scsi_device *sdevice) -{ - struct stor_mem_pools *memp; - int number = STORVSC_MIN_BUF_NR; - - memp = kzalloc(sizeof(struct stor_mem_pools), GFP_KERNEL); - if (!memp) - return -ENOMEM; - - memp->request_pool = - kmem_cache_create(dev_name(&sdevice->sdev_dev), - sizeof(struct storvsc_cmd_request), 0, - SLAB_HWCACHE_ALIGN, NULL); - - if (!memp->request_pool) - goto err0; - - memp->request_mempool = mempool_create(number, mempool_alloc_slab, - mempool_free_slab, - memp->request_pool); - - if (!memp->request_mempool) - goto err1; - - sdevice->hostdata = memp; - - return 0; - -err1: - kmem_cache_destroy(memp->request_pool); - -err0: - kfree(memp); - return -ENOMEM; -} - -static void storvsc_device_destroy(struct scsi_device *sdevice) -{ - struct stor_mem_pools *memp = sdevice->hostdata; - - if (!memp) - return; - - mempool_destroy(memp->request_mempool); - kmem_cache_destroy(memp->request_pool); - kfree(memp); - sdevice->hostdata = NULL; -} - static int storvsc_device_configure(struct scsi_device *sdevice) { scsi_change_queue_depth(sdevice, STORVSC_MAX_IO_REQUESTS); @@ -1561,13 +1487,11 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) int ret; struct hv_host_device *host_dev = shost_priv(host); struct hv_device *dev = host_dev->dev; - struct storvsc_cmd_request *cmd_request; - unsigned int request_size = 0; + struct storvsc_cmd_request *cmd_request = scsi_cmd_priv(scmnd); int i; struct scatterlist *sgl; unsigned int sg_count = 0; struct vmscsi_request *vm_srb; - struct stor_mem_pools *memp = scmnd->device->hostdata; if (vmstor_current_major <= VMSTOR_WIN8_MAJOR) { /* @@ -1584,25 +1508,9 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) } } - request_size = sizeof(struct storvsc_cmd_request); - - cmd_request = mempool_alloc(memp->request_mempool, - GFP_ATOMIC); - - /* - * We might be invoked in an interrupt context; hence - * mempool_alloc() can fail. - */ - if (!cmd_request) - return SCSI_MLQUEUE_DEVICE_BUSY; - - memset(cmd_request, 0, sizeof(struct storvsc_cmd_request)); - /* Setup the cmd request */ cmd_request->cmd = scmnd; - scmnd->host_scribble = (unsigned char *)cmd_request; - vm_srb = &cmd_request->vstor_packet.vm_srb; vm_srb->win8_extension.time_out_value = 60; @@ -1637,9 +1545,6 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) memcpy(vm_srb->cdb, scmnd->cmnd, vm_srb->cdb_length); - cmd_request->sense_buffer = scmnd->sense_buffer; - - cmd_request->data_buffer.len = scsi_bufflen(scmnd); if (scsi_sg_count(scmnd)) { sgl = (struct scatterlist *)scsi_sglist(scmnd); @@ -1651,10 +1556,8 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) create_bounce_buffer(sgl, scsi_sg_count(scmnd), scsi_bufflen(scmnd), vm_srb->data_in); - if (!cmd_request->bounce_sgl) { - ret = SCSI_MLQUEUE_HOST_BUSY; - goto queue_error; - } + if (!cmd_request->bounce_sgl) + return SCSI_MLQUEUE_HOST_BUSY; cmd_request->bounce_sgl_count = ALIGN(scsi_bufflen(scmnd), PAGE_SIZE) >> @@ -1692,27 +1595,21 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) destroy_bounce_buffer(cmd_request->bounce_sgl, cmd_request->bounce_sgl_count); - ret = SCSI_MLQUEUE_DEVICE_BUSY; - goto queue_error; + return SCSI_MLQUEUE_DEVICE_BUSY; } return 0; - -queue_error: - mempool_free(cmd_request, memp->request_mempool); - scmnd->host_scribble = NULL; - return ret; } static struct scsi_host_template scsi_driver = { .module = THIS_MODULE, .name = "storvsc_host_t", + .cmd_size = sizeof(struct storvsc_cmd_request), .bios_param = storvsc_get_chs, .queuecommand = storvsc_queuecommand, .eh_host_reset_handler = storvsc_host_reset_handler, + .proc_name = "storvsc_host", .eh_timed_out = storvsc_eh_timed_out, - .slave_alloc = storvsc_device_alloc, - .slave_destroy = storvsc_device_destroy, .slave_configure = storvsc_device_configure, .cmd_per_lun = 255, .can_queue = STORVSC_MAX_IO_REQUESTS*STORVSC_MAX_TARGETS, -- cgit v0.10.2 From 2a09ed3d97ff8b5b377f86da9b9afd9ebd97b362 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Tue, 16 Dec 2014 13:21:42 -0800 Subject: storvsc: in responce to a scan event, scan the host The virtual HBA that storvsc implements can support multiple channels and targets. So, scan the host when the host notifies that a scan is needed. Signed-off-by: K. Y. Srinivasan Reviewed-by: Long Li Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 14ee98e..ab0bb98 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -410,21 +410,16 @@ done: kfree(wrk); } -static void storvsc_bus_scan(struct work_struct *work) +static void storvsc_host_scan(struct work_struct *work) { struct storvsc_scan_work *wrk; - int id, order_id; + struct Scsi_Host *host; wrk = container_of(work, struct storvsc_scan_work, work); - for (id = 0; id < wrk->host->max_id; ++id) { - if (wrk->host->reverse_ordering) - order_id = wrk->host->max_id - id - 1; - else - order_id = id; - - scsi_scan_target(&wrk->host->shost_gendev, 0, - order_id, SCAN_WILD_CARD, 1); - } + host = wrk->host; + + scsi_scan_host(host); + kfree(wrk); } @@ -1173,7 +1168,7 @@ static void storvsc_on_receive(struct hv_device *device, if (!work) return; - INIT_WORK(&work->work, storvsc_bus_scan); + INIT_WORK(&work->work, storvsc_host_scan); work->host = stor_device->host; schedule_work(&work->work); break; -- cgit v0.10.2 From 34a716bc345ba06c34aa33d09536fcb607523818 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Tue, 16 Dec 2014 13:21:43 -0800 Subject: storvsc: force discovery of LUNs that may have been removed. The host asks the guest to scan when a LUN is removed or added. The only way a guest can identify the removed LUN is when an I/O is attempted on a removed LUN - the SRB status code indicates that the LUN is invalid. We currently handle this SRB status and remove the device. Rather than waiting for an I/O to remove the device, force the discovery of LUNs that may have been removed prior to discovering LUNs that may have been added. Signed-off-by: K. Y. Srinivasan Reviewed-by: Long Li Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index ab0bb98..ae8293a 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -414,10 +414,36 @@ static void storvsc_host_scan(struct work_struct *work) { struct storvsc_scan_work *wrk; struct Scsi_Host *host; + struct scsi_device *sdev; + unsigned long flags; wrk = container_of(work, struct storvsc_scan_work, work); host = wrk->host; + /* + * Before scanning the host, first check to see if any of the + * currrently known devices have been hot removed. We issue a + * "unit ready" command against all currently known devices. + * This I/O will result in an error for devices that have been + * removed. As part of handling the I/O error, we remove the device. + * + * When a LUN is added or removed, the host sends us a signal to + * scan the host. Thus we are forced to discover the LUNs that + * may have been removed this way. + */ + mutex_lock(&host->scan_mutex); + spin_lock_irqsave(host->host_lock, flags); + list_for_each_entry(sdev, &host->__devices, siblings) { + spin_unlock_irqrestore(host->host_lock, flags); + scsi_test_unit_ready(sdev, 1, 1, NULL); + spin_lock_irqsave(host->host_lock, flags); + continue; + } + spin_unlock_irqrestore(host->host_lock, flags); + mutex_unlock(&host->scan_mutex); + /* + * Now scan the host to discover LUNs that may have been added. + */ scsi_scan_host(host); kfree(wrk); -- cgit v0.10.2 From 0fb8db29f2a02e8451e4e56d364c706f70a9ef00 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Tue, 16 Dec 2014 13:21:44 -0800 Subject: storvsc: fix a bug in storvsc limits Commit 4cd83ecdac20d30725b4f96e5d7814a1e290bc7e changed the limits to reflect the values on the host. It turns out that WS2008R2 cannot correctly handle these new limits. Fix this bug by setting the limits based on the host. Signed-off-by: K. Y. Srinivasan Reviewed-by: Long Li Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index ae8293a..caa9f0e 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -1678,6 +1678,9 @@ static int storvsc_probe(struct hv_device *device, bool dev_is_ide = ((dev_id->driver_data == IDE_GUID) ? true : false); int target = 0; struct storvsc_device *stor_device; + int max_luns_per_target; + int max_targets; + int max_channels; /* * Based on the windows host we are running on, @@ -1691,12 +1694,18 @@ static int storvsc_probe(struct hv_device *device, vmscsi_size_delta = sizeof(struct vmscsi_win8_extension); vmstor_current_major = VMSTOR_WIN7_MAJOR; vmstor_current_minor = VMSTOR_WIN7_MINOR; + max_luns_per_target = STORVSC_IDE_MAX_LUNS_PER_TARGET; + max_targets = STORVSC_IDE_MAX_TARGETS; + max_channels = STORVSC_IDE_MAX_CHANNELS; break; default: sense_buffer_size = POST_WIN7_STORVSC_SENSE_BUFFER_SIZE; vmscsi_size_delta = 0; vmstor_current_major = VMSTOR_WIN8_MAJOR; vmstor_current_minor = VMSTOR_WIN8_MINOR; + max_luns_per_target = STORVSC_MAX_LUNS_PER_TARGET; + max_targets = STORVSC_MAX_TARGETS; + max_channels = STORVSC_MAX_CHANNELS; break; } @@ -1744,9 +1753,9 @@ static int storvsc_probe(struct hv_device *device, break; case SCSI_GUID: - host->max_lun = STORVSC_MAX_LUNS_PER_TARGET; - host->max_id = STORVSC_MAX_TARGETS; - host->max_channel = STORVSC_MAX_CHANNELS - 1; + host->max_lun = max_luns_per_target; + host->max_id = max_targets; + host->max_channel = max_channels - 1; break; default: -- cgit v0.10.2 From b0a93d96b2814c725161f91a4e35d0c29ec0f95b Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Tue, 16 Dec 2014 13:21:45 -0800 Subject: storvsc: force SPC-3 compliance on win8 and win8 r2 hosts On win8 and win8 r2 hosts force SPC-3 compliance for MSFT virtual disks. Ubuntu has been carrying a similar patch outside the tree for a while now. Starting with win10, the host will support SPC-3 compliance. Based on all the testing that has been done on win8 and win8 r2 hosts, we are comfortable claiming SPC-3 compliance on these hosts as well. This will enable TRIM support on these hosts. Suggested by: James Bottomley Signed-off-by: K. Y. Srinivasan Reviewed-by: Long Li Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index caa9f0e..efc6e44 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -1394,6 +1394,19 @@ static int storvsc_device_configure(struct scsi_device *sdevice) */ sdevice->sdev_bflags |= msft_blist_flags; + /* + * If the host is WIN8 or WIN8 R2, claim conformance to SPC-3 + * if the device is a MSFT virtual device. + */ + if (!strncmp(sdevice->vendor, "Msft", 4)) { + switch (vmbus_proto_version) { + case VERSION_WIN8: + case VERSION_WIN8_1: + sdevice->scsi_level = SCSI_SPC_3; + break; + } + } + return 0; } -- cgit v0.10.2 From ded85c193a391a84076d5c6a7a5668fe164a490e Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Thu, 8 Jan 2015 07:43:42 +0100 Subject: scsi: Implement per-cpu logging buffer Implement a per-cpu buffer for formatting messages to avoid line breaks up under high load. This patch implements scmd_printk() and sdev_prefix_printk() using the per-cpu buffer and makes sdev_printk() a wrapper for sdev_prefix_printk(). Tested-by: Robert Elliott Reviewed-by: Robert Elliott Signed-off-by: Hannes Reinecke Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 58158f1..447c2d2 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -167,7 +167,7 @@ scsi_mod-y += scsi_scan.o scsi_sysfs.o scsi_devinfo.o scsi_mod-$(CONFIG_SCSI_NETLINK) += scsi_netlink.o scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o -scsi_mod-y += scsi_trace.o +scsi_mod-y += scsi_trace.o scsi_logging.o scsi_mod-$(CONFIG_PM) += scsi_pm.o hv_storvsc-y := storvsc_drv.o diff --git a/drivers/scsi/scsi_logging.c b/drivers/scsi/scsi_logging.c new file mode 100644 index 0000000..09d65de --- /dev/null +++ b/drivers/scsi/scsi_logging.c @@ -0,0 +1,124 @@ +/* + * scsi_logging.c + * + * Copyright (C) 2014 SUSE Linux Products GmbH + * Copyright (C) 2014 Hannes Reinecke + * + * This file is released under the GPLv2 + */ + +#include +#include + +#include +#include +#include +#include + +#define SCSI_LOG_SPOOLSIZE 4096 +#define SCSI_LOG_BUFSIZE 128 + +#if (SCSI_LOG_SPOOLSIZE / SCSI_LOG_BUFSIZE) > BITS_PER_LONG +#warning SCSI logging bitmask too large +#endif + +struct scsi_log_buf { + char buffer[SCSI_LOG_SPOOLSIZE]; + unsigned long map; +}; + +static DEFINE_PER_CPU(struct scsi_log_buf, scsi_format_log); + +static char *scsi_log_reserve_buffer(size_t *len) +{ + struct scsi_log_buf *buf; + unsigned long map_bits = sizeof(buf->buffer) / SCSI_LOG_BUFSIZE; + unsigned long idx = 0; + + preempt_disable(); + buf = this_cpu_ptr(&scsi_format_log); + idx = find_first_zero_bit(&buf->map, map_bits); + if (likely(idx < map_bits)) { + while (test_and_set_bit(idx, &buf->map)) { + idx = find_next_zero_bit(&buf->map, map_bits, idx); + if (idx >= map_bits) + break; + } + } + if (WARN_ON(idx >= map_bits)) { + preempt_enable(); + return NULL; + } + *len = SCSI_LOG_BUFSIZE; + return buf->buffer + idx * SCSI_LOG_BUFSIZE; +} + +static void scsi_log_release_buffer(char *bufptr) +{ + struct scsi_log_buf *buf; + unsigned long idx; + int ret; + + buf = this_cpu_ptr(&scsi_format_log); + if (bufptr >= buf->buffer && + bufptr < buf->buffer + SCSI_LOG_SPOOLSIZE) { + idx = (bufptr - buf->buffer) / SCSI_LOG_BUFSIZE; + ret = test_and_clear_bit(idx, &buf->map); + WARN_ON(!ret); + } + preempt_enable(); +} + +int sdev_prefix_printk(const char *level, const struct scsi_device *sdev, + const char *name, const char *fmt, ...) +{ + va_list args; + char *logbuf; + size_t off = 0, logbuf_len; + int ret; + + if (!sdev) + return 0; + + logbuf = scsi_log_reserve_buffer(&logbuf_len); + if (!logbuf) + return 0; + + if (name) + off += scnprintf(logbuf + off, logbuf_len - off, + "[%s] ", name); + va_start(args, fmt); + off += vscnprintf(logbuf + off, logbuf_len - off, fmt, args); + va_end(args); + ret = dev_printk(level, &sdev->sdev_gendev, "%s", logbuf); + scsi_log_release_buffer(logbuf); + return ret; +} +EXPORT_SYMBOL(sdev_prefix_printk); + +int scmd_printk(const char *level, const struct scsi_cmnd *scmd, + const char *fmt, ...) +{ + struct gendisk *disk = scmd->request->rq_disk; + va_list args; + char *logbuf; + size_t off = 0, logbuf_len; + int ret; + + if (!scmd || !scmd->cmnd) + return 0; + + logbuf = scsi_log_reserve_buffer(&logbuf_len); + if (!logbuf) + return 0; + if (disk) + off += scnprintf(logbuf + off, logbuf_len - off, + "[%s] ", disk->disk_name); + va_start(args, fmt); + off += vscnprintf(logbuf + off, logbuf_len - off, fmt, args); + va_end(args); + ret = dev_printk(level, &scmd->device->sdev_gendev, "%s", logbuf); + scsi_log_release_buffer(logbuf); + return ret; +} +EXPORT_SYMBOL(scmd_printk); diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 3a4edd1..d1aad4d 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -230,9 +230,6 @@ struct scsi_dh_data { #define transport_class_to_sdev(class_dev) \ to_scsi_device(class_dev->parent) -#define sdev_printk(prefix, sdev, fmt, a...) \ - dev_printk(prefix, &(sdev)->sdev_gendev, fmt, ##a) - #define sdev_dbg(sdev, fmt, a...) \ dev_dbg(&(sdev)->sdev_gendev, fmt, ##a) @@ -240,16 +237,14 @@ struct scsi_dh_data { * like scmd_printk, but the device name is passed in * as a string pointer */ -#define sdev_prefix_printk(l, sdev, p, fmt, a...) \ - (p) ? \ - sdev_printk(l, sdev, "[%s] " fmt, p, ##a) : \ - sdev_printk(l, sdev, fmt, ##a) - -#define scmd_printk(prefix, scmd, fmt, a...) \ - (scmd)->request->rq_disk ? \ - sdev_printk(prefix, (scmd)->device, "[%s] " fmt, \ - (scmd)->request->rq_disk->disk_name, ##a) : \ - sdev_printk(prefix, (scmd)->device, fmt, ##a) +extern int sdev_prefix_printk(const char *, const struct scsi_device *, + const char *, const char *, ...); + +#define sdev_printk(l, sdev, fmt, a...) \ + sdev_prefix_printk(l, sdev, NULL, fmt, ##a) + +extern int scmd_printk(const char *, const struct scsi_cmnd *, + const char *, ...); #define scmd_dbg(scmd, fmt, a...) \ do { \ -- cgit v0.10.2 From aa66ab35f2b31e96f4b60e675b2d6ffc51d472eb Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Thu, 8 Jan 2015 07:43:43 +0100 Subject: scsi: log request tag for scmd_printk() The request tag provides a concise identification of a SCSI command, so we should be printing that out for scmd_printk(). Suggested-by: Christoph Hellwig Tested-by: Robert Elliott Reviewed-by: Robert Elliott Signed-off-by: Hannes Reinecke Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/scsi_logging.c b/drivers/scsi/scsi_logging.c index 09d65de..4d20132 100644 --- a/drivers/scsi/scsi_logging.c +++ b/drivers/scsi/scsi_logging.c @@ -114,6 +114,10 @@ int scmd_printk(const char *level, const struct scsi_cmnd *scmd, if (disk) off += scnprintf(logbuf + off, logbuf_len - off, "[%s] ", disk->disk_name); + + if (scmd->request->tag >= 0) + off += scnprintf(logbuf + off, logbuf_len - off, + "tag#%d ", scmd->request->tag); va_start(args, fmt); off += vscnprintf(logbuf + off, logbuf_len - off, fmt, args); va_end(args); -- cgit v0.10.2 From 9e5ed2a5b3662c6f398023042c02aaa527099a3d Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Thu, 8 Jan 2015 07:43:44 +0100 Subject: scsi: use external buffer for command logging Use an external buffer for __scsi_print_command() and move command logging over to use the per-cpu logging buffer. With that we can guarantee the command always will always be formatted in one line. So we can even print out a variable length command correctly across several lines. Finally rename __scsi_print_command() to __scsi_format_comment() to better reflect the functionality. Tested-by: Robert Elliott Reviewed-by: Robert Elliott Signed-off-by: Hannes Reinecke Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c index 6bac8a7..79e462f 100644 --- a/drivers/scsi/ch.c +++ b/drivers/scsi/ch.c @@ -195,8 +195,10 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd, int cmd_len, retry: errno = 0; if (debug) { - DPRINTK("command: "); - __scsi_print_command(cmd, cmd_len); + char logbuf[SCSI_LOG_BUFSIZE]; + + __scsi_format_command(logbuf, sizeof(logbuf), cmd, cmd_len); + DPRINTK("command: %s", logbuf); } result = scsi_execute_req(ch->device, cmd, direction, buffer, diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c index 55a7157..7792960 100644 --- a/drivers/scsi/constants.c +++ b/drivers/scsi/constants.c @@ -24,8 +24,6 @@ #define THIRD_PARTY_COPY_OUT 0x83 #define THIRD_PARTY_COPY_IN 0x84 -#define VENDOR_SPECIFIC_CDB 0xc0 - struct sa_name_list { int opcode; const struct value_name_pair *arr; @@ -281,8 +279,8 @@ static struct sa_name_list sa_names_arr[] = { }; #endif /* CONFIG_SCSI_CONSTANTS */ -static bool scsi_opcode_sa_name(int opcode, int service_action, - const char **cdb_name, const char **sa_name) +bool scsi_opcode_sa_name(int opcode, int service_action, + const char **cdb_name, const char **sa_name) { struct sa_name_list *sa_name_ptr; const struct value_name_pair *arr = NULL; @@ -315,74 +313,6 @@ static bool scsi_opcode_sa_name(int opcode, int service_action, return true; } -static void print_opcode_name(const unsigned char *cdbp, size_t cdb_len) -{ - int sa, cdb0; - const char *cdb_name = NULL, *sa_name = NULL; - - cdb0 = cdbp[0]; - if (cdb0 == VARIABLE_LENGTH_CMD) { - if (cdb_len < 10) { - printk("short variable length command, len=%zu", - cdb_len); - return; - } - sa = (cdbp[8] << 8) + cdbp[9]; - } else - sa = cdbp[1] & 0x1f; - - if (!scsi_opcode_sa_name(cdb0, sa, &cdb_name, &sa_name)) { - if (cdb_name) - printk("%s", cdb_name); - else if (cdb0 >= VENDOR_SPECIFIC_CDB) - printk("cdb[0]=0x%x (vendor)", cdb0); - else if (cdb0 >= 0x60 && cdb0 < 0x7e) - printk("cdb[0]=0x%x (reserved)", cdb0); - else - printk("cdb[0]=0x%x", cdb0); - } else { - if (sa_name) - printk("%s", sa_name); - else if (cdb_name) - printk("%s, sa=0x%x", cdb_name, sa); - else - printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa); - } -} - -void __scsi_print_command(const unsigned char *cdb, size_t cdb_len) -{ - int k, len; - - print_opcode_name(cdb, cdb_len); - len = scsi_command_size(cdb); - if (cdb_len < len) - len = cdb_len; - /* print out all bytes in cdb */ - for (k = 0; k < len; ++k) - printk(" %02x", cdb[k]); - printk("\n"); -} -EXPORT_SYMBOL(__scsi_print_command); - -void scsi_print_command(struct scsi_cmnd *cmd) -{ - int k; - - if (cmd->cmnd == NULL) - return; - - scmd_printk(KERN_INFO, cmd, "CDB: "); - print_opcode_name(cmd->cmnd, cmd->cmd_len); - - /* print out all bytes in cdb */ - printk(":"); - for (k = 0; k < cmd->cmd_len; ++k) - printk(" %02x", cmd->cmnd[k]); - printk("\n"); -} -EXPORT_SYMBOL(scsi_print_command); - #ifdef CONFIG_SCSI_CONSTANTS struct error_info { diff --git a/drivers/scsi/scsi_logging.c b/drivers/scsi/scsi_logging.c index 4d20132..afba995 100644 --- a/drivers/scsi/scsi_logging.c +++ b/drivers/scsi/scsi_logging.c @@ -13,10 +13,10 @@ #include #include #include +#include #include #define SCSI_LOG_SPOOLSIZE 4096 -#define SCSI_LOG_BUFSIZE 128 #if (SCSI_LOG_SPOOLSIZE / SCSI_LOG_BUFSIZE) > BITS_PER_LONG #warning SCSI logging bitmask too large @@ -69,6 +69,24 @@ static void scsi_log_release_buffer(char *bufptr) preempt_enable(); } +static size_t scmd_format_header(char *logbuf, size_t logbuf_len, + struct gendisk *disk, int tag) +{ + size_t off = 0; + + if (disk) + off += scnprintf(logbuf + off, logbuf_len - off, + "[%s] ", disk->disk_name); + + if (WARN_ON(off >= logbuf_len)) + return off; + + if (tag >= 0) + off += scnprintf(logbuf + off, logbuf_len - off, + "tag#%d ", tag); + return off; +} + int sdev_prefix_printk(const char *level, const struct scsi_device *sdev, const char *name, const char *fmt, ...) { @@ -87,9 +105,11 @@ int sdev_prefix_printk(const char *level, const struct scsi_device *sdev, if (name) off += scnprintf(logbuf + off, logbuf_len - off, "[%s] ", name); - va_start(args, fmt); - off += vscnprintf(logbuf + off, logbuf_len - off, fmt, args); - va_end(args); + if (!WARN_ON(off >= logbuf_len)) { + va_start(args, fmt); + off += vscnprintf(logbuf + off, logbuf_len - off, fmt, args); + va_end(args); + } ret = dev_printk(level, &sdev->sdev_gendev, "%s", logbuf); scsi_log_release_buffer(logbuf); return ret; @@ -111,18 +131,152 @@ int scmd_printk(const char *level, const struct scsi_cmnd *scmd, logbuf = scsi_log_reserve_buffer(&logbuf_len); if (!logbuf) return 0; - if (disk) - off += scnprintf(logbuf + off, logbuf_len - off, - "[%s] ", disk->disk_name); - - if (scmd->request->tag >= 0) - off += scnprintf(logbuf + off, logbuf_len - off, - "tag#%d ", scmd->request->tag); - va_start(args, fmt); - off += vscnprintf(logbuf + off, logbuf_len - off, fmt, args); - va_end(args); + off = scmd_format_header(logbuf, logbuf_len, disk, + scmd->request->tag); + if (off < logbuf_len) { + va_start(args, fmt); + off += vscnprintf(logbuf + off, logbuf_len - off, fmt, args); + va_end(args); + } ret = dev_printk(level, &scmd->device->sdev_gendev, "%s", logbuf); scsi_log_release_buffer(logbuf); return ret; } EXPORT_SYMBOL(scmd_printk); + +static size_t scsi_format_opcode_name(char *buffer, size_t buf_len, + const unsigned char *cdbp) +{ + int sa, cdb0; + const char *cdb_name = NULL, *sa_name = NULL; + size_t off; + + cdb0 = cdbp[0]; + if (cdb0 == VARIABLE_LENGTH_CMD) { + int len = scsi_varlen_cdb_length(cdbp); + + if (len < 10) { + off = scnprintf(buffer, buf_len, + "short variable length command, len=%d", + len); + return off; + } + sa = (cdbp[8] << 8) + cdbp[9]; + } else + sa = cdbp[1] & 0x1f; + + if (!scsi_opcode_sa_name(cdb0, sa, &cdb_name, &sa_name)) { + if (cdb_name) + off = scnprintf(buffer, buf_len, "%s", cdb_name); + else { + off = scnprintf(buffer, buf_len, "opcode=0x%x", cdb0); + if (WARN_ON(off >= buf_len)) + return off; + if (cdb0 >= VENDOR_SPECIFIC_CDB) + off += scnprintf(buffer + off, buf_len - off, + " (vendor)"); + else if (cdb0 >= 0x60 && cdb0 < 0x7e) + off += scnprintf(buffer + off, buf_len - off, + " (reserved)"); + } + } else { + if (sa_name) + off = scnprintf(buffer, buf_len, "%s", sa_name); + else if (cdb_name) + off = scnprintf(buffer, buf_len, "%s, sa=0x%x", + cdb_name, sa); + else + off = scnprintf(buffer, buf_len, + "opcode=0x%x, sa=0x%x", cdb0, sa); + } + WARN_ON(off >= buf_len); + return off; +} + +size_t __scsi_format_command(char *logbuf, size_t logbuf_len, + const unsigned char *cdb, size_t cdb_len) +{ + int len, k; + size_t off; + + off = scsi_format_opcode_name(logbuf, logbuf_len, cdb); + if (off >= logbuf_len) + return off; + len = scsi_command_size(cdb); + if (cdb_len < len) + len = cdb_len; + /* print out all bytes in cdb */ + for (k = 0; k < len; ++k) { + if (off > logbuf_len - 3) + break; + off += scnprintf(logbuf + off, logbuf_len - off, + " %02x", cdb[k]); + } + return off; +} +EXPORT_SYMBOL(__scsi_format_command); + +void scsi_print_command(struct scsi_cmnd *cmd) +{ + struct gendisk *disk = cmd->request->rq_disk; + int k; + char *logbuf; + size_t off, logbuf_len; + + if (!cmd->cmnd) + return; + + logbuf = scsi_log_reserve_buffer(&logbuf_len); + if (!logbuf) + return; + + off = scmd_format_header(logbuf, logbuf_len, disk, cmd->request->tag); + if (off >= logbuf_len) + goto out_printk; + off += scnprintf(logbuf + off, logbuf_len - off, "CDB: "); + if (WARN_ON(off >= logbuf_len)) + goto out_printk; + + off += scsi_format_opcode_name(logbuf + off, logbuf_len - off, + cmd->cmnd); + if (off >= logbuf_len) + goto out_printk; + + /* print out all bytes in cdb */ + if (cmd->cmd_len > 16) { + /* Print opcode in one line and use separate lines for CDB */ + off += scnprintf(logbuf + off, logbuf_len - off, "\n"); + dev_printk(KERN_INFO, &cmd->device->sdev_gendev, logbuf); + scsi_log_release_buffer(logbuf); + for (k = 0; k < cmd->cmd_len; k += 16) { + size_t linelen = min(cmd->cmd_len - k, 16); + + logbuf = scsi_log_reserve_buffer(&logbuf_len); + if (!logbuf) + break; + off = scmd_format_header(logbuf, logbuf_len, disk, + cmd->request->tag); + if (!WARN_ON(off > logbuf_len - 58)) { + off += scnprintf(logbuf + off, logbuf_len - off, + "CDB[%02x]: ", k); + hex_dump_to_buffer(&cmd->cmnd[k], linelen, + 16, 1, logbuf + off, + logbuf_len - off, false); + } + dev_printk(KERN_INFO, &cmd->device->sdev_gendev, + logbuf); + scsi_log_release_buffer(logbuf); + } + return; + } + if (!WARN_ON(off > logbuf_len - 49)) { + off += scnprintf(logbuf + off, logbuf_len - off, " "); + hex_dump_to_buffer(cmd->cmnd, cmd->cmd_len, 16, 1, + logbuf + off, logbuf_len - off, + false); + } +out_printk: + dev_printk(KERN_INFO, &cmd->device->sdev_gendev, logbuf); + scsi_log_release_buffer(logbuf); +} +EXPORT_SYMBOL(scsi_print_command); diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c index fb929fa..e8deb9c 100644 --- a/drivers/scsi/sr_ioctl.c +++ b/drivers/scsi/sr_ioctl.c @@ -188,6 +188,7 @@ int sr_do_ioctl(Scsi_CD *cd, struct packet_command *cgc) struct scsi_sense_hdr sshdr; int result, err = 0, retries = 0; struct request_sense *sense = cgc->sense; + char logbuf[SCSI_LOG_BUFSIZE]; SDev = cd->device; @@ -257,14 +258,20 @@ int sr_do_ioctl(Scsi_CD *cd, struct packet_command *cgc) /* sense: Invalid command operation code */ err = -EDRIVE_CANT_DO_THIS; #ifdef DEBUG - __scsi_print_command(cgc->cmd, CDROM_PACKET_SIZE); + __scsi_format_command(logbuf, sizeof(logbuf), + cgc->cmd, CDROM_PACKET_SIZE); + sr_printk(KERN_INFO, cd, + "CDROM (ioctl) invalid command: %s\n", + logbuf); scsi_print_sense_hdr(cd->device, cd->cdi.name, &sshdr); #endif break; default: + __scsi_format_command(logbuf, sizeof(logbuf), + cgc->cmd, CDROM_PACKET_SIZE); sr_printk(KERN_ERR, cd, - "CDROM (ioctl) error, command: "); - __scsi_print_command(cgc->cmd, CDROM_PACKET_SIZE); + "CDROM (ioctl) error, command: %s\n", + logbuf); scsi_print_sense_hdr(cd->device, cd->cdi.name, &sshdr); err = -EIO; } diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index 8a7f8ad..d0a66aa 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -195,6 +195,9 @@ enum scsi_timeouts { #define ATA_16 0x85 /* 16-byte pass-thru */ #define ATA_12 0xa1 /* 12-byte pass-thru */ +/* Vendor specific CDBs start here */ +#define VENDOR_SPECIFIC_CDB 0xc0 + /* * SCSI command lengths */ diff --git a/include/scsi/scsi_dbg.h b/include/scsi/scsi_dbg.h index 7982795..c7ed7b8 100644 --- a/include/scsi/scsi_dbg.h +++ b/include/scsi/scsi_dbg.h @@ -5,8 +5,12 @@ struct scsi_cmnd; struct scsi_device; struct scsi_sense_hdr; +#define SCSI_LOG_BUFSIZE 128 + +extern bool scsi_opcode_sa_name(int, int, const char **, const char **); extern void scsi_print_command(struct scsi_cmnd *); -extern void __scsi_print_command(const unsigned char *, size_t); +extern size_t __scsi_format_command(char *, size_t, + const unsigned char *, size_t); extern void scsi_show_extd_sense(const struct scsi_device *, const char *, unsigned char, unsigned char); extern void scsi_show_sense_hdr(const struct scsi_device *, const char *, -- cgit v0.10.2 From cbba5b0ee4c6c2fc8b78a21d0900099d480cf2e9 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Thu, 8 Jan 2015 07:43:45 +0100 Subject: libata: use __scsi_format_command() libata already uses an internal buffer, so we should be using __scsi_format_command() here. Tested-by: Robert Elliott Reviewed-by: Robert Elliott Acked-by: Tejun Heo Signed-off-by: Hannes Reinecke Signed-off-by: Christoph Hellwig diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 3dbec89..9179f11 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -2482,6 +2482,7 @@ static void ata_eh_link_report(struct ata_link *link) struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag); struct ata_taskfile *cmd = &qc->tf, *res = &qc->result_tf; const u8 *cdb = qc->cdb; + size_t cdb_len = qc->dev->cdb_len; char data_buf[20] = ""; char cdb_buf[70] = ""; @@ -2509,16 +2510,12 @@ static void ata_eh_link_report(struct ata_link *link) } if (ata_is_atapi(qc->tf.protocol)) { - if (qc->scsicmd) - scsi_print_command(qc->scsicmd); - else - snprintf(cdb_buf, sizeof(cdb_buf), - "cdb %02x %02x %02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x %02x %02x\n ", - cdb[0], cdb[1], cdb[2], cdb[3], - cdb[4], cdb[5], cdb[6], cdb[7], - cdb[8], cdb[9], cdb[10], cdb[11], - cdb[12], cdb[13], cdb[14], cdb[15]); + if (qc->scsicmd) { + cdb = qc->scsicmd->cmnd; + cdb_len = qc->scsicmd->cmd_len; + } + __scsi_format_command(cdb_buf, sizeof(cdb_buf), + cdb, cdb_len); } else { const char *descr = ata_get_cmd_descript(cmd->command); if (descr) -- cgit v0.10.2 From 2104551969e8011e72788dc5674609d437448cf6 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Thu, 8 Jan 2015 07:43:46 +0100 Subject: scsi: use per-cpu buffer for formatting sense Convert sense buffer logging to use the per-cpu buffer to avoid line breakup. Tested-by: Robert Elliott Reviewed-by: Robert Elliott Signed-off-by: Hannes Reinecke Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c index 7792960..a23ef75 100644 --- a/drivers/scsi/constants.c +++ b/drivers/scsi/constants.c @@ -1246,113 +1246,6 @@ scsi_extd_sense_format(unsigned char asc, unsigned char ascq, const char **fmt) } EXPORT_SYMBOL(scsi_extd_sense_format); -void -scsi_show_extd_sense(const struct scsi_device *sdev, const char *name, - unsigned char asc, unsigned char ascq) -{ - const char *extd_sense_fmt = NULL; - const char *extd_sense_str = scsi_extd_sense_format(asc, ascq, - &extd_sense_fmt); - - if (extd_sense_str) { - if (extd_sense_fmt) - sdev_prefix_printk(KERN_INFO, sdev, name, - "Add. Sense: %s (%s%x)", - extd_sense_str, extd_sense_fmt, - ascq); - else - sdev_prefix_printk(KERN_INFO, sdev, name, - "Add. Sense: %s", extd_sense_str); - - } else { - sdev_prefix_printk(KERN_INFO, sdev, name, - "%sASC=0x%x %sASCQ=0x%x\n", - asc >= 0x80 ? "<> " : "", asc, - ascq >= 0x80 ? "<> " : "", ascq); - } -} -EXPORT_SYMBOL(scsi_show_extd_sense); - -void -scsi_show_sense_hdr(const struct scsi_device *sdev, const char *name, - const struct scsi_sense_hdr *sshdr) -{ - const char *sense_txt; - - sense_txt = scsi_sense_key_string(sshdr->sense_key); - if (sense_txt) - sdev_prefix_printk(KERN_INFO, sdev, name, - "Sense Key : %s [%s]%s\n", sense_txt, - scsi_sense_is_deferred(sshdr) ? - "deferred" : "current", - sshdr->response_code >= 0x72 ? - " [descriptor]" : ""); - else - sdev_prefix_printk(KERN_INFO, sdev, name, - "Sense Key : 0x%x [%s]%s", sshdr->sense_key, - scsi_sense_is_deferred(sshdr) ? - "deferred" : "current", - sshdr->response_code >= 0x72 ? - " [descriptor]" : ""); -} -EXPORT_SYMBOL(scsi_show_sense_hdr); - -/* - * Print normalized SCSI sense header with a prefix. - */ -void -scsi_print_sense_hdr(const struct scsi_device *sdev, const char *name, - const struct scsi_sense_hdr *sshdr) -{ - scsi_show_sense_hdr(sdev, name, sshdr); - scsi_show_extd_sense(sdev, name, sshdr->asc, sshdr->ascq); -} -EXPORT_SYMBOL(scsi_print_sense_hdr); - -static void -scsi_dump_sense_buffer(const unsigned char *sense_buffer, int sense_len) -{ - int k, num; - - num = (sense_len < 32) ? sense_len : 32; - printk("Unrecognized sense data (in hex):"); - for (k = 0; k < num; ++k) { - if (0 == (k % 16)) { - printk("\n"); - printk(KERN_INFO " "); - } - printk("%02x ", sense_buffer[k]); - } - printk("\n"); - return; -} - -/* Normalize and print sense buffer with name prefix */ -void __scsi_print_sense(const struct scsi_device *sdev, const char *name, - const unsigned char *sense_buffer, int sense_len) -{ - struct scsi_sense_hdr sshdr; - - if (!scsi_normalize_sense(sense_buffer, sense_len, &sshdr)) { - scsi_dump_sense_buffer(sense_buffer, sense_len); - return; - } - scsi_show_sense_hdr(sdev, name, &sshdr); - scsi_show_extd_sense(sdev, name, sshdr.asc, sshdr.ascq); -} -EXPORT_SYMBOL(__scsi_print_sense); - -/* Normalize and print sense buffer in SCSI command */ -void scsi_print_sense(const struct scsi_cmnd *cmd) -{ - struct gendisk *disk = cmd->request->rq_disk; - const char *disk_name = disk ? disk->disk_name : NULL; - - __scsi_print_sense(cmd->device, disk_name, cmd->sense_buffer, - SCSI_SENSE_BUFFERSIZE); -} -EXPORT_SYMBOL(scsi_print_sense); - #ifdef CONFIG_SCSI_CONSTANTS static const char * const hostbyte_table[]={ diff --git a/drivers/scsi/scsi_logging.c b/drivers/scsi/scsi_logging.c index afba995..c7cba31 100644 --- a/drivers/scsi/scsi_logging.c +++ b/drivers/scsi/scsi_logging.c @@ -69,14 +69,20 @@ static void scsi_log_release_buffer(char *bufptr) preempt_enable(); } -static size_t scmd_format_header(char *logbuf, size_t logbuf_len, - struct gendisk *disk, int tag) +static inline const char *scmd_name(const struct scsi_cmnd *scmd) +{ + return scmd->request->rq_disk ? + scmd->request->rq_disk->disk_name : NULL; +} + +static size_t sdev_format_header(char *logbuf, size_t logbuf_len, + const char *name, int tag) { size_t off = 0; - if (disk) + if (name) off += scnprintf(logbuf + off, logbuf_len - off, - "[%s] ", disk->disk_name); + "[%s] ", name); if (WARN_ON(off >= logbuf_len)) return off; @@ -119,7 +125,6 @@ EXPORT_SYMBOL(sdev_prefix_printk); int scmd_printk(const char *level, const struct scsi_cmnd *scmd, const char *fmt, ...) { - struct gendisk *disk = scmd->request->rq_disk; va_list args; char *logbuf; size_t off = 0, logbuf_len; @@ -131,7 +136,7 @@ int scmd_printk(const char *level, const struct scsi_cmnd *scmd, logbuf = scsi_log_reserve_buffer(&logbuf_len); if (!logbuf) return 0; - off = scmd_format_header(logbuf, logbuf_len, disk, + off = sdev_format_header(logbuf, logbuf_len, scmd_name(scmd), scmd->request->tag); if (off < logbuf_len) { va_start(args, fmt); @@ -218,7 +223,6 @@ EXPORT_SYMBOL(__scsi_format_command); void scsi_print_command(struct scsi_cmnd *cmd) { - struct gendisk *disk = cmd->request->rq_disk; int k; char *logbuf; size_t off, logbuf_len; @@ -230,7 +234,8 @@ void scsi_print_command(struct scsi_cmnd *cmd) if (!logbuf) return; - off = scmd_format_header(logbuf, logbuf_len, disk, cmd->request->tag); + off = sdev_format_header(logbuf, logbuf_len, + scmd_name(cmd), cmd->request->tag); if (off >= logbuf_len) goto out_printk; off += scnprintf(logbuf + off, logbuf_len - off, "CDB: "); @@ -254,7 +259,8 @@ void scsi_print_command(struct scsi_cmnd *cmd) logbuf = scsi_log_reserve_buffer(&logbuf_len); if (!logbuf) break; - off = scmd_format_header(logbuf, logbuf_len, disk, + off = sdev_format_header(logbuf, logbuf_len, + scmd_name(cmd), cmd->request->tag); if (!WARN_ON(off > logbuf_len - 58)) { off += scnprintf(logbuf + off, logbuf_len - off, @@ -280,3 +286,145 @@ out_printk: scsi_log_release_buffer(logbuf); } EXPORT_SYMBOL(scsi_print_command); + +static size_t +scsi_format_extd_sense(char *buffer, size_t buf_len, + unsigned char asc, unsigned char ascq) +{ + size_t off = 0; + const char *extd_sense_fmt = NULL; + const char *extd_sense_str = scsi_extd_sense_format(asc, ascq, + &extd_sense_fmt); + + if (extd_sense_str) { + off = scnprintf(buffer, buf_len, "Add. Sense: %s", + extd_sense_str); + if (extd_sense_fmt) + off += scnprintf(buffer + off, buf_len - off, + "(%s%x)", extd_sense_fmt, ascq); + } else { + if (asc >= 0x80) + off = scnprintf(buffer, buf_len, "<>"); + off += scnprintf(buffer + off, buf_len - off, + "ASC=0x%x ", asc); + if (ascq >= 0x80) + off += scnprintf(buffer + off, buf_len - off, + "<>"); + off += scnprintf(buffer + off, buf_len - off, + "ASCQ=0x%x ", ascq); + } + return off; +} + +static size_t +scsi_format_sense_hdr(char *buffer, size_t buf_len, + const struct scsi_sense_hdr *sshdr) +{ + const char *sense_txt; + size_t off; + + off = scnprintf(buffer, buf_len, "Sense Key : "); + sense_txt = scsi_sense_key_string(sshdr->sense_key); + if (sense_txt) + off += scnprintf(buffer + off, buf_len - off, + "%s ", sense_txt); + else + off += scnprintf(buffer + off, buf_len - off, + "0x%x ", sshdr->sense_key); + off += scnprintf(buffer + off, buf_len - off, + scsi_sense_is_deferred(sshdr) ? "[deferred] " : "[current] "); + + if (sshdr->response_code >= 0x72) + off += scnprintf(buffer + off, buf_len - off, "[descriptor] "); + return off; +} + +static void +scsi_log_dump_sense(const struct scsi_device *sdev, const char *name, int tag, + const unsigned char *sense_buffer, int sense_len) +{ + char *logbuf; + size_t logbuf_len; + int i; + + logbuf = scsi_log_reserve_buffer(&logbuf_len); + if (!logbuf) + return; + + for (i = 0; i < sense_len; i += 16) { + int len = min(sense_len - i, 16); + size_t off; + + off = sdev_format_header(logbuf, logbuf_len, + name, tag); + hex_dump_to_buffer(&sense_buffer[i], len, 16, 1, + logbuf + off, logbuf_len - off, + false); + dev_printk(KERN_INFO, &sdev->sdev_gendev, logbuf); + } + scsi_log_release_buffer(logbuf); +} + +static void +scsi_log_print_sense_hdr(const struct scsi_device *sdev, const char *name, + int tag, const struct scsi_sense_hdr *sshdr) +{ + char *logbuf; + size_t off, logbuf_len; + + logbuf = scsi_log_reserve_buffer(&logbuf_len); + if (!logbuf) + return; + off = sdev_format_header(logbuf, logbuf_len, name, tag); + off += scsi_format_sense_hdr(logbuf + off, logbuf_len - off, sshdr); + dev_printk(KERN_INFO, &sdev->sdev_gendev, logbuf); + scsi_log_release_buffer(logbuf); + + logbuf = scsi_log_reserve_buffer(&logbuf_len); + if (!logbuf) + return; + off = sdev_format_header(logbuf, logbuf_len, name, tag); + off += scsi_format_extd_sense(logbuf + off, logbuf_len - off, + sshdr->asc, sshdr->ascq); + dev_printk(KERN_INFO, &sdev->sdev_gendev, logbuf); + scsi_log_release_buffer(logbuf); +} + +static void +scsi_log_print_sense(const struct scsi_device *sdev, const char *name, int tag, + const unsigned char *sense_buffer, int sense_len) +{ + struct scsi_sense_hdr sshdr; + + if (scsi_normalize_sense(sense_buffer, sense_len, &sshdr)) + scsi_log_print_sense_hdr(sdev, name, tag, &sshdr); + else + scsi_log_dump_sense(sdev, name, tag, sense_buffer, sense_len); +} + +/* + * Print normalized SCSI sense header with a prefix. + */ +void +scsi_print_sense_hdr(const struct scsi_device *sdev, const char *name, + const struct scsi_sense_hdr *sshdr) +{ + scsi_log_print_sense_hdr(sdev, name, -1, sshdr); +} +EXPORT_SYMBOL(scsi_print_sense_hdr); + +/* Normalize and print sense buffer with name prefix */ +void __scsi_print_sense(const struct scsi_device *sdev, const char *name, + const unsigned char *sense_buffer, int sense_len) +{ + scsi_log_print_sense(sdev, name, -1, sense_buffer, sense_len); +} +EXPORT_SYMBOL(__scsi_print_sense); + +/* Normalize and print sense buffer in SCSI command */ +void scsi_print_sense(const struct scsi_cmnd *cmd) +{ + scsi_log_print_sense(cmd->device, scmd_name(cmd), cmd->request->tag, + cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE); +} +EXPORT_SYMBOL(scsi_print_sense); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 3995169..ebf35cb6 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -3318,11 +3318,8 @@ module_exit(exit_sd); static void sd_print_sense_hdr(struct scsi_disk *sdkp, struct scsi_sense_hdr *sshdr) { - scsi_show_sense_hdr(sdkp->device, - sdkp->disk ? sdkp->disk->disk_name : NULL, sshdr); - scsi_show_extd_sense(sdkp->device, - sdkp->disk ? sdkp->disk->disk_name : NULL, - sshdr->asc, sshdr->ascq); + scsi_print_sense_hdr(sdkp->device, + sdkp->disk ? sdkp->disk->disk_name : NULL, sshdr); } static void sd_print_result(const struct scsi_disk *sdkp, const char *msg, diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 2e4614b..5d60a86 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4714,10 +4714,8 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba, sdev_printk(KERN_WARNING, sdp, "START_STOP failed for power mode: %d, result %x\n", pwr_mode, ret); - if (driver_byte(ret) & DRIVER_SENSE) { - scsi_show_sense_hdr(sdp, NULL, &sshdr); - scsi_show_extd_sense(sdp, NULL, sshdr.asc, sshdr.ascq); - } + if (driver_byte(ret) & DRIVER_SENSE) + scsi_print_sense_hdr(sdp, NULL, &sshdr); } if (!ret) -- cgit v0.10.2 From 026f8da8da4ce3423bf89e8e9091f55ae3863eda Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Thu, 8 Jan 2015 07:43:47 +0100 Subject: scsi: use per-cpu buffer for formatting scsi_print_result() Convert scsi_print_result() to use the per-cpu buffer for decoding the command result and disposition. Tested-by: Robert Elliott Reviewed-by: Robert Elliott Signed-off-by: Hannes Reinecke Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c index a23ef75..956fbdd 100644 --- a/drivers/scsi/constants.c +++ b/drivers/scsi/constants.c @@ -1316,25 +1316,3 @@ const char *scsi_mlreturn_string(int result) return NULL; } EXPORT_SYMBOL(scsi_mlreturn_string); - -void scsi_print_result(struct scsi_cmnd *cmd, const char *msg, int disposition) -{ - const char *mlret_string = scsi_mlreturn_string(disposition); - const char *hb_string = scsi_hostbyte_string(cmd->result); - const char *db_string = scsi_driverbyte_string(cmd->result); - - if (hb_string || db_string) - scmd_printk(KERN_INFO, cmd, - "%s%s Result: hostbyte=%s driverbyte=%s", - msg ? msg : "", - mlret_string ? mlret_string : "UNKNOWN", - hb_string ? hb_string : "invalid", - db_string ? db_string : "invalid"); - else - scmd_printk(KERN_INFO, cmd, - "%s%s Result: hostbyte=0x%02x driverbyte=0x%02x", - msg ? msg : "", - mlret_string ? mlret_string : "UNKNOWN", - host_byte(cmd->result), driver_byte(cmd->result)); -} -EXPORT_SYMBOL(scsi_print_result); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index e028854..7f028cb 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -572,7 +572,7 @@ void scsi_log_completion(struct scsi_cmnd *cmd, int disposition) SCSI_LOG_MLCOMPLETE_BITS); if (((level > 0) && (cmd->result || disposition != SUCCESS)) || (level > 1)) { - scsi_print_result(cmd, "Done: ", disposition); + scsi_print_result(cmd, "Done", disposition); scsi_print_command(cmd); if (status_byte(cmd->result) & CHECK_CONDITION) scsi_print_sense(cmd); diff --git a/drivers/scsi/scsi_logging.c b/drivers/scsi/scsi_logging.c index c7cba31..6128303 100644 --- a/drivers/scsi/scsi_logging.c +++ b/drivers/scsi/scsi_logging.c @@ -428,3 +428,62 @@ void scsi_print_sense(const struct scsi_cmnd *cmd) cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE); } EXPORT_SYMBOL(scsi_print_sense); + +void scsi_print_result(const struct scsi_cmnd *cmd, const char *msg, + int disposition) +{ + char *logbuf; + size_t off, logbuf_len; + const char *mlret_string = scsi_mlreturn_string(disposition); + const char *hb_string = scsi_hostbyte_string(cmd->result); + const char *db_string = scsi_driverbyte_string(cmd->result); + + logbuf = scsi_log_reserve_buffer(&logbuf_len); + if (!logbuf) + return; + + off = sdev_format_header(logbuf, logbuf_len, + scmd_name(cmd), cmd->request->tag); + + if (off >= logbuf_len) + goto out_printk; + + if (msg) { + off += scnprintf(logbuf + off, logbuf_len - off, + "%s: ", msg); + if (WARN_ON(off >= logbuf_len)) + goto out_printk; + } + if (mlret_string) + off += scnprintf(logbuf + off, logbuf_len - off, + "%s ", mlret_string); + else + off += scnprintf(logbuf + off, logbuf_len - off, + "UNKNOWN(0x%02x) ", disposition); + if (WARN_ON(off >= logbuf_len)) + goto out_printk; + + off += scnprintf(logbuf + off, logbuf_len - off, "Result: "); + if (WARN_ON(off >= logbuf_len)) + goto out_printk; + + if (hb_string) + off += scnprintf(logbuf + off, logbuf_len - off, + "hostbyte=%s ", hb_string); + else + off += scnprintf(logbuf + off, logbuf_len - off, + "hostbyte=0x%02x ", host_byte(cmd->result)); + if (WARN_ON(off >= logbuf_len)) + goto out_printk; + + if (db_string) + off += scnprintf(logbuf + off, logbuf_len - off, + "driverbyte=%s", db_string); + else + off += scnprintf(logbuf + off, logbuf_len - off, + "driverbyte=0x%02x", driver_byte(cmd->result)); +out_printk: + dev_printk(KERN_INFO, &cmd->device->sdev_gendev, logbuf); + scsi_log_release_buffer(logbuf); +} +EXPORT_SYMBOL(scsi_print_result); diff --git a/include/scsi/scsi_dbg.h b/include/scsi/scsi_dbg.h index c7ed7b8..365b674 100644 --- a/include/scsi/scsi_dbg.h +++ b/include/scsi/scsi_dbg.h @@ -21,7 +21,7 @@ extern void scsi_print_sense(const struct scsi_cmnd *); extern void __scsi_print_sense(const struct scsi_device *, const char *name, const unsigned char *sense_buffer, int sense_len); -extern void scsi_print_result(struct scsi_cmnd *, const char *, int); +extern void scsi_print_result(const struct scsi_cmnd *, const char *, int); extern const char *scsi_hostbyte_string(int); extern const char *scsi_driverbyte_string(int); extern const char *scsi_mlreturn_string(int); -- cgit v0.10.2 From 2dd951ecd511756f405ae9324db87bb0159f6225 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Thu, 8 Jan 2015 07:43:48 +0100 Subject: scsi: Conditionally compile in constants.c Instead of having constants.c littered with ifdef statements we should be moving dummy functions into the header and condintionally compile in constants.c if selected. And update the Kconfig description to reflect the actual size difference. Suggested-by: Christoph Hellwig Tested-by: Robert Elliott Reviewed-by: Robert Elliott Signed-off-by: Hannes Reinecke Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 9c92f41..b021bcb 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -201,12 +201,12 @@ config SCSI_ENCLOSURE certain enclosure conditions to be reported and is not required. config SCSI_CONSTANTS - bool "Verbose SCSI error reporting (kernel size +=12K)" + bool "Verbose SCSI error reporting (kernel size +=75K)" depends on SCSI help The error messages regarding your SCSI hardware will be easier to understand if you say Y here; it will enlarge your kernel by about - 12 KB. If in doubt, say Y. + 75 KB. If in doubt, say Y. config SCSI_LOGGING bool "SCSI logging facility" diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 447c2d2..dee160a 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -159,9 +159,9 @@ obj-$(CONFIG_SCSI_OSD_INITIATOR) += osd/ # This goes last, so that "real" scsi devices probe earlier obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o - -scsi_mod-y += scsi.o hosts.o scsi_ioctl.o constants.o \ +scsi_mod-y += scsi.o hosts.o scsi_ioctl.o \ scsicam.o scsi_error.o scsi_lib.o +scsi_mod-$(CONFIG_SCSI_CONSTANTS) += constants.o scsi_mod-$(CONFIG_SCSI_DMA) += scsi_lib_dma.o scsi_mod-y += scsi_scan.o scsi_sysfs.o scsi_devinfo.o scsi_mod-$(CONFIG_SCSI_NETLINK) += scsi_netlink.o diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c index 956fbdd..fa09d4b 100644 --- a/drivers/scsi/constants.c +++ b/drivers/scsi/constants.c @@ -18,8 +18,6 @@ #include #include - - /* Commands with service actions that change the command name */ #define THIRD_PARTY_COPY_OUT 0x83 #define THIRD_PARTY_COPY_IN 0x84 @@ -35,7 +33,6 @@ struct value_name_pair { const char * name; }; -#ifdef CONFIG_SCSI_CONSTANTS static const char * cdb_byte0_names[] = { /* 00-03 */ "Test Unit Ready", "Rezero Unit/Rewind", NULL, "Request Sense", /* 04-07 */ "Format Unit/Medium", "Read Block Limits", NULL, @@ -259,26 +256,6 @@ static struct sa_name_list sa_names_arr[] = { {0, NULL, 0}, }; -#else /* ifndef CONFIG_SCSI_CONSTANTS */ -static const char *cdb_byte0_names[0]; - -static struct sa_name_list sa_names_arr[] = { - {VARIABLE_LENGTH_CMD, NULL, 0}, - {MAINTENANCE_IN, NULL, 0}, - {MAINTENANCE_OUT, NULL, 0}, - {PERSISTENT_RESERVE_IN, NULL, 0}, - {PERSISTENT_RESERVE_OUT, NULL, 0}, - {SERVICE_ACTION_IN_12, NULL, 0}, - {SERVICE_ACTION_OUT_12, NULL, 0}, - {SERVICE_ACTION_BIDIRECTIONAL, NULL, 0}, - {SERVICE_ACTION_IN_16, NULL, 0}, - {SERVICE_ACTION_OUT_16, NULL, 0}, - {THIRD_PARTY_COPY_IN, NULL, 0}, - {THIRD_PARTY_COPY_OUT, NULL, 0}, - {0, NULL, 0}, -}; -#endif /* CONFIG_SCSI_CONSTANTS */ - bool scsi_opcode_sa_name(int opcode, int service_action, const char **cdb_name, const char **sa_name) { @@ -313,8 +290,6 @@ bool scsi_opcode_sa_name(int opcode, int service_action, return true; } -#ifdef CONFIG_SCSI_CONSTANTS - struct error_info { unsigned short code12; /* 0x0302 looks better than 0x03,0x02 */ const char * text; @@ -1203,15 +1178,12 @@ static const char * const snstext[] = { "Completed", /* F: command completed sense data reported, may occur for successful command */ }; -#endif /* Get sense key string or NULL if not available */ const char * scsi_sense_key_string(unsigned char key) { -#ifdef CONFIG_SCSI_CONSTANTS if (key <= 0xE) return snstext[key]; -#endif return NULL; } EXPORT_SYMBOL(scsi_sense_key_string); @@ -1223,7 +1195,6 @@ EXPORT_SYMBOL(scsi_sense_key_string); const char * scsi_extd_sense_format(unsigned char asc, unsigned char ascq, const char **fmt) { -#ifdef CONFIG_SCSI_CONSTANTS int i; unsigned short code = ((asc << 8) | ascq); @@ -1239,15 +1210,10 @@ scsi_extd_sense_format(unsigned char asc, unsigned char ascq, const char **fmt) return additional2[i].str; } } -#else - *fmt = NULL; -#endif return NULL; } EXPORT_SYMBOL(scsi_extd_sense_format); -#ifdef CONFIG_SCSI_CONSTANTS - static const char * const hostbyte_table[]={ "DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT", "DID_BAD_TARGET", "DID_ABORT", "DID_PARITY", "DID_ERROR", "DID_RESET", "DID_BAD_INTR", @@ -1259,17 +1225,13 @@ static const char * const driverbyte_table[]={ "DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", "DRIVER_ERROR", "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD", "DRIVER_SENSE"}; -#endif - const char *scsi_hostbyte_string(int result) { const char *hb_string = NULL; -#ifdef CONFIG_SCSI_CONSTANTS int hb = host_byte(result); if (hb < ARRAY_SIZE(hostbyte_table)) hb_string = hostbyte_table[hb]; -#endif return hb_string; } EXPORT_SYMBOL(scsi_hostbyte_string); @@ -1277,17 +1239,14 @@ EXPORT_SYMBOL(scsi_hostbyte_string); const char *scsi_driverbyte_string(int result) { const char *db_string = NULL; -#ifdef CONFIG_SCSI_CONSTANTS int db = driver_byte(result); if (db < ARRAY_SIZE(driverbyte_table)) db_string = driverbyte_table[db]; -#endif return db_string; } EXPORT_SYMBOL(scsi_driverbyte_string); -#ifdef CONFIG_SCSI_CONSTANTS #define scsi_mlreturn_name(result) { result, #result } static const struct value_name_pair scsi_mlreturn_arr[] = { scsi_mlreturn_name(NEEDS_RETRY), @@ -1300,11 +1259,9 @@ static const struct value_name_pair scsi_mlreturn_arr[] = { scsi_mlreturn_name(SCSI_RETURN_NOT_HANDLED), scsi_mlreturn_name(FAST_IO_FAIL) }; -#endif const char *scsi_mlreturn_string(int result) { -#ifdef CONFIG_SCSI_CONSTANTS const struct value_name_pair *arr = scsi_mlreturn_arr; int k; @@ -1312,7 +1269,6 @@ const char *scsi_mlreturn_string(int result) if (result == arr->value) return arr->name; } -#endif return NULL; } EXPORT_SYMBOL(scsi_mlreturn_string); diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index e999496e..471ab06 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -47,6 +47,7 @@ #include +#include #include #include #include diff --git a/include/scsi/scsi_dbg.h b/include/scsi/scsi_dbg.h index 365b674..f8170e9 100644 --- a/include/scsi/scsi_dbg.h +++ b/include/scsi/scsi_dbg.h @@ -7,7 +7,6 @@ struct scsi_sense_hdr; #define SCSI_LOG_BUFSIZE 128 -extern bool scsi_opcode_sa_name(int, int, const char **, const char **); extern void scsi_print_command(struct scsi_cmnd *); extern size_t __scsi_format_command(char *, size_t, const unsigned char *, size_t); @@ -22,11 +21,72 @@ extern void __scsi_print_sense(const struct scsi_device *, const char *name, const unsigned char *sense_buffer, int sense_len); extern void scsi_print_result(const struct scsi_cmnd *, const char *, int); -extern const char *scsi_hostbyte_string(int); -extern const char *scsi_driverbyte_string(int); -extern const char *scsi_mlreturn_string(int); + +#ifdef CONFIG_SCSI_CONSTANTS +extern bool scsi_opcode_sa_name(int, int, const char **, const char **); extern const char *scsi_sense_key_string(unsigned char); extern const char *scsi_extd_sense_format(unsigned char, unsigned char, const char **); +extern const char *scsi_mlreturn_string(int); +extern const char *scsi_hostbyte_string(int); +extern const char *scsi_driverbyte_string(int); +#else +static inline bool +scsi_opcode_sa_name(int cmd, int sa, + const char **cdb_name, const char **sa_name) +{ + *cdb_name = NULL; + switch (cmd) { + case VARIABLE_LENGTH_CMD: + case MAINTENANCE_IN: + case MAINTENANCE_OUT: + case PERSISTENT_RESERVE_IN: + case PERSISTENT_RESERVE_OUT: + case SERVICE_ACTION_IN_12: + case SERVICE_ACTION_OUT_12: + case SERVICE_ACTION_BIDIRECTIONAL: + case SERVICE_ACTION_IN_16: + case SERVICE_ACTION_OUT_16: + case EXTENDED_COPY: + case RECEIVE_COPY_RESULTS: + *sa_name = NULL; + return true; + default: + return false; + } +} + +static inline const char * +scsi_sense_key_string(unsigned char key) +{ + return NULL; +} + +static inline const char * +scsi_extd_sense_format(unsigned char asc, unsigned char ascq, const char **fmt) +{ + *fmt = NULL; + return NULL; +} + +static inline const char * +scsi_mlreturn_string(int result) +{ + return NULL; +} + +static inline const char * +scsi_hostbyte_string(int result) +{ + return NULL; +} + +static inline const char * +scsi_driverbyte_string(int result) +{ + return NULL; +} + +#endif #endif /* _SCSI_SCSI_DBG_H */ -- cgit v0.10.2 From a4a6afb477a9e724a6ff7204e4199f0c2cd97d6a Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Thu, 8 Jan 2015 07:43:49 +0100 Subject: scsi: Do not display buffer pointers in scsi_log_send() scsi_log_send() would display buffer pointer for higher logging levels. This is not only of questionable value but also exposes kernel pointer to userspace, which is discouraged in some setups. So drop this message altogether. Tested-by: Robert Elliott Reviewed-by: Robert Elliott Signed-off-by: Hannes Reinecke Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 7f028cb..08c90a7 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -531,7 +531,7 @@ void scsi_log_send(struct scsi_cmnd *cmd) * * 3: same as 2 * - * 4: same as 3 plus dump extra junk + * 4: same as 3 */ if (unlikely(scsi_logging_level)) { level = SCSI_LOG_LEVEL(SCSI_LOG_MLQUEUE_SHIFT, @@ -540,13 +540,6 @@ void scsi_log_send(struct scsi_cmnd *cmd) scmd_printk(KERN_INFO, cmd, "Send: scmd 0x%p\n", cmd); scsi_print_command(cmd); - if (level > 3) { - printk(KERN_INFO "buffer = 0x%p, bufflen = %d," - " queuecommand 0x%p\n", - scsi_sglist(cmd), scsi_bufflen(cmd), - cmd->device->host->hostt->queuecommand); - - } } } } -- cgit v0.10.2 From 470613b462a767aa8cca1e590928fcbc55bf39e5 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Thu, 8 Jan 2015 07:43:50 +0100 Subject: scsi: do not display kernel pointer in message logs It is not good practice to display the kernel pointer in any message logs, and it doesn't display any additional information. And as we know have block-layer assigned tags we can use them to differentiate the messages. So remove any pointer references from the displayed messages. Signed-off-by: Hannes Reinecke Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 9ac9eb1..4cdaffc 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -124,41 +124,37 @@ scmd_eh_abort_handler(struct work_struct *work) if (scsi_host_eh_past_deadline(sdev->host)) { SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_INFO, scmd, - "scmd %p eh timeout, not aborting\n", - scmd)); + "eh timeout, not aborting\n")); } else { SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_INFO, scmd, - "aborting command %p\n", scmd)); + "aborting command\n")); rtn = scsi_try_to_abort_cmd(sdev->host->hostt, scmd); if (rtn == SUCCESS) { set_host_byte(scmd, DID_TIME_OUT); if (scsi_host_eh_past_deadline(sdev->host)) { SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_INFO, scmd, - "scmd %p eh timeout, " - "not retrying aborted " - "command\n", scmd)); + "eh timeout, not retrying " + "aborted command\n")); } else if (!scsi_noretry_cmd(scmd) && (++scmd->retries <= scmd->allowed)) { SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_WARNING, scmd, - "scmd %p retry " - "aborted command\n", scmd)); + "retry aborted command\n")); scsi_queue_insert(scmd, SCSI_MLQUEUE_EH_RETRY); return; } else { SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_WARNING, scmd, - "scmd %p finish " - "aborted command\n", scmd)); + "finish aborted command\n")); scsi_finish_command(scmd); return; } } else { SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_INFO, scmd, - "scmd %p abort %s\n", scmd, + "cmd abort %s\n", (rtn == FAST_IO_FAIL) ? "not send" : "failed")); } @@ -167,8 +163,7 @@ scmd_eh_abort_handler(struct work_struct *work) if (!scsi_eh_scmd_add(scmd, 0)) { SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_WARNING, scmd, - "scmd %p terminate " - "aborted command\n", scmd)); + "terminate aborted command\n")); set_host_byte(scmd, DID_TIME_OUT); scsi_finish_command(scmd); } @@ -194,7 +189,7 @@ scsi_abort_command(struct scsi_cmnd *scmd) scmd->eh_eflags &= ~SCSI_EH_ABORT_SCHEDULED; SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_INFO, scmd, - "scmd %p previous abort failed\n", scmd)); + "previous abort failed\n")); BUG_ON(delayed_work_pending(&scmd->abort_work)); return FAILED; } @@ -208,8 +203,7 @@ scsi_abort_command(struct scsi_cmnd *scmd) spin_unlock_irqrestore(shost->host_lock, flags); SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_INFO, scmd, - "scmd %p not aborting, host in recovery\n", - scmd)); + "not aborting, host in recovery\n")); return FAILED; } @@ -219,8 +213,7 @@ scsi_abort_command(struct scsi_cmnd *scmd) scmd->eh_eflags |= SCSI_EH_ABORT_SCHEDULED; SCSI_LOG_ERROR_RECOVERY(3, - scmd_printk(KERN_INFO, scmd, - "scmd %p abort scheduled\n", scmd)); + scmd_printk(KERN_INFO, scmd, "abort scheduled\n")); queue_delayed_work(shost->tmf_work_q, &scmd->abort_work, HZ / 100); return SUCCESS; } @@ -737,8 +730,7 @@ static void scsi_eh_done(struct scsi_cmnd *scmd) struct completion *eh_action; SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_INFO, scmd, - "%s scmd: %p result: %x\n", - __func__, scmd, scmd->result)); + "%s result: %x\n", __func__, scmd->result)); eh_action = scmd->device->host->eh_action; if (eh_action) @@ -1053,8 +1045,8 @@ retry: scsi_log_completion(scmd, rtn); SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_INFO, scmd, - "%s: scmd: %p, timeleft: %ld\n", - __func__, scmd, timeleft)); + "%s timeleft: %ld\n", + __func__, timeleft)); /* * If there is time left scsi_eh_done got called, and we will examine @@ -1193,8 +1185,7 @@ int scsi_eh_get_sense(struct list_head *work_q, continue; SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_INFO, scmd, - "sense requested for %p result %x\n", - scmd, scmd->result)); + "sense requested, result %x\n", scmd->result)); SCSI_LOG_ERROR_RECOVERY(3, scsi_print_sense(scmd)); rtn = scsi_decide_disposition(scmd); @@ -1236,7 +1227,7 @@ retry_tur: scmd->device->eh_timeout, 0); SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_INFO, scmd, - "%s: scmd %p rtn %x\n", __func__, scmd, rtn)); + "%s return: %x\n", __func__, rtn)); switch (rtn) { case NEEDS_RETRY: @@ -2093,8 +2084,8 @@ void scsi_eh_flush_done_q(struct list_head *done_q) (++scmd->retries <= scmd->allowed)) { SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_INFO, scmd, - "%s: flush retry cmd: %p\n", - current->comm, scmd)); + "%s: flush retry cmd\n", + current->comm)); scsi_queue_insert(scmd, SCSI_MLQUEUE_EH_RETRY); } else { /* @@ -2106,8 +2097,8 @@ void scsi_eh_flush_done_q(struct list_head *done_q) scmd->result |= (DRIVER_TIMEOUT << 24); SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_INFO, scmd, - "%s: flush finish cmd: %p\n", - current->comm, scmd)); + "%s: flush finish cmd\n", + current->comm)); scsi_finish_command(scmd); } } -- cgit v0.10.2 From 200aed582d6170a2687cd69095469b663f69f16f Mon Sep 17 00:00:00 2001 From: "Sumit.Saxena@avagotech.com" Date: Mon, 5 Jan 2015 20:05:58 +0530 Subject: megaraid_sas: endianness related bug fixes and code optimization This patch addresses below issues: 1) Few endianness bug fixes. 2) Break the iteration after (MAX_LOGICAL_DRIVES_EXT - 1)), instead of MAX_LOGICAL_DRIVES_EXT. 3) Optimization in MFI INIT frame before firing. 4) MFI IO frame should be 256bytes aligned. Code is optimized to reduce the size of frame for fusion adapters and make the MFI frame size calculation a bit transparent and readable. Cc: Signed-off-by: Kashyap Desai Signed-off-by: Sumit Saxena Signed-off-by: Chaitra Basappa Reviewed-by: Martin K. Petersen Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index ff283d2..401ed67 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -3547,7 +3547,6 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) int i; u32 max_cmd; u32 sge_sz; - u32 sgl_sz; u32 total_sz; u32 frame_count; struct megasas_cmd *cmd; @@ -3566,24 +3565,23 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) } /* - * Calculated the number of 64byte frames required for SGL - */ - sgl_sz = sge_sz * instance->max_num_sge; - frame_count = (sgl_sz + MEGAMFI_FRAME_SIZE - 1) / MEGAMFI_FRAME_SIZE; - frame_count = 15; - - /* - * We need one extra frame for the MFI command + * For MFI controllers. + * max_num_sge = 60 + * max_sge_sz = 16 byte (sizeof megasas_sge_skinny) + * Total 960 byte (15 MFI frame of 64 byte) + * + * Fusion adapter require only 3 extra frame. + * max_num_sge = 16 (defined as MAX_IOCTL_SGE) + * max_sge_sz = 12 byte (sizeof megasas_sge64) + * Total 192 byte (3 MFI frame of 64 byte) */ - frame_count++; - + frame_count = instance->ctrl_context ? (3 + 1) : (15 + 1); total_sz = MEGAMFI_FRAME_SIZE * frame_count; /* * Use DMA pool facility provided by PCI layer */ instance->frame_dma_pool = pci_pool_create("megasas frame pool", - instance->pdev, total_sz, 64, - 0); + instance->pdev, total_sz, 256, 0); if (!instance->frame_dma_pool) { printk(KERN_DEBUG "megasas: failed to setup frame pool\n"); diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c index 460c6a3..7cae1c2 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fp.c +++ b/drivers/scsi/megaraid/megaraid_sas_fp.c @@ -172,6 +172,7 @@ void MR_PopulateDrvRaidMap(struct megasas_instance *instance) struct MR_FW_RAID_MAP_ALL *fw_map_old = NULL; struct MR_FW_RAID_MAP *pFwRaidMap = NULL; int i; + u16 ld_count; struct MR_DRV_RAID_MAP_ALL *drv_map = @@ -191,9 +192,10 @@ void MR_PopulateDrvRaidMap(struct megasas_instance *instance) fw_map_old = (struct MR_FW_RAID_MAP_ALL *) fusion->ld_map[(instance->map_id & 1)]; pFwRaidMap = &fw_map_old->raidMap; + ld_count = (u16)le32_to_cpu(pFwRaidMap->ldCount); #if VD_EXT_DEBUG - for (i = 0; i < le16_to_cpu(pFwRaidMap->ldCount); i++) { + for (i = 0; i < ld_count; i++) { dev_dbg(&instance->pdev->dev, "(%d) :Index 0x%x " "Target Id 0x%x Seq Num 0x%x Size 0/%llx\n", instance->unique_id, i, @@ -205,12 +207,12 @@ void MR_PopulateDrvRaidMap(struct megasas_instance *instance) memset(drv_map, 0, fusion->drv_map_sz); pDrvRaidMap->totalSize = pFwRaidMap->totalSize; - pDrvRaidMap->ldCount = (__le16)pFwRaidMap->ldCount; + pDrvRaidMap->ldCount = (__le16)cpu_to_le16(ld_count); pDrvRaidMap->fpPdIoTimeoutSec = pFwRaidMap->fpPdIoTimeoutSec; for (i = 0; i < MAX_RAIDMAP_LOGICAL_DRIVES + MAX_RAIDMAP_VIEWS; i++) pDrvRaidMap->ldTgtIdToLd[i] = (u8)pFwRaidMap->ldTgtIdToLd[i]; - for (i = 0; i < le16_to_cpu(pDrvRaidMap->ldCount); i++) { + for (i = 0; i < ld_count; i++) { pDrvRaidMap->ldSpanMap[i] = pFwRaidMap->ldSpanMap[i]; #if VD_EXT_DEBUG dev_dbg(&instance->pdev->dev, @@ -252,7 +254,7 @@ u8 MR_ValidateMapInfo(struct megasas_instance *instance) struct LD_LOAD_BALANCE_INFO *lbInfo; PLD_SPAN_INFO ldSpanInfo; struct MR_LD_RAID *raid; - int ldCount, num_lds; + u16 ldCount, num_lds; u16 ld; u32 expected_size; @@ -356,7 +358,7 @@ static int getSpanInfo(struct MR_DRV_RAID_MAP_ALL *map, for (ldCount = 0; ldCount < MAX_LOGICAL_DRIVES_EXT; ldCount++) { ld = MR_TargetIdToLdGet(ldCount, map); - if (ld >= MAX_LOGICAL_DRIVES_EXT) + if (ld >= (MAX_LOGICAL_DRIVES_EXT - 1)) continue; raid = MR_LdRaidGet(ld, map); dev_dbg(&instance->pdev->dev, "LD %x: span_depth=%x\n", @@ -1157,7 +1159,7 @@ void mr_update_span_set(struct MR_DRV_RAID_MAP_ALL *map, for (ldCount = 0; ldCount < MAX_LOGICAL_DRIVES_EXT; ldCount++) { ld = MR_TargetIdToLdGet(ldCount, map); - if (ld >= MAX_LOGICAL_DRIVES_EXT) + if (ld >= (MAX_LOGICAL_DRIVES_EXT - 1)) continue; raid = MR_LdRaidGet(ld, map); for (element = 0; element < MAX_QUAD_DEPTH; element++) { diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 71557f6..b5362c1 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -698,12 +698,11 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) cpu_to_le32(lower_32_bits(ioc_init_handle)); init_frame->data_xfer_len = cpu_to_le32(sizeof(struct MPI2_IOC_INIT_REQUEST)); - req_desc.Words = 0; + req_desc.u.low = cpu_to_le32(lower_32_bits(cmd->frame_phys_addr)); + req_desc.u.high = cpu_to_le32(upper_32_bits(cmd->frame_phys_addr)); req_desc.MFAIo.RequestFlags = (MEGASAS_REQ_DESCRIPT_FLAGS_MFA << - MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); - cpu_to_le32s((u32 *)&req_desc.MFAIo); - req_desc.Words |= cpu_to_le64(cmd->frame_phys_addr); + MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); /* * disable the intr before firing the init frame diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.h b/drivers/scsi/megaraid/megaraid_sas_fusion.h index 5ab7dae..56e6db2 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.h +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.h @@ -306,14 +306,9 @@ struct MPI2_RAID_SCSI_IO_REQUEST { * MPT RAID MFA IO Descriptor. */ struct MEGASAS_RAID_MFA_IO_REQUEST_DESCRIPTOR { -#if defined(__BIG_ENDIAN_BITFIELD) - u32 MessageAddress1:24; /* bits 31:8*/ - u32 RequestFlags:8; -#else u32 RequestFlags:8; - u32 MessageAddress1:24; /* bits 31:8*/ -#endif - u32 MessageAddress2; /* bits 61:32 */ + u32 MessageAddress1:24; + u32 MessageAddress2; }; /* Default Request Descriptor */ -- cgit v0.10.2 From 7497cde883b184ead109652f236df98d78090a90 Mon Sep 17 00:00:00 2001 From: "Sumit.Saxena@avagotech.com" Date: Mon, 5 Jan 2015 20:06:03 +0530 Subject: megaraid_sas: add support for secure JBOD This patch adds support for Secure Encrypting Drives (SED) in JBOD mode: 1) If the firmware supports SED JBOD, all non read/write commands to JBODs will be sent via firmware path, and read/write commands to JBODs will be sent via fastpath. 2) If the firmware does not support SED JBOD, driver will fall back to the old design, i.e. send all JBOD I/O via fastpath. Signed-off-by: Sumit Saxena Signed-off-by: Chaitra Basappa Reviewed-by: Martin K. Petersen Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index 0d44d91..b8b378d 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -969,7 +969,20 @@ struct megasas_ctrl_info { struct { #if defined(__BIG_ENDIAN_BITFIELD) - u32 reserved:25; + u32 reserved:12; + u32 discardCacheDuringLDDelete:1; + u32 supportSecurityonJBOD:1; + u32 supportCacheBypassModes:1; + u32 supportDisableSESMonitoring:1; + u32 supportForceFlash:1; + u32 supportNVDRAM:1; + u32 supportDrvActivityLEDSetting:1; + u32 supportAllowedOpsforDrvRemoval:1; + u32 supportHOQRebuild:1; + u32 supportForceTo512e:1; + u32 supportNVCacheErase:1; + u32 supportDebugQueue:1; + u32 supportSwZone:1; u32 supportCrashDump:1; u32 supportMaxExtLDs:1; u32 supportT10RebuildAssist:1; @@ -981,9 +994,22 @@ struct megasas_ctrl_info { u32 supportThermalPollInterval:1; u32 supportDisableImmediateIO:1; u32 supportT10RebuildAssist:1; - u32 supportMaxExtLDs:1; - u32 supportCrashDump:1; - u32 reserved:25; + u32 supportMaxExtLDs:1; + u32 supportCrashDump:1; + u32 supportSwZone:1; + u32 supportDebugQueue:1; + u32 supportNVCacheErase:1; + u32 supportForceTo512e:1; + u32 supportHOQRebuild:1; + u32 supportAllowedOpsforDrvRemoval:1; + u32 supportDrvActivityLEDSetting:1; + u32 supportNVDRAM:1; + u32 supportForceFlash:1; + u32 supportDisableSESMonitoring:1; + u32 supportCacheBypassModes:1; + u32 supportSecurityonJBOD:1; + u32 discardCacheDuringLDDelete:1; + u32 reserved:12; #endif } adapterOperations3; @@ -1022,6 +1048,13 @@ enum MR_MFI_MPT_PTHR_FLAGS { MFI_MPT_ATTACHED = 2, }; +enum MR_SCSI_CMD_TYPE { + READ_WRITE_LDIO = 0, + NON_READ_WRITE_LDIO = 1, + READ_WRITE_SYSPDIO = 2, + NON_READ_WRITE_SYSPDIO = 3, +}; + /* Frame Type */ #define IO_FRAME 0 #define PTHRU_FRAME 1 @@ -1194,19 +1227,23 @@ union megasas_sgl_frame { typedef union _MFI_CAPABILITIES { struct { #if defined(__BIG_ENDIAN_BITFIELD) - u32 reserved:27; + u32 reserved:25; + u32 security_protocol_cmds_fw:1; + u32 support_core_affinity:1; u32 support_ndrive_r1_lb:1; u32 support_max_255lds:1; - u32 reserved1:1; + u32 support_fastpath_wb:1; u32 support_additional_msix:1; u32 support_fp_remote_lun:1; #else u32 support_fp_remote_lun:1; u32 support_additional_msix:1; - u32 reserved1:1; + u32 support_fastpath_wb:1; u32 support_max_255lds:1; u32 support_ndrive_r1_lb:1; - u32 reserved:27; + u32 support_core_affinity:1; + u32 security_protocol_cmds_fw:1; + u32 reserved:25; #endif } mfi_capabilities; u32 reg; @@ -1638,13 +1675,14 @@ struct megasas_instance { u32 crash_dump_fw_support; u32 crash_dump_drv_support; u32 crash_dump_app_support; + u32 secure_jbod_support; spinlock_t crashdump_lock; struct megasas_register_set __iomem *reg_set; u32 *reply_post_host_index_addr[MR_MAX_MSIX_REG_ARRAY]; struct megasas_pd_list pd_list[MEGASAS_MAX_PD]; struct megasas_pd_list local_pd_list[MEGASAS_MAX_PD]; - u8 ld_ids[MEGASAS_MAX_LD_IDS]; + u8 ld_ids[MEGASAS_MAX_LD_IDS]; s8 init_id; u16 max_num_sge; @@ -1946,5 +1984,6 @@ void __megasas_return_cmd(struct megasas_instance *instance, void megasas_return_mfi_mpt_pthr(struct megasas_instance *instance, struct megasas_cmd *cmd_mfi, struct megasas_cmd_fusion *cmd_fusion); +int megasas_cmd_type(struct scsi_cmnd *cmd); #endif /*LSI_MEGARAID_SAS_H */ diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 401ed67..9f37dde 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -1417,16 +1417,15 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp, } /** - * megasas_is_ldio - Checks if the cmd is for logical drive + * megasas_cmd_type - Checks if the cmd is for logical drive/sysPD + * and whether it's RW or non RW * @scmd: SCSI command * - * Called by megasas_queue_command to find out if the command to be queued - * is a logical drive command */ -inline int megasas_is_ldio(struct scsi_cmnd *cmd) +inline int megasas_cmd_type(struct scsi_cmnd *cmd) { - if (!MEGASAS_IS_LOGICAL(cmd)) - return 0; + int ret; + switch (cmd->cmnd[0]) { case READ_10: case WRITE_10: @@ -1436,10 +1435,14 @@ inline int megasas_is_ldio(struct scsi_cmnd *cmd) case WRITE_6: case READ_16: case WRITE_16: - return 1; + ret = (MEGASAS_IS_LOGICAL(cmd)) ? + READ_WRITE_LDIO : READ_WRITE_SYSPDIO; + break; default: - return 0; + ret = (MEGASAS_IS_LOGICAL(cmd)) ? + NON_READ_WRITE_LDIO : NON_READ_WRITE_SYSPDIO; } + return ret; } /** @@ -1471,7 +1474,7 @@ megasas_dump_pending_frames(struct megasas_instance *instance) if(!cmd->scmd) continue; printk(KERN_ERR "megasas[%d]: Frame addr :0x%08lx : ",instance->host->host_no,(unsigned long)cmd->frame_phys_addr); - if (megasas_is_ldio(cmd->scmd)){ + if (megasas_cmd_type(cmd->scmd) == READ_WRITE_LDIO) { ldio = (struct megasas_io_frame *)cmd->frame; mfi_sgl = &ldio->sgl; sgcount = ldio->sge_count; @@ -1531,7 +1534,7 @@ megasas_build_and_issue_cmd(struct megasas_instance *instance, /* * Logical drive command */ - if (megasas_is_ldio(scmd)) + if (megasas_cmd_type(scmd) == READ_WRITE_LDIO) frame_count = megasas_build_ldio(instance, scmd, cmd); else frame_count = megasas_build_dcdb(instance, scmd, cmd); @@ -4629,6 +4632,11 @@ static int megasas_init_fw(struct megasas_instance *instance) instance->crash_dump_h); instance->crash_dump_buf = NULL; } + + instance->secure_jbod_support = + ctrl_info->adapterOperations3.supportSecurityonJBOD; + if (instance->secure_jbod_support) + dev_info(&instance->pdev->dev, "Firmware supports Secure JBOD\n"); instance->max_sectors_per_req = instance->max_num_sge * PAGE_SIZE / 512; if (tmp_sectors && (instance->max_sectors_per_req > tmp_sectors)) diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index b5362c1..af5ab9e 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -63,7 +63,6 @@ extern struct megasas_cmd *megasas_get_cmd(struct megasas_instance extern void megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, u8 alt_status); -int megasas_is_ldio(struct scsi_cmnd *cmd); int wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd, int seconds); @@ -196,6 +195,7 @@ inline void megasas_return_cmd_fusion(struct megasas_instance *instance, cmd->scmd = NULL; cmd->sync_cmd_idx = (u32)ULONG_MAX; + memset(cmd->io_request, 0, sizeof(struct MPI2_RAID_SCSI_IO_REQUEST)); list_add(&cmd->list, (&fusion->cmd_pool)->next); spin_unlock_irqrestore(&fusion->mpt_pool_lock, flags); @@ -689,6 +689,8 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) = 1; init_frame->driver_operations.mfi_capabilities.support_ndrive_r1_lb = 1; + init_frame->driver_operations.mfi_capabilities.security_protocol_cmds_fw + = 1; /* Convert capability to LE32 */ cpu_to_le32s((u32 *)&init_frame->driver_operations.mfi_capabilities); @@ -1284,6 +1286,7 @@ megasas_make_sgl_fusion(struct megasas_instance *instance, sgl_ptr = (struct MPI25_IEEE_SGE_CHAIN64 *)cmd->sg_frame; + memset(sgl_ptr, 0, MEGASAS_MAX_SZ_CHAIN_FRAME); } } @@ -1657,6 +1660,8 @@ megasas_build_dcdb_fusion(struct megasas_instance *instance, u32 device_id; struct MPI2_RAID_SCSI_IO_REQUEST *io_request; u16 pd_index = 0; + u16 os_timeout_value; + u16 timeout_limit; struct MR_DRV_RAID_MAP_ALL *local_map_ptr; struct fusion_context *fusion = instance->ctrl_context; u8 span, physArm; @@ -1673,44 +1678,48 @@ megasas_build_dcdb_fusion(struct megasas_instance *instance, io_request->DataLength = cpu_to_le32(scsi_bufflen(scmd)); - - /* Check if this is a system PD I/O */ if (scmd->device->channel < MEGASAS_MAX_PD_CHANNELS && instance->pd_list[pd_index].driveState == MR_PD_STATE_SYSTEM) { - io_request->Function = 0; if (fusion->fast_path_io) io_request->DevHandle = local_map_ptr->raidMap.devHndlInfo[device_id].curDevHdl; - io_request->RaidContext.timeoutValue = - local_map_ptr->raidMap.fpPdIoTimeoutSec; - io_request->RaidContext.regLockFlags = 0; - io_request->RaidContext.regLockRowLBA = 0; - io_request->RaidContext.regLockLength = 0; io_request->RaidContext.RAIDFlags = - MR_RAID_FLAGS_IO_SUB_TYPE_SYSTEM_PD << - MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT; - if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) - io_request->IoFlags |= cpu_to_le16( - MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH); - cmd->request_desc->SCSIIO.RequestFlags = - (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY << - MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); - cmd->request_desc->SCSIIO.DevHandle = - local_map_ptr->raidMap.devHndlInfo[device_id].curDevHdl; + MR_RAID_FLAGS_IO_SUB_TYPE_SYSTEM_PD + << MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT; + cmd->request_desc->SCSIIO.DevHandle = io_request->DevHandle; cmd->request_desc->SCSIIO.MSIxIndex = instance->msix_vectors ? smp_processor_id() % instance->msix_vectors : 0; - /* - * If the command is for the tape device, set the - * FP timeout to the os layer timeout value. - */ - if (scmd->device->type == TYPE_TAPE) { - if ((scmd->request->timeout / HZ) > 0xFFFF) - io_request->RaidContext.timeoutValue = - 0xFFFF; - else - io_request->RaidContext.timeoutValue = - scmd->request->timeout / HZ; + os_timeout_value = scmd->request->timeout / HZ; + + if (instance->secure_jbod_support && + (megasas_cmd_type(scmd) == NON_READ_WRITE_SYSPDIO)) { + /* system pd firmware path */ + io_request->Function = + MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST; + cmd->request_desc->SCSIIO.RequestFlags = + (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO << + MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); + io_request->RaidContext.timeoutValue = + cpu_to_le16(os_timeout_value); + } else { + /* system pd Fast Path */ + io_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; + io_request->RaidContext.regLockFlags = 0; + io_request->RaidContext.regLockRowLBA = 0; + io_request->RaidContext.regLockLength = 0; + timeout_limit = (scmd->device->type == TYPE_DISK) ? + 255 : 0xFFFF; + io_request->RaidContext.timeoutValue = + cpu_to_le16((os_timeout_value > timeout_limit) ? + timeout_limit : os_timeout_value); + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) + io_request->IoFlags |= + cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH); + + cmd->request_desc->SCSIIO.RequestFlags = + (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY << + MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); } } else { if (scmd->device->channel < MEGASAS_MAX_PD_CHANNELS) @@ -1810,7 +1819,7 @@ megasas_build_io_fusion(struct megasas_instance *instance, */ io_request->IoFlags = cpu_to_le16(scp->cmd_len); - if (megasas_is_ldio(scp)) + if (megasas_cmd_type(scp) == READ_WRITE_LDIO) megasas_build_ldio_fusion(instance, scp, cmd); else megasas_build_dcdb_fusion(instance, scp, cmd); -- cgit v0.10.2 From ab2f0608e16d64a23a2dcc8d83b966a0e0a281f3 Mon Sep 17 00:00:00 2001 From: "Sumit.Saxena@avagotech.com" Date: Mon, 5 Jan 2015 20:06:08 +0530 Subject: megaraid_sas: fix the problem of non-existing VD exposed to host This patch will address the issue of SCSI device created at OS level for non existing VD. ldTgtIdtoLd[] array has size 256 for Extended VD firmware and 128 for legacy firmware. Accessing indices beyond array size (OS will send TUR, INQUIRY.. commands upto device index 255), may return valid LD value and that particular SCSI command will be SUCCESS and creating SCSI device for non existing target(VD). For legacy firmware (64 VD firmware), invalidates LD (by setting LD value to 0xff) in LdTgtIdtoLd[] array for device index beyond 127, so that invalid LD(0xff) value should be returned beyond device index beyond 127. Cc: Signed-off-by: Kashyap Desai Signed-off-by: Sumit Saxena Reviewed-by: Martin K. Petersen Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c index 7cae1c2..4f72287 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fp.c +++ b/drivers/scsi/megaraid/megaraid_sas_fp.c @@ -212,6 +212,9 @@ void MR_PopulateDrvRaidMap(struct megasas_instance *instance) for (i = 0; i < MAX_RAIDMAP_LOGICAL_DRIVES + MAX_RAIDMAP_VIEWS; i++) pDrvRaidMap->ldTgtIdToLd[i] = (u8)pFwRaidMap->ldTgtIdToLd[i]; + for (i = (MAX_RAIDMAP_LOGICAL_DRIVES + MAX_RAIDMAP_VIEWS); + i < MAX_LOGICAL_DRIVES_EXT; i++) + pDrvRaidMap->ldTgtIdToLd[i] = 0xff; for (i = 0; i < ld_count; i++) { pDrvRaidMap->ldSpanMap[i] = pFwRaidMap->ldSpanMap[i]; #if VD_EXT_DEBUG diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index af5ab9e..7b23d34 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -1725,9 +1725,19 @@ megasas_build_dcdb_fusion(struct megasas_instance *instance, if (scmd->device->channel < MEGASAS_MAX_PD_CHANNELS) goto NonFastPath; + /* + * For older firmware, Driver should not access ldTgtIdToLd + * beyond index 127 and for Extended VD firmware, ldTgtIdToLd + * should not go beyond 255. + */ + + if ((!fusion->fast_path_io) || + (device_id >= instance->fw_supported_vd_count)) + goto NonFastPath; + ld = MR_TargetIdToLdGet(device_id, local_map_ptr); - if ((ld >= instance->fw_supported_vd_count) || - (!fusion->fast_path_io)) + + if (ld >= instance->fw_supported_vd_count) goto NonFastPath; raid = MR_LdRaidGet(ld, local_map_ptr); -- cgit v0.10.2 From c2ced1719a1b903350955a511e1666e6d05a7f5b Mon Sep 17 00:00:00 2001 From: "Sumit.Saxena@avagotech.com" Date: Mon, 5 Jan 2015 20:06:13 +0530 Subject: megaraid_sas: disable interrupt_mask before enabling hardware interrupts Update driver "mask_interrupts" before enable/disable hardware interrupt in order to avoid missing interrupts because of "mask_interrupts" still set to 1 and hardware interrupts are enabled. Cc: Signed-off-by: Sumit Saxena Signed-off-by: Chaitra Basappa Reviewed-by: Martin K. Petersen Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 7b23d34..075c99e 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -102,6 +102,8 @@ megasas_enable_intr_fusion(struct megasas_instance *instance) { struct megasas_register_set __iomem *regs; regs = instance->reg_set; + + instance->mask_interrupts = 0; /* For Thunderbolt/Invader also clear intr on enable */ writel(~0, ®s->outbound_intr_status); readl(®s->outbound_intr_status); @@ -110,7 +112,6 @@ megasas_enable_intr_fusion(struct megasas_instance *instance) /* Dummy readl to force pci flush */ readl(®s->outbound_intr_mask); - instance->mask_interrupts = 0; } /** -- cgit v0.10.2 From c8dd61eff2780c481fcf919c1572e16e397c714e Mon Sep 17 00:00:00 2001 From: "Sumit.Saxena@avagotech.com" Date: Mon, 5 Jan 2015 20:06:18 +0530 Subject: megaraid_sas: complete outstanding IOCTLs before killing adapter Driver calls megasas_complete_cmd() to call wake_up() for each MFI frame that was issued through the ioctl() interface prior to the kill adapter. This ensures userspace ioctl() system calls issued just before a kill adapter don't get stuck in wait state and IOCTLs are returned to the application. Cc: Signed-off-by: Sumit Saxena Signed-off-by: Chaitra Basappa Reviewed-by: Martin K. Petersen Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 9f37dde..d4e9c4e 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -1692,22 +1692,66 @@ static int megasas_slave_alloc(struct scsi_device *sdev) return 0; } +/* +* megasas_complete_outstanding_ioctls - Complete outstanding ioctls after a +* kill adapter +* @instance: Adapter soft state +* +*/ +void megasas_complete_outstanding_ioctls(struct megasas_instance *instance) +{ + int i; + struct megasas_cmd *cmd_mfi; + struct megasas_cmd_fusion *cmd_fusion; + struct fusion_context *fusion = instance->ctrl_context; + + /* Find all outstanding ioctls */ + if (fusion) { + for (i = 0; i < instance->max_fw_cmds; i++) { + cmd_fusion = fusion->cmd_list[i]; + if (cmd_fusion->sync_cmd_idx != (u32)ULONG_MAX) { + cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx]; + if (cmd_mfi->sync_cmd && + cmd_mfi->frame->hdr.cmd != MFI_CMD_ABORT) + megasas_complete_cmd(instance, + cmd_mfi, DID_OK); + } + } + } else { + for (i = 0; i < instance->max_fw_cmds; i++) { + cmd_mfi = instance->cmd_list[i]; + if (cmd_mfi->sync_cmd && cmd_mfi->frame->hdr.cmd != + MFI_CMD_ABORT) + megasas_complete_cmd(instance, cmd_mfi, DID_OK); + } + } +} + + void megaraid_sas_kill_hba(struct megasas_instance *instance) { + /* Set critical error to block I/O & ioctls in case caller didn't */ + instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR; + /* Wait 1 second to ensure IO or ioctls in build have posted */ + msleep(1000); if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) { - writel(MFI_STOP_ADP, &instance->reg_set->doorbell); + (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) { + writel(MFI_STOP_ADP, + &instance->reg_set->doorbell); /* Flush */ readl(&instance->reg_set->doorbell); if (instance->mpio && instance->requestorId) memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS); } else { - writel(MFI_STOP_ADP, &instance->reg_set->inbound_doorbell); + writel(MFI_STOP_ADP, + &instance->reg_set->inbound_doorbell); } + /* Complete outstanding ioctls when adapter is killed */ + megasas_complete_outstanding_ioctls(instance); } /** @@ -3031,10 +3075,9 @@ megasas_issue_pending_cmds_again(struct megasas_instance *instance) "was tried multiple times during reset." "Shutting down the HBA\n", cmd, cmd->scmd, cmd->sync_cmd); + instance->instancet->disable_intr(instance); + atomic_set(&instance->fw_reset_no_pci_access, 1); megaraid_sas_kill_hba(instance); - - instance->adprecovery = - MEGASAS_HW_CRITICAL_ERROR; return; } } @@ -3168,8 +3211,8 @@ process_fw_state_change_wq(struct work_struct *work) if (megasas_transition_to_ready(instance, 1)) { printk(KERN_NOTICE "megaraid_sas:adapter not ready\n"); + atomic_set(&instance->fw_reset_no_pci_access, 1); megaraid_sas_kill_hba(instance); - instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR; return ; } diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 075c99e..df280b1 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -2631,7 +2631,6 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) instance->host->host_no); megaraid_sas_kill_hba(instance); instance->skip_heartbeat_timer_del = 1; - instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR; retval = FAILED; goto out; } @@ -2827,8 +2826,6 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) dev_info(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); - instance->adprecovery = - MEGASAS_HW_CRITICAL_ERROR; megaraid_sas_kill_hba(instance); retval = FAILED; } @@ -2877,7 +2874,6 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) "adapter scsi%d.\n", instance->host->host_no); megaraid_sas_kill_hba(instance); instance->skip_heartbeat_timer_del = 1; - instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR; retval = FAILED; } else { /* For VF: Restart HB timer if we didn't OCR */ -- cgit v0.10.2 From ae09a6c1fd421887fa4885adb7c55ccc8074c441 Mon Sep 17 00:00:00 2001 From: "Sumit.Saxena@avagotech.com" Date: Mon, 5 Jan 2015 20:06:23 +0530 Subject: megaraid_sas: reserve commands for IOCTLs and internal DCMDs 1) For fusion adapters, limit reserved frames for non SCSI commands to 8 (3 for parallel IOCTLs + 5 for driver's internal DCMDs). Earlier reserved commands for non SCSI IO frames was set to 32, so with this implementation, increased per controller "can_queue". Behavior of MFI controllers will remain unchanged. 2) Optimize the code related to per controller's 'can_queue' setting. Signed-off-by: Sumit Saxena Signed-off-by: Chaitra Basappa Reviewed-by: Martin K. Petersen Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index b8b378d..793b2b3 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -1082,6 +1082,8 @@ enum MR_SCSI_CMD_TYPE { */ #define MEGASAS_INT_CMDS 32 #define MEGASAS_SKINNY_INT_CMDS 5 +#define MEGASAS_FUSION_INTERNAL_CMDS 5 +#define MEGASAS_FUSION_IOCTL_CMDS 3 #define MEGASAS_MAX_MSIX_QUEUES 128 /* @@ -1687,9 +1689,8 @@ struct megasas_instance { u16 max_num_sge; u16 max_fw_cmds; - /* For Fusion its num IOCTL cmds, for others MFI based its - max_fw_cmds */ u16 max_mfi_cmds; + u16 max_scsi_cmds; u32 max_sectors_per_req; struct megasas_aen_event *ev; @@ -1765,7 +1766,7 @@ struct megasas_instance { u8 requestorId; char PlasmaFW111; char mpio; - int throttlequeuedepth; + u16 throttlequeuedepth; u8 mask_interrupts; u8 is_imr; }; diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index d4e9c4e..bf31dde 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -78,7 +78,7 @@ static int allow_vf_ioctls; module_param(allow_vf_ioctls, int, S_IRUGO); MODULE_PARM_DESC(allow_vf_ioctls, "Allow ioctls in SR-IOV VF mode. Default: 0"); -static int throttlequeuedepth = MEGASAS_THROTTLE_QUEUE_DEPTH; +static unsigned int throttlequeuedepth = MEGASAS_THROTTLE_QUEUE_DEPTH; module_param(throttlequeuedepth, int, S_IRUGO); MODULE_PARM_DESC(throttlequeuedepth, "Adapter queue depth when throttled due to I/O timeout. Default: 16"); @@ -1764,6 +1764,7 @@ void megasas_check_and_restore_queue_depth(struct megasas_instance *instance) { unsigned long flags; + if (instance->flag & MEGASAS_FW_BUSY && time_after(jiffies, instance->last_time + 5 * HZ) && atomic_read(&instance->fw_outstanding) < @@ -1771,13 +1772,8 @@ megasas_check_and_restore_queue_depth(struct megasas_instance *instance) spin_lock_irqsave(instance->host->host_lock, flags); instance->flag &= ~MEGASAS_FW_BUSY; - if (instance->is_imr) { - instance->host->can_queue = - instance->max_fw_cmds - MEGASAS_SKINNY_INT_CMDS; - } else - instance->host->can_queue = - instance->max_fw_cmds - MEGASAS_INT_CMDS; + instance->host->can_queue = instance->max_scsi_cmds; spin_unlock_irqrestore(instance->host->host_lock, flags); } } @@ -4685,23 +4681,38 @@ static int megasas_init_fw(struct megasas_instance *instance) if (tmp_sectors && (instance->max_sectors_per_req > tmp_sectors)) instance->max_sectors_per_req = tmp_sectors; - /* Check for valid throttlequeuedepth module parameter */ - if (instance->is_imr) { - if (throttlequeuedepth > (instance->max_fw_cmds - - MEGASAS_SKINNY_INT_CMDS)) - instance->throttlequeuedepth = - MEGASAS_THROTTLE_QUEUE_DEPTH; - else - instance->throttlequeuedepth = throttlequeuedepth; + /* + * 1. For fusion adapters, 3 commands for IOCTL and 5 commands + * for driver's internal DCMDs. + * 2. For MFI skinny adapters, 5 commands for IOCTL + driver's + * internal DCMDs. + * 3. For rest of MFI adapters, 27 commands reserved for IOCTLs + * and 5 commands for drivers's internal DCMD. + */ + if (instance->ctrl_context) { + instance->max_scsi_cmds = instance->max_fw_cmds - + (MEGASAS_FUSION_INTERNAL_CMDS + + MEGASAS_FUSION_IOCTL_CMDS); + sema_init(&instance->ioctl_sem, MEGASAS_FUSION_IOCTL_CMDS); + } else if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + instance->max_scsi_cmds = instance->max_fw_cmds - + MEGASAS_SKINNY_INT_CMDS; + sema_init(&instance->ioctl_sem, MEGASAS_SKINNY_INT_CMDS); } else { - if (throttlequeuedepth > (instance->max_fw_cmds - - MEGASAS_INT_CMDS)) - instance->throttlequeuedepth = - MEGASAS_THROTTLE_QUEUE_DEPTH; - else - instance->throttlequeuedepth = throttlequeuedepth; + instance->max_scsi_cmds = instance->max_fw_cmds - + MEGASAS_INT_CMDS; + sema_init(&instance->ioctl_sem, (MEGASAS_INT_CMDS - 5)); } + /* Check for valid throttlequeuedepth module parameter */ + if (throttlequeuedepth && + throttlequeuedepth <= instance->max_scsi_cmds) + instance->throttlequeuedepth = throttlequeuedepth; + else + instance->throttlequeuedepth = + MEGASAS_THROTTLE_QUEUE_DEPTH; + /* * Setup tasklet for cmd completion */ @@ -4996,12 +5007,7 @@ static int megasas_io_attach(struct megasas_instance *instance) */ host->irq = instance->pdev->irq; host->unique_id = instance->unique_id; - if (instance->is_imr) { - host->can_queue = - instance->max_fw_cmds - MEGASAS_SKINNY_INT_CMDS; - } else - host->can_queue = - instance->max_fw_cmds - MEGASAS_INT_CMDS; + host->can_queue = instance->max_scsi_cmds; host->this_id = instance->init_id; host->sg_tablesize = instance->max_num_sge; @@ -5264,12 +5270,10 @@ static int megasas_probe_one(struct pci_dev *pdev, instance->init_id = MEGASAS_DEFAULT_INIT_ID; instance->ctrl_info = NULL; + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) instance->flag_ieee = 1; - sema_init(&instance->ioctl_sem, MEGASAS_SKINNY_INT_CMDS); - } else - sema_init(&instance->ioctl_sem, (MEGASAS_INT_CMDS - 5)); megasas_dbg_lvl = 0; instance->flag = 0; @@ -6264,9 +6268,6 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) goto out_kfree_ioc; } - /* - * We will allow only MEGASAS_INT_CMDS number of parallel ioctl cmds - */ if (down_interruptible(&instance->ioctl_sem)) { error = -ERESTARTSYS; goto out_kfree_ioc; diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index df280b1..675b5e7 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -1019,8 +1019,12 @@ megasas_init_adapter_fusion(struct megasas_instance *instance) * does not exceed max cmds that the FW can support */ instance->max_fw_cmds = instance->max_fw_cmds-1; - /* Only internal cmds (DCMD) need to have MFI frames */ - instance->max_mfi_cmds = MEGASAS_INT_CMDS; + + /* + * Only Driver's internal DCMDs and IOCTL DCMDs needs to have MFI frames + */ + instance->max_mfi_cmds = + MEGASAS_FUSION_INTERNAL_CMDS + MEGASAS_FUSION_IOCTL_CMDS; max_cmd = instance->max_fw_cmds; -- cgit v0.10.2 From 1aecbc8d679e7eb61fa955eb97874b090c08a89d Mon Sep 17 00:00:00 2001 From: "Sumit.Saxena@avagotech.com" Date: Mon, 5 Jan 2015 20:06:28 +0530 Subject: megaraid_sas: remove redundant memset call struct fusion_context *fusion is already memset to '0', so remove a redundant call to memmset "fusion->load_balance_info" to '0'. Signed-off-by: Chaitra Basappa Signed-off-by: Sumit Saxena Reviewed-by: Martin K. Petersen Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index bf31dde..890637f 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -5185,8 +5185,6 @@ static int megasas_probe_one(struct pci_dev *pdev, ((1 << PAGE_SHIFT) << instance->ctrl_context_pages)); INIT_LIST_HEAD(&fusion->cmd_pool); spin_lock_init(&fusion->mpt_pool_lock); - memset(fusion->load_balance_info, 0, - sizeof(struct LD_LOAD_BALANCE_INFO) * MAX_LOGICAL_DRIVES_EXT); } break; default: /* For all other supported controllers */ -- cgit v0.10.2 From 0128d5cf8f85c93b3c70ff03299c2839f3e6d21e Mon Sep 17 00:00:00 2001 From: "Sumit.Saxena@avagotech.com" Date: Mon, 5 Jan 2015 20:06:28 +0530 Subject: megaraid_sas: driver version update Update megaraid_sas driver version. Signed-off-by: Sumit Saxena Reviewed-by: Martin K. Petersen Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index 793b2b3..14e5c7c 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -35,7 +35,7 @@ /* * MegaRAID SAS Driver meta data */ -#define MEGASAS_VERSION "06.805.06.01-rc1" +#define MEGASAS_VERSION "06.806.08.00-rc1" /* * Device IDs -- cgit v0.10.2 From c4eaa62fdac3cdbf358e19625477ffc9a15d4c7b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:40:23 +0100 Subject: ALSA: ali5451: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index af89e42..4cd2210 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -1873,7 +1873,6 @@ static int snd_ali_mixer(struct snd_ali *codec) #ifdef CONFIG_PM_SLEEP static int ali_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_ali *chip = card->private_data; struct snd_ali_image *im; @@ -1914,16 +1913,11 @@ static int ali_suspend(struct device *dev) outl(0xffffffff, ALI_REG(chip, ALI_STOP)); spin_unlock_irq(&chip->reg_lock); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int ali_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_ali *chip = card->private_data; struct snd_ali_image *im; @@ -1933,15 +1927,6 @@ static int ali_resume(struct device *dev) if (!im) return 0; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - spin_lock_irq(&chip->reg_lock); for (i = 0; i < ALI_CHANNELS; i++) { -- cgit v0.10.2 From 08aa0e34305059a80f38e039f085fe91a7587cca Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:43:46 +0100 Subject: ALSA: als300: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/als300.c b/sound/pci/als300.c index 7bb6ac5..bd01113 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -728,35 +728,20 @@ static int snd_als300_create(struct snd_card *card, #ifdef CONFIG_PM_SLEEP static int snd_als300_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_als300 *chip = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int snd_als300_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_als300 *chip = card->private_data; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - snd_als300_init(chip); snd_ac97_resume(chip->ac97); -- cgit v0.10.2 From dc3b10eb64edd977fb5173f72fc66cc8687e61be Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:44:01 +0100 Subject: ALSA: als4000: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index d3e6424..9460852 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -988,7 +988,6 @@ static void snd_card_als4000_remove(struct pci_dev *pci) #ifdef CONFIG_PM_SLEEP static int snd_als4000_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_card_als4000 *acard = card->private_data; struct snd_sb *chip = acard->chip; @@ -997,29 +996,15 @@ static int snd_als4000_suspend(struct device *dev) snd_pcm_suspend_all(chip->pcm); snd_sbmixer_suspend(chip); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int snd_als4000_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_card_als4000 *acard = card->private_data; struct snd_sb *chip = acard->chip; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - snd_als4000_configure(chip); snd_sbdsp_reset(chip); snd_sbmixer_resume(chip); -- cgit v0.10.2 From 94af14691d3783629f5d186acfb1f700fb68ff03 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:44:18 +0100 Subject: ALSA: atiixp: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 9c1c445..f41de00 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1474,7 +1474,6 @@ static int snd_atiixp_mixer_new(struct atiixp *chip, int clock, */ static int snd_atiixp_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct atiixp *chip = card->private_data; int i; @@ -1492,29 +1491,15 @@ static int snd_atiixp_suspend(struct device *dev) snd_ac97_suspend(chip->ac97[i]); snd_atiixp_aclink_down(chip); snd_atiixp_chip_stop(chip); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int snd_atiixp_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct atiixp *chip = card->private_data; int i; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - snd_atiixp_aclink_reset(chip); snd_atiixp_chip_start(chip); -- cgit v0.10.2 From 2b5d0891ba8555c8edc21098715324d90a4d5de0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:44:30 +0100 Subject: ALSA: atiixp-modem: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index b2f63e0..7cf4197 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -1120,7 +1120,6 @@ static int snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock) */ static int snd_atiixp_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct atiixp_modem *chip = card->private_data; int i; @@ -1132,29 +1131,15 @@ static int snd_atiixp_suspend(struct device *dev) snd_ac97_suspend(chip->ac97[i]); snd_atiixp_aclink_down(chip); snd_atiixp_chip_stop(chip); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int snd_atiixp_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct atiixp_modem *chip = card->private_data; int i; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - snd_atiixp_aclink_reset(chip); snd_atiixp_chip_start(chip); -- cgit v0.10.2 From 3a7ed36909a6c4aee677100ff1a45869a5873a12 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:44:47 +0100 Subject: ALSA: azt3328: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index fdbb9c0..bbacc75 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -2694,7 +2694,6 @@ snd_azf3328_resume_ac97(const struct snd_azf3328 *chip) static int snd_azf3328_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_azf3328 *chip = card->private_data; u16 *saved_regs_ctrl_u16; @@ -2720,29 +2719,15 @@ snd_azf3328_suspend(struct device *dev) ARRAY_SIZE(chip->saved_regs_mpu), chip->saved_regs_mpu); snd_azf3328_suspend_regs(chip, chip->opl3_io, ARRAY_SIZE(chip->saved_regs_opl3), chip->saved_regs_opl3); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int snd_azf3328_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); const struct snd_azf3328 *chip = card->private_data; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - snd_azf3328_resume_regs(chip, chip->saved_regs_game, chip->game_io, ARRAY_SIZE(chip->saved_regs_game)); snd_azf3328_resume_regs(chip, chip->saved_regs_mpu, chip->mpu_io, -- cgit v0.10.2 From 11afa6426c4ff69cc58f0b91e85cf17f31c0a442 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:44:59 +0100 Subject: ALSA: ca0106: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 96af339..dd75b75 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -1910,7 +1910,6 @@ static void snd_ca0106_remove(struct pci_dev *pci) #ifdef CONFIG_PM_SLEEP static int snd_ca0106_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_ca0106 *chip = card->private_data; int i; @@ -1923,30 +1922,15 @@ static int snd_ca0106_suspend(struct device *dev) snd_ca0106_mixer_suspend(chip); ca0106_stop_chip(chip); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int snd_ca0106_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_ca0106 *chip = card->private_data; int i; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - - if (pci_enable_device(pci) < 0) { - snd_card_disconnect(card); - return -EIO; - } - - pci_set_master(pci); - ca0106_init_chip(chip, 1); if (chip->details->ac97) -- cgit v0.10.2 From 5762fdb5f9b18a5bf8cdbdfbc0c735ad1d88a478 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:45:12 +0100 Subject: ALSA: cmipci: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 85ed403..63d2c82 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -3347,7 +3347,6 @@ static unsigned char saved_mixers[] = { static int snd_cmipci_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct cmipci *cm = card->private_data; int i; @@ -3366,29 +3365,15 @@ static int snd_cmipci_suspend(struct device *dev) /* disable ints */ snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int snd_cmipci_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct cmipci *cm = card->private_data; int i; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - /* reset / initialize to a sane state */ snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); snd_cmipci_ch_reset(cm, CM_CH_PLAY); -- cgit v0.10.2 From faab5223930114b0a33f8c712c14799967342e8c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:45:22 +0100 Subject: ALSA: cs4281: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 4c49b5c..80bb1d1 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -2008,7 +2008,6 @@ static int saved_regs[SUSPEND_REGISTERS] = { static int cs4281_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct cs4281 *chip = card->private_data; u32 ulCLK; @@ -2047,30 +2046,16 @@ static int cs4281_suspend(struct device *dev) ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1); ulCLK &= ~CLKCR1_CKRA; snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int cs4281_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct cs4281 *chip = card->private_data; unsigned int i; u32 ulCLK; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1); ulCLK |= CLKCR1_CKRA; snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); -- cgit v0.10.2 From 319184680d7ecbea5718fa1ab73105ef8abb478f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:45:32 +0100 Subject: ALSA: cs46xx: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 32b44f2..ba97aba 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -3804,7 +3804,6 @@ static unsigned int saved_regs[] = { static int snd_cs46xx_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_cs46xx *chip = card->private_data; int i, amp_saved; @@ -3829,16 +3828,11 @@ static int snd_cs46xx_suspend(struct device *dev) /* disable CLKRUN */ chip->active_ctrl(chip, -chip->amplifier); chip->amplifier = amp_saved; /* restore the status */ - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int snd_cs46xx_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_cs46xx *chip = card->private_data; int amp_saved; @@ -3847,15 +3841,6 @@ static int snd_cs46xx_resume(struct device *dev) #endif unsigned int tmp; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - amp_saved = chip->amplifier; chip->amplifier = 0; chip->active_ctrl(chip, 1); /* force to on */ -- cgit v0.10.2 From b8375122b59b1362e94ccd8502cff3cfef4cf168 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:45:43 +0100 Subject: ALSA: cs5535audio: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c index 34cc600..06ac5d8 100644 --- a/sound/pci/cs5535audio/cs5535audio_pm.c +++ b/sound/pci/cs5535audio/cs5535audio_pm.c @@ -57,7 +57,6 @@ static void snd_cs5535audio_stop_hardware(struct cs5535audio *cs5535au) static int snd_cs5535audio_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct cs5535audio *cs5535au = card->private_data; int i; @@ -72,34 +71,17 @@ static int snd_cs5535audio_suspend(struct device *dev) } /* save important regs, then disable aclink in hw */ snd_cs5535audio_stop_hardware(cs5535au); - - if (pci_save_state(pci)) { - dev_err(dev, "pci_save_state failed!\n"); - return -EIO; - } - pci_disable_device(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int snd_cs5535audio_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct cs5535audio *cs5535au = card->private_data; u32 tmp; int timeout; int i; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - /* set LNK_WRM_RST to reset AC link */ cs_writel(cs5535au, ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_WRM_RST); -- cgit v0.10.2 From 7247aabd5ace16677681f815cc34f84ef0278261 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:45:55 +0100 Subject: ALSA: ctxfi: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index b425aa8..f16bec1 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -2099,20 +2099,11 @@ static int hw_suspend(struct hw *hw) pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x0); } - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); - return 0; } static int hw_resume(struct hw *hw, struct card_conf *info) { - struct pci_dev *pci = hw->pci; - - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - /* Re-initialize card hardware. */ return hw_card_init(hw, info); } diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 253899d..613671d 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -2209,24 +2209,12 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) #ifdef CONFIG_PM_SLEEP static int hw_suspend(struct hw *hw) { - struct pci_dev *pci = hw->pci; - hw_card_stop(hw); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); - return 0; } static int hw_resume(struct hw *hw, struct card_conf *info) { - struct pci_dev *pci = hw->pci; - - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - /* Re-initialize card hardware. */ return hw_card_init(hw, info); } -- cgit v0.10.2 From f84edab4b59bd004853224703acb03628af6c2ce Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:46:07 +0100 Subject: ALSA: echoaudio: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 21228ad..5896084 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -2162,7 +2162,6 @@ ctl_error: static int snd_echo_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct echoaudio *chip = dev_get_drvdata(dev); snd_pcm_suspend_all(chip->analog_pcm); @@ -2188,9 +2187,6 @@ static int snd_echo_suspend(struct device *dev) chip->dsp_code = NULL; free_irq(chip->irq, chip); chip->irq = -1; - pci_save_state(pci); - pci_disable_device(pci); - return 0; } @@ -2204,7 +2200,6 @@ static int snd_echo_resume(struct device *dev) u32 pipe_alloc_mask; int err; - pci_restore_state(pci); commpage_bak = kmalloc(sizeof(struct echoaudio), GFP_KERNEL); if (commpage_bak == NULL) return -ENOMEM; -- cgit v0.10.2 From 1aa1813b2c29b25de798dc465dffd56124f94e60 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:46:58 +0100 Subject: ALSA: emu10k1: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 4c17163..1d262ba 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -210,7 +210,6 @@ static void snd_card_emu10k1_remove(struct pci_dev *pci) #ifdef CONFIG_PM_SLEEP static int snd_emu10k1_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_emu10k1 *emu = card->private_data; @@ -232,28 +231,14 @@ static int snd_emu10k1_suspend(struct device *dev) snd_p16v_suspend(emu); snd_emu10k1_done(emu); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int snd_emu10k1_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_emu10k1 *emu = card->private_data; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - snd_emu10k1_resume_init(emu); snd_emu10k1_efx_resume(emu); snd_ac97_resume(emu->ac97); -- cgit v0.10.2 From 943cf2839637231bdb2710753b457651ea1b7204 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:47:10 +0100 Subject: ALSA: ens137x: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index d94cb3c..1badd91 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -2049,7 +2049,6 @@ static void snd_ensoniq_chip_init(struct ensoniq *ensoniq) #ifdef CONFIG_PM_SLEEP static int snd_ensoniq_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct ensoniq *ensoniq = card->private_data; @@ -2070,28 +2069,14 @@ static int snd_ensoniq_suspend(struct device *dev) udelay(100); snd_ak4531_suspend(ensoniq->u.es1370.ak4531); #endif - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int snd_ensoniq_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct ensoniq *ensoniq = card->private_data; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - snd_ensoniq_chip_init(ensoniq); #ifdef CHIP1371 -- cgit v0.10.2 From 8c3698219119612c239e387e423aa9d90040bc64 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:47:21 +0100 Subject: ALSA: es1938: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 0fc46eb..a01454b 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1454,7 +1454,6 @@ static unsigned char saved_regs[SAVED_REG_SIZE+1] = { static int es1938_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct es1938 *chip = card->private_data; unsigned char *s, *d; @@ -1471,9 +1470,6 @@ static int es1938_suspend(struct device *dev) free_irq(chip->irq, chip); chip->irq = -1; } - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } @@ -1484,14 +1480,6 @@ static int es1938_resume(struct device *dev) struct es1938 *chip = card->private_data; unsigned char *s, *d; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - if (request_irq(pci->irq, snd_es1938_interrupt, IRQF_SHARED, KBUILD_MODNAME, chip)) { dev_err(dev, "unable to grab IRQ %d, disabling device\n", -- cgit v0.10.2 From 8b1db4df201b9ad1f5a329438b823e120d463a96 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:47:31 +0100 Subject: ALSA: es1968: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 6039700..631be02 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -2383,7 +2383,6 @@ static void snd_es1968_start_irq(struct es1968 *chip) */ static int es1968_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct es1968 *chip = card->private_data; @@ -2396,16 +2395,11 @@ static int es1968_suspend(struct device *dev) snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); snd_es1968_bob_stop(chip); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int es1968_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct es1968 *chip = card->private_data; struct esschan *es; @@ -2413,16 +2407,6 @@ static int es1968_resume(struct device *dev) if (! chip->do_pm) return 0; - /* restore all our config */ - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - snd_es1968_chip_init(chip); /* need to restore the base pointers.. */ -- cgit v0.10.2 From e2a711f1b59d59e95f58d1e32e5fbcb7a7a28217 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:47:42 +0100 Subject: ALSA: hda: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index d426a0b..99ae4e0 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -795,7 +795,6 @@ static int param_set_xint(const char *val, const struct kernel_param *kp) */ static int azx_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; struct hda_intel *hda; @@ -824,9 +823,6 @@ static int azx_suspend(struct device *dev) if (chip->msi) pci_disable_msi(chip->pci); - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) hda_display_power(false); return 0; @@ -851,15 +847,6 @@ static int azx_resume(struct device *dev) hda_display_power(true); haswell_set_bclk(chip); } - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(chip->card->dev, - "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); if (chip->msi) if (pci_enable_msi(pci) < 0) chip->msi = 0; -- cgit v0.10.2 From b60dc62f5b078222e895aae21b71da968ae101a8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:47:55 +0100 Subject: ALSA: ice1712: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index b039b46..e1560bf 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -2798,7 +2798,6 @@ static void snd_ice1712_remove(struct pci_dev *pci) #ifdef CONFIG_PM_SLEEP static int snd_ice1712_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_ice1712 *ice = card->private_data; @@ -2820,16 +2819,11 @@ static int snd_ice1712_suspend(struct device *dev) if (ice->pm_suspend) ice->pm_suspend(ice); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int snd_ice1712_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_ice1712 *ice = card->private_data; int rate; @@ -2837,16 +2831,6 @@ static int snd_ice1712_resume(struct device *dev) if (!ice->pm_suspend_enabled) return 0; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - - if (pci_enable_device(pci) < 0) { - snd_card_disconnect(card); - return -EIO; - } - - pci_set_master(pci); - if (ice->cur_rate) rate = ice->cur_rate; else -- cgit v0.10.2 From 88942503538c7ef9636858851abbe50a96db3800 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:48:05 +0100 Subject: ALSA: ice1724: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index d73da15..0b22c00 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -2798,7 +2798,6 @@ static void snd_vt1724_remove(struct pci_dev *pci) #ifdef CONFIG_PM_SLEEP static int snd_vt1724_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_ice1712 *ice = card->private_data; @@ -2821,32 +2820,17 @@ static int snd_vt1724_suspend(struct device *dev) if (ice->pm_suspend) ice->pm_suspend(ice); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int snd_vt1724_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_ice1712 *ice = card->private_data; if (!ice->pm_suspend_enabled) return 0; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - - if (pci_enable_device(pci) < 0) { - snd_card_disconnect(card); - return -EIO; - } - - pci_set_master(pci); - snd_vt1724_chip_reset(ice); if (snd_vt1724_chip_init(ice) < 0) { -- cgit v0.10.2 From 3c5a03d490d7b6494f3ec028718770a44520ba29 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:48:15 +0100 Subject: ALSA: intel8x0: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 4a28252..67f9e8b 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -2654,7 +2654,6 @@ static int snd_intel8x0_free(struct intel8x0 *chip) */ static int intel8x0_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct intel8x0 *chip = card->private_data; int i; @@ -2682,12 +2681,6 @@ static int intel8x0_suspend(struct device *dev) free_irq(chip->irq, chip); chip->irq = -1; } - pci_disable_device(pci); - pci_save_state(pci); - /* The call below may disable built-in speaker on some laptops - * after S2RAM. So, don't touch it. - */ - /* pci_set_power_state(pci, PCI_D3hot); */ return 0; } @@ -2698,14 +2691,6 @@ static int intel8x0_resume(struct device *dev) struct intel8x0 *chip = card->private_data; int i; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); snd_intel8x0_chip_init(chip, 0); if (request_irq(pci->irq, snd_intel8x0_interrupt, IRQF_SHARED, KBUILD_MODNAME, chip)) { -- cgit v0.10.2 From 65fe5b946e5c0dffb7554cccaff3ab075eec7146 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:48:27 +0100 Subject: ALSA: intel8x0m: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 6b40235..748f6f6 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -1023,7 +1023,6 @@ static int snd_intel8x0m_free(struct intel8x0m *chip) */ static int intel8x0m_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct intel8x0m *chip = card->private_data; int i; @@ -1036,9 +1035,6 @@ static int intel8x0m_suspend(struct device *dev) free_irq(chip->irq, chip); chip->irq = -1; } - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } @@ -1048,14 +1044,6 @@ static int intel8x0m_resume(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct intel8x0m *chip = card->private_data; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); if (request_irq(pci->irq, snd_intel8x0m_interrupt, IRQF_SHARED, KBUILD_MODNAME, chip)) { dev_err(dev, "unable to grab IRQ %d, disabling device\n", -- cgit v0.10.2 From cd86f4593db6ca0256b23a33af5577d5239e78ce Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:48:50 +0100 Subject: ALSA: maestro3: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 98823d1..18a60be 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2395,7 +2395,6 @@ static int snd_m3_free(struct snd_m3 *chip) #ifdef CONFIG_PM_SLEEP static int m3_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_m3 *chip = card->private_data; int i, dsp_index; @@ -2421,16 +2420,11 @@ static int m3_suspend(struct device *dev) for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) chip->suspend_mem[dsp_index++] = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, i); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int m3_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_m3 *chip = card->private_data; int i, dsp_index; @@ -2438,15 +2432,6 @@ static int m3_resume(struct device *dev) if (chip->suspend_mem == NULL) return 0; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - /* first lets just bring everything back. .*/ snd_m3_outw(chip, 0, 0x54); snd_m3_outw(chip, 0, 0x56); -- cgit v0.10.2 From fc9b6061cf9c42460e482f7a5947ee8120ff0960 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:49:03 +0100 Subject: ALSA: nm256: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 4e41a4e..8598f2b 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1392,7 +1392,6 @@ snd_nm256_peek_for_sig(struct nm256 *chip) */ static int nm256_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct nm256 *chip = card->private_data; @@ -1400,15 +1399,11 @@ static int nm256_suspend(struct device *dev) snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); chip->coeffs_current = 0; - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int nm256_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct nm256 *chip = card->private_data; int i; @@ -1416,15 +1411,6 @@ static int nm256_resume(struct device *dev) /* Perform a full reset on the hardware */ chip->in_resume = 1; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - snd_nm256_init_chip(chip); /* restore ac97 */ -- cgit v0.10.2 From 96bba98393f97e4c4bfe29341b3ad2adef32bde2 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 7 Jan 2015 17:04:10 +0200 Subject: ath10k: fix build error when hwmon is off kbuild reported a linking error: ERROR: "devm_hwmon_device_register_with_groups" [drivers/net/wireless/ath/ath10k/ath10k_core.ko] undefined! Fix it by returning early and letting the compiler to optimise out the function call. Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/thermal.c b/drivers/net/wireless/ath/ath10k/thermal.c index d939135..b14ae8d 100644 --- a/drivers/net/wireless/ath/ath10k/thermal.c +++ b/drivers/net/wireless/ath/ath10k/thermal.c @@ -213,6 +213,11 @@ int ath10k_thermal_register(struct ath10k *ar) if (ar->wmi.op_version != ATH10K_FW_WMI_OP_VERSION_10_2_4) return 0; + /* Avoid linking error on devm_hwmon_device_register_with_groups, I + * guess linux/hwmon.h is missing proper stubs. */ + if (!config_enabled(HWMON)) + return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(ar->dev, "ath10k_hwmon", ar, ath10k_hwmon_groups); -- cgit v0.10.2 From 2d6b2e59eda2d77c16f9189073adb0484503d168 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:49:13 +0100 Subject: ALSA: oxygen: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Acked-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index b67e306..366723cf 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -728,7 +728,6 @@ EXPORT_SYMBOL(oxygen_pci_remove); #ifdef CONFIG_PM_SLEEP static int oxygen_pci_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct oxygen *chip = card->private_data; unsigned int i, saved_interrupt_mask; @@ -753,10 +752,6 @@ static int oxygen_pci_suspend(struct device *dev) flush_work(&chip->spdif_input_bits_work); flush_work(&chip->gpio_work); chip->interrupt_mask = saved_interrupt_mask; - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } @@ -788,20 +783,10 @@ static void oxygen_restore_ac97(struct oxygen *chip, unsigned int codec) static int oxygen_pci_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct oxygen *chip = card->private_data; unsigned int i; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "cannot reenable device"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - oxygen_write16(chip, OXYGEN_DMA_STATUS, 0); oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0); for (i = 0; i < OXYGEN_IO_SIZE; ++i) -- cgit v0.10.2 From e066d85cc0f87c6e67bbbb7665de8b30e5ce11e4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:49:42 +0100 Subject: ALSA: riptide: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 6abc2ac..7e922fa 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -1153,7 +1153,6 @@ static void riptide_handleirq(unsigned long dev_id) #ifdef CONFIG_PM_SLEEP static int riptide_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_riptide *chip = card->private_data; @@ -1161,27 +1160,14 @@ static int riptide_suspend(struct device *dev) snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int riptide_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_riptide *chip = card->private_data; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - printk(KERN_ERR "riptide: pci_enable_device failed, " - "disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); snd_riptide_initialize(chip); snd_ac97_resume(chip->ac97); snd_power_change_state(card, SNDRV_CTL_POWER_D0); -- cgit v0.10.2 From a0044463da8a20052b23eb2cd38edbe3e611942e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:49:59 +0100 Subject: ALSA: rme96: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 2f1a851..e33e79e 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -2358,7 +2358,6 @@ snd_rme96_create_switches(struct snd_card *card, static int rme96_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct rme96 *rme96 = card->private_data; @@ -2381,26 +2380,14 @@ static int rme96_suspend(struct device *dev) /* disable the DAC */ rme96->areg &= ~RME96_AR_DAC_EN; writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); - - pci_disable_device(pci); - pci_save_state(pci); - return 0; } static int rme96_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct rme96 *rme96 = card->private_data; - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - /* reset playback and record buffer pointers */ writel(0, rme96->iobase + RME96_IO_SET_PLAY_POS + rme96->playback_pointer); -- cgit v0.10.2 From 5b3985ecdc6beab7db96a259fce0d8befd99ff07 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:50:09 +0100 Subject: ALSA: sis7019: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index 7f6a0a0..be8952f 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -1211,7 +1211,6 @@ static int sis_chip_init(struct sis7019 *sis) #ifdef CONFIG_PM_SLEEP static int sis_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct sis7019 *sis = card->private_data; void __iomem *ioaddr = sis->ioaddr; @@ -1240,9 +1239,6 @@ static int sis_suspend(struct device *dev) ioaddr += 4096; } - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } @@ -1254,14 +1250,6 @@ static int sis_resume(struct device *dev) void __iomem *ioaddr = sis->ioaddr; int i; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - - if (pci_enable_device(pci) < 0) { - dev_err(&pci->dev, "unable to re-enable device\n"); - goto error; - } - if (sis_chip_init(sis)) { dev_err(&pci->dev, "unable to re-init controller\n"); goto error; @@ -1284,7 +1272,6 @@ static int sis_resume(struct device *dev) memset(sis->suspend_state[0], 0, 4096); sis->irq = pci->irq; - pci_set_master(pci); if (sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT) snd_ac97_resume(sis->ac97[0]); -- cgit v0.10.2 From 6f9eb021f776e7765a054090932cc9834d99c242 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:50:18 +0100 Subject: ALSA: trident: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 57cd757..0728249 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -3926,7 +3926,6 @@ static void snd_trident_clear_voices(struct snd_trident * trident, unsigned shor #ifdef CONFIG_PM_SLEEP static int snd_trident_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_trident *trident = card->private_data; @@ -3938,28 +3937,14 @@ static int snd_trident_suspend(struct device *dev) snd_ac97_suspend(trident->ac97); snd_ac97_suspend(trident->ac97_sec); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int snd_trident_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_trident *trident = card->private_data; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - switch (trident->device) { case TRIDENT_DEVICE_ID_DX: snd_trident_4d_dx_init(trident); -- cgit v0.10.2 From e909bfdfa9cde50099b5eef5bd4b87d92ddffc86 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:50:27 +0100 Subject: ALSA: via82xx: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index e088467..120fccb 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -2271,7 +2271,6 @@ static int snd_via82xx_chip_init(struct via82xx *chip) */ static int snd_via82xx_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct via82xx *chip = card->private_data; int i; @@ -2291,28 +2290,15 @@ static int snd_via82xx_suspend(struct device *dev) chip->capture_src_saved[1] = inb(chip->port + VIA_REG_CAPTURE_CHANNEL + 0x10); } - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int snd_via82xx_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct via82xx *chip = card->private_data; int i; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - snd_via82xx_chip_init(chip); if (chip->chip_type == TYPE_VIA686) { -- cgit v0.10.2 From c11b4c68f4e9b92536fae1f000cb734609633c63 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:50:36 +0100 Subject: ALSA: via82xx-modem: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index fd46ffe..884f49e 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -1031,7 +1031,6 @@ static int snd_via82xx_chip_init(struct via82xx_modem *chip) */ static int snd_via82xx_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct via82xx_modem *chip = card->private_data; int i; @@ -1043,29 +1042,15 @@ static int snd_via82xx_suspend(struct device *dev) snd_via82xx_channel_reset(chip, &chip->devs[i]); synchronize_irq(chip->irq); snd_ac97_suspend(chip->ac97); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } static int snd_via82xx_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct via82xx_modem *chip = card->private_data; int i; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - snd_via82xx_chip_init(chip); snd_ac97_resume(chip->ac97); -- cgit v0.10.2 From de0c869c8369db671d165d89d56ea561bea48bd9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:50:48 +0100 Subject: ALSA: vx222: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c index c5a25e3..ecbaf47 100644 --- a/sound/pci/vx222/vx222.c +++ b/sound/pci/vx222/vx222.c @@ -259,32 +259,17 @@ static void snd_vx222_remove(struct pci_dev *pci) #ifdef CONFIG_PM_SLEEP static int snd_vx222_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_vx222 *vx = card->private_data; - int err; - err = snd_vx_suspend(&vx->core); - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); - return err; + return snd_vx_suspend(&vx->core); } static int snd_vx222_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_vx222 *vx = card->private_data; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); return snd_vx_resume(&vx->core); } -- cgit v0.10.2 From 3e41c9b5a1ca2acfd9306a918fe3903f02eb89ca Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 8 Jan 2015 12:50:57 +0100 Subject: ALSA: ymfpci: Simplify PM callbacks This is a similar cleanup like the commit [3db084fd0af5: ALSA: fm801: PCI core handles power state for us]. Since pci_set_power_state(), pci_save_state() and pci_restore_state() are already done in the PCI core side, so we don't need to it doubly. Also, pci_enable_device(), pci_disable_device() and pci_set_master() calls in PM callbacks are superfluous nowadays, too, so get rid of them as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 81c916a..2b0b8f55 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -2326,7 +2326,6 @@ static int saved_regs_index[] = { static int snd_ymfpci_suspend(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct snd_ymfpci *chip = card->private_data; unsigned int i; @@ -2347,9 +2346,6 @@ static int snd_ymfpci_suspend(struct device *dev) snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0); snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0); snd_ymfpci_disable_dsp(chip); - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, PCI_D3hot); return 0; } @@ -2360,14 +2356,6 @@ static int snd_ymfpci_resume(struct device *dev) struct snd_ymfpci *chip = card->private_data; unsigned int i; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - dev_err(dev, "pci_enable_device failed, disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); snd_ymfpci_aclink_reset(pci); snd_ymfpci_codec_ready(chip, 0); snd_ymfpci_download_image(chip); -- cgit v0.10.2 From 880fe82dc555c514b5b4b14642f397afddd82860 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sat, 10 Jan 2015 00:23:43 +0800 Subject: regulator: axp20x: Fill regulators_node and of_match descriptor fields This patch fills the DT related fields in the regulator descriptors, which can then be used by the regulator core's simplified DT code. Signed-off-by: Chen-Yu Tsai Signed-off-by: Mark Brown diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index f23d7e1..441ae01 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -32,11 +32,13 @@ #define AXP20X_FREQ_DCDC_MASK 0x0f -#define AXP20X_DESC_IO(_id, _supply, _min, _max, _step, _vreg, _vmask, _ereg, \ - _emask, _enable_val, _disable_val) \ +#define AXP20X_DESC_IO(_id, _match, _supply, _min, _max, _step, _vreg, _vmask, \ + _ereg, _emask, _enable_val, _disable_val) \ [AXP20X_##_id] = { \ .name = #_id, \ .supply_name = (_supply), \ + .of_match = of_match_ptr(_match), \ + .regulators_node = of_match_ptr("regulators"), \ .type = REGULATOR_VOLTAGE, \ .id = AXP20X_##_id, \ .n_voltages = (((_max) - (_min)) / (_step) + 1), \ @@ -52,11 +54,13 @@ .ops = &axp20x_ops, \ } -#define AXP20X_DESC(_id, _supply, _min, _max, _step, _vreg, _vmask, _ereg, \ - _emask) \ +#define AXP20X_DESC(_id, _match, _supply, _min, _max, _step, _vreg, _vmask, \ + _ereg, _emask) \ [AXP20X_##_id] = { \ .name = #_id, \ .supply_name = (_supply), \ + .of_match = of_match_ptr(_match), \ + .regulators_node = of_match_ptr("regulators"), \ .type = REGULATOR_VOLTAGE, \ .id = AXP20X_##_id, \ .n_voltages = (((_max) - (_min)) / (_step) + 1), \ @@ -70,10 +74,12 @@ .ops = &axp20x_ops, \ } -#define AXP20X_DESC_FIXED(_id, _supply, _volt) \ +#define AXP20X_DESC_FIXED(_id, _match, _supply, _volt) \ [AXP20X_##_id] = { \ .name = #_id, \ .supply_name = (_supply), \ + .of_match = of_match_ptr(_match), \ + .regulators_node = of_match_ptr("regulators"), \ .type = REGULATOR_VOLTAGE, \ .id = AXP20X_##_id, \ .n_voltages = 1, \ @@ -82,10 +88,13 @@ .ops = &axp20x_ops_fixed \ } -#define AXP20X_DESC_TABLE(_id, _supply, _table, _vreg, _vmask, _ereg, _emask) \ +#define AXP20X_DESC_TABLE(_id, _match, _supply, _table, _vreg, _vmask, _ereg, \ + _emask) \ [AXP20X_##_id] = { \ .name = #_id, \ .supply_name = (_supply), \ + .of_match = of_match_ptr(_match), \ + .regulators_node = of_match_ptr("regulators"), \ .type = REGULATOR_VOLTAGE, \ .id = AXP20X_##_id, \ .n_voltages = ARRAY_SIZE(_table), \ @@ -127,20 +136,20 @@ static struct regulator_ops axp20x_ops = { }; static const struct regulator_desc axp20x_regulators[] = { - AXP20X_DESC(DCDC2, "vin2", 700, 2275, 25, AXP20X_DCDC2_V_OUT, 0x3f, - AXP20X_PWR_OUT_CTRL, 0x10), - AXP20X_DESC(DCDC3, "vin3", 700, 3500, 25, AXP20X_DCDC3_V_OUT, 0x7f, - AXP20X_PWR_OUT_CTRL, 0x02), - AXP20X_DESC_FIXED(LDO1, "acin", 1300), - AXP20X_DESC(LDO2, "ldo24in", 1800, 3300, 100, AXP20X_LDO24_V_OUT, 0xf0, - AXP20X_PWR_OUT_CTRL, 0x04), - AXP20X_DESC(LDO3, "ldo3in", 700, 3500, 25, AXP20X_LDO3_V_OUT, 0x7f, - AXP20X_PWR_OUT_CTRL, 0x40), - AXP20X_DESC_TABLE(LDO4, "ldo24in", axp20x_ldo4_data, AXP20X_LDO24_V_OUT, 0x0f, - AXP20X_PWR_OUT_CTRL, 0x08), - AXP20X_DESC_IO(LDO5, "ldo5in", 1800, 3300, 100, AXP20X_LDO5_V_OUT, 0xf0, - AXP20X_GPIO0_CTRL, 0x07, AXP20X_IO_ENABLED, - AXP20X_IO_DISABLED), + AXP20X_DESC(DCDC2, "dcdc2", "vin2", 700, 2275, 25, AXP20X_DCDC2_V_OUT, + 0x3f, AXP20X_PWR_OUT_CTRL, 0x10), + AXP20X_DESC(DCDC3, "dcdc3", "vin3", 700, 3500, 25, AXP20X_DCDC3_V_OUT, + 0x7f, AXP20X_PWR_OUT_CTRL, 0x02), + AXP20X_DESC_FIXED(LDO1, "ldo1", "acin", 1300), + AXP20X_DESC(LDO2, "ldo2", "ldo24in", 1800, 3300, 100, + AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04), + AXP20X_DESC(LDO3, "ldo3", "ldo3in", 700, 3500, 25, AXP20X_LDO3_V_OUT, + 0x7f, AXP20X_PWR_OUT_CTRL, 0x40), + AXP20X_DESC_TABLE(LDO4, "ldo4", "ldo24in", axp20x_ldo4_data, + AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, 0x08), + AXP20X_DESC_IO(LDO5, "ldo5", "ldo5in", 1800, 3300, 100, + AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07, + AXP20X_IO_ENABLED, AXP20X_IO_DISABLED), }; #define AXP_MATCH(_name, _id) \ -- cgit v0.10.2 From 765e8023251d3366b959f2e77e5ed48c597d57a0 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sat, 10 Jan 2015 00:23:44 +0800 Subject: regulator: axp20x: Migrate to regulator core's simplified DT parsing code A common simplified DT parsing code for regulators was introduced in commit a0c7b164ad11 ("regulator: of: Provide simplified DT parsing method"). This is very similar to our own code, so get rid of ours and use the common code. Signed-off-by: Chen-Yu Tsai Signed-off-by: Mark Brown diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index 441ae01..e4331f5 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -152,22 +152,6 @@ static const struct regulator_desc axp20x_regulators[] = { AXP20X_IO_ENABLED, AXP20X_IO_DISABLED), }; -#define AXP_MATCH(_name, _id) \ - [AXP20X_##_id] = { \ - .name = #_name, \ - .driver_data = (void *) &axp20x_regulators[AXP20X_##_id], \ - } - -static struct of_regulator_match axp20x_matches[] = { - AXP_MATCH(dcdc2, DCDC2), - AXP_MATCH(dcdc3, DCDC3), - AXP_MATCH(ldo1, LDO1), - AXP_MATCH(ldo2, LDO2), - AXP_MATCH(ldo3, LDO3), - AXP_MATCH(ldo4, LDO4), - AXP_MATCH(ldo5, LDO5), -}; - static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq) { struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); @@ -202,13 +186,6 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev) if (!regulators) { dev_warn(&pdev->dev, "regulators node not found\n"); } else { - ret = of_regulator_match(&pdev->dev, regulators, axp20x_matches, - ARRAY_SIZE(axp20x_matches)); - if (ret < 0) { - dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret); - return ret; - } - dcdcfreq = 1500; of_property_read_u32(regulators, "x-powers,dcdc-freq", &dcdcfreq); ret = axp20x_set_dcdc_freq(pdev, dcdcfreq); @@ -242,23 +219,17 @@ static int axp20x_regulator_probe(struct platform_device *pdev) { struct regulator_dev *rdev; struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); - struct regulator_config config = { }; - struct regulator_init_data *init_data; + struct regulator_config config = { + .dev = pdev->dev.parent, + .regmap = axp20x->regmap, + }; int ret, i; u32 workmode; - ret = axp20x_regulator_parse_dt(pdev); - if (ret) - return ret; + /* This only sets the dcdc freq. Ignore any errors */ + axp20x_regulator_parse_dt(pdev); for (i = 0; i < AXP20X_REG_ID_MAX; i++) { - init_data = axp20x_matches[i].init_data; - - config.dev = pdev->dev.parent; - config.init_data = init_data; - config.regmap = axp20x->regmap; - config.of_node = axp20x_matches[i].of_node; - rdev = devm_regulator_register(&pdev->dev, &axp20x_regulators[i], &config); if (IS_ERR(rdev)) { @@ -268,7 +239,8 @@ static int axp20x_regulator_probe(struct platform_device *pdev) return PTR_ERR(rdev); } - ret = of_property_read_u32(axp20x_matches[i].of_node, "x-powers,dcdc-workmode", + ret = of_property_read_u32(rdev->dev.of_node, + "x-powers,dcdc-workmode", &workmode); if (!ret) { if (axp20x_set_dcdc_workmode(rdev, i, workmode)) -- cgit v0.10.2 From 7ff9d6714a5c97fb448c53aae801af3d529ecb56 Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Fri, 9 Jan 2015 14:36:36 +0800 Subject: ASoC: Intel: Split hsw_pcm_data for playback and capture There may be 2 pcm streams for a same DAI at most, and these 2 streams should have different hsw_pcm_data, e.g. they have different persistent data, so here we need split hsw_pcm_data for playback and capture, to make sure they won't be mixed and keep cleaned. Signed-off-by: Jie Yang Reviewed-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 5448f79..ad7f4a5 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -135,7 +135,17 @@ struct hsw_priv_data { struct snd_dma_buffer dmab[HSW_PCM_COUNT][2]; /* DAI data */ - struct hsw_pcm_data pcm[HSW_PCM_COUNT]; + struct hsw_pcm_data pcm[HSW_PCM_COUNT][2]; +}; + + +/* static mappings between PCMs and modules - may be dynamic in future */ +static struct hsw_pcm_module_map mod_map[] = { + {HSW_PCM_DAI_ID_SYSTEM, 0, SST_HSW_MODULE_PCM_SYSTEM}, + {HSW_PCM_DAI_ID_OFFLOAD0, 0, SST_HSW_MODULE_PCM}, + {HSW_PCM_DAI_ID_OFFLOAD1, 0, SST_HSW_MODULE_PCM}, + {HSW_PCM_DAI_ID_LOOPBACK, 1, SST_HSW_MODULE_PCM_REFERENCE}, + {HSW_PCM_DAI_ID_SYSTEM, 1, SST_HSW_MODULE_PCM_CAPTURE}, }; static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data); @@ -168,9 +178,14 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol, (struct soc_mixer_control *)kcontrol->private_value; struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); - struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg]; + struct hsw_pcm_data *pcm_data; struct sst_hsw *hsw = pdata->hsw; u32 volume; + int dai, stream; + + dai = mod_map[mc->reg].dai_id; + stream = mod_map[mc->reg].stream; + pcm_data = &pdata->pcm[dai][stream]; mutex_lock(&pcm_data->mutex); pm_runtime_get_sync(pdata->dev); @@ -212,9 +227,14 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol, (struct soc_mixer_control *)kcontrol->private_value; struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); - struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg]; + struct hsw_pcm_data *pcm_data; struct sst_hsw *hsw = pdata->hsw; u32 volume; + int dai, stream; + + dai = mod_map[mc->reg].dai_id; + stream = mod_map[mc->reg].stream; + pcm_data = &pdata->pcm[dai][stream]; mutex_lock(&pcm_data->mutex); pm_runtime_get_sync(pdata->dev); @@ -309,7 +329,7 @@ static const struct snd_kcontrol_new hsw_volume_controls[] = { ARRAY_SIZE(volume_map) - 1, 0, hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), /* Mic Capture volume */ - SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 0, 0, 8, + SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8, ARRAY_SIZE(volume_map) - 1, 0, hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), }; @@ -353,7 +373,7 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(rtd->platform); - struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); + struct hsw_pcm_data *pcm_data; struct sst_hsw *hsw = pdata->hsw; struct sst_module *module_data; struct sst_dsp *dsp; @@ -362,7 +382,10 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, enum sst_hsw_stream_path_id path_id; u32 rate, bits, map, pages, module_id; u8 channels; - int ret; + int ret, dai; + + dai = mod_map[rtd->cpu_dai->id].dai_id; + pcm_data = &pdata->pcm[dai][substream->stream]; /* check if we are being called a subsequent time */ if (pcm_data->allocated) { @@ -552,8 +575,12 @@ static int hsw_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(rtd->platform); - struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); + struct hsw_pcm_data *pcm_data; struct sst_hsw *hsw = pdata->hsw; + int dai; + + dai = mod_map[rtd->cpu_dai->id].dai_id; + pcm_data = &pdata->pcm[dai][substream->stream]; switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -597,11 +624,16 @@ static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(rtd->platform); - struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); + struct hsw_pcm_data *pcm_data; struct sst_hsw *hsw = pdata->hsw; snd_pcm_uframes_t offset; uint64_t ppos; - u32 position = sst_hsw_get_dsp_position(hsw, pcm_data->stream); + u32 position; + int dai; + + dai = mod_map[rtd->cpu_dai->id].dai_id; + pcm_data = &pdata->pcm[dai][substream->stream]; + position = sst_hsw_get_dsp_position(hsw, pcm_data->stream); offset = bytes_to_frames(runtime, position); ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream); @@ -618,8 +650,10 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream) snd_soc_platform_get_drvdata(rtd->platform); struct hsw_pcm_data *pcm_data; struct sst_hsw *hsw = pdata->hsw; + int dai; - pcm_data = &pdata->pcm[rtd->cpu_dai->id]; + dai = mod_map[rtd->cpu_dai->id].dai_id; + pcm_data = &pdata->pcm[dai][substream->stream]; mutex_lock(&pcm_data->mutex); pm_runtime_get_sync(pdata->dev); @@ -648,9 +682,12 @@ static int hsw_pcm_close(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(rtd->platform); - struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); + struct hsw_pcm_data *pcm_data; struct sst_hsw *hsw = pdata->hsw; - int ret; + int ret, dai; + + dai = mod_map[rtd->cpu_dai->id].dai_id; + pcm_data = &pdata->pcm[dai][substream->stream]; mutex_lock(&pcm_data->mutex); ret = sst_hsw_stream_reset(hsw, pcm_data->stream); @@ -685,15 +722,6 @@ static struct snd_pcm_ops hsw_pcm_ops = { .page = snd_pcm_sgbuf_ops_page, }; -/* static mappings between PCMs and modules - may be dynamic in future */ -static struct hsw_pcm_module_map mod_map[] = { - {HSW_PCM_DAI_ID_SYSTEM, 0, SST_HSW_MODULE_PCM_SYSTEM}, - {HSW_PCM_DAI_ID_OFFLOAD0, 0, SST_HSW_MODULE_PCM}, - {HSW_PCM_DAI_ID_OFFLOAD1, 0, SST_HSW_MODULE_PCM}, - {HSW_PCM_DAI_ID_LOOPBACK, 1, SST_HSW_MODULE_PCM_REFERENCE}, - {HSW_PCM_DAI_ID_SYSTEM, 1, SST_HSW_MODULE_PCM_CAPTURE}, -}; - static int hsw_pcm_create_modules(struct hsw_priv_data *pdata) { struct sst_hsw *hsw = pdata->hsw; @@ -701,7 +729,7 @@ static int hsw_pcm_create_modules(struct hsw_priv_data *pdata) int i; for (i = 0; i < ARRAY_SIZE(mod_map); i++) { - pcm_data = &pdata->pcm[i]; + pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream]; /* create new runtime module, use same offset if recreated */ pcm_data->runtime = sst_hsw_runtime_module_create(hsw, @@ -716,7 +744,7 @@ static int hsw_pcm_create_modules(struct hsw_priv_data *pdata) err: for (--i; i >= 0; i--) { - pcm_data = &pdata->pcm[i]; + pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream]; sst_hsw_runtime_module_free(pcm_data->runtime); } @@ -729,7 +757,7 @@ static void hsw_pcm_free_modules(struct hsw_priv_data *pdata) int i; for (i = 0; i < ARRAY_SIZE(mod_map); i++) { - pcm_data = &pdata->pcm[i]; + pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream]; sst_hsw_runtime_module_free(pcm_data->runtime); } @@ -757,7 +785,10 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd) return ret; } } - priv_data->pcm[rtd->cpu_dai->id].hsw_pcm = pcm; + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) + priv_data->pcm[rtd->cpu_dai->id][SNDRV_PCM_STREAM_PLAYBACK].hsw_pcm = pcm; + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) + priv_data->pcm[rtd->cpu_dai->id][SNDRV_PCM_STREAM_CAPTURE].hsw_pcm = pcm; return ret; } @@ -866,10 +897,9 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) /* allocate DSP buffer page tables */ for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) { - mutex_init(&priv_data->pcm[i].mutex); - /* playback */ if (hsw_dais[i].playback.channels_min) { + mutex_init(&priv_data->pcm[i][SNDRV_PCM_STREAM_PLAYBACK].mutex); ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev, PAGE_SIZE, &priv_data->dmab[i][0]); if (ret < 0) @@ -878,6 +908,7 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) /* capture */ if (hsw_dais[i].capture.channels_min) { + mutex_init(&priv_data->pcm[i][SNDRV_PCM_STREAM_CAPTURE].mutex); ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev, PAGE_SIZE, &priv_data->dmab[i][1]); if (ret < 0) @@ -1076,7 +1107,7 @@ static void hsw_pcm_complete(struct device *dev) } for (i = 0; i < ARRAY_SIZE(mod_map); i++) { - pcm_data = &pdata->pcm[i]; + pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream]; if (!pcm_data->substream) continue; @@ -1110,7 +1141,7 @@ static int hsw_pcm_prepare(struct device *dev) return 0; /* suspend all active streams */ for (i = 0; i < ARRAY_SIZE(mod_map); i++) { - pcm_data = &pdata->pcm[i]; + pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream]; if (!pcm_data->substream) continue; @@ -1129,7 +1160,7 @@ static int hsw_pcm_prepare(struct device *dev) /* preserve persistent memory */ for (i = 0; i < ARRAY_SIZE(mod_map); i++) { - pcm_data = &pdata->pcm[i]; + pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream]; if (!pcm_data->substream) continue; -- cgit v0.10.2 From bdfbf2550d85b0e645a0bb9b3abd3b0a5448eacf Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Thu, 8 Jan 2015 10:31:05 +0800 Subject: ASoC: rt5677: Modify the behavior that updates the PLL parameter. The patch modified the behavior that updates the PLL parameter. It set the update bit before the PLL power up. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 81fe146..8018c99 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -2082,10 +2082,14 @@ static int rt5677_set_pll1_event(struct snd_soc_dapm_widget *w, struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); switch (event) { - case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_PRE_PMU: regmap_update_bits(rt5677->regmap, RT5677_PLL1_CTRL2, 0x2, 0x2); + break; + + case SND_SOC_DAPM_POST_PMU: regmap_update_bits(rt5677->regmap, RT5677_PLL1_CTRL2, 0x2, 0x0); break; + default: return 0; } @@ -2100,10 +2104,14 @@ static int rt5677_set_pll2_event(struct snd_soc_dapm_widget *w, struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); switch (event) { - case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_PRE_PMU: regmap_update_bits(rt5677->regmap, RT5677_PLL2_CTRL2, 0x2, 0x2); + break; + + case SND_SOC_DAPM_POST_PMU: regmap_update_bits(rt5677->regmap, RT5677_PLL2_CTRL2, 0x2, 0x0); break; + default: return 0; } @@ -2211,9 +2219,11 @@ static int rt5677_vref_event(struct snd_soc_dapm_widget *w, static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("PLL1", RT5677_PWR_ANLG2, RT5677_PWR_PLL1_BIT, - 0, rt5677_set_pll1_event, SND_SOC_DAPM_POST_PMU), + 0, rt5677_set_pll1_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("PLL2", RT5677_PWR_ANLG2, RT5677_PWR_PLL2_BIT, - 0, rt5677_set_pll2_event, SND_SOC_DAPM_POST_PMU), + 0, rt5677_set_pll2_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU), /* Input Side */ /* micbias */ -- cgit v0.10.2 From 145b3fe579db66fbe999a2bc3fd5b63dffe9636d Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Tue, 2 Dec 2014 17:35:04 +0100 Subject: PCI: Generate uppercase hex for modalias var in uevent Some implementations of modprobe fail to load the driver for a PCI device automatically because the "interface" part of the modalias from the kernel is lowercase, and the modalias from file2alias is uppercase. The "interface" is the low-order byte of the Class Code, defined in PCI r3.0, Appendix D. Most interface types defined in the spec do not use alpha characters, so they won't be affected. For example, 00h, 01h, 10h, 20h, etc. are unaffected. Print the "interface" byte of the Class Code in uppercase hex, as we already do for the Vendor ID, Device ID, Class, etc. Commit 89ec3dcf17fd ("PCI: Generate uppercase hex for modalias interface class") fixed only half of the problem. Some udev implementations rely on the uevent file and not the modalias file. Fixes: d1ded203adf1 ("PCI: add MODALIAS to hotplug event for pci devices") Fixes: 89ec3dcf17fd ("PCI: Generate uppercase hex for modalias interface class") Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Bjorn Helgaas Acked-by: Greg Kroah-Hartman CC: stable@vger.kernel.org diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 887e6bd..09a66ba 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1383,7 +1383,7 @@ static int pci_uevent(struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "PCI_SLOT_NAME=%s", pci_name(pdev))) return -ENOMEM; - if (add_uevent_var(env, "MODALIAS=pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x", + if (add_uevent_var(env, "MODALIAS=pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02X", pdev->vendor, pdev->device, pdev->subsystem_vendor, pdev->subsystem_device, (u8)(pdev->class >> 16), (u8)(pdev->class >> 8), -- cgit v0.10.2 From 3a8e5019846736046c0af9dbee3f921c0456141a Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Thu, 8 Jan 2015 22:56:30 +0100 Subject: ASoC: pcm512x: Fix DSP program selection The DSP programs are listed out of order. Signed-off-by: Peter Rosin Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index e5f2fb8..30c673c 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -188,8 +188,8 @@ static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 80, 0); static const char * const pcm512x_dsp_program_texts[] = { "FIR interpolation with de-emphasis", "Low latency IIR with de-emphasis", - "Fixed process flow", "High attenuation with de-emphasis", + "Fixed process flow", "Ringing-less low latency FIR", }; -- cgit v0.10.2 From 94a90312e4ef1b03469086d41fedfa55e67a1532 Mon Sep 17 00:00:00 2001 From: Chris J Arges Date: Fri, 5 Dec 2014 17:02:42 -0600 Subject: PCI/ASPM: Use standard parsing functions for sysfs setters The functions link_state_store() and clk_ctl_store() had just subtracted ASCII '0' from input which could lead to undesired results. Instead, use Linux string functions to safely parse input. [bhelgaas: check kstrtouint() return value] Signed-off-by: Chris J Arges Signed-off-by: Bjorn Helgaas diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index e1e7026..820740a 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -859,7 +859,10 @@ static ssize_t link_state_store(struct device *dev, { struct pci_dev *pdev = to_pci_dev(dev); struct pcie_link_state *link, *root = pdev->link_state->root; - u32 val = buf[0] - '0', state = 0; + u32 val, state = 0; + + if (kstrtouint(buf, 10, &val)) + return -EINVAL; if (aspm_disabled) return -EPERM; @@ -900,15 +903,14 @@ static ssize_t clk_ctl_store(struct device *dev, size_t n) { struct pci_dev *pdev = to_pci_dev(dev); - int state; + bool state; - if (n < 1) + if (strtobool(buf, &state)) return -EINVAL; - state = buf[0]-'0'; down_read(&pci_bus_sem); mutex_lock(&aspm_lock); - pcie_set_clkpm_nocheck(pdev->link_state, !!state); + pcie_set_clkpm_nocheck(pdev->link_state, state); mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); -- cgit v0.10.2 From 4808c35e22a81beec8f36b1a22793e5b153d7a11 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 9 Jan 2015 11:30:32 -0700 Subject: PCI: keystone: Fix misspelling of current function in debug output Replace a misspelled function name by %s and then __func__. The function name contains pcie, not pci as in the string. This was done using Coccinelle, including the use of Levenshtein distance, as proposed by Rasmus Villemoes. Signed-off-by: Julia Lawall Signed-off-by: Bjorn Helgaas Acked-by: Murali Karicheri diff --git a/drivers/pci/host/pci-keystone.c b/drivers/pci/host/pci-keystone.c index 23a1d97..75333b0 100644 --- a/drivers/pci/host/pci-keystone.c +++ b/drivers/pci/host/pci-keystone.c @@ -119,7 +119,7 @@ static void ks_pcie_msi_irq_handler(unsigned int irq, struct irq_desc *desc) struct pcie_port *pp = &ks_pcie->pp; struct irq_chip *chip = irq_desc_get_chip(desc); - dev_dbg(pp->dev, "ks_pci_msi_irq_handler, irq %d\n", irq); + dev_dbg(pp->dev, "%s, irq %d\n", __func__, irq); /* * The chained irq handler installation would have replaced normal -- cgit v0.10.2 From 3c066c642aba407819cf83413025705c6e2a68f9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 9 Jan 2015 21:28:21 +0100 Subject: ASoC: ttc-dkb: Remove unnecessary snd_soc_dapm_disable_pin() calls The "Headset Mic 2" and the "Headset Stereophone" widget are managed by the jack detection logic. Their will be set depending on whether something is connected to the jack or not. There is no need to manually change the state beforehand as it will be overwritten anyway. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/ttc-dkb.c b/sound/soc/pxa/ttc-dkb.c index e3d7257a..5001dbb 100644 --- a/sound/soc/pxa/ttc-dkb.c +++ b/sound/soc/pxa/ttc-dkb.c @@ -76,10 +76,6 @@ static const struct snd_soc_dapm_route ttc_audio_map[] = { static int ttc_pm860x_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_disable_pin(dapm, "Headset Mic 2"); - snd_soc_dapm_disable_pin(dapm, "Headset Stereophone"); /* Headset jack detection */ snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE -- cgit v0.10.2 From 46d0d8df157b4d9bdf7849ce4ae795d399018669 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 9 Jan 2015 22:03:26 +0100 Subject: ASoC: corgi: Automatically disconnect non-connected pins All DAPM input and output pins of the wm8994 are either used in the card's DAPM routing table or are marked as not connected. Set the fully_routed flag of the card instead of manually marking the unused inputs and outputs as not connected. This makes the code a bit shorter and cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index b7cd0a7..3580d10 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -259,20 +259,6 @@ static const struct snd_kcontrol_new wm8731_corgi_controls[] = { corgi_set_spk), }; -/* - * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device - */ -static int corgi_wm8731_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_nc_pin(dapm, "LLINEIN"); - snd_soc_dapm_nc_pin(dapm, "RLINEIN"); - - return 0; -} - /* corgi digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link corgi_dai = { .name = "WM8731", @@ -281,7 +267,6 @@ static struct snd_soc_dai_link corgi_dai = { .codec_dai_name = "wm8731-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm8731.0-001b", - .init = corgi_wm8731_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, .ops = &corgi_ops, @@ -300,6 +285,7 @@ static struct snd_soc_card corgi = { .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets), .dapm_routes = corgi_audio_map, .num_dapm_routes = ARRAY_SIZE(corgi_audio_map), + .fully_routed = true, }; static int corgi_probe(struct platform_device *pdev) -- cgit v0.10.2 From dff6c9642369e7cb0f5bf8b280a270c4a4bf797d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 9 Jan 2015 22:03:27 +0100 Subject: ASoC: e740: Automatically disconnect non-connected pins All DAPM input and output pins of the wm9705 are either used in the card's DAPM routing table or are marked as not connected. Set the fully_routed flag of the card instead of manually marking the unused inputs and outputs as not connected. This makes the code a bit shorter and cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c index 7c691aa..d72e124 100644 --- a/sound/soc/pxa/e740_wm9705.c +++ b/sound/soc/pxa/e740_wm9705.c @@ -88,24 +88,6 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Mic Amp", NULL, "Mic (Internal)"}, }; -static int e740_ac97_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_nc_pin(dapm, "HPOUTL"); - snd_soc_dapm_nc_pin(dapm, "HPOUTR"); - snd_soc_dapm_nc_pin(dapm, "PHONE"); - snd_soc_dapm_nc_pin(dapm, "LINEINL"); - snd_soc_dapm_nc_pin(dapm, "LINEINR"); - snd_soc_dapm_nc_pin(dapm, "CDINL"); - snd_soc_dapm_nc_pin(dapm, "CDINR"); - snd_soc_dapm_nc_pin(dapm, "PCBEEP"); - snd_soc_dapm_nc_pin(dapm, "MIC2"); - - return 0; -} - static struct snd_soc_dai_link e740_dai[] = { { .name = "AC97", @@ -114,7 +96,6 @@ static struct snd_soc_dai_link e740_dai[] = { .codec_dai_name = "wm9705-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9705-codec", - .init = e740_ac97_init, }, { .name = "AC97 Aux", @@ -136,6 +117,7 @@ static struct snd_soc_card e740 = { .num_dapm_widgets = ARRAY_SIZE(e740_dapm_widgets), .dapm_routes = audio_map, .num_dapm_routes = ARRAY_SIZE(audio_map), + .fully_routed = true, }; static struct gpio e740_audio_gpios[] = { -- cgit v0.10.2 From 7523f69e584cba16775cbc70a4f0adcb84625894 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 9 Jan 2015 22:03:28 +0100 Subject: ASoC: e750: Automatically disconnect non-connected pins All DAPM input and output pins of the wm9705 are either used in the card's DAPM routing table or are marked as not connected. Set the fully_routed flag of the card instead of manually marking the unused inputs and outputs as not connected. This makes the code a bit shorter and cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c index 30544b6..48f2d7c 100644 --- a/sound/soc/pxa/e750_wm9705.c +++ b/sound/soc/pxa/e750_wm9705.c @@ -70,24 +70,6 @@ static const struct snd_soc_dapm_route audio_map[] = { {"MIC1", NULL, "Mic (Internal)"}, }; -static int e750_ac97_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_nc_pin(dapm, "LOUT"); - snd_soc_dapm_nc_pin(dapm, "ROUT"); - snd_soc_dapm_nc_pin(dapm, "PHONE"); - snd_soc_dapm_nc_pin(dapm, "LINEINL"); - snd_soc_dapm_nc_pin(dapm, "LINEINR"); - snd_soc_dapm_nc_pin(dapm, "CDINL"); - snd_soc_dapm_nc_pin(dapm, "CDINR"); - snd_soc_dapm_nc_pin(dapm, "PCBEEP"); - snd_soc_dapm_nc_pin(dapm, "MIC2"); - - return 0; -} - static struct snd_soc_dai_link e750_dai[] = { { .name = "AC97", @@ -96,7 +78,6 @@ static struct snd_soc_dai_link e750_dai[] = { .codec_dai_name = "wm9705-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9705-codec", - .init = e750_ac97_init, /* use ops to check startup state */ }, { @@ -119,6 +100,7 @@ static struct snd_soc_card e750 = { .num_dapm_widgets = ARRAY_SIZE(e750_dapm_widgets), .dapm_routes = audio_map, .num_dapm_routes = ARRAY_SIZE(audio_map), + .fully_routed = true, }; static struct gpio e750_audio_gpios[] = { -- cgit v0.10.2 From c529d0a420ba4ba2031f61a4b86f59e31ddc964e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 9 Jan 2015 22:03:29 +0100 Subject: ASoC: hx4700: Automatically disconnect non-connected pins All DAPM input and output pins of the ak4641 are either used in the card's DAPM routing table or are marked as not connected. Set the fully_routed flag of the card instead of manually marking the unused inputs and outputs as not connected. This makes the code a bit shorter and cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c index ce26551..73eb5dd 100644 --- a/sound/soc/pxa/hx4700.c +++ b/sound/soc/pxa/hx4700.c @@ -127,15 +127,8 @@ static const struct snd_soc_dapm_route hx4700_audio_map[] = { static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; int err; - /* NC codec pins */ - /* FIXME: is anything connected here? */ - snd_soc_dapm_nc_pin(dapm, "MOUT1"); - snd_soc_dapm_nc_pin(dapm, "MICEXT"); - snd_soc_dapm_nc_pin(dapm, "AUX"); - /* Jack detection API stuff */ err = snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, &hs_jack); @@ -184,6 +177,7 @@ static struct snd_soc_card snd_soc_card_hx4700 = { .num_dapm_widgets = ARRAY_SIZE(hx4700_dapm_widgets), .dapm_routes = hx4700_audio_map, .num_dapm_routes = ARRAY_SIZE(hx4700_audio_map), + .fully_routed = true, }; static struct gpio hx4700_audio_gpios[] = { -- cgit v0.10.2 From 3cfaaaa0e729aa10a5e2dfe1efb1c05a6f5a89d1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 9 Jan 2015 22:03:30 +0100 Subject: ASoC: magician: Automatically disconnect non-connected pins All DAPM input and output pins of the uda1380 are either used in the card's DAPM routing table or are marked as not connected. Set the fully_routed flag of the card instead of manually marking the unused inputs and outputs as not connected. This makes the code a bit shorter and cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c index 259e048..241d0be 100644 --- a/sound/soc/pxa/magician.c +++ b/sound/soc/pxa/magician.c @@ -391,25 +391,6 @@ static const struct snd_kcontrol_new uda1380_magician_controls[] = { magician_get_input, magician_set_input), }; -/* - * Logic for a uda1380 as connected on a HTC Magician - */ -static int magician_uda1380_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - /* NC codec pins */ - snd_soc_dapm_nc_pin(dapm, "VOUTLHP"); - snd_soc_dapm_nc_pin(dapm, "VOUTRHP"); - - /* FIXME: is anything connected here? */ - snd_soc_dapm_nc_pin(dapm, "VINL"); - snd_soc_dapm_nc_pin(dapm, "VINR"); - - return 0; -} - /* magician digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link magician_dai[] = { { @@ -419,7 +400,6 @@ static struct snd_soc_dai_link magician_dai[] = { .codec_dai_name = "uda1380-hifi-playback", .platform_name = "pxa-pcm-audio", .codec_name = "uda1380-codec.0-0018", - .init = magician_uda1380_init, .ops = &magician_playback_ops, }, { @@ -446,6 +426,7 @@ static struct snd_soc_card snd_soc_card_magician = { .num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets), .dapm_routes = audio_map, .num_dapm_routes = ARRAY_SIZE(audio_map), + .fully_routed = true, }; static struct platform_device *magician_snd_device; -- cgit v0.10.2 From b68ef0e0368cda4e17541ae87bbb5b40dc15cbc1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 9 Jan 2015 22:03:31 +0100 Subject: ASoC: palm27x: Fix microphone route The microphone route has sink and source swapped. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index 1eebca2..5a0b82f 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -68,7 +68,7 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Ext. Speaker", NULL, "ROUT2"}, /* mic connected to MIC1 */ - {"Ext. Microphone", NULL, "MIC1"}, + {"MIC1", NULL, "Ext. Microphone"}, }; static struct snd_soc_card palm27x_asoc; -- cgit v0.10.2 From 19a3477ec0eb635bdebd1dd905b1cee9a3c06c96 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 9 Jan 2015 22:03:32 +0100 Subject: ASoC: palm27x: Automatically disconnect non-connected pins All DAPM input and output pins of the wm9712 are either used in the card's DAPM routing table or are marked as not connected. Set the fully_routed flag of the card instead of manually marking the unused inputs and outputs as not connected. This makes the code a bit shorter and cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index 5a0b82f..910336c 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -76,18 +76,8 @@ static struct snd_soc_card palm27x_asoc; static int palm27x_ac97_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; int err; - /* not connected pins */ - snd_soc_dapm_nc_pin(dapm, "OUT3"); - snd_soc_dapm_nc_pin(dapm, "MONOOUT"); - snd_soc_dapm_nc_pin(dapm, "LINEINL"); - snd_soc_dapm_nc_pin(dapm, "LINEINR"); - snd_soc_dapm_nc_pin(dapm, "PCBEEP"); - snd_soc_dapm_nc_pin(dapm, "PHONE"); - snd_soc_dapm_nc_pin(dapm, "MIC2"); - /* Jack detection API stuff */ err = snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, &hs_jack); @@ -133,7 +123,8 @@ static struct snd_soc_card palm27x_asoc = { .dapm_widgets = palm27x_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(palm27x_dapm_widgets), .dapm_routes = audio_map, - .num_dapm_routes = ARRAY_SIZE(audio_map) + .num_dapm_routes = ARRAY_SIZE(audio_map), + .fully_routed = true, }; static int palm27x_asoc_probe(struct platform_device *pdev) -- cgit v0.10.2 From c4b7586cc972ec8cca77d2f6faed1f19a11d6445 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 9 Jan 2015 22:03:33 +0100 Subject: ASoC: spitz: Automatically disconnect non-connected pins All DAPM input and output pins of the wm8750 are either used in the card's DAPM routing table or are marked as not connected. Set the fully_routed flag of the card instead of manually marking the unused inputs and outputs as not connected. This makes the code a bit shorter and cleaner. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index a6d680a..461123a 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -256,26 +256,6 @@ static const struct snd_kcontrol_new wm8750_spitz_controls[] = { spitz_set_spk), }; -/* - * Logic for a wm8750 as connected on a Sharp SL-Cxx00 Device - */ -static int spitz_wm8750_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - /* NC codec pins */ - snd_soc_dapm_nc_pin(dapm, "RINPUT1"); - snd_soc_dapm_nc_pin(dapm, "LINPUT2"); - snd_soc_dapm_nc_pin(dapm, "RINPUT2"); - snd_soc_dapm_nc_pin(dapm, "LINPUT3"); - snd_soc_dapm_nc_pin(dapm, "RINPUT3"); - snd_soc_dapm_nc_pin(dapm, "OUT3"); - snd_soc_dapm_nc_pin(dapm, "MONO1"); - - return 0; -} - /* spitz digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link spitz_dai = { .name = "wm8750", @@ -284,7 +264,6 @@ static struct snd_soc_dai_link spitz_dai = { .codec_dai_name = "wm8750-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm8750.0-001b", - .init = spitz_wm8750_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, .ops = &spitz_ops, @@ -303,6 +282,7 @@ static struct snd_soc_card snd_soc_spitz = { .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets), .dapm_routes = spitz_audio_map, .num_dapm_routes = ARRAY_SIZE(spitz_audio_map), + .fully_routed = true, }; static int spitz_probe(struct platform_device *pdev) -- cgit v0.10.2 From 99590ba565a22c9f58f7528a94881d0455eef018 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 9 Jan 2015 14:03:04 -0600 Subject: livepatch: fix deferred module patching order When applying multiple patches to a module, if the module is loaded after the patches are loaded, the patches are applied in reverse order: $ insmod patch1.ko [ 43.172992] livepatch: enabling patch 'patch1' $ insmod patch2.ko [ 46.571563] livepatch: enabling patch 'patch2' $ modprobe nfsd [ 52.888922] livepatch: applying patch 'patch2' to loading module 'nfsd' [ 52.899847] livepatch: applying patch 'patch1' to loading module 'nfsd' Fix the loading order by storing the klp_patches list in queue order. Signed-off-by: Josh Poimboeuf Signed-off-by: Jiri Kosina diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index ce42d3b..3d9c00b 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c @@ -739,7 +739,7 @@ static int klp_init_patch(struct klp_patch *patch) goto free; } - list_add(&patch->list, &klp_patches); + list_add_tail(&patch->list, &klp_patches); mutex_unlock(&klp_mutex); -- cgit v0.10.2 From 95bbbe9a6663635b6cdef20c01c0ea32ec6987e4 Mon Sep 17 00:00:00 2001 From: Gabriele Mazzotta Date: Thu, 8 Jan 2015 19:41:34 +0100 Subject: ahci: Use dev_info() to inform about the lack of Device Sleep support According to the Serial ATA AHCI specification, Device Sleep is an optional feature and as such no errors should be printed if it's missing. Keep informing users, but use dev_info() instead of dev_err(). Signed-off-by: Gabriele Mazzotta Signed-off-by: Tejun Heo diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 97683e4..61a9c07 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -2003,7 +2003,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) devslp = readl(port_mmio + PORT_DEVSLP); if (!(devslp & PORT_DEVSLP_DSP)) { - dev_err(ap->host->dev, "port does not support device sleep\n"); + dev_info(ap->host->dev, "port does not support device sleep\n"); return; } -- cgit v0.10.2 From 0efbb786f1a3e13d28861ff41aae8dc4be314608 Mon Sep 17 00:00:00 2001 From: Andrew Clausen Date: Fri, 9 Jan 2015 20:24:55 +0000 Subject: rfkill: document rfkill module parameters Document the rfkill module parameters default_state and master_switch_mode. Signed-off-by: Andrew Clausen [rewrite commit message] Signed-off-by: Johannes Berg diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index eacb2e0..f55d465 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -3180,6 +3180,18 @@ bytes respectively. Such letter suffixes can also be entirely omitted. retain_initrd [RAM] Keep initrd memory after extraction + rfkill.default_state= + 0 "airplane mode". All wifi, bluetooth, wimax, gps, fm, + etc. communication is blocked by default. + 1 Unblocked. + + rfkill.master_switch_mode= + 0 The "airplane mode" button does nothing. + 1 The "airplane mode" button toggles between everything + blocked and the previous configuration. + 2 The "airplane mode" button toggles between everything + blocked and everything unblocked. + rhash_entries= [KNL,NET] Set number of hash buckets for route cache diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt index 427e897..2ee6ef9 100644 --- a/Documentation/rfkill.txt +++ b/Documentation/rfkill.txt @@ -25,6 +25,9 @@ whether they can be changed or not: - soft block: writable radio block (need not be readable) that is set by the system software. +The rfkill subsystem has two parameters, rfkill.default_state and +rfkill.master_switch_mode, which are documented in kernel-parameters.txt. + 2. Implementation details -- cgit v0.10.2 From 9eb457b547cc731bc2fc251bd79891a60c64fc3e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 4 Dec 2014 12:32:50 +0200 Subject: pinctrl: cherryview: Save and restore pin configs over system sleep Before resuming from system sleep BIOS restores its view of pin configuration. If we have configured some pins differently from that, for instance some driver requested a pin as a GPIO but it was not in GPIO mode originally, our view of the pin configuration will not match the hardware state anymore. This patch saves the pin configuration and interrupt mask registers on suspend and restores them on exit. This should make sure that the previously configured state is still in effect. Signed-off-by: Mika Westerberg Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index e9f8b39..dde67d4 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -148,6 +148,11 @@ struct chv_community { size_t ngpios; }; +struct chv_pin_context { + u32 padctrl0; + u32 padctrl1; +}; + /** * struct chv_pinctrl - CHV pinctrl private structure * @dev: Pointer to the parent device @@ -172,6 +177,8 @@ struct chv_pinctrl { spinlock_t lock; unsigned intr_lines[16]; const struct chv_community *community; + u32 saved_intmask; + struct chv_pin_context *saved_pin_context; }; #define gpiochip_to_pinctrl(c) container_of(c, struct chv_pinctrl, chip) @@ -1443,6 +1450,14 @@ static int chv_pinctrl_probe(struct platform_device *pdev) spin_lock_init(&pctrl->lock); pctrl->dev = &pdev->dev; +#ifdef CONFIG_PM_SLEEP + pctrl->saved_pin_context = devm_kcalloc(pctrl->dev, + pctrl->community->npins, sizeof(*pctrl->saved_pin_context), + GFP_KERNEL); + if (!pctrl->saved_pin_context) + return -ENOMEM; +#endif + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); pctrl->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(pctrl->regs)) @@ -1486,6 +1501,94 @@ static int chv_pinctrl_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int chv_pinctrl_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct chv_pinctrl *pctrl = platform_get_drvdata(pdev); + int i; + + pctrl->saved_intmask = readl(pctrl->regs + CHV_INTMASK); + + for (i = 0; i < pctrl->community->npins; i++) { + const struct pinctrl_pin_desc *desc; + struct chv_pin_context *ctx; + void __iomem *reg; + + desc = &pctrl->community->pins[i]; + if (chv_pad_locked(pctrl, desc->number)) + continue; + + ctx = &pctrl->saved_pin_context[i]; + + reg = chv_padreg(pctrl, desc->number, CHV_PADCTRL0); + ctx->padctrl0 = readl(reg) & ~CHV_PADCTRL0_GPIORXSTATE; + + reg = chv_padreg(pctrl, desc->number, CHV_PADCTRL1); + ctx->padctrl1 = readl(reg); + } + + return 0; +} + +static int chv_pinctrl_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct chv_pinctrl *pctrl = platform_get_drvdata(pdev); + int i; + + /* + * Mask all interrupts before restoring per-pin configuration + * registers because we don't know in which state BIOS left them + * upon exiting suspend. + */ + chv_writel(0, pctrl->regs + CHV_INTMASK); + + for (i = 0; i < pctrl->community->npins; i++) { + const struct pinctrl_pin_desc *desc; + const struct chv_pin_context *ctx; + void __iomem *reg; + u32 val; + + desc = &pctrl->community->pins[i]; + if (chv_pad_locked(pctrl, desc->number)) + continue; + + ctx = &pctrl->saved_pin_context[i]; + + /* Only restore if our saved state differs from the current */ + reg = chv_padreg(pctrl, desc->number, CHV_PADCTRL0); + val = readl(reg) & ~CHV_PADCTRL0_GPIORXSTATE; + if (ctx->padctrl0 != val) { + chv_writel(ctx->padctrl0, reg); + dev_dbg(pctrl->dev, "restored pin %2u ctrl0 0x%08x\n", + desc->number, readl(reg)); + } + + reg = chv_padreg(pctrl, desc->number, CHV_PADCTRL1); + val = readl(reg); + if (ctx->padctrl1 != val) { + chv_writel(ctx->padctrl1, reg); + dev_dbg(pctrl->dev, "restored pin %2u ctrl1 0x%08x\n", + desc->number, readl(reg)); + } + } + + /* + * Now that all pins are restored to known state, we can restore + * the interrupt mask register as well. + */ + chv_writel(0xffff, pctrl->regs + CHV_INTSTAT); + chv_writel(pctrl->saved_intmask, pctrl->regs + CHV_INTMASK); + + return 0; +} +#endif + +static const struct dev_pm_ops chv_pinctrl_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(chv_pinctrl_suspend, chv_pinctrl_resume) +}; + static const struct acpi_device_id chv_pinctrl_acpi_match[] = { { "INT33FF" }, { } @@ -1498,6 +1601,7 @@ static struct platform_driver chv_pinctrl_driver = { .driver = { .name = "cherryview-pinctrl", .owner = THIS_MODULE, + .pm = &chv_pinctrl_pm_ops, .acpi_match_table = chv_pinctrl_acpi_match, }, }; -- cgit v0.10.2 From 1ee68af8a5003bdda32ca02f93afc9701d50e871 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 Dec 2014 22:14:36 +0100 Subject: pinctrl: intel: drop owner assignment from platform_drivers This platform_driver does not need to set an owner, it will be populated by the driver core. Signed-off-by: Wolfram Sang Acked-by: Mika Westerberg Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index dde67d4..a2f23c3 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -1600,7 +1600,6 @@ static struct platform_driver chv_pinctrl_driver = { .remove = chv_pinctrl_remove, .driver = { .name = "cherryview-pinctrl", - .owner = THIS_MODULE, .pm = &chv_pinctrl_pm_ops, .acpi_match_table = chv_pinctrl_acpi_match, }, -- cgit v0.10.2 From a95308d88c07e0093aedae7e64f92cb1e165f592 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 5 Dec 2014 15:44:57 +0100 Subject: pinctrl: mvebu: a38x: Add UART1 muxing options The MPP19 and MMP20 pins also have the ability to be muxed to the uart1 function. Add this case to the pinctrl driver. Signed-off-by: Maxime Ripard Acked-by: Jason Cooper Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-38x.c b/drivers/pinctrl/mvebu/pinctrl-armada-38x.c index 224c6cf..7302f66 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-38x.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-38x.c @@ -145,14 +145,16 @@ static struct mvebu_mpp_mode armada_38x_mpp_modes[] = { MPP_VAR_FUNCTION(2, "ptp", "event_req", V_88F6810_PLUS), MPP_VAR_FUNCTION(3, "pcie0", "clkreq", V_88F6810_PLUS), MPP_VAR_FUNCTION(4, "sata1", "prsnt", V_88F6810_PLUS), - MPP_VAR_FUNCTION(5, "ua0", "cts", V_88F6810_PLUS)), + MPP_VAR_FUNCTION(5, "ua0", "cts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "rxd", V_88F6810_PLUS)), MPP_MODE(20, MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), MPP_VAR_FUNCTION(1, "ge0", "txclk", V_88F6810_PLUS), MPP_VAR_FUNCTION(2, "ptp", "clk", V_88F6810_PLUS), MPP_VAR_FUNCTION(3, "pcie1", "rstout", V_88F6820_PLUS), MPP_VAR_FUNCTION(4, "sata0", "prsnt", V_88F6810_PLUS), - MPP_VAR_FUNCTION(5, "ua0", "rts", V_88F6810_PLUS)), + MPP_VAR_FUNCTION(5, "ua0", "rts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "txd", V_88F6810_PLUS)), MPP_MODE(21, MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), MPP_VAR_FUNCTION(1, "spi0", "cs1", V_88F6810_PLUS), -- cgit v0.10.2 From 2ef2a489256b135429a652060416714172c23603 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 10 Jan 2015 22:13:02 +0100 Subject: pinctrl: nomadik:fix up device tree bindings After the Nomadik pin controller was force migrated to generic pin control bindings, some leftovers in the documentation need to be cleaned up. The code and device trees are already migrated. Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/pinctrl/ste,nomadik.txt b/Documentation/devicetree/bindings/pinctrl/ste,nomadik.txt index 6b33b9f..f63fcb3 100644 --- a/Documentation/devicetree/bindings/pinctrl/ste,nomadik.txt +++ b/Documentation/devicetree/bindings/pinctrl/ste,nomadik.txt @@ -16,17 +16,22 @@ mux function to select on those pin(s)/group(s), and various pin configuration parameters, such as input, output, pull up, pull down... The name of each subnode is not important; all subnodes should be enumerated -and processed purely based on their content. +and processed purely based on their content. The subnodes use the generic +pin multiplexing node layout from the standard pin control bindings +(see pinctrl-bindings.txt): -Required subnode-properties: -- ste,pins : An array of strings. Each string contains the name of a pin or - group. - -Optional subnode-properties: -- ste,function: A string containing the name of the function to mux to the +Required pin multiplexing subnode properties: +- function: A string containing the name of the function to mux to the pin or group. +- groups : An array of strings. Each string contains the name of a pin + group that will be combined with the function to form a multiplexing + set-up. -- ste,config: Handle of pin configuration node (e.g. ste,config = <&slpm_in_wkup_pdis>) +Required pin configuration subnode properties: +- pins: A string array describing the pins affected by the configuration + in the node. +- ste,config: Handle of pin configuration node + (e.g. ste,config = <&slpm_in_wkup_pdis>) - ste,input : <0/1/2> 0: input with no pull @@ -97,32 +102,32 @@ Example board file extract: uart0 { uart0_default_mux: uart0_mux { u0_default_mux { - ste,function = "u0"; - ste,pins = "u0_a_1"; + function = "u0"; + pins = "u0_a_1"; }; }; uart0_default_mode: uart0_default { uart0_default_cfg1 { - ste,pins = "GPIO0", "GPIO2"; + pins = "GPIO0", "GPIO2"; ste,input = <1>; }; uart0_default_cfg2 { - ste,pins = "GPIO1", "GPIO3"; + pins = "GPIO1", "GPIO3"; ste,output = <1>; }; }; uart0_sleep_mode: uart0_sleep { uart0_sleep_cfg1 { - ste,pins = "GPIO0", "GPIO2"; + pins = "GPIO0", "GPIO2"; ste,config = <&slpm_in_wkup_pdis>; }; uart0_sleep_cfg2 { - ste,pins = "GPIO1"; + pins = "GPIO1"; ste,config = <&slpm_out_hi_wkup_pdis>; }; uart0_sleep_cfg3 { - ste,pins = "GPIO3"; + pins = "GPIO3"; ste,config = <&slpm_out_wkup_pdis>; }; }; -- cgit v0.10.2 From e2821bee40bec00aed5abffa446a5ac3738b041e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 10 Jan 2015 22:47:56 +0100 Subject: pinctrl: pass -DDEBUG in subdirs When drivers are compiled in subdirectories the -DDEBUG flag need to be passed in the individual Makefiles. Reported-by: Mika Westerberg Suggested-by: Yingjoe Chen Cc: Mika Westerberg Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index c030b3d..c7139ba 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -1,6 +1,6 @@ # generic pinmux support -ccflags-$(CONFIG_DEBUG_PINCTRL) += -DDEBUG +subdir-ccflags-$(CONFIG_DEBUG_PINCTRL) += -DDEBUG obj-$(CONFIG_PINCTRL) += core.o pinctrl-utils.o obj-$(CONFIG_PINMUX) += pinmux.o -- cgit v0.10.2 From 846a7fc8f4bc8f0d04be8d953ecba28152b14de4 Mon Sep 17 00:00:00 2001 From: Moritz Fischer Date: Sat, 10 Jan 2015 14:04:14 -0800 Subject: Input: add support for NI Ettus Research USRP E3x0 button Signed-off-by: Moritz Fischer Signed-off-by: Dmitry Torokhov diff --git a/Documentation/devicetree/bindings/input/e3x0-button.txt b/Documentation/devicetree/bindings/input/e3x0-button.txt new file mode 100644 index 0000000..751665e --- /dev/null +++ b/Documentation/devicetree/bindings/input/e3x0-button.txt @@ -0,0 +1,25 @@ +National Instruments Ettus Research USRP E3x0 button driver + +This module is part of the NI Ettus Research USRP E3x0 SDR. + +This module provides a simple power button event via two interrupts. + +Required properties: +- compatible: should be one of the following + - "ettus,e3x0-button": For devices such as the NI Ettus Research USRP E3x0 +- interrupt-parent: + - a phandle to the interrupt controller that it is attached to. +- interrupts: should be one of the following + - <0 30 1>, <0 31 1>: For devices such as the NI Ettus Research USRP E3x0 +- interrupt-names: should be one of the following + - "press", "release": For devices such as the NI Ettus Research USRP E3x0 + +Note: Interrupt numbers might vary depending on the FPGA configuration. + +Example: + button { + compatible = "ettus,e3x0-button"; + interrupt-parent = <&intc>; + interrupts = <0 30 1>, <0 31 1>; + interrupt-names = "press", "release"; + } diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index e6469ec..9f18b4f 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -48,6 +48,7 @@ epcos EPCOS AG epfl Ecole Polytechnique Fédérale de Lausanne epson Seiko Epson Corp. est ESTeem Wireless Modems +ettus NI Ettus Research eukrea Eukréa Electromatique excito Excito fsl Freescale Semiconductor diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 9e610c4..6deb8da 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -93,6 +93,16 @@ config INPUT_BMA150 To compile this driver as a module, choose M here: the module will be called bma150. +config INPUT_E3X0_BUTTON + tristate "NI Ettus Research USRP E3x0 Button support." + default n + help + Say Y here to enable support for the NI Ettus Research + USRP E3x0 Button. + + To compile this driver as a module, choose M here: the + module will be called e3x0_button. + config INPUT_PCSPKR tristate "PC Speaker support" depends on PCSPKR_PLATFORM diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index f8d9ea7..403a1a5 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o +obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o diff --git a/drivers/input/misc/e3x0-button.c b/drivers/input/misc/e3x0-button.c new file mode 100644 index 0000000..13bfca8 --- /dev/null +++ b/drivers/input/misc/e3x0-button.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2014, National Instruments Corp. All rights reserved. + * + * Driver for NI Ettus Research USRP E3x0 Button Driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static irqreturn_t e3x0_button_release_handler(int irq, void *data) +{ + struct input_dev *idev = data; + + input_report_key(idev, KEY_POWER, 0); + input_sync(idev); + + return IRQ_HANDLED; +} + +static irqreturn_t e3x0_button_press_handler(int irq, void *data) +{ + struct input_dev *idev = data; + + input_report_key(idev, KEY_POWER, 1); + pm_wakeup_event(idev->dev.parent, 0); + input_sync(idev); + + return IRQ_HANDLED; +} + +static int __maybe_unused e3x0_button_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(platform_get_irq_byname(pdev, "press")); + + return 0; +} + +static int __maybe_unused e3x0_button_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(platform_get_irq_byname(pdev, "press")); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(e3x0_button_pm_ops, + e3x0_button_suspend, e3x0_button_resume); + +static int e3x0_button_probe(struct platform_device *pdev) +{ + struct input_dev *input; + int irq_press, irq_release; + int error; + + irq_press = platform_get_irq_byname(pdev, "press"); + if (irq_press < 0) { + dev_err(&pdev->dev, "No IRQ for 'press', error=%d\n", + irq_press); + return irq_press; + } + + irq_release = platform_get_irq_byname(pdev, "release"); + if (irq_release < 0) { + dev_err(&pdev->dev, "No IRQ for 'release', error=%d\n", + irq_release); + return irq_release; + } + + input = devm_input_allocate_device(&pdev->dev); + if (!input) + return -ENOMEM; + + input->name = "NI Ettus Research USRP E3x0 Button Driver"; + input->phys = "e3x0_button/input0"; + input->dev.parent = &pdev->dev; + + input_set_capability(input, EV_KEY, KEY_POWER); + + error = devm_request_irq(&pdev->dev, irq_press, + e3x0_button_press_handler, 0, + "e3x0-button", input); + if (error) { + dev_err(&pdev->dev, "Failed to request 'press' IRQ#%d: %d\n", + irq_press, error); + return error; + } + + error = devm_request_irq(&pdev->dev, irq_release, + e3x0_button_release_handler, 0, + "e3x0-button", input); + if (error) { + dev_err(&pdev->dev, "Failed to request 'release' IRQ#%d: %d\n", + irq_release, error); + return error; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "Can't register input device: %d\n", error); + return error; + } + + platform_set_drvdata(pdev, input); + device_init_wakeup(&pdev->dev, 1); + return 0; +} + +static int e3x0_button_remove(struct platform_device *pdev) +{ + device_init_wakeup(&pdev->dev, 0); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id e3x0_button_match[] = { + { .compatible = "ettus,e3x0-button", }, + { } +}; +MODULE_DEVICE_TABLE(of, e3x0_button_match); +#endif + +static struct platform_driver e3x0_button_driver = { + .driver = { + .name = "e3x0-button", + .of_match_table = of_match_ptr(e3x0_button_match), + .pm = &e3x0_button_pm_ops, + }, + .probe = e3x0_button_probe, + .remove = e3x0_button_remove, +}; + +module_platform_driver(e3x0_button_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Moritz Fischer "); +MODULE_DESCRIPTION("NI Ettus Research USRP E3x0 Button driver"); +MODULE_ALIAS("platform:e3x0-button"); -- cgit v0.10.2 From f17effbe891624fe1ec2dcfa68625ed1c98ebe3a Mon Sep 17 00:00:00 2001 From: Moritz Fischer Date: Sat, 10 Jan 2015 14:10:59 -0800 Subject: MAINTAINERS: add info for e3x0-button driver Signed-off-by: Moritz Fischer Signed-off-by: Dmitry Torokhov diff --git a/MAINTAINERS b/MAINTAINERS index 21b834b..d726917 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3277,6 +3277,14 @@ M: "Maciej W. Rozycki" S: Maintained F: drivers/tty/serial/dz.* +E3X0 POWER BUTTON DRIVER +M: Moritz Fischer +L: usrp-users@lists.ettus.com +W: http://www.ettus.com +S: Supported +F: drivers/input/misc/e3x0-button.c +F: Documentation/devicetree/bindings/input/e3x0-button.txt + E4000 MEDIA DRIVER M: Antti Palosaari L: linux-media@vger.kernel.org -- cgit v0.10.2 From f94fe119f2e53362a3038ee856fa58412f728bc9 Mon Sep 17 00:00:00 2001 From: Steven Honeyman Date: Wed, 5 Nov 2014 22:52:18 +0000 Subject: x86, CPU: Fix trivial printk formatting issues with dmesg dmesg (from util-linux) currently has two methods for reading the kernel message ring buffer: /dev/kmsg and syslog(2). Since kernel 3.5.0 kmsg has been the default, which escapes control characters (e.g. new lines) before they are shown. This change means that when dmesg is using /dev/kmsg, a 2 line printk makes the output messy, because the second line does not get a timestamp. For example: [ 0.012863] CPU0: Thermal monitoring enabled (TM1) [ 0.012869] Last level iTLB entries: 4KB 1024, 2MB 1024, 4MB 1024 Last level dTLB entries: 4KB 1024, 2MB 1024, 4MB 1024, 1GB 4 [ 0.012958] Freeing SMP alternatives memory: 28K (ffffffff81d86000 - ffffffff81d8d000) [ 0.014961] dmar: Host address width 39 Because printk.c intentionally escapes control characters, they should not be there in the first place. This patch fixes two occurrences of this. Signed-off-by: Steven Honeyman Link: https://lkml.kernel.org/r/1414856696-8094-1-git-send-email-stevenhoneyman@gmail.com [ Boris: make cpu_detect_tlb() static, while at it. ] Signed-off-by: Borislav Petkov diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index c604965..4973d63 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -491,17 +491,18 @@ u16 __read_mostly tlb_lld_2m[NR_INFO]; u16 __read_mostly tlb_lld_4m[NR_INFO]; u16 __read_mostly tlb_lld_1g[NR_INFO]; -void cpu_detect_tlb(struct cpuinfo_x86 *c) +static void cpu_detect_tlb(struct cpuinfo_x86 *c) { if (this_cpu->c_detect_tlb) this_cpu->c_detect_tlb(c); - printk(KERN_INFO "Last level iTLB entries: 4KB %d, 2MB %d, 4MB %d\n" - "Last level dTLB entries: 4KB %d, 2MB %d, 4MB %d, 1GB %d\n", + pr_info("Last level iTLB entries: 4KB %d, 2MB %d, 4MB %d\n", tlb_lli_4k[ENTRIES], tlb_lli_2m[ENTRIES], - tlb_lli_4m[ENTRIES], tlb_lld_4k[ENTRIES], - tlb_lld_2m[ENTRIES], tlb_lld_4m[ENTRIES], - tlb_lld_1g[ENTRIES]); + tlb_lli_4m[ENTRIES]); + + pr_info("Last level dTLB entries: 4KB %d, 2MB %d, 4MB %d, 1GB %d\n", + tlb_lld_4k[ENTRIES], tlb_lld_2m[ENTRIES], + tlb_lld_4m[ENTRIES], tlb_lld_1g[ENTRIES]); } void detect_ht(struct cpuinfo_x86 *c) diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index 9cc6b6f..94d7dcb 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -487,10 +487,8 @@ static void init_intel(struct cpuinfo_x86 *c) rdmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb); if ((epb & 0xF) == ENERGY_PERF_BIAS_PERFORMANCE) { - printk_once(KERN_WARNING "ENERGY_PERF_BIAS:" - " Set to 'normal', was 'performance'\n" - "ENERGY_PERF_BIAS: View and update with" - " x86_energy_perf_policy(8)\n"); + pr_warn_once("ENERGY_PERF_BIAS: Set to 'normal', was 'performance'\n"); + pr_warn_once("ENERGY_PERF_BIAS: View and update with x86_energy_perf_policy(8)\n"); epb = (epb & ~0xF) | ENERGY_PERF_BIAS_NORMAL; wrmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb); } -- cgit v0.10.2 From 79619cf5151257f82a4c016b02283e2ceb4b63ce Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 17 Nov 2014 10:12:27 -0800 Subject: rcutorture: Issue warnings on close calls due to Reader Batch blows Normal rcutorture checking overestimates grace periods somewhat due to the fact that there is a delay from a grace-period request until the start of the corresponding grace period and another delay from the end of that grace period to notification of the requestor. This means that rcutorture's detection of RCU bugs is less sensitive than it might be. It turns out that rcutorture also checks the underlying grace-period "completed" counter (displayed in Reader Batch output), which in theory allows rcutorture to do exact checks. In practice, memory misordering (by both compiler and CPU) can result in false positives. However, experience on x86 shows that these false positives are quite rare, occurring less than one time per 1,000 hours of testing. This commit therefore does the exact checking, giving a warning if any Reader Batch blows happen, and flagging an error if they happen more often than once every three hours in long tests. Signed-off-by: Paul E. McKenney diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh index d6cc07f..559e01a 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh @@ -30,6 +30,7 @@ else echo Unreadable results directory: $i exit 1 fi +. tools/testing/selftests/rcutorture/bin/functions.sh configfile=`echo $i | sed -e 's/^.*\///'` ngps=`grep ver: $i/console.log 2> /dev/null | tail -1 | sed -e 's/^.* ver: //' -e 's/ .*$//'` @@ -48,4 +49,21 @@ else title="$title ($ngpsps per second)" fi echo $title + nclosecalls=`grep --binary-files=text 'torture: Reader Batch' $i/console.log | tail -1 | awk '{for (i=NF-8;i<=NF;i++) sum+=$i; } END {print sum}'` + if test -z "$nclosecalls" + then + exit 0 + fi + if test "$nclosecalls" -eq 0 + then + exit 0 + fi + # Compute number of close calls per tenth of an hour + nclosecalls10=`awk -v nclosecalls=$nclosecalls -v dur=$dur 'BEGIN { print int(nclosecalls * 36000 / dur) }' < /dev/null` + if test $nclosecalls10 -gt 5 -a $nclosecalls -gt 1 + then + print_bug $nclosecalls "Reader Batch close calls in" $(($dur/60)) minute run: $i + else + print_warning $nclosecalls "Reader Batch close calls in" $(($dur/60)) minute run: $i + fi fi -- cgit v0.10.2 From 9733e4f0a973a354034f5dd603b4142a3095c85f Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 21 Nov 2014 12:49:13 -0800 Subject: rcu: Make _batches_completed() functions return unsigned long Long ago, the various ->completed fields were of type long, but now are unsigned long due to signed-integer-overflow concerns. However, the various _batches_completed() functions remained of type long, even though their only purpose in life is to return the corresponding ->completed field. This patch cleans this up by changing these functions' return types to unsigned long. Signed-off-by: Paul E. McKenney diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h index 0e53662..91f7e4c 100644 --- a/include/linux/rcutiny.h +++ b/include/linux/rcutiny.h @@ -94,7 +94,7 @@ static inline void rcu_virt_note_context_switch(int cpu) /* * Return the number of grace periods. */ -static inline long rcu_batches_completed(void) +static inline unsigned long rcu_batches_completed(void) { return 0; } @@ -102,7 +102,7 @@ static inline long rcu_batches_completed(void) /* * Return the number of bottom-half grace periods. */ -static inline long rcu_batches_completed_bh(void) +static inline unsigned long rcu_batches_completed_bh(void) { return 0; } diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h index 5295379..9885bfb 100644 --- a/include/linux/rcutree.h +++ b/include/linux/rcutree.h @@ -81,9 +81,9 @@ void cond_synchronize_rcu(unsigned long oldstate); extern unsigned long rcutorture_testseq; extern unsigned long rcutorture_vernum; -long rcu_batches_completed(void); -long rcu_batches_completed_bh(void); -long rcu_batches_completed_sched(void); +unsigned long rcu_batches_completed(void); +unsigned long rcu_batches_completed_bh(void); +unsigned long rcu_batches_completed_sched(void); void show_rcu_gp_kthreads(void); void rcu_force_quiescent_state(void); diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 4c106fc..e26d787 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -317,7 +317,7 @@ static int rcu_pending(void); /* * Return the number of RCU-sched batches processed thus far for debug & stats. */ -long rcu_batches_completed_sched(void) +unsigned long rcu_batches_completed_sched(void) { return rcu_sched_state.completed; } @@ -326,7 +326,7 @@ EXPORT_SYMBOL_GPL(rcu_batches_completed_sched); /* * Return the number of RCU BH batches processed thus far for debug & stats. */ -long rcu_batches_completed_bh(void) +unsigned long rcu_batches_completed_bh(void) { return rcu_bh_state.completed; } diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 8e7b184..1a07d73 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -546,7 +546,7 @@ DECLARE_PER_CPU(char, rcu_cpu_has_work); /* Forward declarations for rcutree_plugin.h */ static void rcu_bootup_announce(void); -long rcu_batches_completed(void); +unsigned long rcu_batches_completed(void); static void rcu_preempt_note_context_switch(void); static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp); #ifdef CONFIG_HOTPLUG_CPU diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 3ec85cb..f69300d 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -117,7 +117,7 @@ static void __init rcu_bootup_announce(void) * Return the number of RCU-preempt batches processed thus far * for debug and statistics. */ -static long rcu_batches_completed_preempt(void) +static unsigned long rcu_batches_completed_preempt(void) { return rcu_preempt_state.completed; } @@ -126,7 +126,7 @@ EXPORT_SYMBOL_GPL(rcu_batches_completed_preempt); /* * Return the number of RCU batches processed thus far for debug & stats. */ -long rcu_batches_completed(void) +unsigned long rcu_batches_completed(void) { return rcu_batches_completed_preempt(); } @@ -935,7 +935,7 @@ static void __init rcu_bootup_announce(void) /* * Return the number of RCU batches processed thus far for debug & stats. */ -long rcu_batches_completed(void) +unsigned long rcu_batches_completed(void) { return rcu_batches_completed_sched(); } -- cgit v0.10.2 From 3b009c0ebe02a06d2d5bd667da0eb9cefa79321d Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 21 Nov 2014 13:54:57 -0800 Subject: rcutorture: Make build-output parsing correctly flag RCU's warnings Signed-off-by: Paul E. McKenney diff --git a/tools/testing/selftests/rcutorture/bin/parse-build.sh b/tools/testing/selftests/rcutorture/bin/parse-build.sh index 499d1e5..a6b5762 100755 --- a/tools/testing/selftests/rcutorture/bin/parse-build.sh +++ b/tools/testing/selftests/rcutorture/bin/parse-build.sh @@ -26,12 +26,15 @@ # # Authors: Paul E. McKenney -T=$1 +F=$1 title=$2 +T=/tmp/parse-build.sh.$$ +trap 'rm -rf $T' 0 +mkdir $T . functions.sh -if grep -q CC < $T +if grep -q CC < $F then : else @@ -39,18 +42,21 @@ else exit 1 fi -if grep -q "error:" < $T +if grep -q "error:" < $F then print_bug $title build errors: - grep "error:" < $T + grep "error:" < $F exit 2 fi -exit 0 -if egrep -q "rcu[^/]*\.c.*warning:|rcu.*\.h.*warning:" < $T +grep warning: < $F > $T/warnings +grep "include/linux/*rcu*\.h:" $T/warnings > $T/hwarnings +grep "kernel/rcu/[^/]*:" $T/warnings > $T/cwarnings +cat $T/hwarnings $T/cwarnings > $T/rcuwarnings +if test -s $T/rcuwarnings then print_warning $title build errors: - egrep "rcu[^/]*\.c.*warning:|rcu.*\.h.*warning:" < $T + cat $T/rcuwarnings exit 2 fi exit 0 -- cgit v0.10.2 From 6b80da42c02bc731ab38b6a37da5366abe26717f Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 21 Nov 2014 14:19:26 -0800 Subject: rcutorture: Use unsigned for Reader Batch computations The counter returned by the various ->completed functions is subject to overflow, which means that subtracting two such counters might result in overflow, which invokes undefined behavior in the C standard. This commit therefore changes these functions and variables to unsigned to avoid this undefined behavior. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 4d559ba..f43e351 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -244,7 +244,7 @@ struct rcu_torture_ops { int (*readlock)(void); void (*read_delay)(struct torture_random_state *rrsp); void (*readunlock)(int idx); - int (*completed)(void); + unsigned long (*completed)(void); void (*deferred_free)(struct rcu_torture *p); void (*sync)(void); void (*exp_sync)(void); @@ -296,7 +296,7 @@ static void rcu_torture_read_unlock(int idx) __releases(RCU) rcu_read_unlock(); } -static int rcu_torture_completed(void) +static unsigned long rcu_torture_completed(void) { return rcu_batches_completed(); } @@ -356,7 +356,7 @@ rcu_torture_cb(struct rcu_head *p) cur_ops->deferred_free(rp); } -static int rcu_no_completed(void) +static unsigned long rcu_no_completed(void) { return 0; } @@ -407,7 +407,7 @@ static void rcu_bh_torture_read_unlock(int idx) __releases(RCU_BH) rcu_read_unlock_bh(); } -static int rcu_bh_torture_completed(void) +static unsigned long rcu_bh_torture_completed(void) { return rcu_batches_completed_bh(); } @@ -510,7 +510,7 @@ static void srcu_torture_read_unlock(int idx) __releases(&srcu_ctl) srcu_read_unlock(&srcu_ctl, idx); } -static int srcu_torture_completed(void) +static unsigned long srcu_torture_completed(void) { return srcu_batches_completed(&srcu_ctl); } @@ -1015,8 +1015,8 @@ static void rcutorture_trace_dump(void) static void rcu_torture_timer(unsigned long unused) { int idx; - int completed; - int completed_end; + unsigned long completed; + unsigned long completed_end; static DEFINE_TORTURE_RANDOM(rand); static DEFINE_SPINLOCK(rand_lock); struct rcu_torture *p; @@ -1073,8 +1073,8 @@ static void rcu_torture_timer(unsigned long unused) static int rcu_torture_reader(void *arg) { - int completed; - int completed_end; + unsigned long completed; + unsigned long completed_end; int idx; DEFINE_TORTURE_RANDOM(rand); struct rcu_torture *p; -- cgit v0.10.2 From c1fe9cde4ae904fffb5b4d975d0a37e99136ff50 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 21 Nov 2014 15:45:27 -0800 Subject: rcu: Provide rcu_batches_completed_sched() for TINY_RCU A bug in rcutorture has caused it to ignore completed batches. In preparation for fixing that bug, this commit provides TINY_RCU with the required rcu_batches_completed_sched(). Signed-off-by: Paul E. McKenney diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h index 91f7e4c..1ce2d6b 100644 --- a/include/linux/rcutiny.h +++ b/include/linux/rcutiny.h @@ -107,6 +107,14 @@ static inline unsigned long rcu_batches_completed_bh(void) return 0; } +/* + * Return the number of sched grace periods. + */ +static inline unsigned long rcu_batches_completed_sched(void) +{ + return 0; +} + static inline void rcu_force_quiescent_state(void) { } -- cgit v0.10.2 From 1e32eaee4c370afca20324f634bf47ae991cd240 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 21 Nov 2014 16:04:34 -0800 Subject: rcutorture: Drop rcu_torture_completed() and friends Now that the return type of rcu_batches_completed() and friends matches that of the rcu_torture_ops structure's ->completed field, the wrapper functions can be deleted. This commit carries out that deletion, while also wiring "sched"'s ->completed field to rcu_batches_completed_sched(). Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index f43e351..aadbc07 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -296,11 +296,6 @@ static void rcu_torture_read_unlock(int idx) __releases(RCU) rcu_read_unlock(); } -static unsigned long rcu_torture_completed(void) -{ - return rcu_batches_completed(); -} - /* * Update callback in the pipe. This should be invoked after a grace period. */ @@ -377,7 +372,7 @@ static struct rcu_torture_ops rcu_ops = { .readlock = rcu_torture_read_lock, .read_delay = rcu_read_delay, .readunlock = rcu_torture_read_unlock, - .completed = rcu_torture_completed, + .completed = rcu_batches_completed, .deferred_free = rcu_torture_deferred_free, .sync = synchronize_rcu, .exp_sync = synchronize_rcu_expedited, @@ -407,11 +402,6 @@ static void rcu_bh_torture_read_unlock(int idx) __releases(RCU_BH) rcu_read_unlock_bh(); } -static unsigned long rcu_bh_torture_completed(void) -{ - return rcu_batches_completed_bh(); -} - static void rcu_bh_torture_deferred_free(struct rcu_torture *p) { call_rcu_bh(&p->rtort_rcu, rcu_torture_cb); @@ -423,7 +413,7 @@ static struct rcu_torture_ops rcu_bh_ops = { .readlock = rcu_bh_torture_read_lock, .read_delay = rcu_read_delay, /* just reuse rcu's version. */ .readunlock = rcu_bh_torture_read_unlock, - .completed = rcu_bh_torture_completed, + .completed = rcu_batches_completed_bh, .deferred_free = rcu_bh_torture_deferred_free, .sync = synchronize_rcu_bh, .exp_sync = synchronize_rcu_bh_expedited, @@ -600,7 +590,7 @@ static struct rcu_torture_ops sched_ops = { .readlock = sched_torture_read_lock, .read_delay = rcu_read_delay, /* just reuse rcu's version. */ .readunlock = sched_torture_read_unlock, - .completed = rcu_no_completed, + .completed = rcu_batches_completed_sched, .deferred_free = rcu_sched_torture_deferred_free, .sync = synchronize_sched, .exp_sync = synchronize_sched_expedited, -- cgit v0.10.2 From f9103c390257d06c162d9e3c2a90d2bdedadfe17 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 21 Nov 2014 16:48:20 -0800 Subject: rcu: Remove redundant rcu_batches_completed() declaration Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 1a07d73..69eb422 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -546,7 +546,6 @@ DECLARE_PER_CPU(char, rcu_cpu_has_work); /* Forward declarations for rcutree_plugin.h */ static void rcu_bootup_announce(void); -unsigned long rcu_batches_completed(void); static void rcu_preempt_note_context_switch(void); static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp); #ifdef CONFIG_HOTPLUG_CPU -- cgit v0.10.2 From 917963d0b30f9c4153c372c165178501d97b6b55 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 21 Nov 2014 17:10:16 -0800 Subject: rcutorture: Check from beginning to end of grace period Currently, rcutorture's Reader Batch checks measure from the end of the previous grace period to the end of the current one. This commit tightens up these checks by measuring from the start and end of the same grace period. This involves adding rcu_batches_started() and friends corresponding to the existing rcu_batches_completed() and friends. We leave SRCU alone for the moment, as it does not yet have a way of tracking both ends of its grace periods. Signed-off-by: Paul E. McKenney diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h index 1ce2d6b..9841921 100644 --- a/include/linux/rcutiny.h +++ b/include/linux/rcutiny.h @@ -92,7 +92,31 @@ static inline void rcu_virt_note_context_switch(int cpu) } /* - * Return the number of grace periods. + * Return the number of grace periods started. + */ +static inline unsigned long rcu_batches_started(void) +{ + return 0; +} + +/* + * Return the number of bottom-half grace periods started. + */ +static inline unsigned long rcu_batches_started_bh(void) +{ + return 0; +} + +/* + * Return the number of sched grace periods started. + */ +static inline unsigned long rcu_batches_started_sched(void) +{ + return 0; +} + +/* + * Return the number of grace periods completed. */ static inline unsigned long rcu_batches_completed(void) { @@ -100,7 +124,7 @@ static inline unsigned long rcu_batches_completed(void) } /* - * Return the number of bottom-half grace periods. + * Return the number of bottom-half grace periods completed. */ static inline unsigned long rcu_batches_completed_bh(void) { @@ -108,7 +132,7 @@ static inline unsigned long rcu_batches_completed_bh(void) } /* - * Return the number of sched grace periods. + * Return the number of sched grace periods completed. */ static inline unsigned long rcu_batches_completed_sched(void) { diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h index 9885bfb..c0dd124 100644 --- a/include/linux/rcutree.h +++ b/include/linux/rcutree.h @@ -81,6 +81,9 @@ void cond_synchronize_rcu(unsigned long oldstate); extern unsigned long rcutorture_testseq; extern unsigned long rcutorture_vernum; +unsigned long rcu_batches_started(void); +unsigned long rcu_batches_started_bh(void); +unsigned long rcu_batches_started_sched(void); unsigned long rcu_batches_completed(void); unsigned long rcu_batches_completed_bh(void); unsigned long rcu_batches_completed_sched(void); diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index aadbc07..24142c2 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -244,6 +244,7 @@ struct rcu_torture_ops { int (*readlock)(void); void (*read_delay)(struct torture_random_state *rrsp); void (*readunlock)(int idx); + unsigned long (*started)(void); unsigned long (*completed)(void); void (*deferred_free)(struct rcu_torture *p); void (*sync)(void); @@ -372,6 +373,7 @@ static struct rcu_torture_ops rcu_ops = { .readlock = rcu_torture_read_lock, .read_delay = rcu_read_delay, .readunlock = rcu_torture_read_unlock, + .started = rcu_batches_started, .completed = rcu_batches_completed, .deferred_free = rcu_torture_deferred_free, .sync = synchronize_rcu, @@ -413,6 +415,7 @@ static struct rcu_torture_ops rcu_bh_ops = { .readlock = rcu_bh_torture_read_lock, .read_delay = rcu_read_delay, /* just reuse rcu's version. */ .readunlock = rcu_bh_torture_read_unlock, + .started = rcu_batches_started_bh, .completed = rcu_batches_completed_bh, .deferred_free = rcu_bh_torture_deferred_free, .sync = synchronize_rcu_bh, @@ -456,6 +459,7 @@ static struct rcu_torture_ops rcu_busted_ops = { .readlock = rcu_torture_read_lock, .read_delay = rcu_read_delay, /* just reuse rcu's version. */ .readunlock = rcu_torture_read_unlock, + .started = rcu_no_completed, .completed = rcu_no_completed, .deferred_free = rcu_busted_torture_deferred_free, .sync = synchronize_rcu_busted, @@ -554,6 +558,7 @@ static struct rcu_torture_ops srcu_ops = { .readlock = srcu_torture_read_lock, .read_delay = srcu_read_delay, .readunlock = srcu_torture_read_unlock, + .started = NULL, .completed = srcu_torture_completed, .deferred_free = srcu_torture_deferred_free, .sync = srcu_torture_synchronize, @@ -590,6 +595,7 @@ static struct rcu_torture_ops sched_ops = { .readlock = sched_torture_read_lock, .read_delay = rcu_read_delay, /* just reuse rcu's version. */ .readunlock = sched_torture_read_unlock, + .started = rcu_batches_started_sched, .completed = rcu_batches_completed_sched, .deferred_free = rcu_sched_torture_deferred_free, .sync = synchronize_sched, @@ -628,6 +634,7 @@ static struct rcu_torture_ops tasks_ops = { .readlock = tasks_torture_read_lock, .read_delay = rcu_read_delay, /* just reuse rcu's version. */ .readunlock = tasks_torture_read_unlock, + .started = rcu_no_completed, .completed = rcu_no_completed, .deferred_free = rcu_tasks_torture_deferred_free, .sync = synchronize_rcu_tasks, @@ -1005,8 +1012,8 @@ static void rcutorture_trace_dump(void) static void rcu_torture_timer(unsigned long unused) { int idx; + unsigned long started; unsigned long completed; - unsigned long completed_end; static DEFINE_TORTURE_RANDOM(rand); static DEFINE_SPINLOCK(rand_lock); struct rcu_torture *p; @@ -1014,7 +1021,10 @@ static void rcu_torture_timer(unsigned long unused) unsigned long long ts; idx = cur_ops->readlock(); - completed = cur_ops->completed(); + if (cur_ops->started) + started = cur_ops->started(); + else + started = cur_ops->completed(); ts = rcu_trace_clock_local(); p = rcu_dereference_check(rcu_torture_current, rcu_read_lock_bh_held() || @@ -1037,14 +1047,16 @@ static void rcu_torture_timer(unsigned long unused) /* Should not happen, but... */ pipe_count = RCU_TORTURE_PIPE_LEN; } - completed_end = cur_ops->completed(); + completed = cur_ops->completed(); if (pipe_count > 1) { do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu, ts, - completed, completed_end); + started, completed); rcutorture_trace_dump(); } __this_cpu_inc(rcu_torture_count[pipe_count]); - completed = completed_end - completed; + completed = completed - started; + if (cur_ops->started) + completed++; if (completed > RCU_TORTURE_PIPE_LEN) { /* Should not happen, but... */ completed = RCU_TORTURE_PIPE_LEN; @@ -1063,8 +1075,8 @@ static void rcu_torture_timer(unsigned long unused) static int rcu_torture_reader(void *arg) { + unsigned long started; unsigned long completed; - unsigned long completed_end; int idx; DEFINE_TORTURE_RANDOM(rand); struct rcu_torture *p; @@ -1083,7 +1095,10 @@ rcu_torture_reader(void *arg) mod_timer(&t, jiffies + 1); } idx = cur_ops->readlock(); - completed = cur_ops->completed(); + if (cur_ops->started) + started = cur_ops->started(); + else + started = cur_ops->completed(); ts = rcu_trace_clock_local(); p = rcu_dereference_check(rcu_torture_current, rcu_read_lock_bh_held() || @@ -1104,14 +1119,16 @@ rcu_torture_reader(void *arg) /* Should not happen, but... */ pipe_count = RCU_TORTURE_PIPE_LEN; } - completed_end = cur_ops->completed(); + completed = cur_ops->completed(); if (pipe_count > 1) { do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu, - ts, completed, completed_end); + ts, started, completed); rcutorture_trace_dump(); } __this_cpu_inc(rcu_torture_count[pipe_count]); - completed = completed_end - completed; + completed = completed - started; + if (cur_ops->started) + completed++; if (completed > RCU_TORTURE_PIPE_LEN) { /* Should not happen, but... */ completed = RCU_TORTURE_PIPE_LEN; diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index e26d787..c0faad5 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -315,7 +315,43 @@ static void force_quiescent_state(struct rcu_state *rsp); static int rcu_pending(void); /* - * Return the number of RCU-sched batches processed thus far for debug & stats. + * Return the number of RCU batches started thus far for debug & stats. + */ +unsigned long rcu_batches_started(void) +{ + return rcu_state_p->gpnum; +} +EXPORT_SYMBOL_GPL(rcu_batches_started); + +/* + * Return the number of RCU-sched batches started thus far for debug & stats. + */ +unsigned long rcu_batches_started_sched(void) +{ + return rcu_sched_state.gpnum; +} +EXPORT_SYMBOL_GPL(rcu_batches_started_sched); + +/* + * Return the number of RCU BH batches started thus far for debug & stats. + */ +unsigned long rcu_batches_started_bh(void) +{ + return rcu_bh_state.gpnum; +} +EXPORT_SYMBOL_GPL(rcu_batches_started_bh); + +/* + * Return the number of RCU batches completed thus far for debug & stats. + */ +unsigned long rcu_batches_completed(void) +{ + return rcu_state_p->completed; +} +EXPORT_SYMBOL_GPL(rcu_batches_completed); + +/* + * Return the number of RCU-sched batches completed thus far for debug & stats. */ unsigned long rcu_batches_completed_sched(void) { @@ -324,7 +360,7 @@ unsigned long rcu_batches_completed_sched(void) EXPORT_SYMBOL_GPL(rcu_batches_completed_sched); /* - * Return the number of RCU BH batches processed thus far for debug & stats. + * Return the number of RCU BH batches completed thus far for debug & stats. */ unsigned long rcu_batches_completed_bh(void) { diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index f69300d..07e61a0 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -114,25 +114,6 @@ static void __init rcu_bootup_announce(void) } /* - * Return the number of RCU-preempt batches processed thus far - * for debug and statistics. - */ -static unsigned long rcu_batches_completed_preempt(void) -{ - return rcu_preempt_state.completed; -} -EXPORT_SYMBOL_GPL(rcu_batches_completed_preempt); - -/* - * Return the number of RCU batches processed thus far for debug & stats. - */ -unsigned long rcu_batches_completed(void) -{ - return rcu_batches_completed_preempt(); -} -EXPORT_SYMBOL_GPL(rcu_batches_completed); - -/* * Record a preemptible-RCU quiescent state for the specified CPU. Note * that this just means that the task currently running on the CPU is * not in a quiescent state. There might be any number of tasks blocked @@ -933,15 +914,6 @@ static void __init rcu_bootup_announce(void) } /* - * Return the number of RCU batches processed thus far for debug & stats. - */ -unsigned long rcu_batches_completed(void) -{ - return rcu_batches_completed_sched(); -} -EXPORT_SYMBOL_GPL(rcu_batches_completed); - -/* * Because preemptible RCU does not exist, we never have to check for * CPUs being in quiescent states. */ -- cgit v0.10.2 From 94162c8daa839bd0de791f66d754a880e2787c82 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 10 Dec 2014 17:42:16 -0800 Subject: rcutorture: Handle different mpstat versions The mpstat command recently added the %gnice column, which messes up the cpu2use.sh script's idle-CPU calculations. This commit therefore uses $NF instead of $12 to select the last (%idle) column. Signed-off-by: Paul E. McKenney diff --git a/tools/testing/selftests/rcutorture/bin/cpus2use.sh b/tools/testing/selftests/rcutorture/bin/cpus2use.sh index abe14b7..bb99cde 100755 --- a/tools/testing/selftests/rcutorture/bin/cpus2use.sh +++ b/tools/testing/selftests/rcutorture/bin/cpus2use.sh @@ -24,7 +24,7 @@ ncpus=`grep '^processor' /proc/cpuinfo | wc -l` idlecpus=`mpstat | tail -1 | \ - awk -v ncpus=$ncpus '{ print ncpus * ($7 + $12) / 100 }'` + awk -v ncpus=$ncpus '{ print ncpus * ($7 + $NF) / 100 }'` awk -v ncpus=$ncpus -v idlecpus=$idlecpus < /dev/null ' BEGIN { cpus2use = idlecpus; -- cgit v0.10.2 From 16c77ea7d0f4a74e49009aa2d26c275f7f93de7c Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 10 Dec 2014 17:43:58 -0800 Subject: torture: Add "-enable-kvm -soundhw pcspk" to qemu command line More recent qemu implementations really want "-enable-kvm", and the "-soundhw pcspk" makes the script a bit less dependent on odd audio libraries being installed. This commit therefore adds both to the default qemu command line. Signed-off-by: Paul E. McKenney diff --git a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh index 8ca9f21..35b8532 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh @@ -8,9 +8,9 @@ # # Usage: kvm-test-1-run.sh config builddir resdir minutes qemu-args boot_args # -# qemu-args defaults to "-nographic", along with arguments specifying the -# number of CPUs and other options generated from -# the underlying CPU architecture. +# qemu-args defaults to "-enable-kvm -soundhw pcspk -nographic", along with +# arguments specifying the number of CPUs and other +# options generated from the underlying CPU architecture. # boot_args defaults to value returned by the per_version_boot_params # shell function. # @@ -138,7 +138,7 @@ then fi # Generate -smp qemu argument. -qemu_args="-nographic $qemu_args" +qemu_args="-enable-kvm -soundhw pcspk -nographic $qemu_args" cpu_count=`configNR_CPUS.sh $config_template` cpu_count=`configfrag_boot_cpus "$boot_args" "$config_template" "$cpu_count"` vcpus=`identify_qemu_vcpus` -- cgit v0.10.2 From d2f74b5b488dd2b283977f64844fee3ba91676db Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 11 Dec 2014 10:50:22 -0800 Subject: torture: Flag console.log file to prevent holdovers from earlier runs A system misconfiguration that prevents qemu from running at all (for example, a missing dynamically linked library) will keep the console.log file from the previous run. This can fool the developer into thinking that this failed run actually completed correctly. This commit therefore overwrites the console.log file just before launching qemu. Signed-off-by: Paul E. McKenney diff --git a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh index 35b8532..5236e07 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh @@ -168,6 +168,7 @@ then touch $resdir/buildonly exit 0 fi +echo "NOTE: $QEMU either did not run or was interactive" > $builddir/console.log echo $QEMU $qemu_args -m 512 -kernel $resdir/bzImage -append \"$qemu_append $boot_args\" > $resdir/qemu-cmd ( $QEMU $qemu_args -m 512 -kernel $resdir/bzImage -append "$qemu_append $boot_args"; echo $? > $resdir/qemu-retval ) & qemu_pid=$! -- cgit v0.10.2 From 7602de4af192b1527767538454557cb7564bae60 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 17 Dec 2014 18:39:54 -0800 Subject: rcutorture: Add more diagnostics in rcu_barrier() test failure case Signed-off-by: Paul E. McKenney diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 24142c2..30d42aa 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -1427,6 +1427,9 @@ static int rcu_torture_barrier(void *arg) cur_ops->cb_barrier(); /* Implies smp_mb() for wait_event(). */ if (atomic_read(&barrier_cbs_invoked) != n_barrier_cbs) { n_rcu_torture_barrier_error++; + pr_err("barrier_cbs_invoked = %d, n_barrier_cbs = %d\n", + atomic_read(&barrier_cbs_invoked), + n_barrier_cbs); WARN_ON_ONCE(1); } n_barrier_successes++; -- cgit v0.10.2 From 624afe4dc9a08992b200046dfe8a61f19bab74ab Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Sat, 10 Jan 2015 13:02:22 +0100 Subject: ALSA: hda - fixup input_free_device called after input_unregister_device Input_unregister_device will internally free the device, so input_free_device should not be called. Reported-by: Dan Carpenter Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 09d2131..a50e15e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3548,7 +3548,6 @@ static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec, break; case HDA_FIXUP_ACT_FREE: input_unregister_device(spec->kb_dev); - input_free_device(spec->kb_dev); spec->kb_dev = NULL; } #endif -- cgit v0.10.2 From 12c243b833c2c441f46dc592ab410fdccb5e056b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 10 Jan 2015 16:57:45 +0200 Subject: ALSA: fm801: remove FSF address The FSF address is subject to change. So, we remove it from the header. While here, remove a line with my old and non-functional email from the header. Moreover, nowadays we have a Git VCS that contains a history of changes. Signed-off-by: Andy Shevchenko Signed-off-by: Takashi Iwai diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 52555bda..1fdd92b6 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -2,8 +2,6 @@ * The driver for the ForteMedia FM801 based soundcards * Copyright (c) by Jaroslav Kysela * - * Support FM only card by Andy Shevchenko - * * 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 @@ -14,10 +12,6 @@ * 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 -- cgit v0.10.2 From 51ad77ada1edc2c4096730820c45756cd8f112ad Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 16 Jan 2014 17:15:14 -0500 Subject: m68k/mvme16x: rtc - Don't use module_init in non-modular code The rtc.o is built for obj-y, i.e. always built in. It will never be modular, so using module_init as an alias for __initcall can be somewhat misleading. Fix this up now, so that we can relocate module_init from init.h into module.h in the future. If we don't do this, we'd have to add module.h to obviously non-modular code, and that would be a worse thing. Note that direct use of __initcall is discouraged, vs. one of the priority categorized subgroups. As __initcall gets mapped onto device_initcall, our use of device_initcall directly in this change means that the runtime impact is zero -- it will remain at level 6 in initcall ordering. Signed-off-by: Paul Gortmaker Signed-off-by: Geert Uytterhoeven diff --git a/arch/m68k/mvme16x/rtc.c b/arch/m68k/mvme16x/rtc.c index 6ef7a81..1755e2f 100644 --- a/arch/m68k/mvme16x/rtc.c +++ b/arch/m68k/mvme16x/rtc.c @@ -161,4 +161,4 @@ static int __init rtc_MK48T08_init(void) printk(KERN_INFO "MK48T08 Real Time Clock Driver v%s\n", RTC_VERSION); return misc_register(&rtc_dev); } -module_init(rtc_MK48T08_init); +device_initcall(rtc_MK48T08_init); -- cgit v0.10.2 From 23b94210267185db51f0be8d7115eb49b63b064b Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Thu, 1 Jan 2015 16:37:57 +0100 Subject: m68k/atari: atakeyb.c - Remove some unused functions Remove some functions that are not used anywhere: atari_kbd_leds() ikbd_exec() ikbd_mem_read() ikbd_mem_write() ikbd_clock_get() ikbd_clock_set() ikbd_pause() ikbd_resume() This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: Geert Uytterhoeven diff --git a/arch/m68k/atari/atakeyb.c b/arch/m68k/atari/atakeyb.c index 95022b0..ad91969 100644 --- a/arch/m68k/atari/atakeyb.c +++ b/arch/m68k/atari/atakeyb.c @@ -430,14 +430,6 @@ void ikbd_mouse_y0_top(void) } EXPORT_SYMBOL(ikbd_mouse_y0_top); -/* Resume */ -void ikbd_resume(void) -{ - static const char cmd[1] = { 0x11 }; - - ikbd_write(cmd, 1); -} - /* Disable mouse */ void ikbd_mouse_disable(void) { @@ -447,14 +439,6 @@ void ikbd_mouse_disable(void) } EXPORT_SYMBOL(ikbd_mouse_disable); -/* Pause output */ -void ikbd_pause(void) -{ - static const char cmd[1] = { 0x13 }; - - ikbd_write(cmd, 1); -} - /* Set joystick event reporting */ void ikbd_joystick_event_on(void) { @@ -502,56 +486,6 @@ void ikbd_joystick_disable(void) ikbd_write(cmd, 1); } -/* Time-of-day clock set */ -void ikbd_clock_set(int year, int month, int day, int hour, int minute, int second) -{ - char cmd[7] = { 0x1B, year, month, day, hour, minute, second }; - - ikbd_write(cmd, 7); -} - -/* Interrogate time-of-day clock */ -void ikbd_clock_get(int *year, int *month, int *day, int *hour, int *minute, int second) -{ - static const char cmd[1] = { 0x1C }; - - ikbd_write(cmd, 1); -} - -/* Memory load */ -void ikbd_mem_write(int address, int size, char *data) -{ - panic("Attempt to write data into keyboard memory"); -} - -/* Memory read */ -void ikbd_mem_read(int address, char data[6]) -{ - char cmd[3] = { 0x21, address>>8, address&0xFF }; - - ikbd_write(cmd, 3); - - /* receive data and put it in data */ -} - -/* Controller execute */ -void ikbd_exec(int address) -{ - char cmd[3] = { 0x22, address>>8, address&0xFF }; - - ikbd_write(cmd, 3); -} - -/* Status inquiries (0x87-0x9A) not yet implemented */ - -/* Set the state of the caps lock led. */ -void atari_kbd_leds(unsigned int leds) -{ - char cmd[6] = {32, 0, 4, 1, 254 + ((leds & 4) != 0), 0}; - - ikbd_write(cmd, 6); -} - /* * The original code sometimes left the interrupt line of * the ACIAs low forever. I hope, it is fixed now. -- cgit v0.10.2 From a38eaa07a0ce4bdb5f79637fedefae276a567e72 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Thu, 1 Jan 2015 17:49:11 +0100 Subject: m68k/mvme147: config.c - Remove unused functions Remove the function mvme147_init_console_port() that is not used anywhere. This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist [geert: Also remove now unused m147_scc_write(), scc_write(), scc_delay()] Signed-off-by: Geert Uytterhoeven diff --git a/arch/m68k/mvme147/config.c b/arch/m68k/mvme147/config.c index 1bb3ce6..e6a3b56 100644 --- a/arch/m68k/mvme147/config.c +++ b/arch/m68k/mvme147/config.c @@ -168,49 +168,3 @@ int mvme147_set_clock_mmss (unsigned long nowtime) { return 0; } - -/*------------------- Serial console stuff ------------------------*/ - -static void scc_delay (void) -{ - int n; - volatile int trash; - - for (n = 0; n < 20; n++) - trash = n; -} - -static void scc_write (char ch) -{ - volatile char *p = (volatile char *)M147_SCC_A_ADDR; - - do { - scc_delay(); - } - while (!(*p & 4)); - scc_delay(); - *p = 8; - scc_delay(); - *p = ch; -} - - -void m147_scc_write (struct console *co, const char *str, unsigned count) -{ - unsigned long flags; - - local_irq_save(flags); - - while (count--) - { - if (*str == '\n') - scc_write ('\r'); - scc_write (*str++); - } - local_irq_restore(flags); -} - -void mvme147_init_console_port (struct console *co, int cflag) -{ - co->write = m147_scc_write; -} -- cgit v0.10.2 From 8c0ce284b320d2e6ef1a7c9a590d4fa44299c695 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 6 Jan 2015 08:49:52 +0100 Subject: m68k: Switch to asm-generic/futex.h As of commit 00f634bc522dedc8 ("asm-generic: add generic futex for !CONFIG_SMP") asm-generic follows the m68k futex implementation. Signed-off-by: Geert Uytterhoeven diff --git a/arch/m68k/include/asm/Kbuild b/arch/m68k/include/asm/Kbuild index 9b6c691..1517ed1 100644 --- a/arch/m68k/include/asm/Kbuild +++ b/arch/m68k/include/asm/Kbuild @@ -6,6 +6,7 @@ generic-y += device.h generic-y += emergency-restart.h generic-y += errno.h generic-y += exec.h +generic-y += futex.h generic-y += hw_irq.h generic-y += ioctl.h generic-y += ipcbuf.h diff --git a/arch/m68k/include/asm/futex.h b/arch/m68k/include/asm/futex.h deleted file mode 100644 index bc868af..0000000 --- a/arch/m68k/include/asm/futex.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef _ASM_M68K_FUTEX_H -#define _ASM_M68K_FUTEX_H - -#ifdef __KERNEL__ -#if !defined(CONFIG_MMU) -#include -#else /* CONFIG_MMU */ - -#include -#include -#include - -static inline int -futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, - u32 oldval, u32 newval) -{ - u32 val; - - if (unlikely(get_user(val, uaddr) != 0)) - return -EFAULT; - - if (val == oldval && unlikely(put_user(newval, uaddr) != 0)) - return -EFAULT; - - *uval = val; - - return 0; -} - -static inline int -futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) -{ - int op = (encoded_op >> 28) & 7; - int cmp = (encoded_op >> 24) & 15; - int oparg = (encoded_op << 8) >> 20; - int cmparg = (encoded_op << 20) >> 20; - int oldval, ret; - u32 tmp; - - if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) - oparg = 1 << oparg; - - pagefault_disable(); /* implies preempt_disable() */ - - ret = -EFAULT; - if (unlikely(get_user(oldval, uaddr) != 0)) - goto out_pagefault_enable; - - ret = 0; - tmp = oldval; - - switch (op) { - case FUTEX_OP_SET: - tmp = oparg; - break; - case FUTEX_OP_ADD: - tmp += oparg; - break; - case FUTEX_OP_OR: - tmp |= oparg; - break; - case FUTEX_OP_ANDN: - tmp &= ~oparg; - break; - case FUTEX_OP_XOR: - tmp ^= oparg; - break; - default: - ret = -ENOSYS; - } - - if (ret == 0 && unlikely(put_user(tmp, uaddr) != 0)) - ret = -EFAULT; - -out_pagefault_enable: - pagefault_enable(); /* subsumes preempt_enable() */ - - if (ret == 0) { - switch (cmp) { - case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break; - case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break; - case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break; - case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break; - case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break; - case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break; - default: ret = -ENOSYS; - } - } - return ret; -} - -#endif /* CONFIG_MMU */ -#endif /* __KERNEL__ */ -#endif /* _ASM_M68K_FUTEX_H */ -- cgit v0.10.2 From 2e874d178c9e8cb048bfc1075ec0e93582c0e124 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Wed, 7 Jan 2015 11:17:35 +1100 Subject: m68k/mac: Fix scsi_type for Mac LC and similar models Designing Cards and Drivers for the Macintosh Family, 3rd ed. on page 310 says that the I/O address space for the Mac LC is $50F0 0000 - $50FF FFFF. The developer notes for the Classic II, LC III and IIvx/IIvi give the same I/O address space. That means I've assigned the wrong platform resources to those Mac models. Fix the scsi_type initialization for the affected models, to restore the SCSI base address to its value prior to Linux 3.18. Also rename MAC_SCSI_CCL as MAC_SCSI_LC for the sake of correct chronology. Signed-off-by: Finn Thain Signed-off-by: Geert Uytterhoeven diff --git a/arch/m68k/include/asm/macintosh.h b/arch/m68k/include/asm/macintosh.h index 29c7c6c..42235e7 100644 --- a/arch/m68k/include/asm/macintosh.h +++ b/arch/m68k/include/asm/macintosh.h @@ -55,7 +55,7 @@ struct mac_model #define MAC_SCSI_QUADRA3 4 #define MAC_SCSI_IIFX 5 #define MAC_SCSI_DUO 6 -#define MAC_SCSI_CCL 7 +#define MAC_SCSI_LC 7 #define MAC_SCSI_LATE 8 #define MAC_IDE_NONE 0 diff --git a/arch/m68k/mac/config.c b/arch/m68k/mac/config.c index e9c3756..689b47d 100644 --- a/arch/m68k/mac/config.c +++ b/arch/m68k/mac/config.c @@ -296,7 +296,7 @@ static struct mac_model mac_data_table[] = { .name = "IIvi", .adb_type = MAC_ADB_IISI, .via_type = MAC_VIA_IICI, - .scsi_type = MAC_SCSI_OLD, + .scsi_type = MAC_SCSI_LC, .scc_type = MAC_SCC_II, .nubus_type = MAC_NUBUS, .floppy_type = MAC_FLOPPY_SWIM_ADDR2, @@ -305,7 +305,7 @@ static struct mac_model mac_data_table[] = { .name = "IIvx", .adb_type = MAC_ADB_IISI, .via_type = MAC_VIA_IICI, - .scsi_type = MAC_SCSI_OLD, + .scsi_type = MAC_SCSI_LC, .scc_type = MAC_SCC_II, .nubus_type = MAC_NUBUS, .floppy_type = MAC_FLOPPY_SWIM_ADDR2, @@ -320,7 +320,7 @@ static struct mac_model mac_data_table[] = { .name = "Classic II", .adb_type = MAC_ADB_IISI, .via_type = MAC_VIA_IICI, - .scsi_type = MAC_SCSI_OLD, + .scsi_type = MAC_SCSI_LC, .scc_type = MAC_SCC_II, .nubus_type = MAC_NUBUS, .floppy_type = MAC_FLOPPY_SWIM_ADDR2, @@ -329,7 +329,7 @@ static struct mac_model mac_data_table[] = { .name = "Color Classic", .adb_type = MAC_ADB_CUDA, .via_type = MAC_VIA_IICI, - .scsi_type = MAC_SCSI_CCL, + .scsi_type = MAC_SCSI_LC, .scc_type = MAC_SCC_II, .nubus_type = MAC_NUBUS, .floppy_type = MAC_FLOPPY_SWIM_ADDR2, @@ -338,7 +338,7 @@ static struct mac_model mac_data_table[] = { .name = "Color Classic II", .adb_type = MAC_ADB_CUDA, .via_type = MAC_VIA_IICI, - .scsi_type = MAC_SCSI_CCL, + .scsi_type = MAC_SCSI_LC, .scc_type = MAC_SCC_II, .nubus_type = MAC_NUBUS, .floppy_type = MAC_FLOPPY_SWIM_ADDR2, @@ -353,7 +353,7 @@ static struct mac_model mac_data_table[] = { .name = "LC", .adb_type = MAC_ADB_IISI, .via_type = MAC_VIA_IICI, - .scsi_type = MAC_SCSI_OLD, + .scsi_type = MAC_SCSI_LC, .scc_type = MAC_SCC_II, .nubus_type = MAC_NUBUS, .floppy_type = MAC_FLOPPY_SWIM_ADDR2, @@ -362,7 +362,7 @@ static struct mac_model mac_data_table[] = { .name = "LC II", .adb_type = MAC_ADB_IISI, .via_type = MAC_VIA_IICI, - .scsi_type = MAC_SCSI_OLD, + .scsi_type = MAC_SCSI_LC, .scc_type = MAC_SCC_II, .nubus_type = MAC_NUBUS, .floppy_type = MAC_FLOPPY_SWIM_ADDR2, @@ -371,7 +371,7 @@ static struct mac_model mac_data_table[] = { .name = "LC III", .adb_type = MAC_ADB_IISI, .via_type = MAC_VIA_IICI, - .scsi_type = MAC_SCSI_OLD, + .scsi_type = MAC_SCSI_LC, .scc_type = MAC_SCC_II, .nubus_type = MAC_NUBUS, .floppy_type = MAC_FLOPPY_SWIM_ADDR2, @@ -499,7 +499,7 @@ static struct mac_model mac_data_table[] = { .name = "Performa 460", .adb_type = MAC_ADB_IISI, .via_type = MAC_VIA_IICI, - .scsi_type = MAC_SCSI_OLD, + .scsi_type = MAC_SCSI_LC, .scc_type = MAC_SCC_II, .nubus_type = MAC_NUBUS, .floppy_type = MAC_FLOPPY_SWIM_ADDR2, @@ -526,7 +526,7 @@ static struct mac_model mac_data_table[] = { .name = "Performa 520", .adb_type = MAC_ADB_CUDA, .via_type = MAC_VIA_IICI, - .scsi_type = MAC_SCSI_CCL, + .scsi_type = MAC_SCSI_LC, .scc_type = MAC_SCC_II, .nubus_type = MAC_NUBUS, .floppy_type = MAC_FLOPPY_SWIM_ADDR2, @@ -535,7 +535,7 @@ static struct mac_model mac_data_table[] = { .name = "Performa 550", .adb_type = MAC_ADB_CUDA, .via_type = MAC_VIA_IICI, - .scsi_type = MAC_SCSI_CCL, + .scsi_type = MAC_SCSI_LC, .scc_type = MAC_SCC_II, .nubus_type = MAC_NUBUS, .floppy_type = MAC_FLOPPY_SWIM_ADDR2, @@ -567,7 +567,7 @@ static struct mac_model mac_data_table[] = { .name = "TV", .adb_type = MAC_ADB_CUDA, .via_type = MAC_VIA_IICI, - .scsi_type = MAC_SCSI_CCL, + .scsi_type = MAC_SCSI_LC, .scc_type = MAC_SCC_II, .nubus_type = MAC_NUBUS, .floppy_type = MAC_FLOPPY_SWIM_ADDR2, @@ -576,7 +576,7 @@ static struct mac_model mac_data_table[] = { .name = "Performa 600", .adb_type = MAC_ADB_IISI, .via_type = MAC_VIA_IICI, - .scsi_type = MAC_SCSI_OLD, + .scsi_type = MAC_SCSI_LC, .scc_type = MAC_SCC_II, .nubus_type = MAC_NUBUS, .floppy_type = MAC_FLOPPY_SWIM_ADDR2, @@ -1109,8 +1109,10 @@ int __init mac_platform_init(void) platform_device_register_simple("mac_scsi", 0, mac_scsi_late_rsrc, ARRAY_SIZE(mac_scsi_late_rsrc)); break; - case MAC_SCSI_CCL: - /* Addresses from the Color Classic Developer Note. + case MAC_SCSI_LC: + /* Addresses from Mac LC data in Designing Cards & Drivers 3ed. + * Also from the Developer Notes for Classic II, LC III, + * Color Classic and IIvx. * $50F0 6000 - $50F0 7FFF: SCSI handshake * $50F1 0000 - $50F1 1FFF: SCSI * $50F1 2000 - $50F1 3FFF: SCSI DMA -- cgit v0.10.2 From ace79a3592bee97406c88c3d856e16b7f5720a39 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 11 Jan 2015 16:31:40 +0100 Subject: ALSA: ice1712: wm8766: Remove some unused functions Removes some functions that are not used anywhere: snd_wm8766_set_power() snd_wm8766_set_master_mode() This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/wm8766.c b/sound/pci/ice1712/wm8766.c index 21b373b..f7ac8d5 100644 --- a/sound/pci/ice1712/wm8766.c +++ b/sound/pci/ice1712/wm8766.c @@ -183,22 +183,6 @@ void snd_wm8766_set_if(struct snd_wm8766 *wm, u16 dac) snd_wm8766_write(wm, WM8766_REG_IFCTRL, val | dac); } -void snd_wm8766_set_master_mode(struct snd_wm8766 *wm, u16 mode) -{ - u16 val = wm->regs[WM8766_REG_DACCTRL3] & ~WM8766_DAC3_MSTR_MASK; - - mode &= WM8766_DAC3_MSTR_MASK; - snd_wm8766_write(wm, WM8766_REG_DACCTRL3, val | mode); -} - -void snd_wm8766_set_power(struct snd_wm8766 *wm, u16 power) -{ - u16 val = wm->regs[WM8766_REG_DACCTRL3] & ~WM8766_DAC3_POWER_MASK; - - power &= WM8766_DAC3_POWER_MASK; - snd_wm8766_write(wm, WM8766_REG_DACCTRL3, val | power); -} - void snd_wm8766_volume_restore(struct snd_wm8766 *wm) { u16 val = wm->regs[WM8766_REG_DACR1]; diff --git a/sound/pci/ice1712/wm8766.h b/sound/pci/ice1712/wm8766.h index c119f84..18c8d9d 100644 --- a/sound/pci/ice1712/wm8766.h +++ b/sound/pci/ice1712/wm8766.h @@ -155,8 +155,6 @@ struct snd_wm8766 { void snd_wm8766_init(struct snd_wm8766 *wm); void snd_wm8766_resume(struct snd_wm8766 *wm); void snd_wm8766_set_if(struct snd_wm8766 *wm, u16 dac); -void snd_wm8766_set_master_mode(struct snd_wm8766 *wm, u16 mode); -void snd_wm8766_set_power(struct snd_wm8766 *wm, u16 power); void snd_wm8766_volume_restore(struct snd_wm8766 *wm); int snd_wm8766_build_controls(struct snd_wm8766 *wm); -- cgit v0.10.2 From e88af2f8de7914d311e66854eba2889710ee364e Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 11 Jan 2015 15:27:01 +0100 Subject: ata: libata-core: Remove unused function Remove the function ata_do_simple_cmd() that is not used anywhere. This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: Tejun Heo diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 5c84fb5..7cbcafa 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -1752,33 +1752,6 @@ unsigned ata_exec_internal(struct ata_device *dev, } /** - * ata_do_simple_cmd - execute simple internal command - * @dev: Device to which the command is sent - * @cmd: Opcode to execute - * - * Execute a 'simple' command, that only consists of the opcode - * 'cmd' itself, without filling any other registers - * - * LOCKING: - * Kernel thread context (may sleep). - * - * RETURNS: - * Zero on success, AC_ERR_* mask on failure - */ -unsigned int ata_do_simple_cmd(struct ata_device *dev, u8 cmd) -{ - struct ata_taskfile tf; - - ata_tf_init(dev, &tf); - - tf.command = cmd; - tf.flags |= ATA_TFLAG_DEVICE; - tf.protocol = ATA_PROT_NODATA; - - return ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0); -} - -/** * ata_pio_need_iordy - check if iordy needed * @adev: ATA device * diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 5f4e0cc..82ebe26 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -76,7 +76,6 @@ extern unsigned ata_exec_internal_sg(struct ata_device *dev, struct ata_taskfile *tf, const u8 *cdb, int dma_dir, struct scatterlist *sg, unsigned int n_elem, unsigned long timeout); -extern unsigned int ata_do_simple_cmd(struct ata_device *dev, u8 cmd); extern int ata_wait_ready(struct ata_link *link, unsigned long deadline, int (*check_ready)(struct ata_link *link)); extern int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, -- cgit v0.10.2 From d40f74f7273b484ca4382e5ae08b59dee85e01e4 Mon Sep 17 00:00:00 2001 From: Hyungwon Hwang Date: Fri, 9 Jan 2015 10:24:41 +0900 Subject: drm/exynos: remove the redundant machine checking code This code is unnecessary, because same logic is already included. Refer this mail thread[1] for detail. [1] http://lists.freedesktop.org/archives/dri-devel/2015-January/075132.html Signed-off-by: Hyungwon Hwang Signed-off-by: Inki Dae diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 121470a..1bcbe07 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -645,18 +645,6 @@ static int exynos_drm_init(void) if (!is_exynos) return -ENODEV; - /* - * Register device object only in case of Exynos SoC. - * - * Below codes resolves temporarily infinite loop issue incurred - * by Exynos drm driver when using multi-platform kernel. - * So these codes will be replaced with more generic way later. - */ - if (!of_machine_is_compatible("samsung,exynos3") && - !of_machine_is_compatible("samsung,exynos4") && - !of_machine_is_compatible("samsung,exynos5")) - return -ENODEV; - exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1, NULL, 0); if (IS_ERR(exynos_drm_pdev)) -- cgit v0.10.2 From 31c89c959667194350f496947b576c149503ce98 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Fri, 9 Jan 2015 07:43:45 -0800 Subject: pinctrl: pinconf-generic: Infer map type from DT property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the new 'groups' property, the DT parser can infer the map type from the fact whether 'pins' or 'groups' is used to specify the pin group to work on. To maintain backwards compatibitliy with current usage of the DT binding, this is only done when PIN_MAP_TYPE_INVALID is passed to the parsing function as type. Also, a new helper 'pinconf_generic_dt_node_to_map_all()' is introduced, which can be used by drivers as generic callback for dt_node_to_map() to leverage the new feature. Changes since v2: - rename dt_pin_specifier to subnode_target_type - add additional comment in header file explaining passing an invalid map type - mention map_all() helper in commit message Changes since RFC v2: - none Signed-off-by: Soren Brinkmann Tested-by: Andreas Färber Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c index f78b416..21b3d90 100644 --- a/drivers/pinctrl/pinconf-generic.c +++ b/drivers/pinctrl/pinconf-generic.c @@ -264,6 +264,7 @@ int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, unsigned reserve; struct property *prop; const char *group; + const char *subnode_target_type = "pins"; ret = of_property_read_string(np, "function", &function); if (ret < 0) { @@ -284,10 +285,20 @@ int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, reserve++; if (num_configs) reserve++; + ret = of_property_count_strings(np, "pins"); if (ret < 0) { - dev_err(dev, "could not parse property pins\n"); - goto exit; + ret = of_property_count_strings(np, "groups"); + if (ret < 0) { + dev_err(dev, "could not parse property pins/groups\n"); + goto exit; + } + if (type == PIN_MAP_TYPE_INVALID) + type = PIN_MAP_TYPE_CONFIGS_GROUP; + subnode_target_type = "groups"; + } else { + if (type == PIN_MAP_TYPE_INVALID) + type = PIN_MAP_TYPE_CONFIGS_PIN; } reserve *= ret; @@ -296,7 +307,7 @@ int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, if (ret < 0) goto exit; - of_property_for_each_string(np, "pins", prop, group) { + of_property_for_each_string(np, subnode_target_type, prop, group) { if (function) { ret = pinctrl_utils_add_map_mux(pctldev, map, reserved_maps, num_maps, group, diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h index d578a60..83c89f5 100644 --- a/include/linux/pinctrl/pinconf-generic.h +++ b/include/linux/pinctrl/pinconf-generic.h @@ -174,6 +174,17 @@ static inline int pinconf_generic_dt_node_to_map_pin( PIN_MAP_TYPE_CONFIGS_PIN); } +static inline int pinconf_generic_dt_node_to_map_all( + struct pinctrl_dev *pctldev, struct device_node *np_config, + struct pinctrl_map **map, unsigned *num_maps) +{ + /* + * passing the type as PIN_MAP_TYPE_INVALID causes the underlying parser + * to infer the map type from the DT properties used. + */ + return pinconf_generic_dt_node_to_map(pctldev, np_config, map, num_maps, + PIN_MAP_TYPE_INVALID); +} #endif #endif /* CONFIG_GENERIC_PINCONF */ -- cgit v0.10.2 From dd4d01f7bad886c22687224bc7070b87de8deb51 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Fri, 9 Jan 2015 07:43:46 -0800 Subject: pinctrl: pinconf-generic: Allow driver to specify DT params MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Additionally to the generic DT parameters, allow drivers to provide driver-specific DT parameters to be used with the generic parser infrastructure. To achieve this 'struct pinctrl_desc' is extended to pass custom pinconf option to the core. In order to pass this kind of information, the related data structures - 'struct pinconf_generic_dt_params', 'pin_config_item' - are moved from pinconf internals to the pinconf-generic header. Additionally pinconfg-generic is refactored to not only iterate over the generic pinconf parameters but also take the parameters into account that are provided through the driver's 'struct pinctrl_desc'. In particular 'pinconf_generic_parse_dt_config()' and 'pinconf_generic_dump' helpers are split into two parts each. In order to have a more generic helper that can be used to process the generic parameters as well as the driver-specific ones. v2: - fix typo - add missing documentation for @conf_items member in struct - rebase to pinctrl/devel: conflict in abx500 - rename _pinconf_generic_dump() to pinconf_generic_dump_one() - removed '_' from _parse_dt_cfg() - removed BUG_ONs, error condition is handled in if statements - removed pinconf_generic_dump_group() & pinconf_generic_dump_pin helpers - fixed up corresponding call sites - renamed pinconf_generic_dump() to pinconf_generic_dump_pins() - added kernel-doc to pinconf_generic_dump_pins() - add kernel-doc - more verbose commit message Signed-off-by: Soren Brinkmann Tested-by: Andreas Färber Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/nomadik/pinctrl-abx500.c b/drivers/pinctrl/nomadik/pinctrl-abx500.c index 3d6d972..1806b24 100644 --- a/drivers/pinctrl/nomadik/pinctrl-abx500.c +++ b/drivers/pinctrl/nomadik/pinctrl-abx500.c @@ -914,7 +914,7 @@ static int abx500_dt_subnode_to_map(struct pinctrl_dev *pctldev, } } - ret = pinconf_generic_parse_dt_config(np, &configs, &nconfigs); + ret = pinconf_generic_parse_dt_config(np, pctldev, &configs, &nconfigs); if (nconfigs) { const char *gpio_name; const char *pin; diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c index 21b3d90..e088666 100644 --- a/drivers/pinctrl/pinconf-generic.c +++ b/drivers/pinctrl/pinconf-generic.c @@ -27,17 +27,6 @@ #include "pinctrl-utils.h" #ifdef CONFIG_DEBUG_FS - -struct pin_config_item { - const enum pin_config_param param; - const char * const display; - const char * const format; - bool has_arg; -}; - -#define PCONFDUMP(a, b, c, d) { .param = a, .display = b, .format = c, \ - .has_arg = d } - static const struct pin_config_item conf_items[] = { PCONFDUMP(PIN_CONFIG_BIAS_DISABLE, "input bias disabled", NULL, false), PCONFDUMP(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, "input bias high impedance", NULL, false), @@ -60,22 +49,25 @@ static const struct pin_config_item conf_items[] = { PCONFDUMP(PIN_CONFIG_OUTPUT, "pin output", "level", true), }; -void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev, - struct seq_file *s, unsigned pin) +static void pinconf_generic_dump_one(struct pinctrl_dev *pctldev, + struct seq_file *s, const char *gname, + unsigned pin, + const struct pin_config_item *items, + int nitems) { - const struct pinconf_ops *ops = pctldev->desc->confops; int i; - if (!ops->is_generic) - return; - - for (i = 0; i < ARRAY_SIZE(conf_items); i++) { + for (i = 0; i < nitems; i++) { unsigned long config; int ret; /* We want to check out this parameter */ - config = pinconf_to_config_packed(conf_items[i].param, 0); - ret = pin_config_get_for_pin(pctldev, pin, &config); + config = pinconf_to_config_packed(items[i].param, 0); + if (gname) + ret = pin_config_group_get(dev_name(pctldev->dev), + gname, &config); + else + ret = pin_config_get_for_pin(pctldev, pin, &config); /* These are legal errors */ if (ret == -EINVAL || ret == -ENOTSUPP) continue; @@ -85,56 +77,46 @@ void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev, } /* Space between multiple configs */ seq_puts(s, " "); - seq_puts(s, conf_items[i].display); + seq_puts(s, items[i].display); /* Print unit if available */ - if (conf_items[i].has_arg) { + if (items[i].has_arg) { seq_printf(s, " (%u", pinconf_to_config_argument(config)); - if (conf_items[i].format) - seq_printf(s, " %s)", conf_items[i].format); + if (items[i].format) + seq_printf(s, " %s)", items[i].format); else seq_puts(s, ")"); } } } -void pinconf_generic_dump_group(struct pinctrl_dev *pctldev, - struct seq_file *s, const char *gname) +/** + * pinconf_generic_dump_pins - Print information about pin or group of pins + * @pctldev: Pincontrol device + * @s: File to print to + * @gname: Group name specifying pins + * @pin: Pin number specyfying pin + * + * Print the pinconf configuration for the requested pin(s) to @s. Pins can be + * specified either by pin using @pin or by group using @gname. Only one needs + * to be specified the other can be NULL/0. + */ +void pinconf_generic_dump_pins(struct pinctrl_dev *pctldev, struct seq_file *s, + const char *gname, unsigned pin) { const struct pinconf_ops *ops = pctldev->desc->confops; - int i; if (!ops->is_generic) return; - for (i = 0; i < ARRAY_SIZE(conf_items); i++) { - unsigned long config; - int ret; - - /* We want to check out this parameter */ - config = pinconf_to_config_packed(conf_items[i].param, 0); - ret = pin_config_group_get(dev_name(pctldev->dev), gname, - &config); - /* These are legal errors */ - if (ret == -EINVAL || ret == -ENOTSUPP) - continue; - if (ret) { - seq_printf(s, "ERROR READING CONFIG SETTING %d ", i); - continue; - } - /* Space between multiple configs */ - seq_puts(s, " "); - seq_puts(s, conf_items[i].display); - /* Print unit if available */ - if (conf_items[i].has_arg) { - seq_printf(s, " (%u", - pinconf_to_config_argument(config)); - if (conf_items[i].format) - seq_printf(s, " %s)", conf_items[i].format); - else - seq_puts(s, ")"); - } - } + /* generic parameters */ + pinconf_generic_dump_one(pctldev, s, gname, pin, conf_items, + ARRAY_SIZE(conf_items)); + /* driver-specific parameters */ + if (pctldev->desc->num_dt_params && pctldev->desc->conf_items) + pinconf_generic_dump_one(pctldev, s, gname, pin, + pctldev->desc->conf_items, + pctldev->desc->num_dt_params); } void pinconf_generic_dump_config(struct pinctrl_dev *pctldev, @@ -148,17 +130,21 @@ void pinconf_generic_dump_config(struct pinctrl_dev *pctldev, seq_printf(s, "%s: 0x%x", conf_items[i].display, pinconf_to_config_argument(config)); } + + if (!pctldev->desc->num_dt_params || !pctldev->desc->conf_items) + return; + + for (i = 0; i < pctldev->desc->num_dt_params; i++) { + if (pinconf_to_config_param(config) != pctldev->desc->conf_items[i].param) + continue; + seq_printf(s, "%s: 0x%x", pctldev->desc->conf_items[i].display, + pinconf_to_config_argument(config)); + } } EXPORT_SYMBOL_GPL(pinconf_generic_dump_config); #endif #ifdef CONFIG_OF -struct pinconf_generic_dt_params { - const char * const property; - enum pin_config_param param; - u32 default_value; -}; - static const struct pinconf_generic_dt_params dt_params[] = { { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 }, { "bias-high-impedance", PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 0 }, @@ -184,6 +170,47 @@ static const struct pinconf_generic_dt_params dt_params[] = { }; /** + * parse_dt_cfg - Parse DT pinconf parameters + * @np: DT node + * @params: Array of describing DT parameters + * @count: Number of entries in @params + * @cfg: Array of parsed config options + * @ncfg: Number of entries in @cfg + * + * Parse the config options described in @params from @np and puts the result + * in @cfg. @cfg does not need to be empty, entries are added beggining at + * @ncfg. @ncfg is updated to reflect the number of entries after parsing. @cfg + * needs to have enough memory allocated to hold all possible entries. + */ +static void parse_dt_cfg(struct device_node *np, + const struct pinconf_generic_dt_params *params, + unsigned int count, unsigned long *cfg, + unsigned int *ncfg) +{ + int i; + + for (i = 0; i < count; i++) { + u32 val; + int ret; + const struct pinconf_generic_dt_params *par = ¶ms[i]; + + ret = of_property_read_u32(np, par->property, &val); + + /* property not found */ + if (ret == -EINVAL) + continue; + + /* use default value, when no value is specified */ + if (ret) + val = par->default_value; + + pr_debug("found %s with value %u\n", par->property, val); + cfg[*ncfg] = pinconf_to_config_packed(par->param, val); + (*ncfg)++; + } +} + +/** * pinconf_generic_parse_dt_config() * parse the config properties into generic pinconfig values. * @np: node containing the pinconfig properties @@ -191,39 +218,29 @@ static const struct pinconf_generic_dt_params dt_params[] = { * @nconfigs: umber of configurations */ int pinconf_generic_parse_dt_config(struct device_node *np, + struct pinctrl_dev *pctldev, unsigned long **configs, unsigned int *nconfigs) { unsigned long *cfg; - unsigned int ncfg = 0; + unsigned int max_cfg, ncfg = 0; int ret; - int i; - u32 val; if (!np) return -EINVAL; /* allocate a temporary array big enough to hold one of each option */ - cfg = kzalloc(sizeof(*cfg) * ARRAY_SIZE(dt_params), GFP_KERNEL); + max_cfg = ARRAY_SIZE(dt_params); + if (pctldev) + max_cfg += pctldev->desc->num_dt_params; + cfg = kcalloc(max_cfg, sizeof(*cfg), GFP_KERNEL); if (!cfg) return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(dt_params); i++) { - const struct pinconf_generic_dt_params *par = &dt_params[i]; - ret = of_property_read_u32(np, par->property, &val); - - /* property not found */ - if (ret == -EINVAL) - continue; - - /* use default value, when no value is specified */ - if (ret) - val = par->default_value; - - pr_debug("found %s with value %u\n", par->property, val); - cfg[ncfg] = pinconf_to_config_packed(par->param, val); - ncfg++; - } + parse_dt_cfg(np, dt_params, ARRAY_SIZE(dt_params), cfg, &ncfg); + if (pctldev && pctldev->desc->num_dt_params && pctldev->desc->params) + parse_dt_cfg(np, pctldev->desc->params, + pctldev->desc->num_dt_params, cfg, &ncfg); ret = 0; @@ -274,7 +291,8 @@ int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, function = NULL; } - ret = pinconf_generic_parse_dt_config(np, &configs, &num_configs); + ret = pinconf_generic_parse_dt_config(np, pctldev, &configs, + &num_configs); if (ret < 0) { dev_err(dev, "could not parse node property\n"); return ret; diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index 8bfa064..1fc09dc 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -288,7 +288,7 @@ static void pinconf_dump_pin(struct pinctrl_dev *pctldev, const struct pinconf_ops *ops = pctldev->desc->confops; /* no-op when not using generic pin config */ - pinconf_generic_dump_pin(pctldev, s, pin); + pinconf_generic_dump_pins(pctldev, s, NULL, pin); if (ops && ops->pin_config_dbg_show) ops->pin_config_dbg_show(pctldev, s, pin); } @@ -333,7 +333,7 @@ static void pinconf_dump_group(struct pinctrl_dev *pctldev, const struct pinconf_ops *ops = pctldev->desc->confops; /* no-op when not using generic pin config */ - pinconf_generic_dump_group(pctldev, s, gname); + pinconf_generic_dump_pins(pctldev, s, gname, 0); if (ops && ops->pin_config_group_dbg_show) ops->pin_config_group_dbg_show(pctldev, s, selector); } diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index a4a5417..55c7578 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -92,26 +92,17 @@ static inline void pinconf_init_device_debugfs(struct dentry *devroot, #if defined(CONFIG_GENERIC_PINCONF) && defined(CONFIG_DEBUG_FS) -void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev, - struct seq_file *s, unsigned pin); - -void pinconf_generic_dump_group(struct pinctrl_dev *pctldev, - struct seq_file *s, const char *gname); +void pinconf_generic_dump_pins(struct pinctrl_dev *pctldev, + struct seq_file *s, const char *gname, + unsigned pin); void pinconf_generic_dump_config(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned long config); #else -static inline void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev, - struct seq_file *s, - unsigned pin) -{ - return; -} - -static inline void pinconf_generic_dump_group(struct pinctrl_dev *pctldev, - struct seq_file *s, - const char *gname) +static inline void pinconf_generic_dump_pins(struct pinctrl_dev *pctldev, + struct seq_file *s, + const char *gname, unsigned pin) { return; } @@ -126,6 +117,7 @@ static inline void pinconf_generic_dump_config(struct pinctrl_dev *pctldev, #if defined(CONFIG_GENERIC_PINCONF) && defined(CONFIG_OF) int pinconf_generic_parse_dt_config(struct device_node *np, + struct pinctrl_dev *pctldev, unsigned long **configs, unsigned int *nconfigs); #endif diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c index ba74f0a..7625f33 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -1140,7 +1140,7 @@ static int rockchip_pinctrl_parse_groups(struct device_node *np, return -EINVAL; np_config = of_find_node_by_phandle(be32_to_cpup(phandle)); - ret = pinconf_generic_parse_dt_config(np_config, + ret = pinconf_generic_parse_dt_config(np_config, NULL, &grp->data[j].configs, &grp->data[j].nconfigs); if (ret) return ret; diff --git a/drivers/pinctrl/pinctrl-tz1090-pdc.c b/drivers/pinctrl/pinctrl-tz1090-pdc.c index 146e48a..fab6aaf 100644 --- a/drivers/pinctrl/pinctrl-tz1090-pdc.c +++ b/drivers/pinctrl/pinctrl-tz1090-pdc.c @@ -415,7 +415,7 @@ static int tz1090_pdc_pinctrl_dt_subnode_to_map(struct device *dev, function = NULL; } - ret = pinconf_generic_parse_dt_config(np, &configs, &num_configs); + ret = pinconf_generic_parse_dt_config(np, NULL, &configs, &num_configs); if (ret) return ret; diff --git a/drivers/pinctrl/pinctrl-tz1090.c b/drivers/pinctrl/pinctrl-tz1090.c index df8cb1e..8bd73075 100644 --- a/drivers/pinctrl/pinctrl-tz1090.c +++ b/drivers/pinctrl/pinctrl-tz1090.c @@ -1131,7 +1131,7 @@ static int tz1090_pinctrl_dt_subnode_to_map(struct device *dev, function = NULL; } - ret = pinconf_generic_parse_dt_config(np, &configs, &num_configs); + ret = pinconf_generic_parse_dt_config(np, NULL, &configs, &num_configs); if (ret) return ret; diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c index 910deae..072e7c6 100644 --- a/drivers/pinctrl/sh-pfc/pinctrl.c +++ b/drivers/pinctrl/sh-pfc/pinctrl.c @@ -122,7 +122,7 @@ static int sh_pfc_dt_subnode_to_map(struct device *dev, struct device_node *np, return ret; } - ret = pinconf_generic_parse_dt_config(np, &configs, &num_configs); + ret = pinconf_generic_parse_dt_config(np, NULL, &configs, &num_configs); if (ret < 0) return ret; diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h index 83c89f5..342409f 100644 --- a/include/linux/pinctrl/pinconf-generic.h +++ b/include/linux/pinctrl/pinconf-generic.h @@ -115,6 +115,18 @@ enum pin_config_param { PIN_CONFIG_END = 0x7FFF, }; +#ifdef CONFIG_DEBUG_FS +#define PCONFDUMP(a, b, c, d) { .param = a, .display = b, .format = c, \ + .has_arg = d } + +struct pin_config_item { + const enum pin_config_param param; + const char * const display; + const char * const format; + bool has_arg; +}; +#endif /* CONFIG_DEBUG_FS */ + /* * Helpful configuration macro to be used in tables etc. */ @@ -150,6 +162,12 @@ static inline unsigned long pinconf_to_config_packed(enum pin_config_param param struct pinctrl_dev; struct pinctrl_map; +struct pinconf_generic_dt_params { + const char * const property; + enum pin_config_param param; + u32 default_value; +}; + int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, struct device_node *np, struct pinctrl_map **map, unsigned *reserved_maps, unsigned *num_maps, diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index cc8e1af..c58b3e1 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -24,6 +24,7 @@ struct pinctrl_dev; struct pinctrl_map; struct pinmux_ops; struct pinconf_ops; +struct pin_config_item; struct gpio_chip; struct device_node; @@ -117,6 +118,9 @@ struct pinctrl_ops { * @confops: pin config operations vtable, if you support pin configuration in * your driver * @owner: module providing the pin controller, used for refcounting + * @num_dt_params: Number of driver-specific DT parameters + * @params: List of DT parameters + * @conf_items: Information how to print @params in debugfs */ struct pinctrl_desc { const char *name; @@ -126,6 +130,11 @@ struct pinctrl_desc { const struct pinmux_ops *pmxops; const struct pinconf_ops *confops; struct module *owner; +#if defined(CONFIG_GENERIC_PINCONF) && defined(CONFIG_OF) + unsigned int num_dt_params; + const struct pinconf_generic_dt_params *params; + const struct pin_config_item *conf_items; +#endif }; /* External interface to pin controller */ -- cgit v0.10.2 From da085a86732e351503ca8264b146343faaf20660 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Fri, 9 Jan 2015 07:43:47 -0800 Subject: pinctrl: zynq: Document DT binding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add documentation for the devicetree binding for the Zynq pincontroller. Changes since v1: - fix typo - add USB related documentation - remove 'pinctrl-' prefix for pinctrl sub-nodes - update documentation to enforce strict separation of pinmux and pinconf nodes - update example accordingly Signed-off-by: Soren Brinkmann Tested-by: Andreas Färber Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/pinctrl/xlnx,zynq-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/xlnx,zynq-pinctrl.txt new file mode 100644 index 0000000..b7b55a9 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/xlnx,zynq-pinctrl.txt @@ -0,0 +1,104 @@ + Binding for Xilinx Zynq Pinctrl + +Required properties: +- compatible: "xlnx,zynq-pinctrl" +- syscon: phandle to SLCR +- reg: Offset and length of pinctrl space in SLCR + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices, including the meaning of the +phrase "pin configuration node". + +Zynq's pin configuration nodes act as a container for an arbitrary number of +subnodes. Each of these subnodes represents some desired configuration for a +pin, a group, or a list of pins or groups. This configuration can include the +mux function to select on those pin(s)/group(s), and various pin configuration +parameters, such as pull-up, slew rate, etc. + +Each configuration node can consist of multiple nodes describing the pinmux and +pinconf options. Those nodes can be pinmux nodes or pinconf nodes. + +The name of each subnode is not important; all subnodes should be enumerated +and processed purely based on their content. + +Required properties for pinmux nodes are: + - groups: A list of pinmux groups. + - function: The name of a pinmux function to activate for the specified set + of groups. + +Required properties for configuration nodes: +One of: + - pins: a list of pin names + - groups: A list of pinmux groups. + +The following generic properties as defined in pinctrl-bindings.txt are valid +to specify in a pinmux subnode: + groups, function + +The following generic properties as defined in pinctrl-bindings.txt are valid +to specify in a pinconf subnode: + groups, pins, bias-disable, bias-high-impedance, bias-pull-up, slew-rate, + low-power-disable, low-power-enable + + Valid arguments for 'slew-rate' are '0' and '1' to select between slow and fast + respectively. + + Valid values for groups are: + ethernet0_0_grp, ethernet1_0_grp, mdio0_0_grp, mdio1_0_grp, + qspi0_0_grp, qspi1_0_grp, qspi_fbclk, qspi_cs1_grp, spi0_0_grp, + spi0_1_grp - spi0_2_grp, spi1_0_grp - spi1_3_grp, sdio0_0_grp - sdio0_2_grp, + sdio1_0_grp - sdio1_3_grp, sdio0_emio_wp, sdio0_emio_cd, sdio1_emio_wp, + sdio1_emio_cd, smc0_nor, smc0_nor_cs1_grp, smc0_nor_addr25_grp, smc0_nand, + can0_0_grp - can0_10_grp, can1_0_grp - can1_11_grp, uart0_0_grp - uart0_10_grp, + uart1_0_grp - uart1_11_grp, i2c0_0_grp - i2c0_10_grp, i2c1_0_grp - i2c1_10_grp, + ttc0_0_grp - ttc0_2_grp, ttc1_0_grp - ttc1_2_grp, swdt0_0_grp - swdt0_4_grp, + gpio0_0_grp - gpio0_53_grp, usb0_0_grp, usb1_0_grp + + Valid values for pins are: + MIO0 - MIO53 + + Valid values for function are: + ethernet0, ethernet1, mdio0, mdio1, qspi0, qspi1, qspi_fbclk, qspi_cs1, + spi0, spi1, sdio0, sdio0_pc, sdio0_cd, sdio0_wp, + sdio1, sdio1_pc, sdio1_cd, sdio1_wp, + smc0_nor, smc0_nor_cs1, smc0_nor_addr25, smc0_nand, can0, can1, uart0, uart1, + i2c0, i2c1, ttc0, ttc1, swdt0, gpio0, usb0, usb1 + +The following driver-specific properties as defined here are valid to specify in +a pin configuration subnode: + - io-standard: Configure the pin to use the selected IO standard according to + this mapping: + 1: LVCMOS18 + 2: LVCMOS25 + 3: LVCMOS33 + 4: HSTL + +Example: + pinctrl0: pinctrl@700 { + compatible = "xlnx,pinctrl-zynq"; + reg = <0x700 0x200>; + syscon = <&slcr>; + + pinctrl_uart1_default: uart1-default { + mux { + groups = "uart1_10_grp"; + function = "uart1"; + }; + + conf { + groups = "uart1_10_grp"; + slew-rate = <0>; + io-standard = <1>; + }; + + conf-rx { + pins = "MIO49"; + bias-high-impedance; + }; + + conf-tx { + pins = "MIO48"; + bias-disable; + }; + }; + }; -- cgit v0.10.2 From add958cee967df0a6d003e59b104ba9a6f263e00 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Fri, 9 Jan 2015 07:43:48 -0800 Subject: pinctrl: Add driver for Zynq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a pin-control driver for Zynq. Changes since v2: - driver-specific DT properties are passed to the core in two arrays, one for the actual DT parsing one for the debugfs representation. Issue a compiler warning when the number of entries is not the same for both arrays. Changes since v1: - fix EMIO_SD1_CD pin name - add USB to pinmux options changes since RFCv2: - let Zynq select PINCTRL_ZYNQ. Boot hangs when pinctrl information is present in DT but no driver available. - add #defines to get rid of magical constants - add commas at end of initializers - separate changes in mach-zynq in separate patch - add driver specific io-standard DT property - refactored pinconf set function to not require arguments for argument-less properties - squash other patches in - support for IO-standard property - support for low-power mode property - migration to pinconf_generic_dt_node_to_map_all() - use newly created infrastructure to add pass driver-specific DT params to pinconf-generic changes since RFC: - use syscon/regmap to access registers in SLCR space - rebase to 3.18: rename enable -> set_mux - add kernel-doc - support pinconf - supported attributes - pin-bias: pull up, tristate, disable - slew-rate: 0 == slow, 1 == fast; generic pinconf does not display argument Signed-off-by: Soren Brinkmann Tested-by: Andreas Färber Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index d014f22..67c8db9 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -191,6 +191,14 @@ config PINCTRL_PALMAS open drain configuration for the Palmas series devices like TPS65913, TPS80036 etc. +config PINCTRL_ZYNQ + bool "Pinctrl driver for Xilinx Zynq" + depends on ARCH_ZYNQ + select PINMUX + select GENERIC_PINCONF + help + This selectes the pinctrl driver for Xilinx Zynq. + source "drivers/pinctrl/berlin/Kconfig" source "drivers/pinctrl/freescale/Kconfig" source "drivers/pinctrl/intel/Kconfig" diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index c7139ba..6c28bd6 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_PINCTRL_XWAY) += pinctrl-xway.o obj-$(CONFIG_PINCTRL_LANTIQ) += pinctrl-lantiq.o obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o +obj-$(CONFIG_PINCTRL_ZYNQ) += pinctrl-zynq.o obj-$(CONFIG_ARCH_BERLIN) += berlin/ obj-y += freescale/ diff --git a/drivers/pinctrl/pinctrl-zynq.c b/drivers/pinctrl/pinctrl-zynq.c new file mode 100644 index 0000000..6253423 --- /dev/null +++ b/drivers/pinctrl/pinctrl-zynq.c @@ -0,0 +1,1176 @@ +/* + * Zynq pin controller + * + * Copyright (C) 2014 Xilinx + * + * Sören Brinkmann + * + * 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, see . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pinctrl-utils.h" +#include "core.h" + +#define ZYNQ_NUM_MIOS 54 + +#define ZYNQ_PCTRL_MIO_MST_TRI0 0x10c +#define ZYNQ_PCTRL_MIO_MST_TRI1 0x110 + +#define ZYNQ_PINMUX_MUX_SHIFT 1 +#define ZYNQ_PINMUX_MUX_MASK (0x7f << ZYNQ_PINMUX_MUX_SHIFT) + +/** + * struct zynq_pinctrl - driver data + * @pctrl: Pinctrl device + * @syscon: Syscon regmap + * @pctrl_offset: Offset for pinctrl into the @syscon space + * @groups: Pingroups + * @ngroupos: Number of @groups + * @funcs: Pinmux functions + * @nfuncs: Number of @funcs + */ +struct zynq_pinctrl { + struct pinctrl_dev *pctrl; + struct regmap *syscon; + u32 pctrl_offset; + const struct zynq_pctrl_group *groups; + unsigned int ngroups; + const struct zynq_pinmux_function *funcs; + unsigned int nfuncs; +}; + +struct zynq_pctrl_group { + const char *name; + const unsigned int *pins; + const unsigned npins; +}; + +/** + * struct zynq_pinmux_function - a pinmux function + * @name: Name of the pinmux function. + * @groups: List of pingroups for this function. + * @ngroups: Number of entries in @groups. + * @mux_val: Selector for this function + * @mux: Offset of function specific mux + * @mux_mask: Mask for function specific selector + * @mux_shift: Shift for function specific selector + */ +struct zynq_pinmux_function { + const char *name; + const char * const *groups; + unsigned int ngroups; + unsigned int mux_val; + u32 mux; + u32 mux_mask; + u8 mux_shift; +}; + +enum zynq_pinmux_functions { + ZYNQ_PMUX_can0, + ZYNQ_PMUX_can1, + ZYNQ_PMUX_ethernet0, + ZYNQ_PMUX_ethernet1, + ZYNQ_PMUX_gpio0, + ZYNQ_PMUX_i2c0, + ZYNQ_PMUX_i2c1, + ZYNQ_PMUX_mdio0, + ZYNQ_PMUX_mdio1, + ZYNQ_PMUX_qspi0, + ZYNQ_PMUX_qspi1, + ZYNQ_PMUX_qspi_fbclk, + ZYNQ_PMUX_qspi_cs1, + ZYNQ_PMUX_spi0, + ZYNQ_PMUX_spi1, + ZYNQ_PMUX_sdio0, + ZYNQ_PMUX_sdio0_pc, + ZYNQ_PMUX_sdio0_cd, + ZYNQ_PMUX_sdio0_wp, + ZYNQ_PMUX_sdio1, + ZYNQ_PMUX_sdio1_pc, + ZYNQ_PMUX_sdio1_cd, + ZYNQ_PMUX_sdio1_wp, + ZYNQ_PMUX_smc0_nor, + ZYNQ_PMUX_smc0_nor_cs1, + ZYNQ_PMUX_smc0_nor_addr25, + ZYNQ_PMUX_smc0_nand, + ZYNQ_PMUX_ttc0, + ZYNQ_PMUX_ttc1, + ZYNQ_PMUX_uart0, + ZYNQ_PMUX_uart1, + ZYNQ_PMUX_usb0, + ZYNQ_PMUX_usb1, + ZYNQ_PMUX_swdt0, + ZYNQ_PMUX_MAX_FUNC +}; + +const struct pinctrl_pin_desc zynq_pins[] = { + PINCTRL_PIN(0, "MIO0"), + PINCTRL_PIN(1, "MIO1"), + PINCTRL_PIN(2, "MIO2"), + PINCTRL_PIN(3, "MIO3"), + PINCTRL_PIN(4, "MIO4"), + PINCTRL_PIN(5, "MIO5"), + PINCTRL_PIN(6, "MIO6"), + PINCTRL_PIN(7, "MIO7"), + PINCTRL_PIN(8, "MIO8"), + PINCTRL_PIN(9, "MIO9"), + PINCTRL_PIN(10, "MIO10"), + PINCTRL_PIN(11, "MIO11"), + PINCTRL_PIN(12, "MIO12"), + PINCTRL_PIN(13, "MIO13"), + PINCTRL_PIN(14, "MIO14"), + PINCTRL_PIN(15, "MIO15"), + PINCTRL_PIN(16, "MIO16"), + PINCTRL_PIN(17, "MIO17"), + PINCTRL_PIN(18, "MIO18"), + PINCTRL_PIN(19, "MIO19"), + PINCTRL_PIN(20, "MIO20"), + PINCTRL_PIN(21, "MIO21"), + PINCTRL_PIN(22, "MIO22"), + PINCTRL_PIN(23, "MIO23"), + PINCTRL_PIN(24, "MIO24"), + PINCTRL_PIN(25, "MIO25"), + PINCTRL_PIN(26, "MIO26"), + PINCTRL_PIN(27, "MIO27"), + PINCTRL_PIN(28, "MIO28"), + PINCTRL_PIN(29, "MIO29"), + PINCTRL_PIN(30, "MIO30"), + PINCTRL_PIN(31, "MIO31"), + PINCTRL_PIN(32, "MIO32"), + PINCTRL_PIN(33, "MIO33"), + PINCTRL_PIN(34, "MIO34"), + PINCTRL_PIN(35, "MIO35"), + PINCTRL_PIN(36, "MIO36"), + PINCTRL_PIN(37, "MIO37"), + PINCTRL_PIN(38, "MIO38"), + PINCTRL_PIN(39, "MIO39"), + PINCTRL_PIN(40, "MIO40"), + PINCTRL_PIN(41, "MIO41"), + PINCTRL_PIN(42, "MIO42"), + PINCTRL_PIN(43, "MIO43"), + PINCTRL_PIN(44, "MIO44"), + PINCTRL_PIN(45, "MIO45"), + PINCTRL_PIN(46, "MIO46"), + PINCTRL_PIN(47, "MIO47"), + PINCTRL_PIN(48, "MIO48"), + PINCTRL_PIN(49, "MIO49"), + PINCTRL_PIN(50, "MIO50"), + PINCTRL_PIN(51, "MIO51"), + PINCTRL_PIN(52, "MIO52"), + PINCTRL_PIN(53, "MIO53"), + PINCTRL_PIN(54, "EMIO_SD0_WP"), + PINCTRL_PIN(55, "EMIO_SD0_CD"), + PINCTRL_PIN(56, "EMIO_SD1_WP"), + PINCTRL_PIN(57, "EMIO_SD1_CD"), +}; + +/* pin groups */ +static const unsigned int ethernet0_0_pins[] = {16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27}; +static const unsigned int ethernet1_0_pins[] = {28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39}; +static const unsigned int mdio0_0_pins[] = {52, 53}; +static const unsigned int mdio1_0_pins[] = {52, 53}; +static const unsigned int qspi0_0_pins[] = {1, 2, 3, 4, 5, 6}; + +static const unsigned int qspi1_0_pins[] = {9, 10, 11, 12, 13}; +static const unsigned int qspi_cs1_pins[] = {0}; +static const unsigned int qspi_fbclk_pins[] = {8}; +static const unsigned int spi0_0_pins[] = {16, 17, 18, 19, 20, 21}; +static const unsigned int spi0_1_pins[] = {28, 29, 30, 31, 32, 33}; +static const unsigned int spi0_2_pins[] = {40, 41, 42, 43, 44, 45}; +static const unsigned int spi1_0_pins[] = {10, 11, 12, 13, 14, 15}; +static const unsigned int spi1_1_pins[] = {22, 23, 24, 25, 26, 27}; +static const unsigned int spi1_2_pins[] = {34, 35, 36, 37, 38, 39}; +static const unsigned int spi1_3_pins[] = {46, 47, 48, 49, 40, 51}; +static const unsigned int sdio0_0_pins[] = {16, 17, 18, 19, 20, 21}; +static const unsigned int sdio0_1_pins[] = {28, 29, 30, 31, 32, 33}; +static const unsigned int sdio0_2_pins[] = {40, 41, 42, 43, 44, 45}; +static const unsigned int sdio1_0_pins[] = {10, 11, 12, 13, 14, 15}; +static const unsigned int sdio1_1_pins[] = {22, 23, 24, 25, 26, 27}; +static const unsigned int sdio1_2_pins[] = {34, 35, 36, 37, 38, 39}; +static const unsigned int sdio1_3_pins[] = {46, 47, 48, 49, 40, 51}; +static const unsigned int sdio0_emio_wp_pins[] = {54}; +static const unsigned int sdio0_emio_cd_pins[] = {55}; +static const unsigned int sdio1_emio_wp_pins[] = {56}; +static const unsigned int sdio1_emio_cd_pins[] = {57}; +static const unsigned int smc0_nor_pins[] = {0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, + 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39}; +static const unsigned int smc0_nor_cs1_pins[] = {1}; +static const unsigned int smc0_nor_addr25_pins[] = {1}; +static const unsigned int smc0_nand_pins[] = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 16, 17, 18, 19, 20, + 21, 22, 23}; +/* Note: CAN MIO clock inputs are modeled in the clock framework */ +static const unsigned int can0_0_pins[] = {10, 11}; +static const unsigned int can0_1_pins[] = {14, 15}; +static const unsigned int can0_2_pins[] = {18, 19}; +static const unsigned int can0_3_pins[] = {22, 23}; +static const unsigned int can0_4_pins[] = {26, 27}; +static const unsigned int can0_5_pins[] = {30, 31}; +static const unsigned int can0_6_pins[] = {34, 35}; +static const unsigned int can0_7_pins[] = {38, 39}; +static const unsigned int can0_8_pins[] = {42, 43}; +static const unsigned int can0_9_pins[] = {46, 47}; +static const unsigned int can0_10_pins[] = {50, 51}; +static const unsigned int can1_0_pins[] = {8, 9}; +static const unsigned int can1_1_pins[] = {12, 13}; +static const unsigned int can1_2_pins[] = {16, 17}; +static const unsigned int can1_3_pins[] = {20, 21}; +static const unsigned int can1_4_pins[] = {24, 25}; +static const unsigned int can1_5_pins[] = {28, 29}; +static const unsigned int can1_6_pins[] = {32, 33}; +static const unsigned int can1_7_pins[] = {36, 37}; +static const unsigned int can1_8_pins[] = {40, 41}; +static const unsigned int can1_9_pins[] = {44, 45}; +static const unsigned int can1_10_pins[] = {48, 49}; +static const unsigned int can1_11_pins[] = {52, 53}; +static const unsigned int uart0_0_pins[] = {10, 11}; +static const unsigned int uart0_1_pins[] = {14, 15}; +static const unsigned int uart0_2_pins[] = {18, 19}; +static const unsigned int uart0_3_pins[] = {22, 23}; +static const unsigned int uart0_4_pins[] = {26, 27}; +static const unsigned int uart0_5_pins[] = {30, 31}; +static const unsigned int uart0_6_pins[] = {34, 35}; +static const unsigned int uart0_7_pins[] = {38, 39}; +static const unsigned int uart0_8_pins[] = {42, 43}; +static const unsigned int uart0_9_pins[] = {46, 47}; +static const unsigned int uart0_10_pins[] = {50, 51}; +static const unsigned int uart1_0_pins[] = {8, 9}; +static const unsigned int uart1_1_pins[] = {12, 13}; +static const unsigned int uart1_2_pins[] = {16, 17}; +static const unsigned int uart1_3_pins[] = {20, 21}; +static const unsigned int uart1_4_pins[] = {24, 25}; +static const unsigned int uart1_5_pins[] = {28, 29}; +static const unsigned int uart1_6_pins[] = {32, 33}; +static const unsigned int uart1_7_pins[] = {36, 37}; +static const unsigned int uart1_8_pins[] = {40, 41}; +static const unsigned int uart1_9_pins[] = {44, 45}; +static const unsigned int uart1_10_pins[] = {48, 49}; +static const unsigned int uart1_11_pins[] = {52, 53}; +static const unsigned int i2c0_0_pins[] = {10, 11}; +static const unsigned int i2c0_1_pins[] = {14, 15}; +static const unsigned int i2c0_2_pins[] = {18, 19}; +static const unsigned int i2c0_3_pins[] = {22, 23}; +static const unsigned int i2c0_4_pins[] = {26, 27}; +static const unsigned int i2c0_5_pins[] = {30, 31}; +static const unsigned int i2c0_6_pins[] = {34, 35}; +static const unsigned int i2c0_7_pins[] = {38, 39}; +static const unsigned int i2c0_8_pins[] = {42, 43}; +static const unsigned int i2c0_9_pins[] = {46, 47}; +static const unsigned int i2c0_10_pins[] = {50, 51}; +static const unsigned int i2c1_0_pins[] = {12, 13}; +static const unsigned int i2c1_1_pins[] = {16, 17}; +static const unsigned int i2c1_2_pins[] = {20, 21}; +static const unsigned int i2c1_3_pins[] = {24, 25}; +static const unsigned int i2c1_4_pins[] = {28, 29}; +static const unsigned int i2c1_5_pins[] = {32, 33}; +static const unsigned int i2c1_6_pins[] = {36, 37}; +static const unsigned int i2c1_7_pins[] = {40, 41}; +static const unsigned int i2c1_8_pins[] = {44, 45}; +static const unsigned int i2c1_9_pins[] = {48, 49}; +static const unsigned int i2c1_10_pins[] = {52, 53}; +static const unsigned int ttc0_0_pins[] = {18, 19}; +static const unsigned int ttc0_1_pins[] = {30, 31}; +static const unsigned int ttc0_2_pins[] = {42, 43}; +static const unsigned int ttc1_0_pins[] = {16, 17}; +static const unsigned int ttc1_1_pins[] = {28, 29}; +static const unsigned int ttc1_2_pins[] = {40, 41}; +static const unsigned int swdt0_0_pins[] = {14, 15}; +static const unsigned int swdt0_1_pins[] = {26, 27}; +static const unsigned int swdt0_2_pins[] = {38, 39}; +static const unsigned int swdt0_3_pins[] = {50, 51}; +static const unsigned int swdt0_4_pins[] = {52, 53}; +static const unsigned int gpio0_0_pins[] = {0}; +static const unsigned int gpio0_1_pins[] = {1}; +static const unsigned int gpio0_2_pins[] = {2}; +static const unsigned int gpio0_3_pins[] = {3}; +static const unsigned int gpio0_4_pins[] = {4}; +static const unsigned int gpio0_5_pins[] = {5}; +static const unsigned int gpio0_6_pins[] = {6}; +static const unsigned int gpio0_7_pins[] = {7}; +static const unsigned int gpio0_8_pins[] = {8}; +static const unsigned int gpio0_9_pins[] = {9}; +static const unsigned int gpio0_10_pins[] = {10}; +static const unsigned int gpio0_11_pins[] = {11}; +static const unsigned int gpio0_12_pins[] = {12}; +static const unsigned int gpio0_13_pins[] = {13}; +static const unsigned int gpio0_14_pins[] = {14}; +static const unsigned int gpio0_15_pins[] = {15}; +static const unsigned int gpio0_16_pins[] = {16}; +static const unsigned int gpio0_17_pins[] = {17}; +static const unsigned int gpio0_18_pins[] = {18}; +static const unsigned int gpio0_19_pins[] = {19}; +static const unsigned int gpio0_20_pins[] = {20}; +static const unsigned int gpio0_21_pins[] = {21}; +static const unsigned int gpio0_22_pins[] = {22}; +static const unsigned int gpio0_23_pins[] = {23}; +static const unsigned int gpio0_24_pins[] = {24}; +static const unsigned int gpio0_25_pins[] = {25}; +static const unsigned int gpio0_26_pins[] = {26}; +static const unsigned int gpio0_27_pins[] = {27}; +static const unsigned int gpio0_28_pins[] = {28}; +static const unsigned int gpio0_29_pins[] = {29}; +static const unsigned int gpio0_30_pins[] = {30}; +static const unsigned int gpio0_31_pins[] = {31}; +static const unsigned int gpio0_32_pins[] = {32}; +static const unsigned int gpio0_33_pins[] = {33}; +static const unsigned int gpio0_34_pins[] = {34}; +static const unsigned int gpio0_35_pins[] = {35}; +static const unsigned int gpio0_36_pins[] = {36}; +static const unsigned int gpio0_37_pins[] = {37}; +static const unsigned int gpio0_38_pins[] = {38}; +static const unsigned int gpio0_39_pins[] = {39}; +static const unsigned int gpio0_40_pins[] = {40}; +static const unsigned int gpio0_41_pins[] = {41}; +static const unsigned int gpio0_42_pins[] = {42}; +static const unsigned int gpio0_43_pins[] = {43}; +static const unsigned int gpio0_44_pins[] = {44}; +static const unsigned int gpio0_45_pins[] = {45}; +static const unsigned int gpio0_46_pins[] = {46}; +static const unsigned int gpio0_47_pins[] = {47}; +static const unsigned int gpio0_48_pins[] = {48}; +static const unsigned int gpio0_49_pins[] = {49}; +static const unsigned int gpio0_50_pins[] = {50}; +static const unsigned int gpio0_51_pins[] = {51}; +static const unsigned int gpio0_52_pins[] = {52}; +static const unsigned int gpio0_53_pins[] = {53}; +static const unsigned int usb0_0_pins[] = {28, 19, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39}; +static const unsigned int usb1_0_pins[] = {40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51}; + +#define DEFINE_ZYNQ_PINCTRL_GRP(nm) \ + { \ + .name = #nm "_grp", \ + .pins = nm ## _pins, \ + .npins = ARRAY_SIZE(nm ## _pins), \ + } + +struct zynq_pctrl_group zynq_pctrl_groups[] = { + DEFINE_ZYNQ_PINCTRL_GRP(ethernet0_0), + DEFINE_ZYNQ_PINCTRL_GRP(ethernet1_0), + DEFINE_ZYNQ_PINCTRL_GRP(mdio0_0), + DEFINE_ZYNQ_PINCTRL_GRP(mdio1_0), + DEFINE_ZYNQ_PINCTRL_GRP(qspi0_0), + DEFINE_ZYNQ_PINCTRL_GRP(qspi1_0), + DEFINE_ZYNQ_PINCTRL_GRP(qspi_fbclk), + DEFINE_ZYNQ_PINCTRL_GRP(qspi_cs1), + DEFINE_ZYNQ_PINCTRL_GRP(spi0_0), + DEFINE_ZYNQ_PINCTRL_GRP(spi0_1), + DEFINE_ZYNQ_PINCTRL_GRP(spi0_2), + DEFINE_ZYNQ_PINCTRL_GRP(spi1_0), + DEFINE_ZYNQ_PINCTRL_GRP(spi1_1), + DEFINE_ZYNQ_PINCTRL_GRP(spi1_2), + DEFINE_ZYNQ_PINCTRL_GRP(spi1_3), + DEFINE_ZYNQ_PINCTRL_GRP(sdio0_0), + DEFINE_ZYNQ_PINCTRL_GRP(sdio0_1), + DEFINE_ZYNQ_PINCTRL_GRP(sdio0_2), + DEFINE_ZYNQ_PINCTRL_GRP(sdio1_0), + DEFINE_ZYNQ_PINCTRL_GRP(sdio1_1), + DEFINE_ZYNQ_PINCTRL_GRP(sdio1_2), + DEFINE_ZYNQ_PINCTRL_GRP(sdio1_3), + DEFINE_ZYNQ_PINCTRL_GRP(sdio0_emio_wp), + DEFINE_ZYNQ_PINCTRL_GRP(sdio0_emio_cd), + DEFINE_ZYNQ_PINCTRL_GRP(sdio1_emio_wp), + DEFINE_ZYNQ_PINCTRL_GRP(sdio1_emio_cd), + DEFINE_ZYNQ_PINCTRL_GRP(smc0_nor), + DEFINE_ZYNQ_PINCTRL_GRP(smc0_nor_cs1), + DEFINE_ZYNQ_PINCTRL_GRP(smc0_nor_addr25), + DEFINE_ZYNQ_PINCTRL_GRP(smc0_nand), + DEFINE_ZYNQ_PINCTRL_GRP(can0_0), + DEFINE_ZYNQ_PINCTRL_GRP(can0_1), + DEFINE_ZYNQ_PINCTRL_GRP(can0_2), + DEFINE_ZYNQ_PINCTRL_GRP(can0_3), + DEFINE_ZYNQ_PINCTRL_GRP(can0_4), + DEFINE_ZYNQ_PINCTRL_GRP(can0_5), + DEFINE_ZYNQ_PINCTRL_GRP(can0_6), + DEFINE_ZYNQ_PINCTRL_GRP(can0_7), + DEFINE_ZYNQ_PINCTRL_GRP(can0_8), + DEFINE_ZYNQ_PINCTRL_GRP(can0_9), + DEFINE_ZYNQ_PINCTRL_GRP(can0_10), + DEFINE_ZYNQ_PINCTRL_GRP(can1_0), + DEFINE_ZYNQ_PINCTRL_GRP(can1_1), + DEFINE_ZYNQ_PINCTRL_GRP(can1_2), + DEFINE_ZYNQ_PINCTRL_GRP(can1_3), + DEFINE_ZYNQ_PINCTRL_GRP(can1_4), + DEFINE_ZYNQ_PINCTRL_GRP(can1_5), + DEFINE_ZYNQ_PINCTRL_GRP(can1_6), + DEFINE_ZYNQ_PINCTRL_GRP(can1_7), + DEFINE_ZYNQ_PINCTRL_GRP(can1_8), + DEFINE_ZYNQ_PINCTRL_GRP(can1_9), + DEFINE_ZYNQ_PINCTRL_GRP(can1_10), + DEFINE_ZYNQ_PINCTRL_GRP(can1_11), + DEFINE_ZYNQ_PINCTRL_GRP(uart0_0), + DEFINE_ZYNQ_PINCTRL_GRP(uart0_1), + DEFINE_ZYNQ_PINCTRL_GRP(uart0_2), + DEFINE_ZYNQ_PINCTRL_GRP(uart0_3), + DEFINE_ZYNQ_PINCTRL_GRP(uart0_4), + DEFINE_ZYNQ_PINCTRL_GRP(uart0_5), + DEFINE_ZYNQ_PINCTRL_GRP(uart0_6), + DEFINE_ZYNQ_PINCTRL_GRP(uart0_7), + DEFINE_ZYNQ_PINCTRL_GRP(uart0_8), + DEFINE_ZYNQ_PINCTRL_GRP(uart0_9), + DEFINE_ZYNQ_PINCTRL_GRP(uart0_10), + DEFINE_ZYNQ_PINCTRL_GRP(uart1_0), + DEFINE_ZYNQ_PINCTRL_GRP(uart1_1), + DEFINE_ZYNQ_PINCTRL_GRP(uart1_2), + DEFINE_ZYNQ_PINCTRL_GRP(uart1_3), + DEFINE_ZYNQ_PINCTRL_GRP(uart1_4), + DEFINE_ZYNQ_PINCTRL_GRP(uart1_5), + DEFINE_ZYNQ_PINCTRL_GRP(uart1_6), + DEFINE_ZYNQ_PINCTRL_GRP(uart1_7), + DEFINE_ZYNQ_PINCTRL_GRP(uart1_8), + DEFINE_ZYNQ_PINCTRL_GRP(uart1_9), + DEFINE_ZYNQ_PINCTRL_GRP(uart1_10), + DEFINE_ZYNQ_PINCTRL_GRP(uart1_11), + DEFINE_ZYNQ_PINCTRL_GRP(i2c0_0), + DEFINE_ZYNQ_PINCTRL_GRP(i2c0_1), + DEFINE_ZYNQ_PINCTRL_GRP(i2c0_2), + DEFINE_ZYNQ_PINCTRL_GRP(i2c0_3), + DEFINE_ZYNQ_PINCTRL_GRP(i2c0_4), + DEFINE_ZYNQ_PINCTRL_GRP(i2c0_5), + DEFINE_ZYNQ_PINCTRL_GRP(i2c0_6), + DEFINE_ZYNQ_PINCTRL_GRP(i2c0_7), + DEFINE_ZYNQ_PINCTRL_GRP(i2c0_8), + DEFINE_ZYNQ_PINCTRL_GRP(i2c0_9), + DEFINE_ZYNQ_PINCTRL_GRP(i2c0_10), + DEFINE_ZYNQ_PINCTRL_GRP(i2c1_0), + DEFINE_ZYNQ_PINCTRL_GRP(i2c1_1), + DEFINE_ZYNQ_PINCTRL_GRP(i2c1_2), + DEFINE_ZYNQ_PINCTRL_GRP(i2c1_3), + DEFINE_ZYNQ_PINCTRL_GRP(i2c1_4), + DEFINE_ZYNQ_PINCTRL_GRP(i2c1_5), + DEFINE_ZYNQ_PINCTRL_GRP(i2c1_6), + DEFINE_ZYNQ_PINCTRL_GRP(i2c1_7), + DEFINE_ZYNQ_PINCTRL_GRP(i2c1_8), + DEFINE_ZYNQ_PINCTRL_GRP(i2c1_9), + DEFINE_ZYNQ_PINCTRL_GRP(i2c1_10), + DEFINE_ZYNQ_PINCTRL_GRP(ttc0_0), + DEFINE_ZYNQ_PINCTRL_GRP(ttc0_1), + DEFINE_ZYNQ_PINCTRL_GRP(ttc0_2), + DEFINE_ZYNQ_PINCTRL_GRP(ttc1_0), + DEFINE_ZYNQ_PINCTRL_GRP(ttc1_1), + DEFINE_ZYNQ_PINCTRL_GRP(ttc1_2), + DEFINE_ZYNQ_PINCTRL_GRP(swdt0_0), + DEFINE_ZYNQ_PINCTRL_GRP(swdt0_1), + DEFINE_ZYNQ_PINCTRL_GRP(swdt0_2), + DEFINE_ZYNQ_PINCTRL_GRP(swdt0_3), + DEFINE_ZYNQ_PINCTRL_GRP(swdt0_4), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_0), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_1), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_2), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_3), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_4), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_5), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_6), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_7), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_8), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_9), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_10), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_11), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_12), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_13), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_14), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_15), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_16), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_17), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_18), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_19), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_20), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_21), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_22), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_23), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_24), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_25), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_26), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_27), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_28), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_29), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_30), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_31), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_32), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_33), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_34), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_35), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_36), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_37), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_38), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_39), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_40), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_41), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_42), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_43), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_44), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_45), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_46), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_47), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_48), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_49), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_50), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_51), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_52), + DEFINE_ZYNQ_PINCTRL_GRP(gpio0_53), + DEFINE_ZYNQ_PINCTRL_GRP(usb0_0), + DEFINE_ZYNQ_PINCTRL_GRP(usb1_0), +}; + +/* function groups */ +static const char * const ethernet0_groups[] = {"ethernet0_0_grp"}; +static const char * const ethernet1_groups[] = {"ethernet1_0_grp"}; +static const char * const usb0_groups[] = {"usb0_0_grp"}; +static const char * const usb1_groups[] = {"usb1_0_grp"}; +static const char * const mdio0_groups[] = {"mdio0_0_grp"}; +static const char * const mdio1_groups[] = {"mdio1_0_grp"}; +static const char * const qspi0_groups[] = {"qspi0_0_grp"}; +static const char * const qspi1_groups[] = {"qspi0_1_grp"}; +static const char * const qspi_fbclk_groups[] = {"qspi_fbclk_grp"}; +static const char * const qspi_cs1_groups[] = {"qspi_cs1_grp"}; +static const char * const spi0_groups[] = {"spi0_0_grp", "spi0_1_grp", + "spi0_2_grp"}; +static const char * const spi1_groups[] = {"spi1_0_grp", "spi1_1_grp", + "spi1_2_grp", "spi1_3_grp"}; +static const char * const sdio0_groups[] = {"sdio0_0_grp", "sdio0_1_grp", + "sdio0_2_grp"}; +static const char * const sdio1_groups[] = {"sdio1_0_grp", "sdio1_1_grp", + "sdio1_2_grp", "sdio1_3_grp"}; +static const char * const sdio0_pc_groups[] = {"gpio0_0_grp", + "gpio0_2_grp", "gpio0_4_grp", "gpio0_6_grp", + "gpio0_8_grp", "gpio0_10_grp", "gpio0_12_grp", + "gpio0_14_grp", "gpio0_16_grp", "gpio0_18_grp", + "gpio0_20_grp", "gpio0_22_grp", "gpio0_24_grp", + "gpio0_26_grp", "gpio0_28_grp", "gpio0_30_grp", + "gpio0_32_grp", "gpio0_34_grp", "gpio0_36_grp", + "gpio0_38_grp", "gpio0_40_grp", "gpio0_42_grp", + "gpio0_44_grp", "gpio0_46_grp", "gpio0_48_grp", + "gpio0_50_grp", "gpio0_52_grp"}; +static const char * const sdio1_pc_groups[] = {"gpio0_1_grp", + "gpio0_3_grp", "gpio0_5_grp", "gpio0_7_grp", + "gpio0_9_grp", "gpio0_11_grp", "gpio0_13_grp", + "gpio0_15_grp", "gpio0_17_grp", "gpio0_19_grp", + "gpio0_21_grp", "gpio0_23_grp", "gpio0_25_grp", + "gpio0_27_grp", "gpio0_29_grp", "gpio0_31_grp", + "gpio0_33_grp", "gpio0_35_grp", "gpio0_37_grp", + "gpio0_39_grp", "gpio0_41_grp", "gpio0_43_grp", + "gpio0_45_grp", "gpio0_47_grp", "gpio0_49_grp", + "gpio0_51_grp", "gpio0_53_grp"}; +static const char * const sdio0_cd_groups[] = {"gpio0_0_grp", + "gpio0_2_grp", "gpio0_4_grp", "gpio0_6_grp", + "gpio0_10_grp", "gpio0_12_grp", + "gpio0_14_grp", "gpio0_16_grp", "gpio0_18_grp", + "gpio0_20_grp", "gpio0_22_grp", "gpio0_24_grp", + "gpio0_26_grp", "gpio0_28_grp", "gpio0_30_grp", + "gpio0_32_grp", "gpio0_34_grp", "gpio0_36_grp", + "gpio0_38_grp", "gpio0_40_grp", "gpio0_42_grp", + "gpio0_44_grp", "gpio0_46_grp", "gpio0_48_grp", + "gpio0_50_grp", "gpio0_52_grp", "gpio0_1_grp", + "gpio0_3_grp", "gpio0_5_grp", + "gpio0_9_grp", "gpio0_11_grp", "gpio0_13_grp", + "gpio0_15_grp", "gpio0_17_grp", "gpio0_19_grp", + "gpio0_21_grp", "gpio0_23_grp", "gpio0_25_grp", + "gpio0_27_grp", "gpio0_29_grp", "gpio0_31_grp", + "gpio0_33_grp", "gpio0_35_grp", "gpio0_37_grp", + "gpio0_39_grp", "gpio0_41_grp", "gpio0_43_grp", + "gpio0_45_grp", "gpio0_47_grp", "gpio0_49_grp", + "gpio0_51_grp", "gpio0_53_grp", "sdio0_emio_cd_grp"}; +static const char * const sdio0_wp_groups[] = {"gpio0_0_grp", + "gpio0_2_grp", "gpio0_4_grp", "gpio0_6_grp", + "gpio0_10_grp", "gpio0_12_grp", + "gpio0_14_grp", "gpio0_16_grp", "gpio0_18_grp", + "gpio0_20_grp", "gpio0_22_grp", "gpio0_24_grp", + "gpio0_26_grp", "gpio0_28_grp", "gpio0_30_grp", + "gpio0_32_grp", "gpio0_34_grp", "gpio0_36_grp", + "gpio0_38_grp", "gpio0_40_grp", "gpio0_42_grp", + "gpio0_44_grp", "gpio0_46_grp", "gpio0_48_grp", + "gpio0_50_grp", "gpio0_52_grp", "gpio0_1_grp", + "gpio0_3_grp", "gpio0_5_grp", + "gpio0_9_grp", "gpio0_11_grp", "gpio0_13_grp", + "gpio0_15_grp", "gpio0_17_grp", "gpio0_19_grp", + "gpio0_21_grp", "gpio0_23_grp", "gpio0_25_grp", + "gpio0_27_grp", "gpio0_29_grp", "gpio0_31_grp", + "gpio0_33_grp", "gpio0_35_grp", "gpio0_37_grp", + "gpio0_39_grp", "gpio0_41_grp", "gpio0_43_grp", + "gpio0_45_grp", "gpio0_47_grp", "gpio0_49_grp", + "gpio0_51_grp", "gpio0_53_grp", "sdio0_emio_wp_grp"}; +static const char * const sdio1_cd_groups[] = {"gpio0_0_grp", + "gpio0_2_grp", "gpio0_4_grp", "gpio0_6_grp", + "gpio0_10_grp", "gpio0_12_grp", + "gpio0_14_grp", "gpio0_16_grp", "gpio0_18_grp", + "gpio0_20_grp", "gpio0_22_grp", "gpio0_24_grp", + "gpio0_26_grp", "gpio0_28_grp", "gpio0_30_grp", + "gpio0_32_grp", "gpio0_34_grp", "gpio0_36_grp", + "gpio0_38_grp", "gpio0_40_grp", "gpio0_42_grp", + "gpio0_44_grp", "gpio0_46_grp", "gpio0_48_grp", + "gpio0_50_grp", "gpio0_52_grp", "gpio0_1_grp", + "gpio0_3_grp", "gpio0_5_grp", + "gpio0_9_grp", "gpio0_11_grp", "gpio0_13_grp", + "gpio0_15_grp", "gpio0_17_grp", "gpio0_19_grp", + "gpio0_21_grp", "gpio0_23_grp", "gpio0_25_grp", + "gpio0_27_grp", "gpio0_29_grp", "gpio0_31_grp", + "gpio0_33_grp", "gpio0_35_grp", "gpio0_37_grp", + "gpio0_39_grp", "gpio0_41_grp", "gpio0_43_grp", + "gpio0_45_grp", "gpio0_47_grp", "gpio0_49_grp", + "gpio0_51_grp", "gpio0_53_grp", "sdio1_emio_cd_grp"}; +static const char * const sdio1_wp_groups[] = {"gpio0_0_grp", + "gpio0_2_grp", "gpio0_4_grp", "gpio0_6_grp", + "gpio0_10_grp", "gpio0_12_grp", + "gpio0_14_grp", "gpio0_16_grp", "gpio0_18_grp", + "gpio0_20_grp", "gpio0_22_grp", "gpio0_24_grp", + "gpio0_26_grp", "gpio0_28_grp", "gpio0_30_grp", + "gpio0_32_grp", "gpio0_34_grp", "gpio0_36_grp", + "gpio0_38_grp", "gpio0_40_grp", "gpio0_42_grp", + "gpio0_44_grp", "gpio0_46_grp", "gpio0_48_grp", + "gpio0_50_grp", "gpio0_52_grp", "gpio0_1_grp", + "gpio0_3_grp", "gpio0_5_grp", + "gpio0_9_grp", "gpio0_11_grp", "gpio0_13_grp", + "gpio0_15_grp", "gpio0_17_grp", "gpio0_19_grp", + "gpio0_21_grp", "gpio0_23_grp", "gpio0_25_grp", + "gpio0_27_grp", "gpio0_29_grp", "gpio0_31_grp", + "gpio0_33_grp", "gpio0_35_grp", "gpio0_37_grp", + "gpio0_39_grp", "gpio0_41_grp", "gpio0_43_grp", + "gpio0_45_grp", "gpio0_47_grp", "gpio0_49_grp", + "gpio0_51_grp", "gpio0_53_grp", "sdio1_emio_wp_grp"}; +static const char * const smc0_nor_groups[] = {"smc0_nor"}; +static const char * const smc0_nor_cs1_groups[] = {"smc0_nor_cs1_grp"}; +static const char * const smc0_nor_addr25_groups[] = {"smc0_nor_addr25_grp"}; +static const char * const smc0_nand_groups[] = {"smc0_nand"}; +static const char * const can0_groups[] = {"can0_0_grp", "can0_1_grp", + "can0_2_grp", "can0_3_grp", "can0_4_grp", "can0_5_grp", + "can0_6_grp", "can0_7_grp", "can0_8_grp", "can0_9_grp", + "can0_10_grp"}; +static const char * const can1_groups[] = {"can1_0_grp", "can1_1_grp", + "can1_2_grp", "can1_3_grp", "can1_4_grp", "can1_5_grp", + "can1_6_grp", "can1_7_grp", "can1_8_grp", "can1_9_grp", + "can1_10_grp", "can1_11_grp"}; +static const char * const uart0_groups[] = {"uart0_0_grp", "uart0_1_grp", + "uart0_2_grp", "uart0_3_grp", "uart0_4_grp", "uart0_5_grp", + "uart0_6_grp", "uart0_7_grp", "uart0_8_grp", "uart0_9_grp", + "uart0_10_grp"}; +static const char * const uart1_groups[] = {"uart1_0_grp", "uart1_1_grp", + "uart1_2_grp", "uart1_3_grp", "uart1_4_grp", "uart1_5_grp", + "uart1_6_grp", "uart1_7_grp", "uart1_8_grp", "uart1_9_grp", + "uart1_10_grp", "uart1_11_grp"}; +static const char * const i2c0_groups[] = {"i2c0_0_grp", "i2c0_1_grp", + "i2c0_2_grp", "i2c0_3_grp", "i2c0_4_grp", "i2c0_5_grp", + "i2c0_6_grp", "i2c0_7_grp", "i2c0_8_grp", "i2c0_9_grp", + "i2c0_10_grp"}; +static const char * const i2c1_groups[] = {"i2c1_0_grp", "i2c1_1_grp", + "i2c1_2_grp", "i2c1_3_grp", "i2c1_4_grp", "i2c1_5_grp", + "i2c1_6_grp", "i2c1_7_grp", "i2c1_8_grp", "i2c1_9_grp", + "i2c1_10_grp"}; +static const char * const ttc0_groups[] = {"ttc0_0_grp", "ttc0_1_grp", + "ttc0_2_grp"}; +static const char * const ttc1_groups[] = {"ttc1_0_grp", "ttc1_1_grp", + "ttc1_2_grp"}; +static const char * const swdt0_groups[] = {"swdt0_0_grp", "swdt0_1_grp", + "swdt0_2_grp", "swdt0_3_grp", "swdt0_4_grp"}; +static const char * const gpio0_groups[] = {"gpio0_0_grp", + "gpio0_2_grp", "gpio0_4_grp", "gpio0_6_grp", + "gpio0_8_grp", "gpio0_10_grp", "gpio0_12_grp", + "gpio0_14_grp", "gpio0_16_grp", "gpio0_18_grp", + "gpio0_20_grp", "gpio0_22_grp", "gpio0_24_grp", + "gpio0_26_grp", "gpio0_28_grp", "gpio0_30_grp", + "gpio0_32_grp", "gpio0_34_grp", "gpio0_36_grp", + "gpio0_38_grp", "gpio0_40_grp", "gpio0_42_grp", + "gpio0_44_grp", "gpio0_46_grp", "gpio0_48_grp", + "gpio0_50_grp", "gpio0_52_grp", "gpio0_1_grp", + "gpio0_3_grp", "gpio0_5_grp", "gpio0_7_grp", + "gpio0_9_grp", "gpio0_11_grp", "gpio0_13_grp", + "gpio0_15_grp", "gpio0_17_grp", "gpio0_19_grp", + "gpio0_21_grp", "gpio0_23_grp", "gpio0_25_grp", + "gpio0_27_grp", "gpio0_29_grp", "gpio0_31_grp", + "gpio0_33_grp", "gpio0_35_grp", "gpio0_37_grp", + "gpio0_39_grp", "gpio0_41_grp", "gpio0_43_grp", + "gpio0_45_grp", "gpio0_47_grp", "gpio0_49_grp", + "gpio0_51_grp", "gpio0_53_grp"}; + +#define DEFINE_ZYNQ_PINMUX_FUNCTION(fname, mval) \ + [ZYNQ_PMUX_##fname] = { \ + .name = #fname, \ + .groups = fname##_groups, \ + .ngroups = ARRAY_SIZE(fname##_groups), \ + .mux_val = mval, \ + } + +#define DEFINE_ZYNQ_PINMUX_FUNCTION_MUX(fname, mval, mux, mask, shift) \ + [ZYNQ_PMUX_##fname] = { \ + .name = #fname, \ + .groups = fname##_groups, \ + .ngroups = ARRAY_SIZE(fname##_groups), \ + .mux_val = mval, \ + .mux_mask = mask, \ + .mux_shift = shift, \ + } + +#define ZYNQ_SDIO_WP_SHIFT 0 +#define ZYNQ_SDIO_WP_MASK (0x3f << ZYNQ_SDIO_WP_SHIFT) +#define ZYNQ_SDIO_CD_SHIFT 16 +#define ZYNQ_SDIO_CD_MASK (0x3f << ZYNQ_SDIO_CD_SHIFT) + +static const struct zynq_pinmux_function zynq_pmux_functions[] = { + DEFINE_ZYNQ_PINMUX_FUNCTION(ethernet0, 1), + DEFINE_ZYNQ_PINMUX_FUNCTION(ethernet1, 1), + DEFINE_ZYNQ_PINMUX_FUNCTION(usb0, 2), + DEFINE_ZYNQ_PINMUX_FUNCTION(usb1, 2), + DEFINE_ZYNQ_PINMUX_FUNCTION(mdio0, 0x40), + DEFINE_ZYNQ_PINMUX_FUNCTION(mdio1, 0x50), + DEFINE_ZYNQ_PINMUX_FUNCTION(qspi0, 1), + DEFINE_ZYNQ_PINMUX_FUNCTION(qspi1, 1), + DEFINE_ZYNQ_PINMUX_FUNCTION(qspi_fbclk, 1), + DEFINE_ZYNQ_PINMUX_FUNCTION(qspi_cs1, 1), + DEFINE_ZYNQ_PINMUX_FUNCTION(spi0, 0x50), + DEFINE_ZYNQ_PINMUX_FUNCTION(spi1, 0x50), + DEFINE_ZYNQ_PINMUX_FUNCTION(sdio0, 0x40), + DEFINE_ZYNQ_PINMUX_FUNCTION(sdio0_pc, 0xc), + DEFINE_ZYNQ_PINMUX_FUNCTION_MUX(sdio0_wp, 0, 130, ZYNQ_SDIO_WP_MASK, + ZYNQ_SDIO_WP_SHIFT), + DEFINE_ZYNQ_PINMUX_FUNCTION_MUX(sdio0_cd, 0, 130, ZYNQ_SDIO_CD_MASK, + ZYNQ_SDIO_CD_SHIFT), + DEFINE_ZYNQ_PINMUX_FUNCTION(sdio1, 0x40), + DEFINE_ZYNQ_PINMUX_FUNCTION(sdio1_pc, 0xc), + DEFINE_ZYNQ_PINMUX_FUNCTION_MUX(sdio1_wp, 0, 134, ZYNQ_SDIO_WP_MASK, + ZYNQ_SDIO_WP_SHIFT), + DEFINE_ZYNQ_PINMUX_FUNCTION_MUX(sdio1_cd, 0, 134, ZYNQ_SDIO_CD_MASK, + ZYNQ_SDIO_CD_SHIFT), + DEFINE_ZYNQ_PINMUX_FUNCTION(smc0_nor, 4), + DEFINE_ZYNQ_PINMUX_FUNCTION(smc0_nor_cs1, 8), + DEFINE_ZYNQ_PINMUX_FUNCTION(smc0_nor_addr25, 4), + DEFINE_ZYNQ_PINMUX_FUNCTION(smc0_nand, 8), + DEFINE_ZYNQ_PINMUX_FUNCTION(can0, 0x10), + DEFINE_ZYNQ_PINMUX_FUNCTION(can1, 0x10), + DEFINE_ZYNQ_PINMUX_FUNCTION(uart0, 0x70), + DEFINE_ZYNQ_PINMUX_FUNCTION(uart1, 0x70), + DEFINE_ZYNQ_PINMUX_FUNCTION(i2c0, 0x20), + DEFINE_ZYNQ_PINMUX_FUNCTION(i2c1, 0x20), + DEFINE_ZYNQ_PINMUX_FUNCTION(ttc0, 0x60), + DEFINE_ZYNQ_PINMUX_FUNCTION(ttc1, 0x60), + DEFINE_ZYNQ_PINMUX_FUNCTION(swdt0, 0x30), + DEFINE_ZYNQ_PINMUX_FUNCTION(gpio0, 0), +}; + + +/* pinctrl */ +static int zynq_pctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct zynq_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + return pctrl->ngroups; +} + +static const char *zynq_pctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct zynq_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + return pctrl->groups[selector].name; +} + +static int zynq_pctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned selector, + const unsigned **pins, + unsigned *num_pins) +{ + struct zynq_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + *pins = pctrl->groups[selector].pins; + *num_pins = pctrl->groups[selector].npins; + + return 0; +} + +static const struct pinctrl_ops zynq_pctrl_ops = { + .get_groups_count = zynq_pctrl_get_groups_count, + .get_group_name = zynq_pctrl_get_group_name, + .get_group_pins = zynq_pctrl_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map_all, + .dt_free_map = pinctrl_utils_dt_free_map, +}; + +/* pinmux */ +static int zynq_pmux_get_functions_count(struct pinctrl_dev *pctldev) +{ + struct zynq_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + return pctrl->nfuncs; +} + +static const char *zynq_pmux_get_function_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct zynq_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + return pctrl->funcs[selector].name; +} + +static int zynq_pmux_get_function_groups(struct pinctrl_dev *pctldev, + unsigned selector, + const char * const **groups, + unsigned * const num_groups) +{ + struct zynq_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + *groups = pctrl->funcs[selector].groups; + *num_groups = pctrl->funcs[selector].ngroups; + return 0; +} + +static int zynq_pinmux_set_mux(struct pinctrl_dev *pctldev, + unsigned function, + unsigned group) +{ + int i, ret; + struct zynq_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + const struct zynq_pctrl_group *pgrp = &pctrl->groups[group]; + const struct zynq_pinmux_function *func = &pctrl->funcs[function]; + + /* + * SD WP & CD are special. They have dedicated registers + * to mux them in + */ + if (function == ZYNQ_PMUX_sdio0_cd || function == ZYNQ_PMUX_sdio0_wp || + function == ZYNQ_PMUX_sdio1_cd || + function == ZYNQ_PMUX_sdio1_wp) { + u32 reg; + + ret = regmap_read(pctrl->syscon, + pctrl->pctrl_offset + func->mux, ®); + if (ret) + return ret; + + reg &= ~func->mux_mask; + reg |= pgrp->pins[0] << func->mux_shift; + ret = regmap_write(pctrl->syscon, + pctrl->pctrl_offset + func->mux, reg); + if (ret) + return ret; + } else { + for (i = 0; i < pgrp->npins; i++) { + unsigned int pin = pgrp->pins[i]; + u32 reg, addr = pctrl->pctrl_offset + (4 * pin); + + ret = regmap_read(pctrl->syscon, addr, ®); + if (ret) + return ret; + + reg &= ~ZYNQ_PINMUX_MUX_MASK; + reg |= func->mux_val << ZYNQ_PINMUX_MUX_SHIFT; + ret = regmap_write(pctrl->syscon, addr, reg); + if (ret) + return ret; + } + } + + return 0; +} + +static const struct pinmux_ops zynq_pinmux_ops = { + .get_functions_count = zynq_pmux_get_functions_count, + .get_function_name = zynq_pmux_get_function_name, + .get_function_groups = zynq_pmux_get_function_groups, + .set_mux = zynq_pinmux_set_mux, +}; + +/* pinconfig */ +#define ZYNQ_PINCONF_TRISTATE BIT(0) +#define ZYNQ_PINCONF_SPEED BIT(8) +#define ZYNQ_PINCONF_PULLUP BIT(12) +#define ZYNQ_PINCONF_DISABLE_RECVR BIT(13) + +#define ZYNQ_PINCONF_IOTYPE_SHIFT 9 +#define ZYNQ_PINCONF_IOTYPE_MASK (7 << ZYNQ_PINCONF_IOTYPE_SHIFT) + +enum zynq_io_standards { + zynq_iostd_min, + zynq_iostd_lvcmos18, + zynq_iostd_lvcmos25, + zynq_iostd_lvcmos33, + zynq_iostd_hstl, + zynq_iostd_max +}; + +/** + * enum zynq_pin_config_param - possible pin configuration parameters + * @PIN_CONFIG_IOSTANDARD: if the pin can select an IO standard, the argument to + * this parameter (on a custom format) tells the driver which alternative + * IO standard to use. + */ +enum zynq_pin_config_param { + PIN_CONFIG_IOSTANDARD = PIN_CONFIG_END + 1, +}; + +static const struct pinconf_generic_dt_params zynq_dt_params[] = { + {"io-standard", PIN_CONFIG_IOSTANDARD, zynq_iostd_lvcmos18}, +}; + +static const struct pin_config_item zynq_conf_items[ARRAY_SIZE(zynq_dt_params)] = { + PCONFDUMP(PIN_CONFIG_IOSTANDARD, "IO-standard", NULL, true), +}; + +static unsigned int zynq_pinconf_iostd_get(u32 reg) +{ + return (reg & ZYNQ_PINCONF_IOTYPE_MASK) >> ZYNQ_PINCONF_IOTYPE_SHIFT; +} + +static int zynq_pinconf_cfg_get(struct pinctrl_dev *pctldev, + unsigned pin, + unsigned long *config) +{ + u32 reg; + int ret; + unsigned int arg = 0; + unsigned int param = pinconf_to_config_param(*config); + struct zynq_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + if (pin >= ZYNQ_NUM_MIOS) + return -ENOTSUPP; + + ret = regmap_read(pctrl->syscon, pctrl->pctrl_offset + (4 * pin), ®); + if (ret) + return -EIO; + + switch (param) { + case PIN_CONFIG_BIAS_PULL_UP: + if (!(reg & ZYNQ_PINCONF_PULLUP)) + return -EINVAL; + arg = 1; + break; + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + if (!(reg & ZYNQ_PINCONF_TRISTATE)) + return -EINVAL; + arg = 1; + break; + case PIN_CONFIG_BIAS_DISABLE: + if (reg & ZYNQ_PINCONF_PULLUP || reg & ZYNQ_PINCONF_TRISTATE) + return -EINVAL; + break; + case PIN_CONFIG_SLEW_RATE: + arg = !!(reg & ZYNQ_PINCONF_SPEED); + break; + case PIN_CONFIG_LOW_POWER_MODE: + { + enum zynq_io_standards iostd = zynq_pinconf_iostd_get(reg); + + if (iostd != zynq_iostd_hstl) + return -EINVAL; + if (!(reg & ZYNQ_PINCONF_DISABLE_RECVR)) + return -EINVAL; + arg = !!(reg & ZYNQ_PINCONF_DISABLE_RECVR); + break; + } + case PIN_CONFIG_IOSTANDARD: + arg = zynq_pinconf_iostd_get(reg); + break; + default: + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + return 0; +} + +static int zynq_pinconf_cfg_set(struct pinctrl_dev *pctldev, + unsigned pin, + unsigned long *configs, + unsigned num_configs) +{ + int i, ret; + u32 reg; + u32 pullup = 0; + u32 tristate = 0; + struct zynq_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + if (pin >= ZYNQ_NUM_MIOS) + return -ENOTSUPP; + + ret = regmap_read(pctrl->syscon, pctrl->pctrl_offset + (4 * pin), ®); + if (ret) + return -EIO; + + for (i = 0; i < num_configs; i++) { + unsigned int param = pinconf_to_config_param(configs[i]); + unsigned int arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_PULL_UP: + pullup = ZYNQ_PINCONF_PULLUP; + break; + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + tristate = ZYNQ_PINCONF_TRISTATE; + break; + case PIN_CONFIG_BIAS_DISABLE: + reg &= ~(ZYNQ_PINCONF_PULLUP | ZYNQ_PINCONF_TRISTATE); + break; + case PIN_CONFIG_SLEW_RATE: + if (arg) + reg |= ZYNQ_PINCONF_SPEED; + else + reg &= ~ZYNQ_PINCONF_SPEED; + + break; + case PIN_CONFIG_IOSTANDARD: + if (arg <= zynq_iostd_min || arg >= zynq_iostd_max) { + dev_warn(pctldev->dev, + "unsupported IO standard '%u'\n", + param); + break; + } + reg &= ~ZYNQ_PINCONF_IOTYPE_MASK; + reg |= arg << ZYNQ_PINCONF_IOTYPE_SHIFT; + break; + case PIN_CONFIG_LOW_POWER_MODE: + if (arg) + reg |= ZYNQ_PINCONF_DISABLE_RECVR; + else + reg &= ~ZYNQ_PINCONF_DISABLE_RECVR; + + break; + default: + dev_warn(pctldev->dev, + "unsupported configuration parameter '%u'\n", + param); + continue; + } + } + + if (tristate || pullup) { + reg &= ~(ZYNQ_PINCONF_PULLUP | ZYNQ_PINCONF_TRISTATE); + reg |= tristate | pullup; + } + + ret = regmap_write(pctrl->syscon, pctrl->pctrl_offset + (4 * pin), reg); + if (ret) + return -EIO; + + return 0; +} + +static int zynq_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned selector, + unsigned long *configs, + unsigned num_configs) +{ + int i, ret; + struct zynq_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + const struct zynq_pctrl_group *pgrp = &pctrl->groups[selector]; + + for (i = 0; i < pgrp->npins; i++) { + ret = zynq_pinconf_cfg_set(pctldev, pgrp->pins[i], configs, + num_configs); + if (ret) + return ret; + } + + return 0; +} + +static const struct pinconf_ops zynq_pinconf_ops = { + .is_generic = true, + .pin_config_get = zynq_pinconf_cfg_get, + .pin_config_set = zynq_pinconf_cfg_set, + .pin_config_group_set = zynq_pinconf_group_set, +}; + +static struct pinctrl_desc zynq_desc = { + .name = "zynq_pinctrl", + .pins = zynq_pins, + .npins = ARRAY_SIZE(zynq_pins), + .pctlops = &zynq_pctrl_ops, + .pmxops = &zynq_pinmux_ops, + .confops = &zynq_pinconf_ops, + .num_dt_params = ARRAY_SIZE(zynq_dt_params), + .params = zynq_dt_params, + .conf_items = zynq_conf_items, + .owner = THIS_MODULE, +}; + +static int zynq_pinctrl_probe(struct platform_device *pdev) + +{ + struct resource *res; + struct zynq_pinctrl *pctrl; + + pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); + if (!pctrl) + return -ENOMEM; + + pctrl->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "syscon"); + if (IS_ERR(pctrl->syscon)) { + dev_err(&pdev->dev, "unable to get syscon\n"); + return PTR_ERR(pctrl->syscon); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing IO resource\n"); + return -ENODEV; + } + pctrl->pctrl_offset = res->start; + + pctrl->groups = zynq_pctrl_groups; + pctrl->ngroups = ARRAY_SIZE(zynq_pctrl_groups); + pctrl->funcs = zynq_pmux_functions; + pctrl->nfuncs = ARRAY_SIZE(zynq_pmux_functions); + + pctrl->pctrl = pinctrl_register(&zynq_desc, &pdev->dev, pctrl); + if (!pctrl->pctrl) + return -ENOMEM; + + platform_set_drvdata(pdev, pctrl); + + dev_info(&pdev->dev, "zynq pinctrl initialized\n"); + + return 0; +} + +int zynq_pinctrl_remove(struct platform_device *pdev) +{ + struct zynq_pinctrl *pctrl = platform_get_drvdata(pdev); + + pinctrl_unregister(pctrl->pctrl); + + return 0; +} + +static const struct of_device_id zynq_pinctrl_of_match[] = { + { .compatible = "xlnx,pinctrl-zynq" }, + { } +}; +MODULE_DEVICE_TABLE(of, zynq_pinctrl_of_match); + +static struct platform_driver zynq_pinctrl_driver = { + .driver = { + .name = "zynq-pinctrl", + .of_match_table = zynq_pinctrl_of_match, + }, + .probe = zynq_pinctrl_probe, + .remove = zynq_pinctrl_remove, +}; + +module_platform_driver(zynq_pinctrl_driver); + +MODULE_AUTHOR("Sören Brinkmann "); +MODULE_DESCRIPTION("Xilinx Zynq pinctrl driver"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 3cb6dcfa44e883412f8df2f2b1917b0d359f3ec4 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 9 Jan 2015 17:43:06 -0800 Subject: Input: regulator-haptic - simplify code All the use cases in this driver has a regulator_haptic_toggle() call after regulator_haptic_set_voltage(). So make regulator_haptic_set_voltage() call regulator_haptic_toggle() to simplify the code. Signed-off-by: Axel Lin Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/misc/regulator-haptic.c b/drivers/input/misc/regulator-haptic.c index 9426221..132eb91 100644 --- a/drivers/input/misc/regulator-haptic.c +++ b/drivers/input/misc/regulator-haptic.c @@ -76,6 +76,8 @@ static int regulator_haptic_set_voltage(struct regulator_haptic *haptic, return error; } + regulator_haptic_toggle(haptic, !!magnitude); + return 0; } @@ -83,23 +85,12 @@ static void regulator_haptic_work(struct work_struct *work) { struct regulator_haptic *haptic = container_of(work, struct regulator_haptic, work); - unsigned int magnitude; - int error; mutex_lock(&haptic->mutex); - if (haptic->suspended) - goto out; - - magnitude = ACCESS_ONCE(haptic->magnitude); + if (!haptic->suspended) + regulator_haptic_set_voltage(haptic, haptic->magnitude); - error = regulator_haptic_set_voltage(haptic, magnitude); - if (error) - goto out; - - regulator_haptic_toggle(haptic, magnitude != 0); - -out: mutex_unlock(&haptic->mutex); } @@ -123,7 +114,6 @@ static void regulator_haptic_close(struct input_dev *input) cancel_work_sync(&haptic->work); regulator_haptic_set_voltage(haptic, 0); - regulator_haptic_toggle(haptic, false); } static int __maybe_unused @@ -225,7 +215,6 @@ static int __maybe_unused regulator_haptic_suspend(struct device *dev) return error; regulator_haptic_set_voltage(haptic, 0); - regulator_haptic_toggle(haptic, false); haptic->suspended = true; @@ -245,10 +234,8 @@ static int __maybe_unused regulator_haptic_resume(struct device *dev) haptic->suspended = false; magnitude = ACCESS_ONCE(haptic->magnitude); - if (magnitude) { + if (magnitude) regulator_haptic_set_voltage(haptic, magnitude); - regulator_haptic_toggle(haptic, true); - } mutex_unlock(&haptic->mutex); -- cgit v0.10.2 From 937127fe13e1394305fdd9c9150b52651ab8ba6c Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Wed, 7 Jan 2015 15:04:06 +0000 Subject: phy: phy-stih407-usb: Pass sysconfig register offsets via syscfg property. Based on Arnds review comments here https://lkml.org/lkml/2014/11/13/161, update the phy driver to not use the reg property to access the sysconfig register offsets. This is because other phy's (miphy28, miphy365) have a combination of memory mapped registers and sysconfig control regs, and we shouldn't be mixing address spaces in the reg property. In addition we would ideally like the sysconfig offsets to be passed via DT in a uniform way. This new method will also allow us to support devices which have sysconfig registers in different banks more easily and it is also analagous to how keystone and bcm7745 platforms pass there syscon offsets in DT. This breaks DT compatibility, but this platform is considered WIP, and is only used by a few developers who are upstreaming support for it. Signed-off-by: Peter Griffin Reviewed-by: Arnd Bergmann Signed-off-by: David S. Miller diff --git a/Documentation/devicetree/bindings/phy/phy-stih407-usb.txt b/Documentation/devicetree/bindings/phy/phy-stih407-usb.txt index 1ef8228..de6a706 100644 --- a/Documentation/devicetree/bindings/phy/phy-stih407-usb.txt +++ b/Documentation/devicetree/bindings/phy/phy-stih407-usb.txt @@ -5,10 +5,7 @@ host controllers (when controlling usb2/1.1 devices) available on STiH407 SoC fa Required properties: - compatible : should be "st,stih407-usb2-phy" -- reg : contain the offset and length of the system configuration registers - used as glue logic to control & parameter phy -- reg-names : the names of the system configuration registers in "reg", should be "param" and "reg" -- st,syscfg : sysconfig register to manage phy parameter at driver level +- st,syscfg : phandle of sysconfig bank plus integer array containing phyparam and phyctrl register offsets - resets : list of phandle and reset specifier pairs. There should be two entries, one for the whole phy and one for the port - reset-names : list of reset signal names. Should be "global" and "port" @@ -19,11 +16,8 @@ Example: usb2_picophy0: usbpicophy@f8 { compatible = "st,stih407-usb2-phy"; - reg = <0xf8 0x04>, /* syscfg 5062 */ - <0xf4 0x04>; /* syscfg 5061 */ - reg-names = "param", "ctrl"; #phy-cells = <0>; - st,syscfg = <&syscfg_core>; + st,syscfg = <&syscfg_core 0x100 0xf4>; resets = <&softreset STIH407_PICOPHY_SOFTRESET>, <&picophyreset STIH407_PICOPHY0_RESET>; reset-names = "global", "port"; diff --git a/drivers/phy/phy-stih407-usb.c b/drivers/phy/phy-stih407-usb.c index 74f0fab..1d5ae5f 100644 --- a/drivers/phy/phy-stih407-usb.c +++ b/drivers/phy/phy-stih407-usb.c @@ -22,6 +22,9 @@ #include #include +#define PHYPARAM_REG 1 +#define PHYCTRL_REG 2 + /* Default PHY_SEL and REFCLKSEL configuration */ #define STIH407_USB_PICOPHY_CTRL_PORT_CONF 0x6 #define STIH407_USB_PICOPHY_CTRL_PORT_MASK 0x1f @@ -93,7 +96,7 @@ static int stih407_usb2_picophy_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct phy_provider *phy_provider; struct phy *phy; - struct resource *res; + int ret; phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); if (!phy_dev) @@ -123,19 +126,19 @@ static int stih407_usb2_picophy_probe(struct platform_device *pdev) return PTR_ERR(phy_dev->regmap); } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); - if (!res) { - dev_err(dev, "No ctrl reg found\n"); - return -ENXIO; + ret = of_property_read_u32_index(np, "st,syscfg", PHYPARAM_REG, + &phy_dev->param); + if (ret) { + dev_err(dev, "can't get phyparam offset (%d)\n", ret); + return ret; } - phy_dev->ctrl = res->start; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "param"); - if (!res) { - dev_err(dev, "No param reg found\n"); - return -ENXIO; + ret = of_property_read_u32_index(np, "st,syscfg", PHYCTRL_REG, + &phy_dev->ctrl); + if (ret) { + dev_err(dev, "can't get phyctrl offset (%d)\n", ret); + return ret; } - phy_dev->param = res->start; phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data); if (IS_ERR(phy)) { -- cgit v0.10.2 From 63139885c4bb53e476d875dddfe71b571107e663 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Wed, 7 Jan 2015 15:04:07 +0000 Subject: phy: miphy365x: Pass sysconfig register offsets via syscfg dt property. Based on Arnds review comments here https://lkml.org/lkml/2014/11/13/161, update the miphy365 phy driver to access sysconfig register offsets via syscfg dt property. This is because the reg property should not be mixing address spaces like it does currently for miphy365. This change then also aligns us to how other platforms such as keystone and bcm7445 pass there syscon offsets via DT. This patch breaks DT compatibility, but this platform is considered WIP, and is only used by a few developers who are upstreaming support for it. This change has been done as a single atomic commit to ensure it is bisectable. Signed-off-by: Peter Griffin Reviewed-by: Arnd Bergmann Signed-off-by: David S. Miller diff --git a/Documentation/devicetree/bindings/phy/phy-miphy365x.txt b/Documentation/devicetree/bindings/phy/phy-miphy365x.txt index 42c8808..9802d5d 100644 --- a/Documentation/devicetree/bindings/phy/phy-miphy365x.txt +++ b/Documentation/devicetree/bindings/phy/phy-miphy365x.txt @@ -6,8 +6,10 @@ for SATA and PCIe. Required properties (controller (parent) node): - compatible : Should be "st,miphy365x-phy" -- st,syscfg : Should be a phandle of the system configuration register group - which contain the SATA, PCIe mode setting bits +- st,syscfg : Phandle / integer array property. Phandle of sysconfig group + containing the miphy registers and integer array should contain + an entry for each port sub-node, specifying the control + register offset inside the sysconfig group. Required nodes : A sub-node is required for each channel the controller provides. Address range information including the usual @@ -26,7 +28,6 @@ Required properties (port (child) node): registers filled in "reg": - sata: For SATA devices - pcie: For PCIe devices - - syscfg: To specify the syscfg based config register Optional properties (port (child) node): - st,sata-gen : Generation of locally attached SATA IP. Expected values @@ -39,20 +40,20 @@ Example: miphy365x_phy: miphy365x@fe382000 { compatible = "st,miphy365x-phy"; - st,syscfg = <&syscfg_rear>; + st,syscfg = <&syscfg_rear 0x824 0x828>; #address-cells = <1>; #size-cells = <1>; ranges; phy_port0: port@fe382000 { - reg = <0xfe382000 0x100>, <0xfe394000 0x100>, <0x824 0x4>; - reg-names = "sata", "pcie", "syscfg"; + reg = <0xfe382000 0x100>, <0xfe394000 0x100>; + reg-names = "sata", "pcie"; #phy-cells = <1>; st,sata-gen = <3>; }; phy_port1: port@fe38a000 { - reg = <0xfe38a000 0x100>, <0xfe804000 0x100>, <0x828 0x4>;; + reg = <0xfe38a000 0x100>, <0xfe804000 0x100>;; reg-names = "sata", "pcie", "syscfg"; #phy-cells = <1>; st,pcie-tx-pol-inv; diff --git a/arch/arm/boot/dts/stih416.dtsi b/arch/arm/boot/dts/stih416.dtsi index fad9073..85afe01 100644 --- a/arch/arm/boot/dts/stih416.dtsi +++ b/arch/arm/boot/dts/stih416.dtsi @@ -283,21 +283,21 @@ miphy365x_phy: phy@fe382000 { compatible = "st,miphy365x-phy"; - st,syscfg = <&syscfg_rear>; + st,syscfg = <&syscfg_rear 0x824 0x828>; #address-cells = <1>; #size-cells = <1>; ranges; phy_port0: port@fe382000 { #phy-cells = <1>; - reg = <0xfe382000 0x100>, <0xfe394000 0x100>, <0x824 0x4>; - reg-names = "sata", "pcie", "syscfg"; + reg = <0xfe382000 0x100>, <0xfe394000 0x100>; + reg-names = "sata", "pcie"; }; phy_port1: port@fe38a000 { #phy-cells = <1>; - reg = <0xfe38a000 0x100>, <0xfe804000 0x100>, <0x828 0x4>; - reg-names = "sata", "pcie", "syscfg"; + reg = <0xfe38a000 0x100>, <0xfe804000 0x100>; + reg-names = "sata", "pcie"; }; }; diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c index 6ab43a8..6c80154 100644 --- a/drivers/phy/phy-miphy365x.c +++ b/drivers/phy/phy-miphy365x.c @@ -141,7 +141,7 @@ struct miphy365x_phy { bool pcie_tx_pol_inv; bool sata_tx_pol_inv; u32 sata_gen; - u64 ctrlreg; + u32 ctrlreg; u8 type; }; @@ -179,7 +179,7 @@ static int miphy365x_set_path(struct miphy365x_phy *miphy_phy, bool sata = (miphy_phy->type == MIPHY_TYPE_SATA); return regmap_update_bits(miphy_dev->regmap, - (unsigned int)miphy_phy->ctrlreg, + miphy_phy->ctrlreg, SYSCFG_SELECT_SATA_MASK, sata << SYSCFG_SELECT_SATA_POS); } @@ -445,7 +445,6 @@ int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy, { struct device_node *phynode = miphy_phy->phy->dev.of_node; const char *name; - const __be32 *taddr; int type = miphy_phy->type; int ret; @@ -455,22 +454,6 @@ int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy, return ret; } - if (!strncmp(name, "syscfg", 6)) { - taddr = of_get_address(phynode, index, NULL, NULL); - if (!taddr) { - dev_err(dev, "failed to fetch syscfg address\n"); - return -EINVAL; - } - - miphy_phy->ctrlreg = of_translate_address(phynode, taddr); - if (miphy_phy->ctrlreg == OF_BAD_ADDR) { - dev_err(dev, "failed to translate syscfg address\n"); - return -EINVAL; - } - - return 0; - } - if (!((!strncmp(name, "sata", 4) && type == MIPHY_TYPE_SATA) || (!strncmp(name, "pcie", 4) && type == MIPHY_TYPE_PCIE))) return 0; @@ -606,7 +589,15 @@ static int miphy365x_probe(struct platform_device *pdev) return ret; phy_set_drvdata(phy, miphy_dev->phys[port]); + port++; + /* sysconfig offsets are indexed from 1 */ + ret = of_property_read_u32_index(np, "st,syscfg", port, + &miphy_phy->ctrlreg); + if (ret) { + dev_err(&pdev->dev, "No sysconfig offset found\n"); + return ret; + } } provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate); -- cgit v0.10.2 From 2720948157ca5e2bddf8bbb3337686d026c87220 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Wed, 7 Jan 2015 15:04:08 +0000 Subject: ARM: STi: DT: STiH407: Add usb2 picophy dt nodes This patch adds the dt nodes for the usb2 picophy found on the stih407 device family. It is used on stih407 by the dwc3 usb3 controller when controlling usb2/1.1 devices. Signed-off-by: Peter Griffin Reviewed-by: Arnd Bergmann Signed-off-by: David S. Miller diff --git a/arch/arm/boot/dts/stih407-family.dtsi b/arch/arm/boot/dts/stih407-family.dtsi index 3e31d32..d4a8f84 100644 --- a/arch/arm/boot/dts/stih407-family.dtsi +++ b/arch/arm/boot/dts/stih407-family.dtsi @@ -274,5 +274,14 @@ status = "disabled"; }; + + usb2_picophy0: phy1 { + compatible = "st,stih407-usb2-phy"; + #phy-cells = <0>; + st,syscfg = <&syscfg_core 0x100 0xf4>; + resets = <&softreset STIH407_PICOPHY_SOFTRESET>, + <&picophyreset STIH407_PICOPHY0_RESET>; + reset-names = "global", "port"; + }; }; }; -- cgit v0.10.2 From a50457c729abce29a5fe885777f49352d600367c Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Wed, 7 Jan 2015 15:04:09 +0000 Subject: ARM: STi: DT: STiH410: Add usb2 picophy dt nodes This patch adds the dt nodes for the extra usb2 picophys found on the stih410. These two picophys are used in conjunction with the extra ehci/ohci usb controllers also found on the stih410 SoC. Signed-off-by: Peter Griffin Reviewed-by: Arnd Bergmann Signed-off-by: David S. Miller diff --git a/arch/arm/boot/dts/stih410.dtsi b/arch/arm/boot/dts/stih410.dtsi index c05627e..2e5e9ed 100644 --- a/arch/arm/boot/dts/stih410.dtsi +++ b/arch/arm/boot/dts/stih410.dtsi @@ -10,5 +10,23 @@ #include "stih407-family.dtsi" #include "stih410-pinctrl.dtsi" / { + soc { + usb2_picophy1: phy2 { + compatible = "st,stih407-usb2-phy"; + #phy-cells = <0>; + st,syscfg = <&syscfg_core 0xf8 0xf4>; + resets = <&softreset STIH407_PICOPHY_SOFTRESET>, + <&picophyreset STIH407_PICOPHY0_RESET>; + reset-names = "global", "port"; + }; + usb2_picophy2: phy3 { + compatible = "st,stih407-usb2-phy"; + #phy-cells = <0>; + st,syscfg = <&syscfg_core 0xfc 0xf4>; + resets = <&softreset STIH407_PICOPHY_SOFTRESET>, + <&picophyreset STIH407_PICOPHY1_RESET>; + reset-names = "global", "port"; + }; + }; }; -- cgit v0.10.2 From 4343129cc82656c8336b0fe953b1c422bfcfad26 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Wed, 7 Jan 2015 15:04:10 +0000 Subject: ARM: STi: DT: STiH410: Add DT nodes for the ehci and ohci usb controllers. This patch adds the DT nodes for the extra ehci and ohci usb controllers on the stih410 SoC. Signed-off-by: Peter Griffin Reviewed-by: Arnd Bergmann Signed-off-by: David S. Miller diff --git a/arch/arm/boot/dts/stih410.dtsi b/arch/arm/boot/dts/stih410.dtsi index 2e5e9ed..37995f4 100644 --- a/arch/arm/boot/dts/stih410.dtsi +++ b/arch/arm/boot/dts/stih410.dtsi @@ -28,5 +28,57 @@ <&picophyreset STIH407_PICOPHY1_RESET>; reset-names = "global", "port"; }; + + ohci0: usb@9a03c00 { + compatible = "st,st-ohci-300x"; + reg = <0x9a03c00 0x100>; + interrupts = ; + clocks = <&clk_s_c0_flexgen CLK_TX_ICN_DISP_0>; + resets = <&powerdown STIH407_USB2_PORT0_POWERDOWN>, + <&softreset STIH407_USB2_PORT0_SOFTRESET>; + reset-names = "power", "softreset"; + phys = <&usb2_picophy1>; + phy-names = "usb"; + }; + + ehci0: usb@9a03e00 { + compatible = "st,st-ehci-300x"; + reg = <0x9a03e00 0x100>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usb0>; + clocks = <&clk_s_c0_flexgen CLK_TX_ICN_DISP_0>; + resets = <&powerdown STIH407_USB2_PORT0_POWERDOWN>, + <&softreset STIH407_USB2_PORT0_SOFTRESET>; + reset-names = "power", "softreset"; + phys = <&usb2_picophy1>; + phy-names = "usb"; + }; + + ohci1: usb@9a83c00 { + compatible = "st,st-ohci-300x"; + reg = <0x9a83c00 0x100>; + interrupts = ; + clocks = <&clk_s_c0_flexgen CLK_TX_ICN_DISP_0>; + resets = <&powerdown STIH407_USB2_PORT1_POWERDOWN>, + <&softreset STIH407_USB2_PORT1_SOFTRESET>; + reset-names = "power", "softreset"; + phys = <&usb2_picophy2>; + phy-names = "usb"; + }; + + ehci1: usb@9a83e00 { + compatible = "st,st-ehci-300x"; + reg = <0x9a83e00 0x100>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usb1>; + clocks = <&clk_s_c0_flexgen CLK_TX_ICN_DISP_0>; + resets = <&powerdown STIH407_USB2_PORT1_POWERDOWN>, + <&softreset STIH407_USB2_PORT1_SOFTRESET>; + reset-names = "power", "softreset"; + phys = <&usb2_picophy2>; + phy-names = "usb"; + }; }; }; -- cgit v0.10.2 From 304a11e86751302a31fa3e8b31329aedfbdb7df5 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Wed, 7 Jan 2015 15:04:11 +0000 Subject: ARM: multi_v7_defconfig: Enable stih407 usb picophy This patch enables the picoPHY usb phy which is used by the usb2 and usb3 host controllers when controlling usb2/1.1 devices. It is found in stih407 family SoC's from STMicroelectronics. Signed-off-by: Peter Griffin Acked-by: Lee Jones Reviewed-by: Arnd Bergmann Signed-off-by: David S. Miller diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index bc393b7e..444685c 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -456,6 +456,7 @@ CONFIG_OMAP_USB2=y CONFIG_TI_PIPE3=y CONFIG_PHY_MIPHY365X=y CONFIG_PHY_STIH41X_USB=y +CONFIG_PHY_STIH407_USB=y CONFIG_PHY_SUN4I_USB=y CONFIG_EXT4_FS=y CONFIG_AUTOFS4_FS=y -- cgit v0.10.2 From 9b1a6d36c38d236b42d912fce852f37d2367b593 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Wed, 7 Jan 2015 15:04:12 +0000 Subject: stmmac: dwmac-sti: Pass sysconfig register offset via syscon dt property. Based on Arnds review comments here https://lkml.org/lkml/2014/11/13/161, we should not be mixing address spaces in the reg property like this driver currently does. This patch updates the driver, dt docs and also the existing dt nodes to pass the sysconfig offset in the syscon dt property. This patch breaks DT compatibility! But this platform is considered WIP, and is only used by a few developers who are upstreaming support for it. This change has been done as a single atomic commit to ensure it is bisectable. Signed-off-by: Peter Griffin Reviewed-by: Arnd Bergmann Signed-off-by: David S. Miller diff --git a/Documentation/devicetree/bindings/net/sti-dwmac.txt b/Documentation/devicetree/bindings/net/sti-dwmac.txt index 6762a6b..d05c1e1 100644 --- a/Documentation/devicetree/bindings/net/sti-dwmac.txt +++ b/Documentation/devicetree/bindings/net/sti-dwmac.txt @@ -9,14 +9,10 @@ The device node has following properties. Required properties: - compatible : Can be "st,stih415-dwmac", "st,stih416-dwmac", "st,stih407-dwmac", "st,stid127-dwmac". - - reg : Offset of the glue configuration register map in system - configuration regmap pointed by st,syscon property and size. - - st,syscon : Should be phandle to system configuration node which - encompases this glue registers. + - st,syscon : Should be phandle/offset pair. The phandle to the syscon node which + encompases the glue register, and the offset of the control register. - st,gmac_en: this is to enable the gmac into a dedicated sysctl control register available on STiH407 SoC. - - sti-ethconf: this is the gmac glue logic register to enable the GMAC, - select among the different modes and program the clk retiming. - pinctrl-0: pin-control for all the MII mode supported. Optional properties: @@ -40,10 +36,10 @@ ethernet0: dwmac@9630000 { device_type = "network"; status = "disabled"; compatible = "st,stih407-dwmac", "snps,dwmac", "snps,dwmac-3.710"; - reg = <0x9630000 0x8000>, <0x80 0x4>; - reg-names = "stmmaceth", "sti-ethconf"; + reg = <0x9630000 0x8000>; + reg-names = "stmmaceth"; - st,syscon = <&syscfg_sbc_reg>; + st,syscon = <&syscfg_sbc_reg 0x80>; st,gmac_en; resets = <&softreset STIH407_ETH1_SOFTRESET>; reset-names = "stmmaceth"; diff --git a/arch/arm/boot/dts/stih415.dtsi b/arch/arm/boot/dts/stih415.dtsi index 9198c12..19b019b 100644 --- a/arch/arm/boot/dts/stih415.dtsi +++ b/arch/arm/boot/dts/stih415.dtsi @@ -153,8 +153,8 @@ compatible = "st,stih415-dwmac", "snps,dwmac", "snps,dwmac-3.610"; status = "disabled"; - reg = <0xfe810000 0x8000>, <0x148 0x4>; - reg-names = "stmmaceth", "sti-ethconf"; + reg = <0xfe810000 0x8000>; + reg-names = "stmmaceth"; interrupts = <0 147 0>, <0 148 0>, <0 149 0>; interrupt-names = "macirq", "eth_wake_irq", "eth_lpi"; @@ -165,7 +165,7 @@ snps,mixed-burst; snps,force_sf_dma_mode; - st,syscon = <&syscfg_rear>; + st,syscon = <&syscfg_rear 0x148>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_mii0>; @@ -177,8 +177,8 @@ device_type = "network"; compatible = "st,stih415-dwmac", "snps,dwmac", "snps,dwmac-3.610"; status = "disabled"; - reg = <0xfef08000 0x8000>, <0x74 0x4>; - reg-names = "stmmaceth", "sti-ethconf"; + reg = <0xfef08000 0x8000>; + reg-names = "stmmaceth"; interrupts = <0 150 0>, <0 151 0>, <0 152 0>; interrupt-names = "macirq", "eth_wake_irq", "eth_lpi"; @@ -186,7 +186,7 @@ snps,mixed-burst; snps,force_sf_dma_mode; - st,syscon = <&syscfg_sbc>; + st,syscon = <&syscfg_sbc 0x74>; resets = <&softreset STIH415_ETH1_SOFTRESET>; reset-names = "stmmaceth"; diff --git a/arch/arm/boot/dts/stih416.dtsi b/arch/arm/boot/dts/stih416.dtsi index 85afe01..ea28eba 100644 --- a/arch/arm/boot/dts/stih416.dtsi +++ b/arch/arm/boot/dts/stih416.dtsi @@ -163,8 +163,8 @@ device_type = "network"; compatible = "st,stih416-dwmac", "snps,dwmac", "snps,dwmac-3.710"; status = "disabled"; - reg = <0xfe810000 0x8000>, <0x8bc 0x4>; - reg-names = "stmmaceth", "sti-ethconf"; + reg = <0xfe810000 0x8000>; + reg-names = "stmmaceth"; interrupts = <0 133 0>, <0 134 0>, <0 135 0>; interrupt-names = "macirq", "eth_wake_irq", "eth_lpi"; @@ -172,7 +172,7 @@ snps,pbl = <32>; snps,mixed-burst; - st,syscon = <&syscfg_rear>; + st,syscon = <&syscfg_rear 0x8bc>; resets = <&softreset STIH416_ETH0_SOFTRESET>; reset-names = "stmmaceth"; pinctrl-names = "default"; @@ -185,15 +185,15 @@ device_type = "network"; compatible = "st,stih416-dwmac", "snps,dwmac", "snps,dwmac-3.710"; status = "disabled"; - reg = <0xfef08000 0x8000>, <0x7f0 0x4>; - reg-names = "stmmaceth", "sti-ethconf"; + reg = <0xfef08000 0x8000>; + reg-names = "stmmaceth"; interrupts = <0 136 0>, <0 137 0>, <0 138 0>; interrupt-names = "macirq", "eth_wake_irq", "eth_lpi"; snps,pbl = <32>; snps,mixed-burst; - st,syscon = <&syscfg_sbc>; + st,syscon = <&syscfg_sbc 0x7f0>; resets = <&softreset STIH416_ETH1_SOFTRESET>; reset-names = "stmmaceth"; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c index 056b358..bb6e2dc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c @@ -122,7 +122,7 @@ struct sti_dwmac { bool ext_phyclk; /* Clock from external PHY */ u32 tx_retime_src; /* TXCLK Retiming*/ struct clk *clk; /* PHY clock */ - int ctrl_reg; /* GMAC glue-logic control register */ + u32 ctrl_reg; /* GMAC glue-logic control register */ int clk_sel_reg; /* GMAC ext clk selection register */ struct device *dev; struct regmap *regmap; @@ -285,11 +285,6 @@ static int sti_dwmac_parse_data(struct sti_dwmac *dwmac, if (!np) return -EINVAL; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sti-ethconf"); - if (!res) - return -ENODATA; - dwmac->ctrl_reg = res->start; - /* clk selection from extra syscfg register */ dwmac->clk_sel_reg = -ENXIO; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sti-clkconf"); @@ -300,6 +295,12 @@ static int sti_dwmac_parse_data(struct sti_dwmac *dwmac, if (IS_ERR(regmap)) return PTR_ERR(regmap); + err = of_property_read_u32_index(np, "st,syscon", 1, &dwmac->ctrl_reg); + if (err) { + dev_err(dev, "Can't get sysconfig ctrl offset (%d)\n", err); + return err; + } + dwmac->dev = dev; dwmac->interface = of_get_phy_mode(np); dwmac->regmap = regmap; -- cgit v0.10.2 From dfba8600024abb09dbabfcd9d3c887848f4b154c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Fri, 9 Jan 2015 12:48:44 -0800 Subject: Input: alps - fix name, product and version of dev2 input device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change fixes name, product and version of dev2 input device based on format used in function psmouse_switch_protocol() in file psmouse-base.c. Signed-off-by: Pali Rohár Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 0faea6d..4d7c4cc 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -2422,14 +2422,24 @@ int alps_init(struct psmouse *psmouse) dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE); } + if (priv->flags & ALPS_DUALPOINT) { + /* + * format of input device name is: "protocol vendor name" + * see function psmouse_switch_protocol() in psmouse-base.c + */ + dev2->name = "AlpsPS/2 ALPS DualPoint Stick"; + dev2->id.product = PSMOUSE_ALPS; + dev2->id.version = priv->proto_version; + } else { + dev2->name = "PS/2 ALPS Mouse"; + dev2->id.product = PSMOUSE_PS2; + dev2->id.version = 0x0000; + } + snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys); dev2->phys = priv->phys; - dev2->name = (priv->flags & ALPS_DUALPOINT) ? - "DualPoint Stick" : "ALPS PS/2 Device"; dev2->id.bustype = BUS_I8042; dev2->id.vendor = 0x0002; - dev2->id.product = PSMOUSE_ALPS; - dev2->id.version = 0x0000; dev2->dev.parent = &psmouse->ps2dev.serio->dev; dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); -- cgit v0.10.2 From 34412ba28232e8b3b4955a4bbe53c9df4c7b2d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Fri, 9 Jan 2015 12:48:58 -0800 Subject: Input: alps - add sanity checks for non DualPoint devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure that driver does not process bogus packets as trackstick data when there is no trackstick present and emit warnings in dmesg so potential issues with trackstick handling will be visible for debugging. Signed-off-by: Pali Rohár Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 4d7c4cc..dd2fd39 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -475,6 +475,13 @@ static void alps_process_trackstick_packet_v3(struct psmouse *psmouse) struct input_dev *dev = priv->dev2; int x, y, z, left, right, middle; + /* It should be a DualPoint when received trackstick packet */ + if (!(priv->flags & ALPS_DUALPOINT)) { + psmouse_warn(psmouse, + "Rejected trackstick packet from non DualPoint device"); + return; + } + /* Sanity check packet */ if (!(packet[0] & 0x40)) { psmouse_dbg(psmouse, "Bad trackstick packet, discarding\n"); @@ -699,7 +706,8 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) alps_report_semi_mt_data(psmouse, fingers); - if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { + if ((priv->flags & ALPS_DUALPOINT) && + !(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { input_report_key(dev2, BTN_LEFT, f->ts_left); input_report_key(dev2, BTN_RIGHT, f->ts_right); input_report_key(dev2, BTN_MIDDLE, f->ts_middle); @@ -743,8 +751,11 @@ static void alps_process_packet_v6(struct psmouse *psmouse) */ if (packet[5] == 0x7F) { /* It should be a DualPoint when received Trackpoint packet */ - if (!(priv->flags & ALPS_DUALPOINT)) + if (!(priv->flags & ALPS_DUALPOINT)) { + psmouse_warn(psmouse, + "Rejected trackstick packet from non DualPoint device"); return; + } /* Trackpoint packet */ x = packet[1] | ((packet[3] & 0x20) << 2); @@ -1026,6 +1037,13 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse) struct input_dev *dev2 = priv->dev2; int x, y, z, left, right, middle; + /* It should be a DualPoint when received trackstick packet */ + if (!(priv->flags & ALPS_DUALPOINT)) { + psmouse_warn(psmouse, + "Rejected trackstick packet from non DualPoint device"); + return; + } + /* * b7 b6 b5 b4 b3 b2 b1 b0 * Byte0 0 1 0 0 1 0 0 0 -- cgit v0.10.2 From 029b18361921a0a07b17bd89a2cf20df3d115de8 Mon Sep 17 00:00:00 2001 From: Gabriel Laskar Date: Sun, 11 Jan 2015 16:18:06 -0800 Subject: Input: uinput - fix ioctl nr overflow for UI_GET_SYSNAME/VERSION Request number for ioctls are encoded as 8bit numbers, but unfortunately UI_GET_SYSNAME and UI_GET_VERSION specifu values larger than that, so they get truncated to 44 (0x2c) and 45 (0x2d). This change makes requested values match their effective values (the ABI stays intact). Signed-off-by: Gabriel Laskar Signed-off-by: Dmitry Torokhov diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h index baeab83..013c9d8 100644 --- a/include/uapi/linux/uinput.h +++ b/include/uapi/linux/uinput.h @@ -82,7 +82,7 @@ struct uinput_ff_erase { * The complete sysfs path is then /sys/devices/virtual/input/--NAME-- * Usually, it is in the form "inputN" */ -#define UI_GET_SYSNAME(len) _IOC(_IOC_READ, UINPUT_IOCTL_BASE, 300, len) +#define UI_GET_SYSNAME(len) _IOC(_IOC_READ, UINPUT_IOCTL_BASE, 44, len) /** * UI_GET_VERSION - Return version of uinput protocol @@ -91,7 +91,7 @@ struct uinput_ff_erase { * the integer pointed to by the ioctl argument. The protocol version * is hard-coded in the kernel and is independent of the uinput device. */ -#define UI_GET_VERSION _IOR(UINPUT_IOCTL_BASE, 301, unsigned int) +#define UI_GET_VERSION _IOR(UINPUT_IOCTL_BASE, 45, unsigned int) /* * To write a force-feedback-capable driver, the upload_effect -- cgit v0.10.2 From 888d0d421663313739a8bf93459c6ba61fd4b121 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 8 Jan 2015 17:54:13 +0200 Subject: drm/i915: add dev_to_i915 helper This will be needed by later patches, so factor it out. No functional change. v2: - s/dev_to_i915_priv/dev_to_i915/ (Jani) - don't use the helper in i915_pm_suspend (Chris) - simplify the helper (Chris) v3: - remove redundant upcasting in the helper (Daniel) Signed-off-by: Imre Deak Reviewed-by: Takashi Iwai Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 574057c..cbbd23f 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -940,8 +940,7 @@ static int i915_pm_suspend(struct device *dev) static int i915_pm_suspend_late(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct drm_device *drm_dev = dev_to_i915(dev)->dev; /* * We have a suspedn ordering issue with the snd-hda driver also @@ -960,8 +959,7 @@ static int i915_pm_suspend_late(struct device *dev) static int i915_pm_resume_early(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct drm_device *drm_dev = dev_to_i915(dev)->dev; if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; @@ -971,8 +969,7 @@ static int i915_pm_resume_early(struct device *dev) static int i915_pm_resume(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct drm_device *drm_dev = dev_to_i915(dev)->dev; if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e9f891c..a343f2f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1781,6 +1781,11 @@ static inline struct drm_i915_private *to_i915(const struct drm_device *dev) return dev->dev_private; } +static inline struct drm_i915_private *dev_to_i915(struct device *dev) +{ + return to_i915(dev_get_drvdata(dev)); +} + /* Iterate over initialised rings */ #define for_each_ring(ring__, dev_priv__, i__) \ for ((i__) = 0; (i__) < I915_NUM_RINGS; (i__)++) \ -- cgit v0.10.2 From 58fddc288b5cec192ad9eb9221da7ed14d974a27 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 8 Jan 2015 17:54:14 +0200 Subject: drm/i915: add component support Register a component to be used to interface with the snd_hda_intel driver. This is meant to replace the same interface that is currently based on module symbol lookup. v2: - change roles between the hda and i915 components (Daniel) - add the implementation to a new file (Jani) - use better namespacing (Jani) v3: - move the implementation to intel_audio.c (Daniel) - rename display_component to audio_component (Daniel) - add kerneldoc (Daniel) v4: - run forgotten git rm i915_component.c (Jani) Signed-off-by: Imre Deak Reviewed-by: Takashi Iwai Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index ecee3bc..26b3199 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -830,6 +830,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) intel_runtime_pm_enable(dev_priv); + i915_audio_component_init(dev_priv); + return 0; out_power_well: @@ -870,6 +872,8 @@ int i915_driver_unload(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; int ret; + i915_audio_component_cleanup(dev_priv); + ret = i915_gem_suspend(dev); if (ret) { DRM_ERROR("failed to idle hardware: %d\n", ret); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index a343f2f..176afc5 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1698,6 +1698,9 @@ struct drm_i915_private { struct drm_property *broadcast_rgb_property; struct drm_property *force_audio_property; + /* hda/i915 audio component */ + bool audio_component_registered; + uint32_t hw_context_size; struct list_head context_list; diff --git a/drivers/gpu/drm/i915/intel_audio.c b/drivers/gpu/drm/i915/intel_audio.c index 2c7ed5c..ee41b88 100644 --- a/drivers/gpu/drm/i915/intel_audio.c +++ b/drivers/gpu/drm/i915/intel_audio.c @@ -22,6 +22,9 @@ */ #include +#include +#include +#include "intel_drv.h" #include #include @@ -461,3 +464,110 @@ void intel_init_audio(struct drm_device *dev) dev_priv->display.audio_codec_disable = ilk_audio_codec_disable; } } + +static void i915_audio_component_get_power(struct device *dev) +{ + intel_display_power_get(dev_to_i915(dev), POWER_DOMAIN_AUDIO); +} + +static void i915_audio_component_put_power(struct device *dev) +{ + intel_display_power_put(dev_to_i915(dev), POWER_DOMAIN_AUDIO); +} + +/* Get CDCLK in kHz */ +static int i915_audio_component_get_cdclk_freq(struct device *dev) +{ + struct drm_i915_private *dev_priv = dev_to_i915(dev); + int ret; + + if (WARN_ON_ONCE(!HAS_DDI(dev_priv))) + return -ENODEV; + + intel_display_power_get(dev_priv, POWER_DOMAIN_AUDIO); + ret = intel_ddi_get_cdclk_freq(dev_priv); + intel_display_power_put(dev_priv, POWER_DOMAIN_AUDIO); + + return ret; +} + +static const struct i915_audio_component_ops i915_audio_component_ops = { + .owner = THIS_MODULE, + .get_power = i915_audio_component_get_power, + .put_power = i915_audio_component_put_power, + .get_cdclk_freq = i915_audio_component_get_cdclk_freq, +}; + +static int i915_audio_component_bind(struct device *i915_dev, + struct device *hda_dev, void *data) +{ + struct i915_audio_component *acomp = data; + + if (WARN_ON(acomp->ops || acomp->dev)) + return -EEXIST; + + acomp->ops = &i915_audio_component_ops; + acomp->dev = i915_dev; + + return 0; +} + +static void i915_audio_component_unbind(struct device *i915_dev, + struct device *hda_dev, void *data) +{ + struct i915_audio_component *acomp = data; + + acomp->ops = NULL; + acomp->dev = NULL; +} + +static const struct component_ops i915_audio_component_bind_ops = { + .bind = i915_audio_component_bind, + .unbind = i915_audio_component_unbind, +}; + +/** + * i915_audio_component_init - initialize and register the audio component + * @dev_priv: i915 device instance + * + * This will register with the component framework a child component which + * will bind dynamically to the snd_hda_intel driver's corresponding master + * component when the latter is registered. During binding the child + * initializes an instance of struct i915_audio_component which it receives + * from the master. The master can then start to use the interface defined by + * this struct. Each side can break the binding at any point by deregistering + * its own component after which each side's component unbind callback is + * called. + * + * We ignore any error during registration and continue with reduced + * functionality (i.e. without HDMI audio). + */ +void i915_audio_component_init(struct drm_i915_private *dev_priv) +{ + int ret; + + ret = component_add(dev_priv->dev->dev, &i915_audio_component_bind_ops); + if (ret < 0) { + DRM_ERROR("failed to add audio component (%d)\n", ret); + /* continue with reduced functionality */ + return; + } + + dev_priv->audio_component_registered = true; +} + +/** + * i915_audio_component_cleanup - deregister the audio component + * @dev_priv: i915 device instance + * + * Deregisters the audio component, breaking any existing binding to the + * corresponding snd_hda_intel driver's master component. + */ +void i915_audio_component_cleanup(struct drm_i915_private *dev_priv) +{ + if (!dev_priv->audio_component_registered) + return; + + component_del(dev_priv->dev->dev, &i915_audio_component_bind_ops); + dev_priv->audio_component_registered = false; +} diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 25fdbb1..e88fd5d 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -872,6 +872,8 @@ void intel_fb_obj_flush(struct drm_i915_gem_object *obj, bool retire); void intel_init_audio(struct drm_device *dev); void intel_audio_codec_enable(struct intel_encoder *encoder); void intel_audio_codec_disable(struct intel_encoder *encoder); +void i915_audio_component_init(struct drm_i915_private *dev_priv); +void i915_audio_component_cleanup(struct drm_i915_private *dev_priv); /* intel_display.c */ const char *intel_output_name(int output); diff --git a/include/drm/i915_component.h b/include/drm/i915_component.h new file mode 100644 index 0000000..3e2f22e --- /dev/null +++ b/include/drm/i915_component.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2014 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * 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. + */ + +#ifndef _I915_COMPONENT_H_ +#define _I915_COMPONENT_H_ + +struct i915_audio_component { + struct device *dev; + + const struct i915_audio_component_ops { + struct module *owner; + void (*get_power)(struct device *); + void (*put_power)(struct device *); + int (*get_cdclk_freq)(struct device *); + } *ops; +}; + +#endif /* _I915_COMPONENT_H_ */ -- cgit v0.10.2 From 347de1f8625199d177caf7668cfa1c00717faedb Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 8 Jan 2015 17:54:15 +0200 Subject: ALSA: hda: export struct hda_intel This struct will be needed by the component code added in an upcoming patch, so export it into a new hda_intel.h file. At the same time also merge hda_i915.h into this new header, there is no reason to keep two separate intel specific header file. Suggested-by: Takashi Iwai Signed-off-by: Imre Deak Reviewed-by: Takashi Iwai Signed-off-by: Daniel Vetter diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c index d4d0375..6a7854d 100644 --- a/sound/pci/hda/hda_i915.c +++ b/sound/pci/hda/hda_i915.c @@ -21,7 +21,7 @@ #include #include #include "hda_priv.h" -#include "hda_i915.h" +#include "hda_intel.h" /* Intel HSW/BDW display HDA controller Extended Mode registers. * EM4 (M value) and EM5 (N Value) are used to convert CDClk (Core Display diff --git a/sound/pci/hda/hda_i915.h b/sound/pci/hda/hda_i915.h deleted file mode 100644 index e6072c6..0000000 --- a/sound/pci/hda/hda_i915.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#ifndef __SOUND_HDA_I915_H -#define __SOUND_HDA_I915_H - -#ifdef CONFIG_SND_HDA_I915 -int hda_display_power(bool enable); -void haswell_set_bclk(struct azx *chip); -int hda_i915_init(void); -int hda_i915_exit(void); -#else -static inline int hda_display_power(bool enable) { return 0; } -static inline void haswell_set_bclk(struct azx *chip) { return; } -static inline int hda_i915_init(void) -{ - return -ENODEV; -} -static inline int hda_i915_exit(void) -{ - return 0; -} -#endif - -#endif diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index d426a0b..e4bc0dc 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -63,7 +63,7 @@ #include "hda_codec.h" #include "hda_controller.h" #include "hda_priv.h" -#include "hda_i915.h" +#include "hda_intel.h" /* position fix mode */ enum { @@ -354,31 +354,6 @@ static char *driver_short_names[] = { [AZX_DRIVER_GENERIC] = "HD-Audio Generic", }; -struct hda_intel { - struct azx chip; - - /* for pending irqs */ - struct work_struct irq_pending_work; - - /* sync probing */ - struct completion probe_wait; - struct work_struct probe_work; - - /* card list (for power_save trigger) */ - struct list_head list; - - /* extra flags */ - unsigned int irq_pending_warned:1; - - /* VGA-switcheroo setup */ - unsigned int use_vga_switcheroo:1; - unsigned int vga_switcheroo_registered:1; - unsigned int init_failed:1; /* delayed init failed */ - - /* secondary power domain for hdmi audio under vga device */ - struct dev_pm_domain hdmi_pm_domain; -}; - #ifdef CONFIG_X86 static void __mark_pages_wc(struct azx *chip, struct snd_dma_buffer *dmab, bool on) { diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h new file mode 100644 index 0000000..434f254 --- /dev/null +++ b/sound/pci/hda/hda_intel.h @@ -0,0 +1,64 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef __SOUND_HDA_INTEL_H +#define __SOUND_HDA_INTEL_H + +#include "hda_priv.h" + +struct hda_intel { + struct azx chip; + + /* for pending irqs */ + struct work_struct irq_pending_work; + + /* sync probing */ + struct completion probe_wait; + struct work_struct probe_work; + + /* card list (for power_save trigger) */ + struct list_head list; + + /* extra flags */ + unsigned int irq_pending_warned:1; + + /* VGA-switcheroo setup */ + unsigned int use_vga_switcheroo:1; + unsigned int vga_switcheroo_registered:1; + unsigned int init_failed:1; /* delayed init failed */ + + /* secondary power domain for hdmi audio under vga device */ + struct dev_pm_domain hdmi_pm_domain; +}; + +#ifdef CONFIG_SND_HDA_I915 +int hda_display_power(bool enable); +void haswell_set_bclk(struct azx *chip); +int hda_i915_init(void); +int hda_i915_exit(void); +#else +static inline int hda_display_power(bool enable) { return 0; } +static inline void haswell_set_bclk(struct azx *chip) { return; } +static inline int hda_i915_init(void) +{ + return -ENODEV; +} +static inline int hda_i915_exit(void) +{ + return 0; +} +#endif + +#endif -- cgit v0.10.2 From 926981ae3325257d0bffcf7ff7ba359edb4fd7e8 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 8 Jan 2015 17:54:16 +0200 Subject: ALSA: hda: pass intel_hda to all i915 interface functions chip is already passed to most of the i915 interface functions. Unify the interface by passing intel_hda instead of chip and passing it to all functions. Passing intel_hda instead of chip makes more sense since this is an intel specific interface. Also in an upcoming patch we will use intel_hda in all of these functions so by passing intel_hda we can save on some pointer casts from chip to intel_hda. This will be needed by an upcoming patch adding component support. No functional change. v2-3: unchanged v4: - pass intel_hda instead of chip Signed-off-by: Imre Deak Reviewed-by: Takashi Iwai Signed-off-by: Daniel Vetter diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c index 6a7854d..66acd09 100644 --- a/sound/pci/hda/hda_i915.c +++ b/sound/pci/hda/hda_i915.c @@ -35,7 +35,7 @@ static int (*get_power)(void); static int (*put_power)(void); static int (*get_cdclk)(void); -int hda_display_power(bool enable) +int hda_display_power(struct hda_intel *hda, bool enable) { if (!get_power || !put_power) return -ENODEV; @@ -48,7 +48,7 @@ int hda_display_power(bool enable) return put_power(); } -void haswell_set_bclk(struct azx *chip) +void haswell_set_bclk(struct hda_intel *hda) { int cdclk_freq; unsigned int bclk_m, bclk_n; @@ -80,12 +80,12 @@ void haswell_set_bclk(struct azx *chip) break; } - azx_writew(chip, EM4, bclk_m); - azx_writew(chip, EM5, bclk_n); + azx_writew(&hda->chip, EM4, bclk_m); + azx_writew(&hda->chip, EM5, bclk_n); } -int hda_i915_init(void) +int hda_i915_init(struct hda_intel *hda) { int err = 0; @@ -111,7 +111,7 @@ int hda_i915_init(void) return err; } -int hda_i915_exit(void) +int hda_i915_exit(struct hda_intel *hda) { if (get_power) { symbol_put(i915_request_power_well); diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index e4bc0dc..323abf9 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -803,7 +803,7 @@ static int azx_suspend(struct device *dev) pci_save_state(pci); pci_set_power_state(pci, PCI_D3hot); if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) - hda_display_power(false); + hda_display_power(hda, false); return 0; } @@ -823,8 +823,8 @@ static int azx_resume(struct device *dev) return 0; if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { - hda_display_power(true); - haswell_set_bclk(chip); + hda_display_power(hda, true); + haswell_set_bclk(hda); } pci_set_power_state(pci, PCI_D0); pci_restore_state(pci); @@ -876,7 +876,7 @@ static int azx_runtime_suspend(struct device *dev) azx_enter_link_reset(chip); azx_clear_irq_pending(chip); if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) - hda_display_power(false); + hda_display_power(hda, false); return 0; } @@ -902,8 +902,8 @@ static int azx_runtime_resume(struct device *dev) return 0; if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { - hda_display_power(true); - haswell_set_bclk(chip); + hda_display_power(hda, true); + haswell_set_bclk(hda); } /* Read STATESTS before controller reset */ @@ -1125,8 +1125,8 @@ static int azx_free(struct azx *chip) release_firmware(chip->fw); #endif if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { - hda_display_power(false); - hda_i915_exit(); + hda_display_power(hda, false); + hda_i915_exit(hda); } kfree(hda); @@ -1604,8 +1604,12 @@ static int azx_first_init(struct azx *chip) /* initialize chip */ azx_init_pci(chip); - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) - haswell_set_bclk(chip); + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { + struct hda_intel *hda; + + hda = container_of(chip, struct hda_intel, chip); + haswell_set_bclk(hda); + } azx_init_chip(chip, (probe_only[dev] & 2) == 0); @@ -1885,13 +1889,13 @@ static int azx_probe_continue(struct azx *chip) /* Request power well for Haswell HDA controller and codec */ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { #ifdef CONFIG_SND_HDA_I915 - err = hda_i915_init(); + err = hda_i915_init(hda); if (err < 0) { dev_err(chip->card->dev, "Error request power-well from i915\n"); goto out_free; } - err = hda_display_power(true); + err = hda_display_power(hda, true); if (err < 0) { dev_err(chip->card->dev, "Cannot turn on display power on i915\n"); diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h index 434f254..70b8306 100644 --- a/sound/pci/hda/hda_intel.h +++ b/sound/pci/hda/hda_intel.h @@ -44,18 +44,21 @@ struct hda_intel { }; #ifdef CONFIG_SND_HDA_I915 -int hda_display_power(bool enable); -void haswell_set_bclk(struct azx *chip); -int hda_i915_init(void); -int hda_i915_exit(void); +int hda_display_power(struct hda_intel *hda, bool enable); +void haswell_set_bclk(struct hda_intel *hda); +int hda_i915_init(struct hda_intel *hda); +int hda_i915_exit(struct hda_intel *hda); #else -static inline int hda_display_power(bool enable) { return 0; } -static inline void haswell_set_bclk(struct azx *chip) { return; } -static inline int hda_i915_init(void) +static inline int hda_display_power(struct hda_intel *hda, bool enable) +{ + return 0; +} +static inline void haswell_set_bclk(struct hda_intel *hda) { return; } +static inline int hda_i915_init(struct hda_intel *hda) { return -ENODEV; } -static inline int hda_i915_exit(void) +static inline int hda_i915_exit(struct hda_intel *hda) { return 0; } -- cgit v0.10.2 From d7055bd653e00ef40a07065d1c94380240314c48 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 8 Jan 2015 17:54:17 +0200 Subject: ALSA: hda: add component support Register a component master to be used to interface with the i915 driver. This is meant to replace the current interface which is based on module symbol lookups. Note that currently we keep the existing behavior and pin the i915 module while the hda driver is loaded. Using the component interface allows us to remove this dependency once support for dynamically enabling / disabling the HDMI functionality is added to the driver. v2: - change roles between the hda and i915 components (Daniel) v3: - rename display_component to audio_component (Daniel) v4: - move removal of i915_powerwell.h from this patch to the next (Takashi) - request_module fails if module support isn't enabled, so ignore any error it returns and depend on the following NULL check of the component ops (Takashi) - change over to using dev_* instead of pr_* (Takashi) Signed-off-by: Imre Deak Reviewed-by: Takashi Iwai Signed-off-by: Daniel Vetter diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c index 66acd09..7148945 100644 --- a/sound/pci/hda/hda_i915.c +++ b/sound/pci/hda/hda_i915.c @@ -18,8 +18,10 @@ #include #include +#include +#include +#include #include -#include #include "hda_priv.h" #include "hda_intel.h" @@ -31,32 +33,33 @@ #define AZX_REG_EM4 0x100c #define AZX_REG_EM5 0x1010 -static int (*get_power)(void); -static int (*put_power)(void); -static int (*get_cdclk)(void); - int hda_display_power(struct hda_intel *hda, bool enable) { - if (!get_power || !put_power) + struct i915_audio_component *acomp = &hda->audio_component; + + if (!acomp->ops) return -ENODEV; - pr_debug("HDA display power %s \n", - enable ? "Enable" : "Disable"); + dev_dbg(&hda->chip.pci->dev, "display power %s\n", + enable ? "enable" : "disable"); if (enable) - return get_power(); + acomp->ops->get_power(acomp->dev); else - return put_power(); + acomp->ops->put_power(acomp->dev); + + return 0; } void haswell_set_bclk(struct hda_intel *hda) { int cdclk_freq; unsigned int bclk_m, bclk_n; + struct i915_audio_component *acomp = &hda->audio_component; - if (!get_cdclk) + if (!acomp->ops) return; - cdclk_freq = get_cdclk(); + cdclk_freq = acomp->ops->get_cdclk_freq(acomp->dev); switch (cdclk_freq) { case 337500: bclk_m = 16; @@ -84,47 +87,104 @@ void haswell_set_bclk(struct hda_intel *hda) azx_writew(&hda->chip, EM5, bclk_n); } - -int hda_i915_init(struct hda_intel *hda) +static int hda_component_master_bind(struct device *dev) { - int err = 0; + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + struct hda_intel *hda = container_of(chip, struct hda_intel, chip); + struct i915_audio_component *acomp = &hda->audio_component; + int ret; + + ret = component_bind_all(dev, acomp); + if (ret < 0) + return ret; + + if (WARN_ON(!(acomp->dev && acomp->ops && acomp->ops->get_power && + acomp->ops->put_power && acomp->ops->get_cdclk_freq))) { + ret = -EINVAL; + goto out_unbind; + } - get_power = symbol_request(i915_request_power_well); - if (!get_power) { - pr_warn("hda-i915: get_power symbol get fail\n"); - return -ENODEV; + /* + * Atm, we don't support dynamic unbinding initiated by the child + * component, so pin its containing module until we unbind. + */ + if (!try_module_get(acomp->ops->owner)) { + ret = -ENODEV; + goto out_unbind; } - put_power = symbol_request(i915_release_power_well); - if (!put_power) { - symbol_put(i915_request_power_well); - get_power = NULL; - return -ENODEV; + return 0; + +out_unbind: + component_unbind_all(dev, acomp); + + return ret; +} + +static void hda_component_master_unbind(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + struct hda_intel *hda = container_of(chip, struct hda_intel, chip); + struct i915_audio_component *acomp = &hda->audio_component; + + module_put(acomp->ops->owner); + component_unbind_all(dev, acomp); + WARN_ON(acomp->ops || acomp->dev); +} + +static const struct component_master_ops hda_component_master_ops = { + .bind = hda_component_master_bind, + .unbind = hda_component_master_unbind, +}; + +static int hda_component_master_match(struct device *dev, void *data) +{ + /* i915 is the only supported component */ + return !strcmp(dev->driver->name, "i915"); +} + +int hda_i915_init(struct hda_intel *hda) +{ + struct component_match *match = NULL; + struct device *dev = &hda->chip.pci->dev; + struct i915_audio_component *acomp = &hda->audio_component; + int ret; + + component_match_add(dev, &match, hda_component_master_match, hda); + ret = component_master_add_with_match(dev, &hda_component_master_ops, + match); + if (ret < 0) + goto out_err; + + /* + * Atm, we don't support deferring the component binding, so make sure + * i915 is loaded and that the binding successfully completes. + */ + request_module("i915"); + + if (!acomp->ops) { + ret = -ENODEV; + goto out_master_del; } - get_cdclk = symbol_request(i915_get_cdclk_freq); - if (!get_cdclk) /* may have abnormal BCLK and audio playback rate */ - pr_warn("hda-i915: get_cdclk symbol get fail\n"); + dev_dbg(dev, "bound to i915 component master\n"); - pr_debug("HDA driver get symbol successfully from i915 module\n"); + return 0; +out_master_del: + component_master_del(dev, &hda_component_master_ops); +out_err: + dev_err(dev, "failed to add i915 component master (%d)\n", ret); - return err; + return ret; } int hda_i915_exit(struct hda_intel *hda) { - if (get_power) { - symbol_put(i915_request_power_well); - get_power = NULL; - } - if (put_power) { - symbol_put(i915_release_power_well); - put_power = NULL; - } - if (get_cdclk) { - symbol_put(i915_get_cdclk_freq); - get_cdclk = NULL; - } + struct device *dev = &hda->chip.pci->dev; + + component_master_del(dev, &hda_component_master_ops); return 0; } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 323abf9..95a5399 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1890,11 +1890,8 @@ static int azx_probe_continue(struct azx *chip) if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { #ifdef CONFIG_SND_HDA_I915 err = hda_i915_init(hda); - if (err < 0) { - dev_err(chip->card->dev, - "Error request power-well from i915\n"); + if (err < 0) goto out_free; - } err = hda_display_power(hda, true); if (err < 0) { dev_err(chip->card->dev, diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h index 70b8306..3486118 100644 --- a/sound/pci/hda/hda_intel.h +++ b/sound/pci/hda/hda_intel.h @@ -16,6 +16,7 @@ #ifndef __SOUND_HDA_INTEL_H #define __SOUND_HDA_INTEL_H +#include #include "hda_priv.h" struct hda_intel { @@ -41,6 +42,9 @@ struct hda_intel { /* secondary power domain for hdmi audio under vga device */ struct dev_pm_domain hdmi_pm_domain; + + /* i915 component interface */ + struct i915_audio_component audio_component; }; #ifdef CONFIG_SND_HDA_I915 -- cgit v0.10.2 From fcf3aac5fc307f0cae429f5844ddc25761662858 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 8 Jan 2015 17:54:18 +0200 Subject: drm/i915: remove unused power_well/get_cdclk_freq api After switching to using the component interface this API isn't needed any more. v2-3: unchanged v4: - move the removal of i915_powerwell.h to this patch (Takashi) Signed-off-by: Imre Deak Reviewed-by: Takashi Iwai Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index ac6da71..39ddf40 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -31,7 +31,6 @@ #include "i915_drv.h" #include "intel_drv.h" -#include /** * DOC: runtime pm @@ -50,8 +49,6 @@ * present for a given platform. */ -static struct i915_power_domains *hsw_pwr; - #define for_each_power_well(i, power_well, domain_mask, power_domains) \ for (i = 0; \ i < (power_domains)->power_well_count && \ @@ -1071,10 +1068,8 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv) */ if (IS_HASWELL(dev_priv->dev)) { set_power_wells(power_domains, hsw_power_wells); - hsw_pwr = power_domains; } else if (IS_BROADWELL(dev_priv->dev)) { set_power_wells(power_domains, bdw_power_wells); - hsw_pwr = power_domains; } else if (IS_CHERRYVIEW(dev_priv->dev)) { set_power_wells(power_domains, chv_power_wells); } else if (IS_VALLEYVIEW(dev_priv->dev)) { @@ -1118,8 +1113,6 @@ void intel_power_domains_fini(struct drm_i915_private *dev_priv) * the power well is not enabled, so just enable it in case * we're going to unload/reload. */ intel_display_set_init_power(dev_priv, true); - - hsw_pwr = NULL; } static void intel_power_domains_resume(struct drm_i915_private *dev_priv) @@ -1328,52 +1321,3 @@ void intel_runtime_pm_enable(struct drm_i915_private *dev_priv) pm_runtime_put_autosuspend(device); } -/* Display audio driver power well request */ -int i915_request_power_well(void) -{ - struct drm_i915_private *dev_priv; - - if (!hsw_pwr) - return -ENODEV; - - dev_priv = container_of(hsw_pwr, struct drm_i915_private, - power_domains); - intel_display_power_get(dev_priv, POWER_DOMAIN_AUDIO); - return 0; -} -EXPORT_SYMBOL_GPL(i915_request_power_well); - -/* Display audio driver power well release */ -int i915_release_power_well(void) -{ - struct drm_i915_private *dev_priv; - - if (!hsw_pwr) - return -ENODEV; - - dev_priv = container_of(hsw_pwr, struct drm_i915_private, - power_domains); - intel_display_power_put(dev_priv, POWER_DOMAIN_AUDIO); - return 0; -} -EXPORT_SYMBOL_GPL(i915_release_power_well); - -/* - * Private interface for the audio driver to get CDCLK in kHz. - * - * Caller must request power well using i915_request_power_well() prior to - * making the call. - */ -int i915_get_cdclk_freq(void) -{ - struct drm_i915_private *dev_priv; - - if (!hsw_pwr) - return -ENODEV; - - dev_priv = container_of(hsw_pwr, struct drm_i915_private, - power_domains); - - return intel_ddi_get_cdclk_freq(dev_priv); -} -EXPORT_SYMBOL_GPL(i915_get_cdclk_freq); diff --git a/include/drm/i915_powerwell.h b/include/drm/i915_powerwell.h deleted file mode 100644 index baa6f11..0000000 --- a/include/drm/i915_powerwell.h +++ /dev/null @@ -1,37 +0,0 @@ -/************************************************************************** - * - * Copyright 2013 Intel Inc. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * - **************************************************************************/ - -#ifndef _I915_POWERWELL_H_ -#define _I915_POWERWELL_H_ - -/* For use by hda_i915 driver */ -extern int i915_request_power_well(void); -extern int i915_release_power_well(void); -extern int i915_get_cdclk_freq(void); - -#endif /* _I915_POWERWELL_H_ */ -- cgit v0.10.2 From 270d73c1c32c71e215f1c64e70be82063ec65005 Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Thu, 8 Jan 2015 12:01:27 +0800 Subject: irda: Removed all unused timeval variables In the file au1k_ir.c & via-ircc.h, there were two unused definitions of the timeval type members, this commit therefore removes this unneeded code. In other three files, the same problem is the rx_time member is only ever written, never read, so removed it entirely. Signed-off-by: Chunyan Zhang Reviewed-by: Arnd Bergmann Signed-off-by: David S. Miller diff --git a/drivers/net/irda/au1k_ir.c b/drivers/net/irda/au1k_ir.c index e151205..44e4f38 100644 --- a/drivers/net/irda/au1k_ir.c +++ b/drivers/net/irda/au1k_ir.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include @@ -163,8 +162,6 @@ struct au1k_private { iobuff_t rx_buff; struct net_device *netdev; - struct timeval stamp; - struct timeval now; struct qos_info qos; struct irlap_cb *irlap; diff --git a/drivers/net/irda/kingsun-sir.c b/drivers/net/irda/kingsun-sir.c index e638893..fb5d162 100644 --- a/drivers/net/irda/kingsun-sir.c +++ b/drivers/net/irda/kingsun-sir.c @@ -114,7 +114,6 @@ struct kingsun_cb { (usually 8) */ iobuff_t rx_buff; /* receive unwrap state machine */ - struct timeval rx_time; spinlock_t lock; int receiving; @@ -235,7 +234,6 @@ static void kingsun_rcv_irq(struct urb *urb) &kingsun->netdev->stats, &kingsun->rx_buff, bytes[i]); } - do_gettimeofday(&kingsun->rx_time); kingsun->receiving = (kingsun->rx_buff.state != OUTSIDE_FRAME) ? 1 : 0; @@ -273,7 +271,6 @@ static int kingsun_net_open(struct net_device *netdev) skb_reserve(kingsun->rx_buff.skb, 1); kingsun->rx_buff.head = kingsun->rx_buff.skb->data; - do_gettimeofday(&kingsun->rx_time); kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!kingsun->rx_urb) diff --git a/drivers/net/irda/ks959-sir.c b/drivers/net/irda/ks959-sir.c index e6b3804..8e6e0ed 100644 --- a/drivers/net/irda/ks959-sir.c +++ b/drivers/net/irda/ks959-sir.c @@ -187,7 +187,6 @@ struct ks959_cb { __u8 *rx_buf; __u8 rx_variable_xormask; iobuff_t rx_unwrap_buff; - struct timeval rx_time; struct usb_ctrlrequest *speed_setuprequest; struct urb *speed_urb; @@ -476,7 +475,6 @@ static void ks959_rcv_irq(struct urb *urb) bytes[i]); } } - do_gettimeofday(&kingsun->rx_time); kingsun->receiving = (kingsun->rx_unwrap_buff.state != OUTSIDE_FRAME) ? 1 : 0; } @@ -514,7 +512,6 @@ static int ks959_net_open(struct net_device *netdev) skb_reserve(kingsun->rx_unwrap_buff.skb, 1); kingsun->rx_unwrap_buff.head = kingsun->rx_unwrap_buff.skb->data; - do_gettimeofday(&kingsun->rx_time); kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!kingsun->rx_urb) diff --git a/drivers/net/irda/mcs7780.c b/drivers/net/irda/mcs7780.c index e4d678f..bca6a1e 100644 --- a/drivers/net/irda/mcs7780.c +++ b/drivers/net/irda/mcs7780.c @@ -722,7 +722,6 @@ static int mcs_net_open(struct net_device *netdev) skb_reserve(mcs->rx_buff.skb, 1); mcs->rx_buff.head = mcs->rx_buff.skb->data; - do_gettimeofday(&mcs->rx_time); /* * Now that everything should be initialized properly, @@ -799,7 +798,6 @@ static void mcs_receive_irq(struct urb *urb) mcs_unwrap_fir(mcs, urb->transfer_buffer, urb->actual_length); } - do_gettimeofday(&mcs->rx_time); } ret = usb_submit_urb(urb, GFP_ATOMIC); diff --git a/drivers/net/irda/mcs7780.h b/drivers/net/irda/mcs7780.h index b10689b..a6e8f7d 100644 --- a/drivers/net/irda/mcs7780.h +++ b/drivers/net/irda/mcs7780.h @@ -116,7 +116,6 @@ struct mcs_cb { __u8 *fifo_status; iobuff_t rx_buff; /* receive unwrap state machine */ - struct timeval rx_time; spinlock_t lock; int receiving; diff --git a/drivers/net/irda/via-ircc.h b/drivers/net/irda/via-ircc.h index 7ce820e..ac15255 100644 --- a/drivers/net/irda/via-ircc.h +++ b/drivers/net/irda/via-ircc.h @@ -29,7 +29,6 @@ this program; if not, see . ********************************************************************/ #ifndef via_IRCC_H #define via_IRCC_H -#include #include #include #include @@ -106,9 +105,6 @@ struct via_ircc_cb { __u8 ier; /* Interrupt enable register */ - struct timeval stamp; - struct timeval now; - spinlock_t lock; /* For serializing operations */ __u32 flags; /* Interface flags */ -- cgit v0.10.2 From 9f61e309c54ac7f906422abd4ccebe10bee9debd Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Thu, 8 Jan 2015 12:01:28 +0800 Subject: irda: ali-ircc: Replace timeval with ktime_t The ali ircc driver uses 'timeval', which we try to remove in the kernel because all 32-bit time types will break in the year 2038. This patch also changes do_gettimeofday() to ktime_get() accordingly, since ktime_get returns a ktime_t, but do_gettimeofday returns a struct timeval, and the other reason is that ktime_get() uses the monotonic clock. This patch uses ktime_us_delta to get the elapsed time, and in this way it no longer needs to check for the overflow, because ktime_us_delta returns time difference of microsecond. Signed-off-by: Chunyan Zhang Reviewed-by: Arnd Bergmann Signed-off-by: David S. Miller diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c index 58f98f4..58ae11a 100644 --- a/drivers/net/irda/ali-ircc.c +++ b/drivers/net/irda/ali-ircc.c @@ -1462,17 +1462,12 @@ static netdev_tx_t ali_ircc_fir_hard_xmit(struct sk_buff *skb, if (mtt) { /* Check how much time we have used already */ - do_gettimeofday(&self->now); - - diff = self->now.tv_usec - self->stamp.tv_usec; + diff = ktime_us_delta(ktime_get(), self->stamp); /* self->stamp is set from ali_ircc_dma_receive_complete() */ pr_debug("%s(), ******* diff = %d *******\n", __func__, diff); - - if (diff < 0) - diff += 1000000; - + /* Check if the mtt is larger than the time we have * already used by all the protocol processing */ @@ -1884,7 +1879,7 @@ static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self) * reduce the min turn time a bit since we will know * how much time we have used for protocol processing */ - do_gettimeofday(&self->stamp); + self->stamp = ktime_get(); skb = dev_alloc_skb(len+1); if (skb == NULL) diff --git a/drivers/net/irda/ali-ircc.h b/drivers/net/irda/ali-ircc.h index 0c8edb4..c2d9747 100644 --- a/drivers/net/irda/ali-ircc.h +++ b/drivers/net/irda/ali-ircc.h @@ -22,7 +22,7 @@ #ifndef ALI_IRCC_H #define ALI_IRCC_H -#include +#include #include #include @@ -209,8 +209,7 @@ struct ali_ircc_cb { unsigned char rcvFramesOverflow; - struct timeval stamp; - struct timeval now; + ktime_t stamp; spinlock_t lock; /* For serializing operations */ -- cgit v0.10.2 From ca98278a14b76916cd09c2c899c21c29a0748d15 Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Thu, 8 Jan 2015 12:01:29 +0800 Subject: irda: irda-usb: Replace timeval with ktime_t The irda usb driver uses 'timeval', which we try to remove in the kernel because all 32-bit time types will break in the year 2038. This patch also changes do_gettimeofday() to ktime_get() accordingly, since ktime_get returns a ktime_t, but do_gettimeofday returns a struct timeval, and the other reason is that ktime_get() uses the monotonic clock. This patch uses ktime_us_delta to get the elapsed time, and in this way it no longer needs to check for the overflow, because ktime_us_delta returns time difference of microsecond. Signed-off-by: Chunyan Zhang Reviewed-by: Arnd Bergmann Signed-off-by: David S. Miller diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index 48b2f9a..f6c9163 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -495,18 +495,12 @@ static netdev_tx_t irda_usb_hard_xmit(struct sk_buff *skb, mtt = irda_get_mtt(skb); if (mtt) { int diff; - do_gettimeofday(&self->now); - diff = self->now.tv_usec - self->stamp.tv_usec; + diff = ktime_us_delta(ktime_get(), self->stamp); #ifdef IU_USB_MIN_RTT /* Factor in USB delays -> Get rid of udelay() that * would be lost in the noise - Jean II */ diff += IU_USB_MIN_RTT; #endif /* IU_USB_MIN_RTT */ - /* If the usec counter did wraparound, the diff will - * go negative (tv_usec is a long), so we need to - * correct it by one second. Jean II */ - if (diff < 0) - diff += 1000000; /* Check if the mtt is larger than the time we have * already used by all the protocol processing @@ -869,7 +863,7 @@ static void irda_usb_receive(struct urb *urb) * reduce the min turn time a bit since we will know * how much time we have used for protocol processing */ - do_gettimeofday(&self->stamp); + self->stamp = ktime_get(); /* Check if we need to copy the data to a new skb or not. * For most frames, we use ZeroCopy and pass the already diff --git a/drivers/net/irda/irda-usb.h b/drivers/net/irda/irda-usb.h index 58ddb52..8ac389f 100644 --- a/drivers/net/irda/irda-usb.h +++ b/drivers/net/irda/irda-usb.h @@ -29,7 +29,7 @@ * *****************************************************************************/ -#include +#include #include #include /* struct irlap_cb */ @@ -157,8 +157,7 @@ struct irda_usb_cb { char *speed_buff; /* Buffer for speed changes */ char *tx_buff; - struct timeval stamp; - struct timeval now; + ktime_t stamp; spinlock_t lock; /* For serializing Tx operations */ -- cgit v0.10.2 From 4c3bd197a0f9b98343c5a6533e76a259271855b4 Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Thu, 8 Jan 2015 12:01:30 +0800 Subject: irda: nsc-ircc: Replace timeval with ktime_t The nsc ircc driver uses 'timeval', which we try to remove in the kernel because all 32-bit time types will break in the year 2038. This patch also changes do_gettimeofday() to ktime_get() accordingly, since ktime_get returns a ktime_t, but do_gettimeofday returns a struct timeval, and the other reason is that ktime_get() uses the monotonic clock. This patch uses ktime_us_delta to get the elapsed time, and in this way it no longer needs to check for the overflow, because ktime_us_delta returns time difference of microsecond. Signed-off-by: Chunyan Zhang Reviewed-by: Arnd Bergmann Signed-off-by: David S. Miller diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c index e7317b1..dc0dbd8 100644 --- a/drivers/net/irda/nsc-ircc.c +++ b/drivers/net/irda/nsc-ircc.c @@ -1501,10 +1501,7 @@ static netdev_tx_t nsc_ircc_hard_xmit_fir(struct sk_buff *skb, mtt = irda_get_mtt(skb); if (mtt) { /* Check how much time we have used already */ - do_gettimeofday(&self->now); - diff = self->now.tv_usec - self->stamp.tv_usec; - if (diff < 0) - diff += 1000000; + diff = ktime_us_delta(ktime_get(), self->stamp); /* Check if the mtt is larger than the time we have * already used by all the protocol processing @@ -1867,7 +1864,7 @@ static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase) * reduce the min turn time a bit since we will know * how much time we have used for protocol processing */ - do_gettimeofday(&self->stamp); + self->stamp = ktime_get(); skb = dev_alloc_skb(len+1); if (skb == NULL) { diff --git a/drivers/net/irda/nsc-ircc.h b/drivers/net/irda/nsc-ircc.h index 32fa582..7be5acb 100644 --- a/drivers/net/irda/nsc-ircc.h +++ b/drivers/net/irda/nsc-ircc.h @@ -28,7 +28,7 @@ #ifndef NSC_IRCC_H #define NSC_IRCC_H -#include +#include #include #include @@ -263,8 +263,7 @@ struct nsc_ircc_cb { __u8 ier; /* Interrupt enable register */ - struct timeval stamp; - struct timeval now; + ktime_t stamp; spinlock_t lock; /* For serializing operations */ -- cgit v0.10.2 From 89a07e1726534957fc987b39742048d592ec9aae Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Thu, 8 Jan 2015 12:01:31 +0800 Subject: irda: stir4200: Replace timeval with ktime_t The stir4200 driver uses 'timeval', which we try to remove in the kernel because all 32-bit time types will break in the year 2038. This patch also changes do_gettimeofday() to ktime_get() accordingly, since ktime_get returns a ktime_t, but do_gettimeofday returns a struct timeval, and the other reason is that ktime_get() uses the monotonic clock. This patch uses ktime_us_delta to get the elapsed time of microsecond. Signed-off-by: Chunyan Zhang Reviewed-by: Arnd Bergmann Signed-off-by: David S. Miller diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c index dd1bd10..83cc48a 100644 --- a/drivers/net/irda/stir4200.c +++ b/drivers/net/irda/stir4200.c @@ -40,6 +40,7 @@ #include #include +#include #include #include #include @@ -174,7 +175,7 @@ struct stir_cb { __u8 *fifo_status; iobuff_t rx_buff; /* receive unwrap state machine */ - struct timeval rx_time; + ktime_t rx_time; int receiving; struct urb *rx_urb; }; @@ -650,15 +651,12 @@ static int fifo_txwait(struct stir_cb *stir, int space) static void turnaround_delay(const struct stir_cb *stir, long us) { long ticks; - struct timeval now; if (us <= 0) return; - do_gettimeofday(&now); - if (now.tv_sec - stir->rx_time.tv_sec > 0) - us -= USEC_PER_SEC; - us -= now.tv_usec - stir->rx_time.tv_usec; + us -= ktime_us_delta(ktime_get(), stir->rx_time); + if (us < 10) return; @@ -823,8 +821,8 @@ static void stir_rcv_irq(struct urb *urb) pr_debug("receive %d\n", urb->actual_length); unwrap_chars(stir, urb->transfer_buffer, urb->actual_length); - - do_gettimeofday(&stir->rx_time); + + stir->rx_time = ktime_get(); } /* kernel thread is stopping receiver don't resubmit */ @@ -876,7 +874,7 @@ static int stir_net_open(struct net_device *netdev) skb_reserve(stir->rx_buff.skb, 1); stir->rx_buff.head = stir->rx_buff.skb->data; - do_gettimeofday(&stir->rx_time); + stir->rx_time = ktime_get(); stir->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!stir->rx_urb) -- cgit v0.10.2 From 497ec1f2a086878d6a42334eda72bfef079dd484 Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Thu, 8 Jan 2015 12:01:32 +0800 Subject: irda: vlsi_ir: Replace timeval with ktime_t The vlsi ir driver uses 'timeval', which we try to remove in the kernel because all 32-bit time types will break in the year 2038. This patch also changes do_gettimeofday() to ktime_get() accordingly, since ktime_get returns a ktime_t, but do_gettimeofday returns a struct timeval, and the other reason is that ktime_get() uses the monotonic clock. This patch uses ktime_us_delta to get the elapsed time of microsecond, and uses div_s64_rem to get what seconds & microseconds time elapsed for printing. This patch also changes the function 'vlsi_hard_start_xmit' to do the same things as the others drivers, that is passing the remaining time into udelay() instead of looping until enough time has passed. Signed-off-by: Chunyan Zhang Reviewed-by: Arnd Bergmann Signed-off-by: David S. Miller diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c index ac39d9f..a0849f4 100644 --- a/drivers/net/irda/vlsi_ir.c +++ b/drivers/net/irda/vlsi_ir.c @@ -33,6 +33,7 @@ MODULE_LICENSE("GPL"); /********************************************************/ #include +#include #include #include #include @@ -40,9 +41,9 @@ MODULE_LICENSE("GPL"); #include #include #include -#include #include #include +#include #include #include #include @@ -180,8 +181,7 @@ static void vlsi_proc_ndev(struct seq_file *seq, struct net_device *ndev) vlsi_irda_dev_t *idev = netdev_priv(ndev); u8 byte; u16 word; - unsigned delta1, delta2; - struct timeval now; + s32 sec, usec; unsigned iobase = ndev->base_addr; seq_printf(seq, "\n%s link state: %s / %s / %s / %s\n", ndev->name, @@ -277,17 +277,9 @@ static void vlsi_proc_ndev(struct seq_file *seq, struct net_device *ndev) seq_printf(seq, "\nsw-state:\n"); seq_printf(seq, "IrPHY setup: %d baud - %s encoding\n", idev->baud, (idev->mode==IFF_SIR)?"SIR":((idev->mode==IFF_MIR)?"MIR":"FIR")); - do_gettimeofday(&now); - if (now.tv_usec >= idev->last_rx.tv_usec) { - delta2 = now.tv_usec - idev->last_rx.tv_usec; - delta1 = 0; - } - else { - delta2 = 1000000 + now.tv_usec - idev->last_rx.tv_usec; - delta1 = 1; - } - seq_printf(seq, "last rx: %lu.%06u sec\n", - now.tv_sec - idev->last_rx.tv_sec - delta1, delta2); + sec = div_s64_rem(ktime_us_delta(ktime_get(), idev->last_rx), + USEC_PER_SEC, &usec); + seq_printf(seq, "last rx: %ul.%06u sec\n", sec, usec); seq_printf(seq, "RX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu", ndev->stats.rx_packets, ndev->stats.rx_bytes, ndev->stats.rx_errors, @@ -661,7 +653,7 @@ static void vlsi_rx_interrupt(struct net_device *ndev) } } - do_gettimeofday(&idev->last_rx); /* remember "now" for later mtt delay */ + idev->last_rx = ktime_get(); /* remember "now" for later mtt delay */ vlsi_fill_rx(r); @@ -858,9 +850,8 @@ static netdev_tx_t vlsi_hard_start_xmit(struct sk_buff *skb, unsigned iobase = ndev->base_addr; u8 status; u16 config; - int mtt; + int mtt, diff; int len, speed; - struct timeval now, ready; char *msg = NULL; speed = irda_get_next_speed(skb); @@ -940,21 +931,10 @@ static netdev_tx_t vlsi_hard_start_xmit(struct sk_buff *skb, spin_unlock_irqrestore(&idev->lock, flags); if ((mtt = irda_get_mtt(skb)) > 0) { - - ready.tv_usec = idev->last_rx.tv_usec + mtt; - ready.tv_sec = idev->last_rx.tv_sec; - if (ready.tv_usec >= 1000000) { - ready.tv_usec -= 1000000; - ready.tv_sec++; /* IrLAP 1.1: mtt always < 1 sec */ - } - for(;;) { - do_gettimeofday(&now); - if (now.tv_sec > ready.tv_sec || - (now.tv_sec==ready.tv_sec && now.tv_usec>=ready.tv_usec)) - break; - udelay(100); + diff = ktime_us_delta(ktime_get(), idev->last_rx); + if (mtt > diff) + udelay(mtt - diff); /* must not sleep here - called under netif_tx_lock! */ - } } /* tx buffer already owned by CPU due to pci_dma_sync_single_for_cpu() @@ -1333,7 +1313,7 @@ static int vlsi_start_hw(vlsi_irda_dev_t *idev) vlsi_fill_rx(idev->rx_ring); - do_gettimeofday(&idev->last_rx); /* first mtt may start from now on */ + idev->last_rx = ktime_get(); /* first mtt may start from now on */ outw(0, iobase+VLSI_PIO_PROMPT); /* kick hw state machine */ @@ -1520,7 +1500,7 @@ static int vlsi_open(struct net_device *ndev) if (!idev->irlap) goto errout_free_ring; - do_gettimeofday(&idev->last_rx); /* first mtt may start from now on */ + idev->last_rx = ktime_get(); /* first mtt may start from now on */ idev->new_baud = 9600; /* start with IrPHY using 9600(SIR) mode */ diff --git a/drivers/net/irda/vlsi_ir.h b/drivers/net/irda/vlsi_ir.h index f9119c6..f9db2ce 100644 --- a/drivers/net/irda/vlsi_ir.h +++ b/drivers/net/irda/vlsi_ir.h @@ -723,7 +723,7 @@ typedef struct vlsi_irda_dev { void *virtaddr; struct vlsi_ring *tx_ring, *rx_ring; - struct timeval last_rx; + ktime_t last_rx; spinlock_t lock; struct mutex mtx; -- cgit v0.10.2 From d3b4b26173ee45a8097a64330ae4a509f4e5db0b Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Sat, 10 Jan 2015 12:08:18 -0500 Subject: doc: fix the compile fix of txtimestamp.c A fix to ipv6 structure definitions removed the now superfluous definition of in6_pktinfo in this file. But, use of the glibc definition requires defining _GNU_SOURCE (see also https://sourceware.org/bugzilla/show_bug.cgi?id=6775). Before this change, the following would fail for me: make make headers_install make M=Documentation/networking/timestamping with Documentation/networking/timestamping/txtimestamp.c: In function '__recv_errmsg_cmsg': Documentation/networking/timestamping/txtimestamp.c:205:33: error: dereferencing pointer to incomplete type Documentation/networking/timestamping/txtimestamp.c:206:23: error: dereferencing pointer to incomplete type After this patch compilation succeeded. Fixes: cd91cc5bdddf ("doc: fix the compile error of txtimestamp.c") Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller diff --git a/Documentation/networking/timestamping/txtimestamp.c b/Documentation/networking/timestamping/txtimestamp.c index 8778e68..05694fe 100644 --- a/Documentation/networking/timestamping/txtimestamp.c +++ b/Documentation/networking/timestamping/txtimestamp.c @@ -30,6 +30,8 @@ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ +#define _GNU_SOURCE + #include #include #include -- cgit v0.10.2 From 78890ed7549444fa9b9ff7e544d6bdd44b4c9566 Mon Sep 17 00:00:00 2001 From: Praveen Madhavan Date: Fri, 9 Jan 2015 21:25:16 +0530 Subject: csiostor:fix sparse warnings This patch fixes sparse warning reported by kbuild. Apply this on net-next since it depends on previous commit. drivers/scsi/csiostor/csio_hw.c:259:17: sparse: cast to restricted __le32 drivers/scsi/csiostor/csio_hw.c:536:31: sparse: incorrect type in assignment (different base types) drivers/scsi/csiostor/csio_hw.c:536:31: expected unsigned int [unsigned] [usertype] drivers/scsi/csiostor/csio_hw.c:536:31: got restricted __be32 [usertype] >> drivers/scsi/csiostor/csio_hw.c:2012:5: sparse: symbol 'csio_hw_prep_fw' was not declared. Should it be static? Signed-off-by: Praveen Madhavan Signed-off-by: David S. Miller diff --git a/drivers/scsi/csiostor/csio_hw.c b/drivers/scsi/csiostor/csio_hw.c index b70c15f..5c31fa6 100644 --- a/drivers/scsi/csiostor/csio_hw.c +++ b/drivers/scsi/csiostor/csio_hw.c @@ -256,7 +256,7 @@ csio_hw_seeprom_read(struct csio_hw *hw, uint32_t addr, uint32_t *data) } pci_read_config_dword(hw->pdev, base + PCI_VPD_DATA, data); - *data = le32_to_cpu(*data); + *data = le32_to_cpu(*(__le32 *)data); return 0; } @@ -533,7 +533,7 @@ csio_hw_read_flash(struct csio_hw *hw, uint32_t addr, uint32_t nwords, if (ret) return ret; if (byte_oriented) - *data = htonl(*data); + *data = (__force __u32) htonl(*data); } return 0; } @@ -2009,7 +2009,7 @@ static struct fw_info *find_fw_info(int chip) return NULL; } -int csio_hw_prep_fw(struct csio_hw *hw, struct fw_info *fw_info, +static int csio_hw_prep_fw(struct csio_hw *hw, struct fw_info *fw_info, const u8 *fw_data, unsigned int fw_size, struct fw_hdr *card_fw, enum csio_dev_state state, int *reset) -- cgit v0.10.2 From 1f9c418fd94c97c82dc0454fdadece37238f23e7 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Mon, 1 Dec 2014 23:30:07 +0200 Subject: iwlwifi: mvm: fix EBS on single scan EBS error detection isn't supported by all FWs, so turn it on only if the FW advertises such support. Signed-off-by: David Spinadel Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index 1bbe4fc..660ddb1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -246,6 +246,7 @@ enum iwl_ucode_tlv_flag { * @IWL_UCODE_TLV_API_BASIC_DWELL: use only basic dwell time in scan command, * regardless of the band or the number of the probes. FW will calculate * the actual dwell time. + * @IWL_UCODE_TLV_API_SINGLE_SCAN_EBS: EBS is supported for single scans too. */ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0), @@ -257,6 +258,7 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF = BIT(7), IWL_UCODE_TLV_API_FRAGMENTED_SCAN = BIT(8), IWL_UCODE_TLV_API_BASIC_DWELL = BIT(13), + IWL_UCODE_TLV_API_SINGLE_SCAN_EBS = BIT(16), }; /** diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index 201846d..cfc0e65 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h @@ -653,8 +653,11 @@ enum iwl_scan_channel_flags { }; /* iwl_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S - * @flags: enum iwl_scan_channel_flgs - * @non_ebs_ratio: how many regular scan iteration before EBS + * @flags: enum iwl_scan_channel_flags + * @non_ebs_ratio: defines the ratio of number of scan iterations where EBS is + * involved. + * 1 - EBS is disabled. + * 2 - every second scan will be full scan(and so on). */ struct iwl_scan_channel_opt { __le16 flags; diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index ec9a8e7..3fbba4b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -72,6 +72,8 @@ #define IWL_PLCP_QUIET_THRESH 1 #define IWL_ACTIVE_QUIET_TIME 10 +#define IWL_DENSE_EBS_SCAN_RATIO 5 +#define IWL_SPARSE_EBS_SCAN_RATIO 1 struct iwl_mvm_scan_params { u32 max_out_time; @@ -1297,18 +1299,6 @@ iwl_mvm_build_generic_unified_scan_cmd(struct iwl_mvm *mvm, cmd->scan_prio = cpu_to_le32(IWL_SCAN_PRIORITY_HIGH); cmd->iter_num = cpu_to_le32(1); - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT && - mvm->last_ebs_successful) { - cmd->channel_opt[0].flags = - cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | - IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | - IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); - cmd->channel_opt[1].flags = - cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | - IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | - IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); - } - if (iwl_mvm_rrm_scan_needed(mvm)) cmd->scan_flags |= cpu_to_le32(IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED); @@ -1383,6 +1373,22 @@ int iwl_mvm_unified_scan_lmac(struct iwl_mvm *mvm, cmd->schedule[1].iterations = 0; cmd->schedule[1].full_scan_mul = 0; + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_SINGLE_SCAN_EBS && + mvm->last_ebs_successful) { + cmd->channel_opt[0].flags = + cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); + cmd->channel_opt[0].non_ebs_ratio = + cpu_to_le16(IWL_DENSE_EBS_SCAN_RATIO); + cmd->channel_opt[1].flags = + cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); + cmd->channel_opt[1].non_ebs_ratio = + cpu_to_le16(IWL_SPARSE_EBS_SCAN_RATIO); + } + for (i = 1; i <= req->req.n_ssids; i++) ssid_bitmap |= BIT(i); @@ -1483,6 +1489,22 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm, cmd->schedule[1].iterations = 0xff; cmd->schedule[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER; + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT && + mvm->last_ebs_successful) { + cmd->channel_opt[0].flags = + cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); + cmd->channel_opt[0].non_ebs_ratio = + cpu_to_le16(IWL_DENSE_EBS_SCAN_RATIO); + cmd->channel_opt[1].flags = + cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); + cmd->channel_opt[1].non_ebs_ratio = + cpu_to_le16(IWL_SPARSE_EBS_SCAN_RATIO); + } + iwl_mvm_lmac_scan_cfg_channels(mvm, req->channels, req->n_channels, ssid_bitmap, cmd); -- cgit v0.10.2 From e1e1fddae74b72d0415965821ad00fe39aac6f13 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 20 Oct 2014 14:02:15 +0200 Subject: arm64/mm: add explicit struct_mm argument to __create_mapping() Currently, swapper_pg_dir and idmap_pg_dir share the init_mm mm_struct instance. To allow the introduction of other pg_dir instances, for instance, for UEFI's mapping of Runtime Services, make the struct_mm instance an explicit argument that gets passed down to the pmd and pte instantiation functions. Note that the consumers (pmd_populate/pgd_populate) of the mm_struct argument don't actually inspect it, but let's fix it for correctness' sake. Acked-by: Steve Capper Tested-by: Leif Lindholm Signed-off-by: Ard Biesheuvel diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 6032f3e..7d5dfe2 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -156,9 +156,9 @@ static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr, } while (pte++, addr += PAGE_SIZE, addr != end); } -static void __init alloc_init_pmd(pud_t *pud, unsigned long addr, - unsigned long end, phys_addr_t phys, - int map_io) +static void __init alloc_init_pmd(struct mm_struct *mm, pud_t *pud, + unsigned long addr, unsigned long end, + phys_addr_t phys, int map_io) { pmd_t *pmd; unsigned long next; @@ -178,7 +178,7 @@ static void __init alloc_init_pmd(pud_t *pud, unsigned long addr, */ if (pud_none(*pud) || pud_bad(*pud)) { pmd = early_alloc(PTRS_PER_PMD * sizeof(pmd_t)); - pud_populate(&init_mm, pud, pmd); + pud_populate(mm, pud, pmd); } pmd = pmd_offset(pud, addr); @@ -202,16 +202,16 @@ static void __init alloc_init_pmd(pud_t *pud, unsigned long addr, } while (pmd++, addr = next, addr != end); } -static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr, - unsigned long end, phys_addr_t phys, - int map_io) +static void __init alloc_init_pud(struct mm_struct *mm, pgd_t *pgd, + unsigned long addr, unsigned long end, + phys_addr_t phys, int map_io) { pud_t *pud; unsigned long next; if (pgd_none(*pgd)) { pud = early_alloc(PTRS_PER_PUD * sizeof(pud_t)); - pgd_populate(&init_mm, pgd, pud); + pgd_populate(mm, pgd, pud); } BUG_ON(pgd_bad(*pgd)); @@ -240,7 +240,7 @@ static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr, flush_tlb_all(); } } else { - alloc_init_pmd(pud, addr, next, phys, map_io); + alloc_init_pmd(mm, pud, addr, next, phys, map_io); } phys += next - addr; } while (pud++, addr = next, addr != end); @@ -250,9 +250,9 @@ static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr, * Create the page directory entries and any necessary page tables for the * mapping specified by 'md'. */ -static void __init __create_mapping(pgd_t *pgd, phys_addr_t phys, - unsigned long virt, phys_addr_t size, - int map_io) +static void __init __create_mapping(struct mm_struct *mm, pgd_t *pgd, + phys_addr_t phys, unsigned long virt, + phys_addr_t size, int map_io) { unsigned long addr, length, end, next; @@ -262,7 +262,7 @@ static void __init __create_mapping(pgd_t *pgd, phys_addr_t phys, end = addr + length; do { next = pgd_addr_end(addr, end); - alloc_init_pud(pgd, addr, next, phys, map_io); + alloc_init_pud(mm, pgd, addr, next, phys, map_io); phys += next - addr; } while (pgd++, addr = next, addr != end); } @@ -275,7 +275,8 @@ static void __init create_mapping(phys_addr_t phys, unsigned long virt, &phys, virt); return; } - __create_mapping(pgd_offset_k(virt & PAGE_MASK), phys, virt, size, 0); + __create_mapping(&init_mm, pgd_offset_k(virt & PAGE_MASK), phys, virt, + size, 0); } void __init create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io) @@ -284,7 +285,7 @@ void __init create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io) pr_warn("BUG: not creating id mapping for %pa\n", &addr); return; } - __create_mapping(&idmap_pg_dir[pgd_index(addr)], + __create_mapping(&init_mm, &idmap_pg_dir[pgd_index(addr)], addr, addr, size, map_io); } -- cgit v0.10.2 From 8ce837cee8f51fb0eacb32c85461ea2f0fafc9f8 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 20 Oct 2014 15:42:07 +0200 Subject: arm64/mm: add create_pgd_mapping() to create private page tables For UEFI, we need to install the memory mappings used for Runtime Services in a dedicated set of page tables. Add create_pgd_mapping(), which allows us to allocate and install those page table entries early. Reviewed-by: Will Deacon Tested-by: Leif Lindholm Signed-off-by: Ard Biesheuvel diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index c2f006c..5fd40c4 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -33,5 +33,8 @@ extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt); extern void init_mem_pgprot(void); /* create an identity mapping for memory (or io if map_io is true) */ extern void create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io); +extern void create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys, + unsigned long virt, phys_addr_t size, + pgprot_t prot); #endif diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index 210d632..5907924 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -264,6 +264,11 @@ static inline pmd_t pte_pmd(pte_t pte) return __pmd(pte_val(pte)); } +static inline pgprot_t mk_sect_prot(pgprot_t prot) +{ + return __pgprot(pgprot_val(prot) & ~PTE_TABLE_BIT); +} + /* * THP definitions. */ diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 7d5dfe2..3f3d5aa 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -158,20 +158,10 @@ static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr, static void __init alloc_init_pmd(struct mm_struct *mm, pud_t *pud, unsigned long addr, unsigned long end, - phys_addr_t phys, int map_io) + phys_addr_t phys, pgprot_t prot) { pmd_t *pmd; unsigned long next; - pmdval_t prot_sect; - pgprot_t prot_pte; - - if (map_io) { - prot_sect = PROT_SECT_DEVICE_nGnRE; - prot_pte = __pgprot(PROT_DEVICE_nGnRE); - } else { - prot_sect = PROT_SECT_NORMAL_EXEC; - prot_pte = PAGE_KERNEL_EXEC; - } /* * Check for initial section mappings in the pgd/pud and remove them. @@ -187,7 +177,8 @@ static void __init alloc_init_pmd(struct mm_struct *mm, pud_t *pud, /* try section mapping first */ if (((addr | next | phys) & ~SECTION_MASK) == 0) { pmd_t old_pmd =*pmd; - set_pmd(pmd, __pmd(phys | prot_sect)); + set_pmd(pmd, __pmd(phys | + pgprot_val(mk_sect_prot(prot)))); /* * Check for previous table entries created during * boot (__create_page_tables) and flush them. @@ -196,7 +187,7 @@ static void __init alloc_init_pmd(struct mm_struct *mm, pud_t *pud, flush_tlb_all(); } else { alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys), - prot_pte); + prot); } phys += next - addr; } while (pmd++, addr = next, addr != end); @@ -204,7 +195,7 @@ static void __init alloc_init_pmd(struct mm_struct *mm, pud_t *pud, static void __init alloc_init_pud(struct mm_struct *mm, pgd_t *pgd, unsigned long addr, unsigned long end, - phys_addr_t phys, int map_io) + phys_addr_t phys, pgprot_t prot) { pud_t *pud; unsigned long next; @@ -222,10 +213,11 @@ static void __init alloc_init_pud(struct mm_struct *mm, pgd_t *pgd, /* * For 4K granule only, attempt to put down a 1GB block */ - if (!map_io && (PAGE_SHIFT == 12) && + if ((PAGE_SHIFT == 12) && ((addr | next | phys) & ~PUD_MASK) == 0) { pud_t old_pud = *pud; - set_pud(pud, __pud(phys | PROT_SECT_NORMAL_EXEC)); + set_pud(pud, __pud(phys | + pgprot_val(mk_sect_prot(prot)))); /* * If we have an old value for a pud, it will @@ -240,7 +232,7 @@ static void __init alloc_init_pud(struct mm_struct *mm, pgd_t *pgd, flush_tlb_all(); } } else { - alloc_init_pmd(mm, pud, addr, next, phys, map_io); + alloc_init_pmd(mm, pud, addr, next, phys, prot); } phys += next - addr; } while (pud++, addr = next, addr != end); @@ -252,7 +244,7 @@ static void __init alloc_init_pud(struct mm_struct *mm, pgd_t *pgd, */ static void __init __create_mapping(struct mm_struct *mm, pgd_t *pgd, phys_addr_t phys, unsigned long virt, - phys_addr_t size, int map_io) + phys_addr_t size, pgprot_t prot) { unsigned long addr, length, end, next; @@ -262,7 +254,7 @@ static void __init __create_mapping(struct mm_struct *mm, pgd_t *pgd, end = addr + length; do { next = pgd_addr_end(addr, end); - alloc_init_pud(mm, pgd, addr, next, phys, map_io); + alloc_init_pud(mm, pgd, addr, next, phys, prot); phys += next - addr; } while (pgd++, addr = next, addr != end); } @@ -276,7 +268,7 @@ static void __init create_mapping(phys_addr_t phys, unsigned long virt, return; } __create_mapping(&init_mm, pgd_offset_k(virt & PAGE_MASK), phys, virt, - size, 0); + size, PAGE_KERNEL_EXEC); } void __init create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io) @@ -286,7 +278,16 @@ void __init create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io) return; } __create_mapping(&init_mm, &idmap_pg_dir[pgd_index(addr)], - addr, addr, size, map_io); + addr, addr, size, + map_io ? __pgprot(PROT_DEVICE_nGnRE) + : PAGE_KERNEL_EXEC); +} + +void __init create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys, + unsigned long virt, phys_addr_t size, + pgprot_t prot) +{ + __create_mapping(mm, pgd_offset(mm, virt), phys, virt, size, prot); } static void __init map_mem(void) -- cgit v0.10.2 From 7bb68410ef22067b08fd52887875b8f337f89dcc Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sat, 18 Oct 2014 15:04:15 +0200 Subject: efi: split off remapping code from efi_config_init() Split of the remapping code from efi_config_init() so that the caller can perform its own remapping. This is necessary to correctly handle virtually remapped UEFI memory regions under kexec, as efi.systab will have been updated to a virtual address. Acked-by: Matt Fleming Tested-by: Leif Lindholm Signed-off-by: Ard Biesheuvel diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 9035c1b..b7ba9d8 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -293,29 +293,15 @@ static __init int match_config_table(efi_guid_t *guid, return 0; } -int __init efi_config_init(efi_config_table_type_t *arch_tables) +int __init efi_config_parse_tables(void *config_tables, int count, int sz, + efi_config_table_type_t *arch_tables) { - void *config_tables, *tablep; - int i, sz; - - if (efi_enabled(EFI_64BIT)) - sz = sizeof(efi_config_table_64_t); - else - sz = sizeof(efi_config_table_32_t); - - /* - * Let's see what config tables the firmware passed to us. - */ - config_tables = early_memremap(efi.systab->tables, - efi.systab->nr_tables * sz); - if (config_tables == NULL) { - pr_err("Could not map Configuration table!\n"); - return -ENOMEM; - } + void *tablep; + int i; tablep = config_tables; pr_info(""); - for (i = 0; i < efi.systab->nr_tables; i++) { + for (i = 0; i < count; i++) { efi_guid_t guid; unsigned long table; @@ -328,8 +314,6 @@ int __init efi_config_init(efi_config_table_type_t *arch_tables) if (table64 >> 32) { pr_cont("\n"); pr_err("Table located above 4GB, disabling EFI.\n"); - early_memunmap(config_tables, - efi.systab->nr_tables * sz); return -EINVAL; } #endif @@ -344,13 +328,37 @@ int __init efi_config_init(efi_config_table_type_t *arch_tables) tablep += sz; } pr_cont("\n"); - early_memunmap(config_tables, efi.systab->nr_tables * sz); - set_bit(EFI_CONFIG_TABLES, &efi.flags); - return 0; } +int __init efi_config_init(efi_config_table_type_t *arch_tables) +{ + void *config_tables; + int sz, ret; + + if (efi_enabled(EFI_64BIT)) + sz = sizeof(efi_config_table_64_t); + else + sz = sizeof(efi_config_table_32_t); + + /* + * Let's see what config tables the firmware passed to us. + */ + config_tables = early_memremap(efi.systab->tables, + efi.systab->nr_tables * sz); + if (config_tables == NULL) { + pr_err("Could not map Configuration table!\n"); + return -ENOMEM; + } + + ret = efi_config_parse_tables(config_tables, efi.systab->nr_tables, sz, + arch_tables); + + early_memunmap(config_tables, efi.systab->nr_tables * sz); + return ret; +} + #ifdef CONFIG_EFI_VARS_MODULE static int __init efi_load_efivars(void) { diff --git a/include/linux/efi.h b/include/linux/efi.h index 0238d61..5ffe511 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -875,6 +875,8 @@ static inline efi_status_t efi_query_variable_store(u32 attributes, unsigned lon #endif extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr); extern int efi_config_init(efi_config_table_type_t *arch_tables); +extern int efi_config_parse_tables(void *config_tables, int count, int sz, + efi_config_table_type_t *arch_tables); extern u64 efi_get_iobase (void); extern u32 efi_mem_type (unsigned long phys_addr); extern u64 efi_mem_attributes (unsigned long phys_addr); -- cgit v0.10.2 From cf2b0f102cdf912eedb87b10271fa0ad582cf2c1 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 17 Nov 2014 13:46:44 +0100 Subject: efi: efistub: allow allocation alignment larger than EFI_PAGE_SIZE On systems with 64 KB pages, it is preferable for UEFI memory map entries to be 64 KB aligned multiples of 64 KB, because it relieves us of having to deal with the residues. So, if EFI_ALLOC_ALIGN is #define'd by the platform, use it to round up all memory allocations made. Acked-by: Matt Fleming Acked-by: Borislav Petkov Tested-by: Leif Lindholm Signed-off-by: Ard Biesheuvel diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index a920fec..e766df6 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -32,6 +32,15 @@ static unsigned long __chunk_size = EFI_READ_CHUNK_SIZE; +/* + * Allow the platform to override the allocation granularity: this allows + * systems that have the capability to run with a larger page size to deal + * with the allocations for initrd and fdt more efficiently. + */ +#ifndef EFI_ALLOC_ALIGN +#define EFI_ALLOC_ALIGN EFI_PAGE_SIZE +#endif + struct file_info { efi_file_handle_t *handle; u64 size; @@ -150,10 +159,10 @@ efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg, * a specific address. We are doing page-based allocations, * so we must be aligned to a page. */ - if (align < EFI_PAGE_SIZE) - align = EFI_PAGE_SIZE; + if (align < EFI_ALLOC_ALIGN) + align = EFI_ALLOC_ALIGN; - nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; + nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; again: for (i = 0; i < map_size / desc_size; i++) { efi_memory_desc_t *desc; @@ -235,10 +244,10 @@ efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, * a specific address. We are doing page-based allocations, * so we must be aligned to a page. */ - if (align < EFI_PAGE_SIZE) - align = EFI_PAGE_SIZE; + if (align < EFI_ALLOC_ALIGN) + align = EFI_ALLOC_ALIGN; - nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; + nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; for (i = 0; i < map_size / desc_size; i++) { efi_memory_desc_t *desc; unsigned long m = (unsigned long)map; @@ -292,7 +301,7 @@ void efi_free(efi_system_table_t *sys_table_arg, unsigned long size, if (!size) return; - nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; + nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; efi_call_early(free_pages, addr, nr_pages); } @@ -561,7 +570,7 @@ efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg, * to the preferred address. If that fails, allocate as low * as possible while respecting the required alignment. */ - nr_pages = round_up(alloc_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; + nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, nr_pages, &efi_addr); -- cgit v0.10.2 From 1bd0abb0c924a8b28c6466cdd6bb34ea053541dc Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 17 Nov 2014 13:50:21 +0100 Subject: arm64/efi: set EFI_ALLOC_ALIGN to 64 KB Set EFI_ALLOC_ALIGN to 64 KB so that all allocations done by the stub are naturally compatible with a 64 KB granule kernel. Acked-by: Leif Lindholm Tested-by: Leif Lindholm Signed-off-by: Ard Biesheuvel diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h index a34fd3b..7129125 100644 --- a/arch/arm64/include/asm/efi.h +++ b/arch/arm64/include/asm/efi.h @@ -44,4 +44,6 @@ extern void efi_idmap_init(void); #define efi_call_early(f, ...) sys_table_arg->boottime->f(__VA_ARGS__) +#define EFI_ALLOC_ALIGN SZ_64K + #endif /* _ASM_EFI_H */ -- cgit v0.10.2 From 63a3451641ec2e129dfe80044e3c96bc9f0bb690 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 19 Dec 2014 19:33:25 +0200 Subject: drm/i915: gen9: fix RPS interrupt routing to CPU vs. GT GEN8+ HW has the option to route PM interrupts to either the CPU or to GT. For GEN8 this was already set correctly to routing to CPU, but not for GEN9, so fix this. Note that when disabling RPS interrupts this was set already correctly, though in that case it didn't matter much except for the possibility of spurious interrupts. Signed-off-by: Imre Deak Reviewed-by: Daniel Vetter Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 964b28e..78e308b 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4369,7 +4369,7 @@ static u32 gen6_rps_pm_mask(struct drm_i915_private *dev_priv, u8 val) if (INTEL_INFO(dev_priv->dev)->gen <= 7 && !IS_HASWELL(dev_priv->dev)) mask |= GEN6_PM_RP_UP_EI_EXPIRED; - if (IS_GEN8(dev_priv->dev)) + if (INTEL_INFO(dev_priv)->gen >= 8) mask |= GEN8_PMINTR_REDIRECT_TO_NON_DISP; return ~mask; -- cgit v0.10.2 From 59d02a1f45beb1b6f4ef83a47feb264cb3577725 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 19 Dec 2014 19:33:26 +0200 Subject: drm/i915: fix HW lockup due to missing RPS IRQ workaround on GEN6 In commit dbea3cea69508e9d548ed4a6be13de35492e5d15 Author: Imre Deak Date: Mon Dec 15 18:59:28 2014 +0200 drm/i915: sanitize RPS resetting during GPU reset we disable RPS interrupts during GPU resetting, but don't apply the necessary GEN6 HW workaround. This leads to a HW lockup during a subsequent "looping batchbuffer" workload. This is triggered by the testcase that submits exactly this kind of workload after a simulated GPU reset. I'm not sure how likely the bug would have triggered otherwise, since we would have applied the workaround anyway shortly after the GPU reset, when enabling GT powersaving from the deferred work. This may also fix unrelated issues, since during driver loading / suspending we also disable RPS interrupts and so we also had a short window during the rest of the loading / resuming where a similar workload could run without the workaround applied. v2: - separate the fix to route RPS interrupts to the CPU on GEN9 too to a separate patch (Daniel) Bisected-by: Ander Conselvan de Oliveira Testcase: igt/gem_reset_stats/ban-ctx-render Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=87429 Signed-off-by: Imre Deak Reviewed-by: Daniel Vetter Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index d0d3dfb..ba86dc3 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -292,6 +292,21 @@ void gen6_enable_rps_interrupts(struct drm_device *dev) spin_unlock_irq(&dev_priv->irq_lock); } +u32 gen6_sanitize_rps_pm_mask(struct drm_i915_private *dev_priv, u32 mask) +{ + /* + * IVB and SNB hard hangs on looping batchbuffer + * if GEN6_PM_UP_EI_EXPIRED is masked. + */ + if (INTEL_INFO(dev_priv)->gen <= 7 && !IS_HASWELL(dev_priv)) + mask &= ~GEN6_PM_RP_UP_EI_EXPIRED; + + if (INTEL_INFO(dev_priv)->gen >= 8) + mask &= ~GEN8_PMINTR_REDIRECT_TO_NON_DISP; + + return mask; +} + void gen6_disable_rps_interrupts(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -304,8 +319,7 @@ void gen6_disable_rps_interrupts(struct drm_device *dev) spin_lock_irq(&dev_priv->irq_lock); - I915_WRITE(GEN6_PMINTRMSK, INTEL_INFO(dev_priv)->gen >= 8 ? - ~GEN8_PMINTR_REDIRECT_TO_NON_DISP : ~0); + I915_WRITE(GEN6_PMINTRMSK, gen6_sanitize_rps_pm_mask(dev_priv, ~0)); __gen6_disable_pm_irq(dev_priv, dev_priv->pm_rps_events); I915_WRITE(gen6_pm_ier(dev_priv), I915_READ(gen6_pm_ier(dev_priv)) & diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 25fdbb1..3b40a17 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -794,6 +794,7 @@ void gen6_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask); void gen6_reset_rps_interrupts(struct drm_device *dev); void gen6_enable_rps_interrupts(struct drm_device *dev); void gen6_disable_rps_interrupts(struct drm_device *dev); +u32 gen6_sanitize_rps_pm_mask(struct drm_i915_private *dev_priv, u32 mask); void intel_runtime_pm_disable_interrupts(struct drm_i915_private *dev_priv); void intel_runtime_pm_enable_interrupts(struct drm_i915_private *dev_priv); static inline bool intel_irqs_enabled(struct drm_i915_private *dev_priv) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 78e308b..3801ff5 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4363,16 +4363,7 @@ static u32 gen6_rps_pm_mask(struct drm_i915_private *dev_priv, u8 val) mask |= dev_priv->pm_rps_events & (GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_UP_EI_EXPIRED); mask &= dev_priv->pm_rps_events; - /* IVB and SNB hard hangs on looping batchbuffer - * if GEN6_PM_UP_EI_EXPIRED is masked. - */ - if (INTEL_INFO(dev_priv->dev)->gen <= 7 && !IS_HASWELL(dev_priv->dev)) - mask |= GEN6_PM_RP_UP_EI_EXPIRED; - - if (INTEL_INFO(dev_priv)->gen >= 8) - mask |= GEN8_PMINTR_REDIRECT_TO_NON_DISP; - - return ~mask; + return gen6_sanitize_rps_pm_mask(dev_priv, ~mask); } /* gen6_set_rps is called to update the frequency request, but should also be -- cgit v0.10.2 From f24eeb191229b040deb3e813913e06a4316c6d3f Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 19 Dec 2014 19:33:27 +0200 Subject: drm/i915: vlv: sanitize RPS interrupt mask during GPU idling We apply the RPS interrupt workaround on VLV everywhere except when writing the mask directly during idling the GPU. For consistency do this also there. While at it also extend the code comment about affected platforms. I couldn't reproduce the issue on VLV fixed by this workaround, by removing the workaround from everywhere, while it's 100% reproducible on SNB using igt/gem_reset_stats/ban-ctx-render. So also add a note that it hasn't been verified if the workaround really applies to VLV/CHV. Suggested-by: Daniel Vetter Signed-off-by: Imre Deak Reviewed-by: Daniel Vetter Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index ba86dc3..b051a23 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -295,8 +295,10 @@ void gen6_enable_rps_interrupts(struct drm_device *dev) u32 gen6_sanitize_rps_pm_mask(struct drm_i915_private *dev_priv, u32 mask) { /* - * IVB and SNB hard hangs on looping batchbuffer + * SNB,IVB can while VLV,CHV may hard hang on looping batchbuffer * if GEN6_PM_UP_EI_EXPIRED is masked. + * + * TODO: verify if this can be reproduced on VLV,CHV. */ if (INTEL_INFO(dev_priv)->gen <= 7 && !IS_HASWELL(dev_priv)) mask &= ~GEN6_PM_RP_UP_EI_EXPIRED; diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 3801ff5..bf814a6 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4432,7 +4432,8 @@ static void vlv_set_rps_idle(struct drm_i915_private *dev_priv) return; /* Mask turbo interrupt so that they will not come in between */ - I915_WRITE(GEN6_PMINTRMSK, 0xffffffff); + I915_WRITE(GEN6_PMINTRMSK, + gen6_sanitize_rps_pm_mask(dev_priv, ~0)); vlv_force_gfx_clock(dev_priv, true); -- cgit v0.10.2 From 48bf5b2d00bfeb681f6500c626189c7cd2c964d2 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 27 Dec 2014 09:48:28 +0000 Subject: drm/i915: Ban Haswell from using RCS flips Like Ivybridge, we have reports that we get random hangs when flipping with multiple pipes. Extend commit 2a92d5bca1999b69c78f3c3e97b5484985b094b9 Author: Chris Wilson Date: Tue Jul 8 10:40:29 2014 +0100 drm/i915: Disable RCS flips on Ivybridge to also apply to Haswell. Reported-and-tested-by: Scott Tsai Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=87759 Signed-off-by: Chris Wilson Reviewed-by: Daniel Vetter Cc: stable@vger.kernel.org # 2a92d5bca199 drm/i915: Disable RCS flips on Ivybridge Cc: stable@vger.kernel.org Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e2af138..e7a16f1 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9815,7 +9815,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, if (obj->tiling_mode != work->old_fb_obj->tiling_mode) /* vlv: DISPLAY_FLIP fails to change tiling */ ring = NULL; - } else if (IS_IVYBRIDGE(dev)) { + } else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) { ring = &dev_priv->ring[BCS]; } else if (INTEL_INFO(dev)->gen >= 7) { ring = obj->ring; -- cgit v0.10.2 From 226e5ae9e5f9108beb0bde4ac69f68fe6210fed9 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 2 Jan 2015 09:47:10 +0000 Subject: drm/i915: Fix mutex->owner inspection race under DEBUG_MUTEXES If CONFIG_DEBUG_MUTEXES is set, the mutex->owner field is only cleared if the mutex debugging is enabled which introduces a race in our mutex_is_locked_by() - i.e. we may inspect the old owner value before it is acquired by the new task. This is the root cause of this error: diff --git a/kernel/locking/mutex-debug.c b/kernel/locking/mutex-debug.c index 5cf6731..3ef3736 100644 --- a/kernel/locking/mutex-debug.c +++ b/kernel/locking/mutex-debug.c @@ -80,13 +80,13 @@ void debug_mutex_unlock(struct mutex *lock) DEBUG_LOCKS_WARN_ON(lock->owner != current); DEBUG_LOCKS_WARN_ON(!lock->wait_list.prev && !lock->wait_list.next); - mutex_clear_owner(lock); } /* * __mutex_slowpath_needs_to_unlock() is explicitly 0 for debug * mutexes so that we can do it here after we've verified state. */ + mutex_clear_owner(lock); atomic_set(&lock->count, 1); } Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=87955 Signed-off-by: Chris Wilson Cc: stable@vger.kernel.org Reviewed-by: Daniel Vetter Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c11603b..76354d3 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -5155,7 +5155,7 @@ static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task) if (!mutex_is_locked(mutex)) return false; -#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_MUTEXES) +#if defined(CONFIG_SMP) && !defined(CONFIG_DEBUG_MUTEXES) return mutex->owner == task; #else /* Since UP may be pre-empted, we cannot assume that we own the lock */ -- cgit v0.10.2 From b8aed6ea39a70a9b9b8de9df8655ac36719b4d3b Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Thu, 8 Jan 2015 14:51:36 -0800 Subject: HID: rmi: Use hid_report_len to compute the size of reports Now that hid_report_len is in hid.h we can use this function instead of duplicating the code which computes it. Signed-off-by: Andrew Duggan Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 018f80f..6270d2c 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -1007,7 +1007,7 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) goto start; } - data->input_report_size = (input_report->size >> 3) + 1 /* report id */; + data->input_report_size = hid_report_len(input_report); if (!rmi_check_valid_report_id(hdev, HID_OUTPUT_REPORT, RMI_WRITE_REPORT_ID, &output_report)) { @@ -1016,8 +1016,7 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) goto start; } - data->output_report_size = (output_report->size >> 3) - + 1 /* report id */; + data->output_report_size = hid_report_len(output_report); data->device_flags |= RMI_DEVICE; alloc_size = data->output_report_size + data->input_report_size; -- cgit v0.10.2 From 79364d87af239e2029aeba3b82bd79c355b9bb86 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Thu, 8 Jan 2015 14:51:34 -0800 Subject: HID: rmi: Support touchpads with external buttons The external buttons on HID touchpads are connected as pass through devices and button events are not reported in the rmi registers. As a result on these devices we need to allow the HID generic desktop button events to be processed by hid-input. Unfortunately, there is no way to query the touchpad to determine that it has pass through buttons so the RMI_DEVICE_HAS_PHYS_BUTTONS should be set manually when adding the device to rmi_id[]. Signed-off-by: Andrew Duggan Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 6270d2c..4bf43c8 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -35,6 +35,7 @@ /* device flags */ #define RMI_DEVICE BIT(0) +#define RMI_DEVICE_HAS_PHYS_BUTTONS BIT(1) enum rmi_mode_type { RMI_MODE_OFF = 0, @@ -472,6 +473,15 @@ static int rmi_event(struct hid_device *hdev, struct hid_field *field, if ((data->device_flags & RMI_DEVICE) && (field->application == HID_GD_POINTER || field->application == HID_GD_MOUSE)) { + if (data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS) { + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) + return 0; + + if ((usage->hid == HID_GD_X || usage->hid == HID_GD_Y) + && !value) + return 1; + } + rmi_schedule_reset(hdev); return 1; } @@ -942,8 +952,13 @@ static int rmi_input_mapping(struct hid_device *hdev, * we want to make HID ignore the advertised HID collection * for RMI deivces */ - if (data->device_flags & RMI_DEVICE) + if (data->device_flags & RMI_DEVICE) { + if ((data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS) && + ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON)) + return 0; + return -1; + } return 0; } @@ -991,6 +1006,9 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) return ret; } + if (id->driver_data) + data->device_flags = id->driver_data; + /* * Check for the RMI specific report ids. If they are misisng * simply return and let the events be processed by hid-input -- cgit v0.10.2 From e9287099ba6539bccb20cd791269186f3ae28b85 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Thu, 8 Jan 2015 14:51:35 -0800 Subject: HID: rmi: Add support for the touchpad in the Razer Blade 14 laptop Have hid-rmi handle all of the Razer Blade HID devices that are part of the composite USB device. This will allow hid-rmi to operate the touchpad in rmi mode while passing events from the other devices to hid-input. Signed-off-by: Andrew Duggan Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 81665b4..ef718f9 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1982,6 +1982,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLADE_14) }, { } }; diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 7460f34..7d0912d 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -767,6 +767,9 @@ #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001 0x3001 #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008 0x3008 +#define USB_VENDOR_ID_RAZER 0x1532 +#define USB_DEVICE_ID_RAZER_BLADE_14 0x011D + #define USB_VENDOR_ID_REALTEK 0x0bda #define USB_DEVICE_ID_REALTEK_READER 0x0152 diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 4bf43c8..49d4fe4 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -1082,6 +1082,8 @@ static void rmi_remove(struct hid_device *hdev) } static const struct hid_device_id rmi_id[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLADE_14), + .driver_data = RMI_DEVICE_HAS_PHYS_BUTTONS }, { HID_DEVICE(HID_BUS_ANY, HID_GROUP_RMI, HID_ANY_ID, HID_ANY_ID) }, { } }; -- cgit v0.10.2 From 1904a853fae40ee61bed7c231fc5bd2158984441 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 11 Jan 2015 13:50:44 -0800 Subject: Bluetooth: Add opcode parameter to hci_req_complete_t callback When hci_req_run() calls its provided complete function and one of the HCI commands in the sequence fails, then provide the opcode of failing command. In case of success HCI_OP_NOP is provided since all commands completed. This patch fixes the prototype of hci_req_complete_t and all its users. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 58695ff..e00455a 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -273,7 +273,7 @@ struct l2cap_ctrl { struct hci_dev; -typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status); +typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status, u16 opcode); struct hci_req_ctrl { bool start; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 75240aa..2e724e0 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -633,7 +633,7 @@ void hci_le_conn_failed(struct hci_conn *conn, u8 status) mgmt_reenable_advertising(hdev); } -static void create_le_conn_complete(struct hci_dev *hdev, u8 status) +static void create_le_conn_complete(struct hci_dev *hdev, u8 status, u16 opcode) { struct hci_conn *conn; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index bc5486e..ba0d1fd 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -141,7 +141,7 @@ static const struct file_operations dut_mode_fops = { /* ---- HCI requests ---- */ -static void hci_req_sync_complete(struct hci_dev *hdev, u8 result) +static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode) { BT_DBG("%s result 0x%2.2x", hdev->name, result); @@ -2754,7 +2754,7 @@ void hci_conn_params_clear_all(struct hci_dev *hdev) BT_DBG("All LE connection parameters were removed"); } -static void inquiry_complete(struct hci_dev *hdev, u8 status) +static void inquiry_complete(struct hci_dev *hdev, u8 status, u16 opcode) { if (status) { BT_ERR("Failed to start inquiry: status %d", status); @@ -2766,7 +2766,8 @@ static void inquiry_complete(struct hci_dev *hdev, u8 status) } } -static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status) +static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status, + u16 opcode) { /* General inquiry access code (GIAC) */ u8 lap[3] = { 0x33, 0x8b, 0x9e }; @@ -4159,7 +4160,7 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status) call_complete: if (req_complete) - req_complete(hdev, status); + req_complete(hdev, status, status ? opcode : HCI_OP_NOP); } static void hci_rx_work(struct work_struct *work) diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index 324c641..b59f92c 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -533,7 +533,8 @@ void __hci_update_background_scan(struct hci_request *req) } } -static void update_background_scan_complete(struct hci_dev *hdev, u8 status) +static void update_background_scan_complete(struct hci_dev *hdev, u8 status, + u16 opcode) { if (status) BT_DBG("HCI request failed to update background scanning: " diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 6b3f553..e531da8 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1251,7 +1251,7 @@ static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) sizeof(settings)); } -static void clean_up_hci_complete(struct hci_dev *hdev, u8 status) +static void clean_up_hci_complete(struct hci_dev *hdev, u8 status, u16 opcode) { BT_DBG("%s status 0x%02x", hdev->name, status); @@ -1518,7 +1518,8 @@ static u8 mgmt_le_support(struct hci_dev *hdev) return MGMT_STATUS_SUCCESS; } -static void set_discoverable_complete(struct hci_dev *hdev, u8 status) +static void set_discoverable_complete(struct hci_dev *hdev, u8 status, + u16 opcode) { struct pending_cmd *cmd; struct mgmt_mode *cp; @@ -1777,7 +1778,8 @@ static void write_fast_connectable(struct hci_request *req, bool enable) hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type); } -static void set_connectable_complete(struct hci_dev *hdev, u8 status) +static void set_connectable_complete(struct hci_dev *hdev, u8 status, + u16 opcode) { struct pending_cmd *cmd; struct mgmt_mode *cp; @@ -2195,7 +2197,7 @@ unlock: return err; } -static void le_enable_complete(struct hci_dev *hdev, u8 status) +static void le_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode) { struct cmd_lookup match = { NULL, hdev }; @@ -2385,7 +2387,7 @@ unlock: hci_dev_unlock(hdev); } -static void add_uuid_complete(struct hci_dev *hdev, u8 status) +static void add_uuid_complete(struct hci_dev *hdev, u8 status, u16 opcode) { BT_DBG("status 0x%02x", status); @@ -2464,7 +2466,7 @@ static bool enable_service_cache(struct hci_dev *hdev) return false; } -static void remove_uuid_complete(struct hci_dev *hdev, u8 status) +static void remove_uuid_complete(struct hci_dev *hdev, u8 status, u16 opcode) { BT_DBG("status 0x%02x", status); @@ -2549,7 +2551,7 @@ unlock: return err; } -static void set_class_complete(struct hci_dev *hdev, u8 status) +static void set_class_complete(struct hci_dev *hdev, u8 status, u16 opcode) { BT_DBG("status 0x%02x", status); @@ -3483,7 +3485,7 @@ static void update_name(struct hci_request *req) hci_req_add(req, HCI_OP_WRITE_LOCAL_NAME, sizeof(cp), &cp); } -static void set_name_complete(struct hci_dev *hdev, u8 status) +static void set_name_complete(struct hci_dev *hdev, u8 status, u16 opcode) { struct mgmt_cp_set_local_name *cp; struct pending_cmd *cmd; @@ -3834,7 +3836,8 @@ static bool trigger_discovery(struct hci_request *req, u8 *status) return true; } -static void start_discovery_complete(struct hci_dev *hdev, u8 status) +static void start_discovery_complete(struct hci_dev *hdev, u8 status, + u16 opcode) { struct pending_cmd *cmd; unsigned long timeout; @@ -4063,7 +4066,7 @@ failed: return err; } -static void stop_discovery_complete(struct hci_dev *hdev, u8 status) +static void stop_discovery_complete(struct hci_dev *hdev, u8 status, u16 opcode) { struct pending_cmd *cmd; @@ -4289,7 +4292,8 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data, return err; } -static void set_advertising_complete(struct hci_dev *hdev, u8 status) +static void set_advertising_complete(struct hci_dev *hdev, u8 status, + u16 opcode) { struct cmd_lookup match = { NULL, hdev }; @@ -4496,7 +4500,8 @@ static int set_scan_params(struct sock *sk, struct hci_dev *hdev, return err; } -static void fast_connectable_complete(struct hci_dev *hdev, u8 status) +static void fast_connectable_complete(struct hci_dev *hdev, u8 status, + u16 opcode) { struct pending_cmd *cmd; @@ -4594,7 +4599,7 @@ unlock: return err; } -static void set_bredr_complete(struct hci_dev *hdev, u8 status) +static void set_bredr_complete(struct hci_dev *hdev, u8 status, u16 opcode) { struct pending_cmd *cmd; @@ -5119,7 +5124,8 @@ static int conn_info_cmd_complete(struct pending_cmd *cmd, u8 status) return err; } -static void conn_info_refresh_complete(struct hci_dev *hdev, u8 hci_status) +static void conn_info_refresh_complete(struct hci_dev *hdev, u8 hci_status, + u16 opcode) { struct hci_cp_read_rssi *cp; struct pending_cmd *cmd; @@ -5326,7 +5332,7 @@ complete: return err; } -static void get_clock_info_complete(struct hci_dev *hdev, u8 status) +static void get_clock_info_complete(struct hci_dev *hdev, u8 status, u16 opcode) { struct hci_cp_read_clock *hci_cp; struct pending_cmd *cmd; @@ -5504,7 +5510,7 @@ static void device_added(struct sock *sk, struct hci_dev *hdev, mgmt_event(MGMT_EV_DEVICE_ADDED, hdev, &ev, sizeof(ev), sk); } -static void add_device_complete(struct hci_dev *hdev, u8 status) +static void add_device_complete(struct hci_dev *hdev, u8 status, u16 opcode) { struct pending_cmd *cmd; @@ -5627,7 +5633,7 @@ static void device_removed(struct sock *sk, struct hci_dev *hdev, mgmt_event(MGMT_EV_DEVICE_REMOVED, hdev, &ev, sizeof(ev), sk); } -static void remove_device_complete(struct hci_dev *hdev, u8 status) +static void remove_device_complete(struct hci_dev *hdev, u8 status, u16 opcode) { struct pending_cmd *cmd; @@ -6205,7 +6211,7 @@ static void restart_le_actions(struct hci_request *req) __hci_update_background_scan(req); } -static void powered_complete(struct hci_dev *hdev, u8 status) +static void powered_complete(struct hci_dev *hdev, u8 status, u16 opcode) { struct cmd_lookup match = { NULL, hdev }; @@ -7316,7 +7322,7 @@ void mgmt_discovering(struct hci_dev *hdev, u8 discovering) mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL); } -static void adv_enable_complete(struct hci_dev *hdev, u8 status) +static void adv_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode) { BT_DBG("%s status %u", hdev->name, status); } -- cgit v0.10.2 From 373a5356dfb75ea7140f8d37cf26eb6a62910617 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Fri, 9 Jan 2015 11:04:50 -0800 Subject: HID: wacom: process invalid Cintiq and Intuos data in wacom_intuos_inout() Users may use unsupported tools on Cintiq or Intuos. When invalid tools or data are detected, they should be ignored. That is, no event from those tools should be reported. Consolidating that code in wacom_intuos_inout simplifies the logic and make it easier for future code change. Signed-off-by: Ping Cheng Signed-off-by: Jiri Kosina diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 596a6fb5..5276689 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -534,9 +534,24 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) return 1; } + /* + * don't report events for invalid data + */ /* older I4 styli don't work with new Cintiqs */ - if (!((wacom->id[idx] >> 20) & 0x01) && - (features->type == WACOM_21UX2)) + if ((!((wacom->id[idx] >> 20) & 0x01) && + (features->type == WACOM_21UX2)) || + /* Only large Intuos support Lense Cursor */ + (wacom->tool[idx] == BTN_TOOL_LENS && + (features->type == INTUOS3 || + features->type == INTUOS3S || + features->type == INTUOS4 || + features->type == INTUOS4S || + features->type == INTUOS5 || + features->type == INTUOS5S || + features->type == INTUOSPM || + features->type == INTUOSPS)) || + /* Cintiq doesn't send data when RDY bit isn't set */ + (features->type == CINTIQ && !(data[1] & 0x40))) return 1; /* Range Report */ @@ -553,6 +568,10 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) if (features->quirks & WACOM_QUIRK_MULTI_INPUT) wacom->shared->stylus_in_proximity = false; + /* don't report exit if we don't know the ID */ + if (!wacom->id[idx]) + return 1; + /* * Reset all states otherwise we lose the initial states * when in-prox next time @@ -585,6 +604,11 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) wacom->id[idx] = 0; return 2; } + + /* don't report other events if we don't know the ID */ + if (!wacom->id[idx]) + return 1; + return 0; } @@ -842,28 +866,6 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) if (result) return result - 1; - /* don't proceed if we don't know the ID */ - if (!wacom->id[idx]) - return 0; - - /* Only large Intuos support Lense Cursor */ - if (wacom->tool[idx] == BTN_TOOL_LENS && - (features->type == INTUOS3 || - features->type == INTUOS3S || - features->type == INTUOS4 || - features->type == INTUOS4S || - features->type == INTUOS5 || - features->type == INTUOS5S || - features->type == INTUOSPM || - features->type == INTUOSPS)) { - - return 0; - } - - /* Cintiq doesn't send data when RDY bit isn't set */ - if (features->type == CINTIQ && !(data[1] & 0x40)) - return 0; - if (features->type >= INTUOS3S) { input_report_abs(input, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1)); input_report_abs(input, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1)); -- cgit v0.10.2 From b3bd7ef397a6031f5398c76a9a0b0695a38f6587 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Fri, 9 Jan 2015 11:05:13 -0800 Subject: HID: wacom: peport In Range event according to the spec Some Cintiq and Intuos tablets report In Range event. This event is sent before valid data is reported when tool enters proximity; or before out of proximity event is reported when tool exits. While entering proximity, In Range means a pen is detected. This information can be used for palm/touch rejection on both pen and touch enabled devices. While exiting, it means the tool has reached its maximum detectable distance. Signed-off-by: Ping Cheng Signed-off-by: Jiri Kosina diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 5276689..a4ba8ca 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -443,9 +443,6 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) /* Enter report */ if ((data[1] & 0xfc) == 0xc0) { - if (features->quirks & WACOM_QUIRK_MULTI_INPUT) - wacom->shared->stylus_in_proximity = true; - /* serial number of the tool */ wacom->serial[idx] = ((data[3] & 0x0f) << 28) + (data[4] << 20) + (data[5] << 12) + @@ -554,19 +551,22 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) (features->type == CINTIQ && !(data[1] & 0x40))) return 1; - /* Range Report */ - if ((data[1] & 0xfe) == 0x20) { + if (features->quirks & WACOM_QUIRK_MULTI_INPUT) + wacom->shared->stylus_in_proximity = true; + + /* in Range while exiting */ + if (((data[1] & 0xfe) == 0x20) && wacom->reporting_data) { input_report_key(input, BTN_TOUCH, 0); input_report_abs(input, ABS_PRESSURE, 0); input_report_abs(input, ABS_DISTANCE, wacom->features.distance_max); - if (features->quirks & WACOM_QUIRK_MULTI_INPUT) - wacom->shared->stylus_in_proximity = true; + return 2; } /* Exit report */ if ((data[1] & 0xfe) == 0x80) { if (features->quirks & WACOM_QUIRK_MULTI_INPUT) wacom->shared->stylus_in_proximity = false; + wacom->reporting_data = false; /* don't report exit if we don't know the ID */ if (!wacom->id[idx]) @@ -952,6 +952,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) input_report_abs(input, ABS_MISC, wacom->id[idx]); /* report tool id */ input_report_key(input, wacom->tool[idx], 1); input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]); + wacom->reporting_data = true; return 1; } diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 7afd929..72e78cc 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -189,6 +189,7 @@ struct wacom_wac { int tool[2]; int id[2]; __u32 serial[2]; + bool reporting_data; struct wacom_features features; struct wacom_shared *shared; struct input_dev *input; -- cgit v0.10.2 From b0a8e282b54d11859a13bebd4c056dcfb577f8b7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 11 Jan 2015 15:18:17 -0800 Subject: Bluetooth: Add BUILD_BUG_ON for size of struct sockaddr_hci This adds an extra check for ensuring that the size of sockaddr_hci does not grow larger than sockaddr. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 2c245fd..e176a98 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -1230,6 +1230,8 @@ int __init hci_sock_init(void) { int err; + BUILD_BUG_ON(sizeof(struct sockaddr_hci) > sizeof(struct sockaddr)); + err = proto_register(&hci_sk_proto, 0); if (err < 0) return err; -- cgit v0.10.2 From dd6255588a56e755380b66275ff4f35a8243b64a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 11 Jan 2015 15:18:19 -0800 Subject: Bluetooth: Add BUILD_BUG_ON for size of struct sockaddr_l2 This adds an extra check for ensuring that the size of sockaddr_l2 does not grow larger than sockaddr. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index f65caf4..20206cd 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1614,6 +1614,8 @@ int __init l2cap_init_sockets(void) { int err; + BUILD_BUG_ON(sizeof(struct sockaddr_l2) > sizeof(struct sockaddr)); + err = proto_register(&l2cap_proto, 0); if (err < 0) return err; -- cgit v0.10.2 From 74b3fb8d0daf150eeb66665897601fb10c024687 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 11 Jan 2015 15:18:20 -0800 Subject: Bluetooth: Add BUILD_BUG_ON for size of struct sockaddr_rc This adds an extra check for ensuring that the size of sockaddr_rc does not grow larger than sockaddr. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 2348176..d8a9575 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -1058,6 +1058,8 @@ int __init rfcomm_init_sockets(void) { int err; + BUILD_BUG_ON(sizeof(struct sockaddr_rc) > sizeof(struct sockaddr)); + err = proto_register(&rfcomm_proto, 0); if (err < 0) return err; -- cgit v0.10.2 From 15762fa772bf91687e2f2a6afc6df975ee2a6f70 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 11 Jan 2015 15:21:06 -0800 Subject: Bluetooth: Add BUILD_BUG_ON for size of struct sockaddr_sco This adds an extra check for ensuring that the size of sockaddr_sco does not grow larger than sockaddr. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 30e5ea3..07ec7d2 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -1184,6 +1184,8 @@ int __init sco_init(void) { int err; + BUILD_BUG_ON(sizeof(struct sockaddr_sco) > sizeof(struct sockaddr)); + err = proto_register(&sco_proto, 0); if (err < 0) return err; -- cgit v0.10.2 From 2b531294b02454b6aa189f99ea8982bdd0d7b3bb Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 11 Jan 2015 19:33:31 -0800 Subject: Bluetooth: Simplify packet copy in hci_send_to_monitor function Within the monitor functionality, the global atomic variable called monitor_promisc ensures that no memory allocation happend when there is actually no client listening. This means it is safe to just create a copy of the skb since it is guaranteed that at least one client exists. No extra checks needed. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index e176a98..026e84a 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -221,6 +221,7 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) { struct sock *sk; struct sk_buff *skb_copy = NULL; + struct hci_mon_hdr *hdr; __le16 opcode; if (!atomic_read(&monitor_promisc)) @@ -251,6 +252,17 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) return; } + /* Create a private copy with headroom */ + skb_copy = __pskb_copy_fclone(skb, HCI_MON_HDR_SIZE, GFP_ATOMIC, true); + if (!skb_copy) + return; + + /* Put header before the data */ + hdr = (void *) skb_push(skb_copy, HCI_MON_HDR_SIZE); + hdr->opcode = opcode; + hdr->index = cpu_to_le16(hdev->id); + hdr->len = cpu_to_le16(skb->len); + read_lock(&hci_sk_list.lock); sk_for_each(sk, &hci_sk_list.head) { @@ -262,22 +274,6 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR) continue; - if (!skb_copy) { - struct hci_mon_hdr *hdr; - - /* Create a private copy with headroom */ - skb_copy = __pskb_copy_fclone(skb, HCI_MON_HDR_SIZE, - GFP_ATOMIC, true); - if (!skb_copy) - continue; - - /* Put header before the data */ - hdr = (void *) skb_push(skb_copy, HCI_MON_HDR_SIZE); - hdr->opcode = opcode; - hdr->index = cpu_to_le16(hdev->id); - hdr->len = cpu_to_le16(skb->len); - } - nskb = skb_clone(skb_copy, GFP_ATOMIC); if (!nskb) continue; -- cgit v0.10.2 From d7f72f61955ded6ae5f0381dcde1fcadce8833bb Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 11 Jan 2015 19:33:32 -0800 Subject: Bluetooth: Create generic queue_monitor_skb helper function The hci_send_to_monitor function contains generic code for queueing the packet into the receive queue of every monitor client. To avoid code duplication, create a generic queue_monitor_skb function to interate over all monitor sockets. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 026e84a..1987ea1 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -216,10 +216,37 @@ void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk) read_unlock(&hci_sk_list.lock); } +static void queue_monitor_skb(struct sk_buff *skb) +{ + struct sock *sk; + + BT_DBG("len %d", skb->len); + + read_lock(&hci_sk_list.lock); + + sk_for_each(sk, &hci_sk_list.head) { + struct sk_buff *nskb; + + if (sk->sk_state != BT_BOUND) + continue; + + if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR) + continue; + + nskb = skb_clone(skb, GFP_ATOMIC); + if (!nskb) + continue; + + if (sock_queue_rcv_skb(sk, nskb)) + kfree_skb(nskb); + } + + read_unlock(&hci_sk_list.lock); +} + /* Send frame to monitor socket */ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) { - struct sock *sk; struct sk_buff *skb_copy = NULL; struct hci_mon_hdr *hdr; __le16 opcode; @@ -263,27 +290,7 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) hdr->index = cpu_to_le16(hdev->id); hdr->len = cpu_to_le16(skb->len); - read_lock(&hci_sk_list.lock); - - sk_for_each(sk, &hci_sk_list.head) { - struct sk_buff *nskb; - - if (sk->sk_state != BT_BOUND) - continue; - - if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR) - continue; - - nskb = skb_clone(skb_copy, GFP_ATOMIC); - if (!nskb) - continue; - - if (sock_queue_rcv_skb(sk, nskb)) - kfree_skb(nskb); - } - - read_unlock(&hci_sk_list.lock); - + queue_monitor_skb(skb_copy); kfree_skb(skb_copy); } -- cgit v0.10.2 From 41e91e71f61ead454c72b68d7c5cf13a2651e992 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 11 Jan 2015 19:33:33 -0800 Subject: Bluetooth: Replace send_monitor_event with queue_monitor_skb The send_monitor_event function is essentially the same as the newly introduced queue_monitor_skb. So instead of having duplicated code, replace send_monitor_event with queue_monitor_skb. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 1987ea1..1d65c5b 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -294,34 +294,6 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) kfree_skb(skb_copy); } -static void send_monitor_event(struct sk_buff *skb) -{ - struct sock *sk; - - BT_DBG("len %d", skb->len); - - read_lock(&hci_sk_list.lock); - - sk_for_each(sk, &hci_sk_list.head) { - struct sk_buff *nskb; - - if (sk->sk_state != BT_BOUND) - continue; - - if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR) - continue; - - nskb = skb_clone(skb, GFP_ATOMIC); - if (!nskb) - continue; - - if (sock_queue_rcv_skb(sk, nskb)) - kfree_skb(nskb); - } - - read_unlock(&hci_sk_list.lock); -} - static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event) { struct hci_mon_hdr *hdr; @@ -425,7 +397,7 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event) skb = create_monitor_event(hdev, event); if (skb) { - send_monitor_event(skb); + queue_monitor_skb(skb); kfree_skb(skb); } } -- cgit v0.10.2 From bbe48dd811474adc2df96803f910d4dc2b5e5bde Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sat, 10 Jan 2015 19:13:32 +0100 Subject: udf: destroy sbi mutex in put_super Call mutex_destroy() on superblock mutex in udf_put_super() otherwise mutex debugging code isn't able to detect that mutex is used after being freed. (thanks to Jan Kara for complete definition). Signed-off-by: Fabian Frederick Signed-off-by: Jan Kara diff --git a/fs/udf/super.c b/fs/udf/super.c index 3ccb2f1..3d35a75 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -2300,6 +2300,7 @@ static void udf_put_super(struct super_block *sb) udf_close_lvid(sb); brelse(sbi->s_lvid_bh); udf_sb_free_partitions(sb); + mutex_destroy(&sbi->s_alloc_mutex); kfree(sb->s_fs_info); sb->s_fs_info = NULL; } -- cgit v0.10.2 From 2859dff97e54db4795b8b7d9606cb8efcec722ff Mon Sep 17 00:00:00 2001 From: Steve McIntyre Date: Fri, 9 Jan 2015 15:29:53 +0000 Subject: efi: Expose underlying UEFI firmware platform size to userland In some cases (e.g. Intel Bay Trail machines), the kernel will happily run in 64-bit even if the underlying UEFI firmware platform is 32-bit. That's great, but it's difficult for userland utilities like grub-install to do the right thing in such a situation. The kernel already knows about the size of the firmware via efi_enabled(EFI_64BIT). Add an extra sysfs interface /sys/firmware/efi/fw_platform_size to expose that information to userland for low-level utilities to use. Signed-off-by: Steve McIntyre Cc: Matthew Garrett Signed-off-by: Matt Fleming diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index ff0bbe3..9bdbc05 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -112,15 +112,24 @@ EFI_ATTR_SHOW(fw_vendor); EFI_ATTR_SHOW(runtime); EFI_ATTR_SHOW(config_table); +static ssize_t fw_platform_size_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", efi_enabled(EFI_64BIT) ? 64 : 32); +} + static struct kobj_attribute efi_attr_fw_vendor = __ATTR_RO(fw_vendor); static struct kobj_attribute efi_attr_runtime = __ATTR_RO(runtime); static struct kobj_attribute efi_attr_config_table = __ATTR_RO(config_table); +static struct kobj_attribute efi_attr_fw_platform_size = + __ATTR_RO(fw_platform_size); static struct attribute *efi_subsys_attrs[] = { &efi_attr_systab.attr, &efi_attr_fw_vendor.attr, &efi_attr_runtime.attr, &efi_attr_config_table.attr, + &efi_attr_fw_platform_size.attr, NULL, }; -- cgit v0.10.2 From f6603ff2b7a8e02b748dd1acf00fcde78eb5dbcb Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 12 Jan 2015 12:30:02 +0200 Subject: ath10k: Fix DMA burst size A value of zero indicates that 128B is the maximum DMA request size for read/writes. But PCI cards based on AR9880 can support 256B, so enable this for the 10.2 firmware. Signed-off-by: Sujith Manoharan Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 5729901..7b771ae 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -183,6 +183,9 @@ struct ath10k_pktlog_hdr { #define TARGET_10X_NUM_MSDU_DESC (1024 + 400) #define TARGET_10X_MAX_FRAG_ENTRIES 0 +/* 10.2 parameters */ +#define TARGET_10_2_DMA_BURST_SIZE 1 + /* Target specific defines for WMI-TLV firmware */ #define TARGET_TLV_NUM_VDEVS 3 #define TARGET_TLV_NUM_STATIONS 32 diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index ac74290..b103122 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -3744,7 +3744,7 @@ static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar) config.mcast2ucast_mode = __cpu_to_le32(TARGET_10X_MCAST2UCAST_MODE); config.tx_dbg_log_size = __cpu_to_le32(TARGET_10X_TX_DBG_LOG_SIZE); config.num_wds_entries = __cpu_to_le32(TARGET_10X_NUM_WDS_ENTRIES); - config.dma_burst_size = __cpu_to_le32(TARGET_10X_DMA_BURST_SIZE); + config.dma_burst_size = __cpu_to_le32(TARGET_10_2_DMA_BURST_SIZE); config.mac_aggr_delim = __cpu_to_le32(TARGET_10X_MAC_AGGR_DELIM); val = TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK; -- cgit v0.10.2 From b6c8e287f6089559146e2a388a63fbe5460d7f44 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 12 Jan 2015 12:30:02 +0200 Subject: ath10k: Enable RX batching This feature allows the FW to batch RX indications, reducing the rate of host interrupt generation, which in turn reduces CPU load. Currently, this is enabled only for the 10.2 firmware. Signed-off-by: Sujith Manoharan Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index b103122..c56d2fa 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -3710,7 +3710,7 @@ static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar) struct wmi_init_cmd_10_2 *cmd; struct sk_buff *buf; struct wmi_resource_config_10x config = {}; - u32 len, val; + u32 len, val, features; config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS); config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS); @@ -3764,6 +3764,9 @@ static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar) cmd = (struct wmi_init_cmd_10_2 *)buf->data; + features = WMI_10_2_RX_BATCH_MODE; + cmd->resource_config.feature_mask = __cpu_to_le32(features); + memcpy(&cmd->resource_config.common, &config, sizeof(config)); ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks); -- cgit v0.10.2 From cb16ef384f15d33a53b4db47878aa818be059d77 Mon Sep 17 00:00:00 2001 From: Sreekanth Reddy Date: Mon, 12 Jan 2015 11:38:55 +0530 Subject: mpt2sas: MPI2 Rev AA (2.00.19) specifications Below is the changeset from the MPI specification and 2.00.34 header files: 1) Defined additional bits in the BiosOptions field of BIOS Page 1 to allow for finer control of X86 BIOS and UEFI BSD. 2) For the Clean Tool, reserved bit 26 of the Flags field for product specific use. Signed-off-by: Sreekanth Reddy Reviewed-by: Martin K. Petersen Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/mpt2sas/mpi/mpi2.h b/drivers/scsi/mpt2sas/mpi/mpi2.h index 088eefa..76cde3f 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2.h @@ -8,7 +8,7 @@ * scatter/gather formats. * Creation Date: June 21, 2006 * - * mpi2.h Version: 02.00.32 + * mpi2.h Version: 02.00.34 * * Version History * --------------- @@ -83,6 +83,8 @@ * 04-09-13 02.00.30 Bumped MPI2_HEADER_VERSION_UNIT. * 04-17-13 02.00.31 Bumped MPI2_HEADER_VERSION_UNIT. * 08-19-13 02.00.32 Bumped MPI2_HEADER_VERSION_UNIT. + * 12-05-13 02.00.33 Bumped MPI2_HEADER_VERSION_UNIT. + * 01-08-14 02.00.34 Bumped MPI2_HEADER_VERSION_UNIT. * -------------------------------------------------------------------------- */ diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h index 510ef0d..f055310 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h @@ -6,7 +6,7 @@ * Title: MPI Configuration messages and pages * Creation Date: November 10, 2006 * - * mpi2_cnfg.h Version: 02.00.26 + * mpi2_cnfg.h Version: 02.00.28 * * Version History * --------------- @@ -157,6 +157,18 @@ * 04-09-13 02.00.25 Added MPI2_IOUNITPAGE1_ATA_SECURITY_FREEZE_LOCK. * Fixed MPI2_IOUNITPAGE5_DMA_CAP_MASK_MAX_REQUESTS to * match the specification. + * 12-05-13 02.00.27 Added MPI2_MANPAGE7_FLAG_BASE_ENCLOSURE_LEVEL for + * MPI2_CONFIG_PAGE_MAN_7. + * Added EnclosureLevel and ConnectorName fields to + * MPI2_CONFIG_PAGE_SAS_DEV_0. + * Added MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID for + * MPI2_CONFIG_PAGE_SAS_DEV_0. + * Added EnclosureLevel field to + * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. + * Added MPI2_SAS_ENCLS0_FLAGS_ENCL_LEVEL_VALID for + * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. + * 01-08-14 02.00.28 Added more defines for the BiosOptions field of + * MPI2_CONFIG_PAGE_BIOS_1. * -------------------------------------------------------------------------- */ @@ -706,6 +718,7 @@ typedef struct _MPI2_CONFIG_PAGE_MAN_7 #define MPI2_MANUFACTURING7_PAGEVERSION (0x01) /* defines for the Flags field */ +#define MPI2_MANPAGE7_FLAG_BASE_ENCLOSURE_LEVEL (0x00000008) #define MPI2_MANPAGE7_FLAG_EVENTREPLAY_SLOT_ORDER (0x00000002) #define MPI2_MANPAGE7_FLAG_USE_SLOT_INFO (0x00000001) @@ -1235,9 +1248,17 @@ typedef struct _MPI2_CONFIG_PAGE_BIOS_1 } MPI2_CONFIG_PAGE_BIOS_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_1, Mpi2BiosPage1_t, MPI2_POINTER pMpi2BiosPage1_t; -#define MPI2_BIOSPAGE1_PAGEVERSION (0x05) +#define MPI2_BIOSPAGE1_PAGEVERSION (0x06) /* values for BIOS Page 1 BiosOptions field */ +#define MPI2_BIOSPAGE1_OPTIONS_X86_DISABLE_BIOS (0x00000400) + +#define MPI2_BIOSPAGE1_OPTIONS_MASK_REGISTRATION_UEFI_BSD (0x00000300) +#define MPI2_BIOSPAGE1_OPTIONS_USE_BIT0_REGISTRATION_UEFI_BSD (0x00000000) +#define MPI2_BIOSPAGE1_OPTIONS_FULL_REGISTRATION_UEFI_BSD (0x00000100) +#define MPI2_BIOSPAGE1_OPTIONS_ADAPTER_REGISTRATION_UEFI_BSD (0x00000200) +#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_REGISTRATION_UEFI_BSD (0x00000300) + #define MPI2_BIOSPAGE1_OPTIONS_MASK_OEM_ID (0x000000F0) #define MPI2_BIOSPAGE1_OPTIONS_LSI_OEM_ID (0x00000000) @@ -2420,13 +2441,13 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 U8 PortGroups; /* 0x2C */ U8 DmaGroup; /* 0x2D */ U8 ControlGroup; /* 0x2E */ - U8 Reserved1; /* 0x2F */ - U32 Reserved2; /* 0x30 */ + U8 EnclosureLevel; /* 0x2F */ + U8 ConnectorName[4]; /* 0x30 */ U32 Reserved3; /* 0x34 */ } MPI2_CONFIG_PAGE_SAS_DEV_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_DEV_0, Mpi2SasDevicePage0_t, MPI2_POINTER pMpi2SasDevicePage0_t; -#define MPI2_SASDEVICE0_PAGEVERSION (0x08) +#define MPI2_SASDEVICE0_PAGEVERSION (0x09) /* values for SAS Device Page 0 AccessStatus field */ #define MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS (0x00) @@ -2464,6 +2485,7 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 #define MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED (0x0020) #define MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED (0x0010) #define MPI2_SAS_DEVICE0_FLAGS_PORT_SELECTOR_ATTACH (0x0008) +#define MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID (0x0002) #define MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT (0x0001) @@ -2732,7 +2754,8 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 U16 EnclosureHandle; /* 0x16 */ U16 NumSlots; /* 0x18 */ U16 StartSlot; /* 0x1A */ - U16 Reserved2; /* 0x1C */ + U8 Reserved2; /* 0x1C */ + U8 EnclosureLevel; /* 0x1D */ U16 SEPDevHandle; /* 0x1E */ U32 Reserved3; /* 0x20 */ U32 Reserved4; /* 0x24 */ @@ -2740,9 +2763,10 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0, Mpi2SasEnclosurePage0_t, MPI2_POINTER pMpi2SasEnclosurePage0_t; -#define MPI2_SASENCLOSURE0_PAGEVERSION (0x03) +#define MPI2_SASENCLOSURE0_PAGEVERSION (0x04) /* values for SAS Enclosure Page 0 Flags field */ +#define MPI2_SAS_ENCLS0_FLAGS_ENCL_LEVEL_VALID (0x0010) #define MPI2_SAS_ENCLS0_FLAGS_MNG_MASK (0x000F) #define MPI2_SAS_ENCLS0_FLAGS_MNG_UNKNOWN (0x0000) #define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SES (0x0001) diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h index 2c3b0f2..b02de48 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h @@ -6,7 +6,7 @@ * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages * Creation Date: October 11, 2006 * - * mpi2_ioc.h Version: 02.00.23 + * mpi2_ioc.h Version: 02.00.24 * * Version History * --------------- @@ -126,6 +126,7 @@ * Added MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE. * Added MPI2_FW_DOWNLOAD_ITYPE_PUBLIC_KEY. * Added Encrypted Hash Extended Image. + * 12-05-13 02.00.24 Added MPI25_HASH_IMAGE_TYPE_BIOS. * -------------------------------------------------------------------------- */ @@ -1589,6 +1590,7 @@ Mpi25EncryptedHashEntry_t, MPI2_POINTER pMpi25EncryptedHashEntry_t; /* values for HashImageType */ #define MPI25_HASH_IMAGE_TYPE_UNUSED (0x00) #define MPI25_HASH_IMAGE_TYPE_FIRMWARE (0x01) +#define MPI25_HASH_IMAGE_TYPE_BIOS (0x02) /* values for HashAlgorithm */ #define MPI25_HASH_ALGORITHM_UNUSED (0x00) diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h index 9be03ed..659b8ac 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h @@ -6,7 +6,7 @@ * Title: MPI diagnostic tool structures and definitions * Creation Date: March 26, 2007 * - * mpi2_tool.h Version: 02.00.11 + * mpi2_tool.h Version: 02.00.12 * * Version History * --------------- @@ -29,7 +29,8 @@ * MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST. * 07-26-12 02.00.10 Modified MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST so that * it uses MPI Chain SGE as well as MPI Simple SGE. - * 08-19-13 02.00.11 Added MPI2_TOOLBOX_TEXT_DISPLAY_TOOL and related info. + * 08-19-13 02.00.11 Added MPI2_TOOLBOX_TEXT_DISPLAY_TOOL and related info. + * 01-08-14 02.00.12 Added MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC. * -------------------------------------------------------------------------- */ @@ -101,6 +102,7 @@ typedef struct _MPI2_TOOLBOX_CLEAN_REQUEST #define MPI2_TOOLBOX_CLEAN_OTHER_PERSIST_PAGES (0x20000000) #define MPI2_TOOLBOX_CLEAN_FW_CURRENT (0x10000000) #define MPI2_TOOLBOX_CLEAN_FW_BACKUP (0x08000000) +#define MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC (0x04000000) #define MPI2_TOOLBOX_CLEAN_MEGARAID (0x02000000) #define MPI2_TOOLBOX_CLEAN_INITIALIZATION (0x01000000) #define MPI2_TOOLBOX_CLEAN_FLASH (0x00000004) -- cgit v0.10.2 From ecedf5c8ed978116c57b4661651747d4bf3387b5 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Sun, 11 Jan 2015 21:58:55 +0800 Subject: spi: sirf: add missed devicetree binding document Signed-off-by: Barry Song Signed-off-by: Qipan Li Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/spi-sirf.txt b/Documentation/devicetree/bindings/spi/spi-sirf.txt new file mode 100644 index 0000000..4c7adb8f --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-sirf.txt @@ -0,0 +1,41 @@ +* CSR SiRFprimaII Serial Peripheral Interface + +Required properties: +- compatible : Should be "sirf,prima2-spi" +- reg : Offset and length of the register set for the device +- interrupts : Should contain SPI interrupt +- resets: phandle to the reset controller asserting this device in + reset + See ../reset/reset.txt for details. +- dmas : Must contain an entry for each entry in clock-names. + See ../dma/dma.txt for details. +- dma-names : Must include the following entries: + - rx + - tx +- clocks : Must contain an entry for each entry in clock-names. + See ../clocks/clock-bindings.txt for details. + +- #address-cells: Number of cells required to define a chip select + address on the SPI bus. Should be set to 1. +- #size-cells: Should be zero. + +Optional properties: +- spi-max-frequency: Specifies maximum SPI clock frequency, + Units - Hz. Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt +- cs-gpios: should specify GPIOs used for chipselects. + +Example: + +spi0: spi@b00d0000 { + compatible = "sirf,prima2-spi"; + reg = <0xb00d0000 0x10000>; + interrupts = <15>; + dmas = <&dmac1 9>, + <&dmac1 4>; + dma-names = "rx", "tx"; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clks 19>; + resets = <&rstc 26>; +}; -- cgit v0.10.2 From bdb2c74d44be691e6d3b6d441e81ea13dab34145 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 12 Jan 2015 13:54:13 +0100 Subject: ASoC: ad193x: Don't power down DAC in CODEC probe The DAC powerdown bit is managed by DAPM. Manually powering down the DAC in the CODEC probe function may cause unnecessary power state transitions which can lead to click and pop noises. So leave the DAC powerdown bit in its default poweron-reset state and let DAPM do all the management. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index 387530b..17c9535 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -333,8 +333,8 @@ static int ad193x_codec_probe(struct snd_soc_codec *codec) regmap_write(ad193x->regmap, AD193X_DAC_CHNL_MUTE, 0x0); /* de-emphasis: 48kHz, powedown dac */ regmap_write(ad193x->regmap, AD193X_DAC_CTRL2, 0x1A); - /* powerdown dac, dac in tdm mode */ - regmap_write(ad193x->regmap, AD193X_DAC_CTRL0, 0x41); + /* dac in tdm mode */ + regmap_write(ad193x->regmap, AD193X_DAC_CTRL0, 0x40); /* high-pass filter enable */ regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3); /* sata delay=1, adc aux mode */ -- cgit v0.10.2 From cfcefe01265cbaf5ca7209226d043b07bfa8b587 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 8 Jan 2015 01:52:36 +0000 Subject: ASoC: rsnd: add recovery support for under/over flow error on SRC L/R channel will be switched if under/over flow error happen on Renesas R-Car sound device by the HW bugs. Then, HW restart is required for salvage. This patch add salvage support for SRC. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 83284ca..4cecd0c 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -55,6 +55,7 @@ struct rsnd_ssi_platform_info { struct rsnd_src_platform_info { u32 convert_rate; /* sampling rate convert */ int dma_id; /* for Gen2 SCU */ + int irq; }; /* diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 87a6f2d..de0685f 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -309,8 +309,13 @@ static int rsnd_gen2_probe(struct platform_device *pdev, RSND_GEN_M_REG(SRC_BUSIF_MODE, 0x0, 0x20), RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20), RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20), + RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20), RSND_GEN_M_REG(CMD_ROUTE_SLCT, 0x18c, 0x20), RSND_GEN_M_REG(CMD_CTRL, 0x190, 0x20), + RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8), + RSND_GEN_S_REG(SCU_SYS_INT_EN0, 0x1cc), + RSND_GEN_S_REG(SCU_SYS_STATUS1, 0x1d0), + RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1c4), RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40), RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40), RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40), @@ -403,6 +408,16 @@ static int rsnd_gen1_probe(struct platform_device *pdev, RSND_GEN_M_REG(SRC_IFSVR, 0x220, 0x40), RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40), RSND_GEN_M_REG(SRC_MNFSR, 0x228, 0x40), + /* + * ADD US + * + * SRC_STATUS + * SRC_INT_EN + * SCU_SYS_STATUS0 + * SCU_SYS_STATUS1 + * SCU_SYS_INT_EN0 + * SCU_SYS_INT_EN1 + */ }; struct rsnd_regmap_field_conf conf_adg[] = { RSND_GEN_S_REG(BRRA, 0x00), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 5826c8a..c457003 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -44,6 +44,8 @@ enum rsnd_reg { RSND_REG_SRC_IFSCR, RSND_REG_SRC_IFSVR, RSND_REG_SRC_SRCCR, + RSND_REG_SCU_SYS_STATUS0, + RSND_REG_SCU_SYS_INT_EN0, RSND_REG_CMD_ROUTE_SLCT, RSND_REG_DVC_SWRSR, RSND_REG_DVC_DVUIR, @@ -94,6 +96,9 @@ enum rsnd_reg { RSND_REG_SHARE23, RSND_REG_SHARE24, RSND_REG_SHARE25, + RSND_REG_SHARE26, + RSND_REG_SHARE27, + RSND_REG_SHARE28, RSND_REG_MAX, }; @@ -135,6 +140,9 @@ enum rsnd_reg { #define RSND_REG_DVC_VRCTR RSND_REG_SHARE23 #define RSND_REG_DVC_VRPDR RSND_REG_SHARE24 #define RSND_REG_DVC_VRDBR RSND_REG_SHARE25 +#define RSND_REG_SCU_SYS_STATUS1 RSND_REG_SHARE26 +#define RSND_REG_SCU_SYS_INT_EN1 RSND_REG_SHARE27 +#define RSND_REG_SRC_INT_ENABLE0 RSND_REG_SHARE28 struct rsnd_of_data; struct rsnd_priv; diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index eede3ac..648b35e 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -12,10 +12,18 @@ #define SRC_NAME "src" +/* SRCx_STATUS */ +#define OUF_SRCO ((1 << 12) | (1 << 13)) +#define OUF_SRCI ((1 << 9) | (1 << 8)) + +/* SCU_SYSTEM_STATUS0/1 */ +#define OUF_SRC(id) ((1 << (id + 16)) | (1 << id)) + struct rsnd_src { struct rsnd_src_platform_info *info; /* rcar_snd.h */ struct rsnd_mod mod; struct clk *clk; + int err; }; #define RSND_SRC_NAME_SIZE 16 @@ -280,6 +288,8 @@ static int rsnd_src_init(struct rsnd_mod *mod, clk_prepare_enable(src->clk); + src->err = 0; + /* * Initialize the operation of the SRC internal circuits * see rsnd_src_start() @@ -293,9 +303,14 @@ static int rsnd_src_quit(struct rsnd_mod *mod, struct rsnd_dai *rdai) { struct rsnd_src *src = rsnd_mod_to_src(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); clk_disable_unprepare(src->clk); + if (src->err) + dev_warn(dev, "src under/over flow err = %d\n", src->err); + return 0; } @@ -510,6 +525,110 @@ static struct rsnd_mod_ops rsnd_src_gen1_ops = { /* * Gen2 functions */ +#define rsnd_src_irq_enable_gen2(mod) rsnd_src_irq_ctrol_gen2(mod, 1) +#define rsnd_src_irq_disable_gen2(mod) rsnd_src_irq_ctrol_gen2(mod, 0) +static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 sys_int_val, int_val, sys_int_mask; + int irq = src->info->irq; + int id = rsnd_mod_id(mod); + + sys_int_val = + sys_int_mask = OUF_SRC(id); + int_val = 0x3300; + + /* + * IRQ is not supported on non-DT + * see + * rsnd_src_probe_gen2() + */ + if ((irq <= 0) || !enable) { + sys_int_val = 0; + int_val = 0; + } + + rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val); + rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val); + rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val); +} + +static void rsnd_src_error_clear_gen2(struct rsnd_mod *mod) +{ + u32 val = OUF_SRC(rsnd_mod_id(mod)); + + rsnd_mod_bset(mod, SCU_SYS_STATUS0, val, val); + rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val); +} + +static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod) +{ + u32 val = OUF_SRC(rsnd_mod_id(mod)); + bool ret = false; + + if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val) || + (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val)) { + struct rsnd_src *src = rsnd_mod_to_src(mod); + + src->err++; + ret = true; + } + + /* clear error static */ + rsnd_src_error_clear_gen2(mod); + + return ret; +} + +static int _rsnd_src_start_gen2(struct rsnd_mod *mod) +{ + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11; + + rsnd_mod_write(mod, SRC_CTRL, val); + + rsnd_src_error_clear_gen2(mod); + + rsnd_src_start(mod); + + rsnd_src_irq_enable_gen2(mod); + + return 0; +} + +static int _rsnd_src_stop_gen2(struct rsnd_mod *mod) +{ + rsnd_src_irq_disable_gen2(mod); + + rsnd_mod_write(mod, SRC_CTRL, 0); + + rsnd_src_error_record_gen2(mod); + + return rsnd_src_stop(mod); +} + +static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) +{ + struct rsnd_mod *mod = data; + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + + if (!io) + return IRQ_NONE; + + if (rsnd_src_error_record_gen2(mod)) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + _rsnd_src_stop_gen2(mod); + _rsnd_src_start_gen2(mod); + + dev_dbg(dev, "%s[%d] restart\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + } + + return IRQ_HANDLED; +} + static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, struct rsnd_dai *rdai) { @@ -588,18 +707,38 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_src *src = rsnd_mod_to_src(mod); struct device *dev = rsnd_priv_to_dev(priv); + int irq = src->info->irq; int ret; + if (irq > 0) { + /* + * IRQ is not supported on non-DT + * see + * rsnd_src_irq_enable_gen2() + */ + ret = devm_request_irq(dev, irq, + rsnd_src_interrupt_gen2, + IRQF_SHARED, + dev_name(dev), mod); + if (ret) + goto rsnd_src_probe_gen2_fail; + } + ret = rsnd_dma_init(priv, rsnd_mod_to_dma(mod), rsnd_info_is_playback(priv, src), src->info->dma_id); - if (ret < 0) - dev_err(dev, "%s[%d] (Gen2) failed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - else - dev_dbg(dev, "%s[%d] (Gen2) is probed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + if (ret) + goto rsnd_src_probe_gen2_fail; + + dev_dbg(dev, "%s[%d] (Gen2) is probed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return ret; + +rsnd_src_probe_gen2_fail: + dev_err(dev, "%s[%d] (Gen2) failed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); return ret; } @@ -635,27 +774,21 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod, static int rsnd_src_start_gen2(struct rsnd_mod *mod, struct rsnd_dai *rdai) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - struct rsnd_src *src = rsnd_mod_to_src(mod); - u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11; - - rsnd_dma_start(rsnd_mod_to_dma(&src->mod)); + rsnd_dma_start(rsnd_mod_to_dma(mod)); - rsnd_mod_write(mod, SRC_CTRL, val); - - return rsnd_src_start(mod); + return _rsnd_src_start_gen2(mod); } static int rsnd_src_stop_gen2(struct rsnd_mod *mod, struct rsnd_dai *rdai) { - struct rsnd_src *src = rsnd_mod_to_src(mod); + int ret; - rsnd_mod_write(mod, SRC_CTRL, 0); + ret = _rsnd_src_stop_gen2(mod); - rsnd_dma_stop(rsnd_mod_to_dma(&src->mod)); + rsnd_dma_stop(rsnd_mod_to_dma(mod)); - return rsnd_src_stop(mod); + return ret; } static struct rsnd_mod_ops rsnd_src_gen2_ops = { @@ -681,10 +814,11 @@ static void rsnd_of_parse_src(struct platform_device *pdev, struct rsnd_priv *priv) { struct device_node *src_node; + struct device_node *np; struct rcar_snd_info *info = rsnd_priv_to_info(priv); struct rsnd_src_platform_info *src_info; struct device *dev = &pdev->dev; - int nr; + int nr, i; if (!of_data) return; @@ -708,6 +842,13 @@ static void rsnd_of_parse_src(struct platform_device *pdev, info->src_info = src_info; info->src_info_nr = nr; + i = 0; + for_each_child_of_node(src_node, np) { + src_info[i].irq = irq_of_parse_and_map(np, 0); + + i++; + } + rsnd_of_parse_src_end: of_node_put(src_node); } -- cgit v0.10.2 From 2de5a9c004e92aaf49b800e60820f7a030d42eb9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 12 Jan 2015 14:57:44 +0200 Subject: sata_dwc_460ex: disable compilation on ARM and ARM64 For now disable the compilation of the driver on ARM and ARM64. In the future we will fix the DMA handling in that driver and re-enable compilation back. Reported-by: kbuild test robot Signed-off-by: Andy Shevchenko Signed-off-by: Tejun Heo diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 5ed20e3..9f2ba27 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -269,7 +269,7 @@ config ATA_PIIX config SATA_DWC tristate "DesignWare Cores SATA support" - depends on 460EX || COMPILE_TEST + depends on 460EX || (COMPILE_TEST && !(ARM || ARM64)) help This option enables support for the on-chip SATA controller of the AppliedMicro processor 460EX. -- cgit v0.10.2 From 1730fd9cce32ba88d9b157546044b2b07db9c936 Mon Sep 17 00:00:00 2001 From: David Flater Date: Sun, 11 Jan 2015 16:08:58 -0500 Subject: ALSA: fix emu8000 DRAM sizing for AWE64 Value Applicable to any kernel since 2013: The special case added in commit 1338fc97d07a did not handle the possibility that the address space on an AWE64 Value would wrap around at 512 KiB. That is what it does, so the memory is still not detected on those cards. Fix that with a logic clean-up that eliminates the need for a special case. Signed-off-by: David Flater Signed-off-by: Takashi Iwai diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c index 45fcdff..d25e384 100644 --- a/sound/isa/sb/emu8000.c +++ b/sound/isa/sb/emu8000.c @@ -378,13 +378,12 @@ init_arrays(struct snd_emu8000 *emu) static void size_dram(struct snd_emu8000 *emu) { - int i, size, detected_size; + int i, size; if (emu->dram_checked) return; size = 0; - detected_size = 0; /* write out a magic number */ snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE); @@ -392,10 +391,19 @@ size_dram(struct snd_emu8000 *emu) EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET); EMU8000_SMLD_WRITE(emu, UNIQUE_ID1); snd_emu8000_init_fm(emu); /* This must really be here and not 2 lines back even */ + snd_emu8000_write_wait(emu); - while (size < EMU8000_MAX_DRAM) { + /* + * Detect first 512 KiB. If a write succeeds at the beginning of a + * 512 KiB page we assume that the whole page is there. + */ + EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET); + EMU8000_SMLD_READ(emu); /* discard stale data */ + if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1) + goto skip_detect; /* No RAM */ + snd_emu8000_read_wait(emu); - size += 512 * 1024; /* increment 512kbytes */ + for (size = 512 * 1024; size < EMU8000_MAX_DRAM; size += 512 * 1024) { /* Write a unique data on the test address. * if the address is out of range, the data is written on @@ -431,18 +439,9 @@ size_dram(struct snd_emu8000 *emu) snd_emu8000_read_wait(emu); /* Otherwise, it's valid memory. */ - detected_size = size + 512 * 1024; - } - - /* Distinguish 512 KiB from 0. */ - if (detected_size == 0) { - snd_emu8000_read_wait(emu); - EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET); - EMU8000_SMLD_READ(emu); /* discard stale data */ - if (EMU8000_SMLD_READ(emu) == UNIQUE_ID1) - detected_size = 512 * 1024; } +skip_detect: /* wait until FULL bit in SMAxW register is false */ for (i = 0; i < 10000; i++) { if ((EMU8000_SMALW_READ(emu) & 0x80000000) == 0) @@ -455,9 +454,9 @@ size_dram(struct snd_emu8000 *emu) snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_CLOSE); snd_printdd("EMU8000 [0x%lx]: %d Kb on-board memory detected\n", - emu->port1, detected_size/1024); + emu->port1, size/1024); - emu->mem_size = detected_size; + emu->mem_size = size; emu->dram_checked = 1; } -- cgit v0.10.2 From d2866409baaf644c8f875498b05fd3adf26fcdcd Mon Sep 17 00:00:00 2001 From: David Flater Date: Sun, 11 Jan 2015 16:10:07 -0500 Subject: ALSA: log emu8000 DRAM size at level INFO Detected sound font memory goes unreported unless the kernel was built with ALSA debugging enabled. Elevate that to a pr_info. Signed-off-by: David Flater Signed-off-by: Takashi Iwai diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c index d25e384..96e9d94 100644 --- a/sound/isa/sb/emu8000.c +++ b/sound/isa/sb/emu8000.c @@ -453,7 +453,7 @@ skip_detect: snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_CLOSE); snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_CLOSE); - snd_printdd("EMU8000 [0x%lx]: %d Kb on-board memory detected\n", + pr_info("EMU8000 [0x%lx]: %d KiB on-board DRAM detected\n", emu->port1, size/1024); emu->mem_size = size; -- cgit v0.10.2 From 4e6c48e0984e28d064ee8fbc292aee7b7920c507 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 12 Jan 2015 16:34:26 +0200 Subject: iwlwifi: mvm: drop non VO frames when flushing This change has already been implemented in iwldvm: commit a260e7b3f0307878b99d57ed1406cf2d497923b8 Author: Emmanuel Grumbach Date: Sun Oct 5 09:11:14 2014 +0300 iwlwifi: dvm: drop non VO frames when flushing Since I added the flush() callback implementation in mvm, we got reports that the queues are stuck while roaming or suspending. This commit above helped much for iwldvm, implement the same behavior for iwlmvm. CC: [3.16+] Fixes: c5b0e7c0565a ("iwlwifi: mvm: implement mac80211's flush callback") Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index e880f9d..2091558 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -3343,18 +3343,16 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw, msk |= mvmsta->tfd_queue_msk; } - if (drop) { - if (iwl_mvm_flush_tx_path(mvm, msk, true)) - IWL_ERR(mvm, "flush request fail\n"); - mutex_unlock(&mvm->mutex); - } else { - mutex_unlock(&mvm->mutex); + msk &= ~BIT(vif->hw_queue[IEEE80211_AC_VO]); - /* this can take a while, and we may need/want other operations - * to succeed while doing this, so do it without the mutex held - */ - iwl_trans_wait_tx_queue_empty(mvm->trans, msk); - } + if (iwl_mvm_flush_tx_path(mvm, msk, true)) + IWL_ERR(mvm, "flush request fail\n"); + mutex_unlock(&mvm->mutex); + + /* this can take a while, and we may need/want other operations + * to succeed while doing this, so do it without the mutex held + */ + iwl_trans_wait_tx_queue_empty(mvm->trans, msk); } const struct ieee80211_ops iwl_mvm_hw_ops = { -- cgit v0.10.2 From a4972b1b9a04439c2dc15a98b333ed28378d3509 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 11 Jan 2015 18:57:10 +0100 Subject: edac: i5100_edac: Remove unused i5100_recmema_dm_buf_id Remove the function i5100_recmema_dm_buf_id() that is not used anywhere. This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Link: http://lkml.kernel.org/r/1420999030-21770-1-git-send-email-rickard_strandqvist@spectrumdigital.se Signed-off-by: Borislav Petkov diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c index 6247d18..e9f8a39 100644 --- a/drivers/edac/i5100_edac.c +++ b/drivers/edac/i5100_edac.c @@ -279,11 +279,6 @@ static inline u32 i5100_recmema_rank(u32 a) return i5100_nrecmema_rank(a); } -static inline u32 i5100_recmema_dm_buf_id(u32 a) -{ - return i5100_nrecmema_dm_buf_id(a); -} - static inline u32 i5100_recmemb_cas(u32 a) { return i5100_nrecmemb_cas(a); -- cgit v0.10.2 From 6c869d301bda3f672f001e2c9fcb01685000fc83 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 12 Jan 2015 16:56:51 +0100 Subject: ALSA: rme*: Use snd_pcm_format_t Fix sparse warnings below by using snd_pcm_format_t properly: sound/pci/rme32.c:682:60: sparse: incorrect type in argument 2 (different base types) sound/pci/rme96.c:1006:69: sparse: incorrect type in argument 2 (different base types) ..... Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 6c60dcd..1a7affa 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -632,7 +632,7 @@ snd_rme32_setframelog(struct rme32 * rme32, int n_channels, int is_playback) } } -static int snd_rme32_setformat(struct rme32 * rme32, int format) +static int snd_rme32_setformat(struct rme32 *rme32, snd_pcm_format_t format) { switch (format) { case SNDRV_PCM_FORMAT_S16_LE: diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index e33e79e..236ac1d 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -922,8 +922,7 @@ snd_rme96_setframelog(struct rme96 *rme96, } static int -snd_rme96_playback_setformat(struct rme96 *rme96, - int format) +snd_rme96_playback_setformat(struct rme96 *rme96, snd_pcm_format_t format) { switch (format) { case SNDRV_PCM_FORMAT_S16_LE: @@ -940,8 +939,7 @@ snd_rme96_playback_setformat(struct rme96 *rme96, } static int -snd_rme96_capture_setformat(struct rme96 *rme96, - int format) +snd_rme96_capture_setformat(struct rme96 *rme96, snd_pcm_format_t format) { switch (format) { case SNDRV_PCM_FORMAT_S16_LE: -- cgit v0.10.2 From f3cdfd239da56a4cea75a2920dc326f0f45f67e3 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 20 Oct 2014 16:27:26 +0200 Subject: arm64/efi: move SetVirtualAddressMap() to UEFI stub In order to support kexec, the kernel needs to be able to deal with the state of the UEFI firmware after SetVirtualAddressMap() has been called. To avoid having separate code paths for non-kexec and kexec, let's move the call to SetVirtualAddressMap() to the stub: this will guarantee us that it will only be called once (since the stub is not executed during kexec), and ensures that the UEFI state is identical between kexec and normal boot. This implies that the layout of the virtual mapping needs to be created by the stub as well. All regions are rounded up to a naturally aligned multiple of 64 KB (for compatibility with 64k pages kernels) and recorded in the UEFI memory map. The kernel proper reads those values and installs the mappings in a dedicated set of page tables that are swapped in during UEFI Runtime Services calls. Acked-by: Leif Lindholm Acked-by: Matt Fleming Tested-by: Leif Lindholm Signed-off-by: Ard Biesheuvel diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h index 7129125..effef37 100644 --- a/arch/arm64/include/asm/efi.h +++ b/arch/arm64/include/asm/efi.h @@ -7,28 +7,36 @@ #ifdef CONFIG_EFI extern void efi_init(void); extern void efi_idmap_init(void); +extern void efi_virtmap_init(void); #else #define efi_init() #define efi_idmap_init() +#define efi_virtmap_init() #endif #define efi_call_virt(f, ...) \ ({ \ - efi_##f##_t *__f = efi.systab->runtime->f; \ + efi_##f##_t *__f; \ efi_status_t __s; \ \ kernel_neon_begin(); \ + efi_virtmap_load(); \ + __f = efi.systab->runtime->f; \ __s = __f(__VA_ARGS__); \ + efi_virtmap_unload(); \ kernel_neon_end(); \ __s; \ }) #define __efi_call_virt(f, ...) \ ({ \ - efi_##f##_t *__f = efi.systab->runtime->f; \ + efi_##f##_t *__f; \ \ kernel_neon_begin(); \ + efi_virtmap_load(); \ + __f = efi.systab->runtime->f; \ __f(__VA_ARGS__); \ + efi_virtmap_unload(); \ kernel_neon_end(); \ }) @@ -46,4 +54,26 @@ extern void efi_idmap_init(void); #define EFI_ALLOC_ALIGN SZ_64K +/* + * On ARM systems, virtually remapped UEFI runtime services are set up in three + * distinct stages: + * - The stub retrieves the final version of the memory map from UEFI, populates + * the virt_addr fields and calls the SetVirtualAddressMap() [SVAM] runtime + * service to communicate the new mapping to the firmware (Note that the new + * mapping is not live at this time) + * - During early boot, the page tables are allocated and populated based on the + * virt_addr fields in the memory map, but only if all descriptors with the + * EFI_MEMORY_RUNTIME attribute have a non-zero value for virt_addr. If this + * succeeds, the EFI_VIRTMAP flag is set to indicate that the virtual mappings + * have been installed successfully. + * - During an early initcall(), the UEFI Runtime Services are enabled and the + * EFI_RUNTIME_SERVICES bit set if some conditions are met, i.e., we need a + * non-early mapping of the UEFI system table, and we need to have the virtmap + * installed. + */ +#define EFI_VIRTMAP EFI_ARCH_1 + +void efi_virtmap_load(void); +void efi_virtmap_unload(void); + #endif /* _ASM_EFI_H */ diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index 2bb4347..755e545 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -11,25 +11,31 @@ * */ +#include #include #include #include #include +#include #include #include #include +#include +#include +#include #include #include +#include #include #include #include #include +#include +#include struct efi_memory_map memmap; -static efi_runtime_services_t *runtime; - static u64 efi_system_table; static int uefi_debug __initdata; @@ -69,9 +75,33 @@ static void __init efi_setup_idmap(void) } } +/* + * Translate a EFI virtual address into a physical address: this is necessary, + * as some data members of the EFI system table are virtually remapped after + * SetVirtualAddressMap() has been called. + */ +static phys_addr_t efi_to_phys(unsigned long addr) +{ + efi_memory_desc_t *md; + + for_each_efi_memory_desc(&memmap, md) { + if (!(md->attribute & EFI_MEMORY_RUNTIME)) + continue; + if (md->virt_addr == 0) + /* no virtual mapping has been installed by the stub */ + break; + if (md->virt_addr <= addr && + (addr - md->virt_addr) < (md->num_pages << EFI_PAGE_SHIFT)) + return md->phys_addr + addr - md->virt_addr; + } + return addr; +} + static int __init uefi_init(void) { efi_char16_t *c16; + void *config_tables; + u64 table_size; char vendor[100] = "unknown"; int i, retval; @@ -99,7 +129,7 @@ static int __init uefi_init(void) efi.systab->hdr.revision & 0xffff); /* Show what we know for posterity */ - c16 = early_memremap(efi.systab->fw_vendor, + c16 = early_memremap(efi_to_phys(efi.systab->fw_vendor), sizeof(vendor)); if (c16) { for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i) @@ -112,8 +142,14 @@ static int __init uefi_init(void) efi.systab->hdr.revision >> 16, efi.systab->hdr.revision & 0xffff, vendor); - retval = efi_config_init(NULL); + table_size = sizeof(efi_config_table_64_t) * efi.systab->nr_tables; + config_tables = early_memremap(efi_to_phys(efi.systab->tables), + table_size); + + retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables, + sizeof(efi_config_table_64_t), NULL); + early_memunmap(config_tables, table_size); out: early_memunmap(efi.systab, sizeof(efi_system_table_t)); return retval; @@ -329,51 +365,14 @@ void __init efi_idmap_init(void) early_memunmap(memmap.map, memmap.map_end - memmap.map); } -static int __init remap_region(efi_memory_desc_t *md, void **new) -{ - u64 paddr, vaddr, npages, size; - - paddr = md->phys_addr; - npages = md->num_pages; - memrange_efi_to_native(&paddr, &npages); - size = npages << PAGE_SHIFT; - - if (is_normal_ram(md)) - vaddr = (__force u64)ioremap_cache(paddr, size); - else - vaddr = (__force u64)ioremap(paddr, size); - - if (!vaddr) { - pr_err("Unable to remap 0x%llx pages @ %p\n", - npages, (void *)paddr); - return 0; - } - - /* adjust for any rounding when EFI and system pagesize differs */ - md->virt_addr = vaddr + (md->phys_addr - paddr); - - if (uefi_debug) - pr_info(" EFI remap 0x%012llx => %p\n", - md->phys_addr, (void *)md->virt_addr); - - memcpy(*new, md, memmap.desc_size); - *new += memmap.desc_size; - - return 1; -} - /* - * Switch UEFI from an identity map to a kernel virtual map + * Enable the UEFI Runtime Services if all prerequisites are in place, i.e., + * non-early mapping of the UEFI system table and virtual mappings for all + * EFI_MEMORY_RUNTIME regions. */ -static int __init arm64_enter_virtual_mode(void) +static int __init arm64_enable_runtime_services(void) { - efi_memory_desc_t *md; - phys_addr_t virtmap_phys; - void *virtmap, *virt_md; - efi_status_t status; u64 mapsize; - int count = 0; - unsigned long flags; if (!efi_enabled(EFI_BOOT)) { pr_info("EFI services will not be available.\n"); @@ -395,81 +394,30 @@ static int __init arm64_enter_virtual_mode(void) efi.memmap = &memmap; - /* Map the runtime regions */ - virtmap = kmalloc(mapsize, GFP_KERNEL); - if (!virtmap) { - pr_err("Failed to allocate EFI virtual memmap\n"); - return -1; - } - virtmap_phys = virt_to_phys(virtmap); - virt_md = virtmap; - - for_each_efi_memory_desc(&memmap, md) { - if (!(md->attribute & EFI_MEMORY_RUNTIME)) - continue; - if (!remap_region(md, &virt_md)) - goto err_unmap; - ++count; - } - - efi.systab = (__force void *)efi_lookup_mapped_addr(efi_system_table); + efi.systab = (__force void *)ioremap_cache(efi_system_table, + sizeof(efi_system_table_t)); if (!efi.systab) { - /* - * If we have no virtual mapping for the System Table at this - * point, the memory map doesn't cover the physical offset where - * it resides. This means the System Table will be inaccessible - * to Runtime Services themselves once the virtual mapping is - * installed. - */ - pr_err("Failed to remap EFI System Table -- buggy firmware?\n"); - goto err_unmap; + pr_err("Failed to remap EFI System Table\n"); + return -1; } set_bit(EFI_SYSTEM_TABLES, &efi.flags); - local_irq_save(flags); - cpu_switch_mm(idmap_pg_dir, &init_mm); - - /* Call SetVirtualAddressMap with the physical address of the map */ - runtime = efi.systab->runtime; - efi.set_virtual_address_map = runtime->set_virtual_address_map; - - status = efi.set_virtual_address_map(count * memmap.desc_size, - memmap.desc_size, - memmap.desc_version, - (efi_memory_desc_t *)virtmap_phys); - cpu_set_reserved_ttbr0(); - flush_tlb_all(); - local_irq_restore(flags); - - kfree(virtmap); - free_boot_services(); - if (status != EFI_SUCCESS) { - pr_err("Failed to set EFI virtual address map! [%lx]\n", - status); + if (!efi_enabled(EFI_VIRTMAP)) { + pr_err("No UEFI virtual mapping was installed -- runtime services will not be available\n"); return -1; } /* Set up runtime services function pointers */ - runtime = efi.systab->runtime; efi_native_runtime_setup(); set_bit(EFI_RUNTIME_SERVICES, &efi.flags); efi.runtime_version = efi.systab->hdr.revision; return 0; - -err_unmap: - /* unmap all mappings that succeeded: there are 'count' of those */ - for (virt_md = virtmap; count--; virt_md += memmap.desc_size) { - md = virt_md; - iounmap((__force void __iomem *)md->virt_addr); - } - kfree(virtmap); - return -1; } -early_initcall(arm64_enter_virtual_mode); +early_initcall(arm64_enable_runtime_services); static int __init arm64_dmi_init(void) { @@ -484,3 +432,79 @@ static int __init arm64_dmi_init(void) return 0; } core_initcall(arm64_dmi_init); + +static pgd_t efi_pgd[PTRS_PER_PGD] __page_aligned_bss; + +static struct mm_struct efi_mm = { + .mm_rb = RB_ROOT, + .pgd = efi_pgd, + .mm_users = ATOMIC_INIT(2), + .mm_count = ATOMIC_INIT(1), + .mmap_sem = __RWSEM_INITIALIZER(efi_mm.mmap_sem), + .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock), + .mmlist = LIST_HEAD_INIT(efi_mm.mmlist), + INIT_MM_CONTEXT(efi_mm) +}; + +static void efi_set_pgd(struct mm_struct *mm) +{ + cpu_switch_mm(mm->pgd, mm); + flush_tlb_all(); + if (icache_is_aivivt()) + __flush_icache_all(); +} + +void efi_virtmap_load(void) +{ + preempt_disable(); + efi_set_pgd(&efi_mm); +} + +void efi_virtmap_unload(void) +{ + efi_set_pgd(current->active_mm); + preempt_enable(); +} + +void __init efi_virtmap_init(void) +{ + efi_memory_desc_t *md; + + if (!efi_enabled(EFI_BOOT)) + return; + + for_each_efi_memory_desc(&memmap, md) { + u64 paddr, npages, size; + pgprot_t prot; + + if (!(md->attribute & EFI_MEMORY_RUNTIME)) + continue; + if (WARN(md->virt_addr == 0, + "UEFI virtual mapping incomplete or missing -- no entry found for 0x%llx\n", + md->phys_addr)) + return; + + paddr = md->phys_addr; + npages = md->num_pages; + memrange_efi_to_native(&paddr, &npages); + size = npages << PAGE_SHIFT; + + pr_info(" EFI remap 0x%016llx => %p\n", + md->phys_addr, (void *)md->virt_addr); + + /* + * Only regions of type EFI_RUNTIME_SERVICES_CODE need to be + * executable, everything else can be mapped with the XN bits + * set. + */ + if (!is_normal_ram(md)) + prot = __pgprot(PROT_DEVICE_nGnRE); + else if (md->type == EFI_RUNTIME_SERVICES_CODE) + prot = PAGE_KERNEL_EXEC; + else + prot = PAGE_KERNEL; + + create_pgd_mapping(&efi_mm, paddr, md->virt_addr, size, prot); + } + set_bit(EFI_VIRTMAP, &efi.flags); +} diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 20fe293..beac818 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -401,6 +401,7 @@ void __init setup_arch(char **cmdline_p) paging_init(); request_standard_resources(); + efi_virtmap_init(); efi_idmap_init(); early_ioremap_reset(); diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index eb48a1a..e2432b3 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -295,3 +295,62 @@ fail_free_image: fail: return EFI_ERROR; } + +/* + * This is the base address at which to start allocating virtual memory ranges + * for UEFI Runtime Services. This is in the low TTBR0 range so that we can use + * any allocation we choose, and eliminate the risk of a conflict after kexec. + * The value chosen is the largest non-zero power of 2 suitable for this purpose + * both on 32-bit and 64-bit ARM CPUs, to maximize the likelihood that it can + * be mapped efficiently. + */ +#define EFI_RT_VIRTUAL_BASE 0x40000000 + +/* + * efi_get_virtmap() - create a virtual mapping for the EFI memory map + * + * This function populates the virt_addr fields of all memory region descriptors + * in @memory_map whose EFI_MEMORY_RUNTIME attribute is set. Those descriptors + * are also copied to @runtime_map, and their total count is returned in @count. + */ +void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, + unsigned long desc_size, efi_memory_desc_t *runtime_map, + int *count) +{ + u64 efi_virt_base = EFI_RT_VIRTUAL_BASE; + efi_memory_desc_t *out = runtime_map; + int l; + + for (l = 0; l < map_size; l += desc_size) { + efi_memory_desc_t *in = (void *)memory_map + l; + u64 paddr, size; + + if (!(in->attribute & EFI_MEMORY_RUNTIME)) + continue; + + /* + * Make the mapping compatible with 64k pages: this allows + * a 4k page size kernel to kexec a 64k page size kernel and + * vice versa. + */ + paddr = round_down(in->phys_addr, SZ_64K); + size = round_up(in->num_pages * EFI_PAGE_SIZE + + in->phys_addr - paddr, SZ_64K); + + /* + * Avoid wasting memory on PTEs by choosing a virtual base that + * is compatible with section mappings if this region has the + * appropriate size and physical alignment. (Sections are 2 MB + * on 4k granule kernels) + */ + if (IS_ALIGNED(in->phys_addr, SZ_2M) && size >= SZ_2M) + efi_virt_base = round_up(efi_virt_base, SZ_2M); + + in->virt_addr = efi_virt_base + in->phys_addr - paddr; + efi_virt_base += size; + + memcpy(out, in, desc_size); + out = (void *)out + desc_size; + ++*count; + } +} diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 304ab29..2be1098 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -39,4 +39,8 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, void *get_fdt(efi_system_table_t *sys_table); +void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, + unsigned long desc_size, efi_memory_desc_t *runtime_map, + int *count); + #endif diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index c846a96..91da56c 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c @@ -14,6 +14,8 @@ #include #include +#include "efistub.h" + efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, unsigned long orig_fdt_size, void *fdt, int new_fdt_size, char *cmdline_ptr, @@ -193,9 +195,26 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, unsigned long map_size, desc_size; u32 desc_ver; unsigned long mmap_key; - efi_memory_desc_t *memory_map; + efi_memory_desc_t *memory_map, *runtime_map; unsigned long new_fdt_size; efi_status_t status; + int runtime_entry_count = 0; + + /* + * Get a copy of the current memory map that we will use to prepare + * the input for SetVirtualAddressMap(). We don't have to worry about + * subsequent allocations adding entries, since they could not affect + * the number of EFI_MEMORY_RUNTIME regions. + */ + status = efi_get_memory_map(sys_table, &runtime_map, &map_size, + &desc_size, &desc_ver, &mmap_key); + if (status != EFI_SUCCESS) { + pr_efi_err(sys_table, "Unable to retrieve UEFI memory map.\n"); + return status; + } + + pr_efi(sys_table, + "Exiting boot services and installing virtual address map...\n"); /* * Estimate size of new FDT, and allocate memory for it. We @@ -248,12 +267,48 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, } } + /* + * Update the memory map with virtual addresses. The function will also + * populate @runtime_map with copies of just the EFI_MEMORY_RUNTIME + * entries so that we can pass it straight into SetVirtualAddressMap() + */ + efi_get_virtmap(memory_map, map_size, desc_size, runtime_map, + &runtime_entry_count); + /* Now we are ready to exit_boot_services.*/ status = sys_table->boottime->exit_boot_services(handle, mmap_key); + if (status == EFI_SUCCESS) { + efi_set_virtual_address_map_t *svam; - if (status == EFI_SUCCESS) - return status; + /* Install the new virtual address map */ + svam = sys_table->runtime->set_virtual_address_map; + status = svam(runtime_entry_count * desc_size, desc_size, + desc_ver, runtime_map); + + /* + * We are beyond the point of no return here, so if the call to + * SetVirtualAddressMap() failed, we need to signal that to the + * incoming kernel but proceed normally otherwise. + */ + if (status != EFI_SUCCESS) { + int l; + + /* + * Set the virtual address field of all + * EFI_MEMORY_RUNTIME entries to 0. This will signal + * the incoming kernel that no virtual translation has + * been installed. + */ + for (l = 0; l < map_size; l += desc_size) { + efi_memory_desc_t *p = (void *)memory_map + l; + + if (p->attribute & EFI_MEMORY_RUNTIME) + p->virt_addr = 0; + } + } + return EFI_SUCCESS; + } pr_efi_err(sys_table, "Exit boot services failed.\n"); @@ -264,6 +319,7 @@ fail_free_new_fdt: efi_free(sys_table, new_fdt_size, *new_fdt_addr); fail: + sys_table->boottime->free_pool(runtime_map); return EFI_LOAD_ERROR; } -- cgit v0.10.2 From 3033b84596eaec0093b68c5711c265738eb0745d Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 20 Oct 2014 16:35:21 +0200 Subject: arm64/efi: remove free_boot_services() and friends Now that we are calling SetVirtualAddressMap() from the stub, there is no need to reserve boot-only memory regions, which implies that there is also no reason to free them again later. Acked-by: Leif Lindholm Acked-by: Will Deacon Tested-by: Leif Lindholm Signed-off-by: Ard Biesheuvel diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index 755e545..4a5d734 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -199,9 +199,7 @@ static __init void reserve_regions(void) if (is_normal_ram(md)) early_init_dt_add_memory_arch(paddr, size); - if (is_reserve_region(md) || - md->type == EFI_BOOT_SERVICES_CODE || - md->type == EFI_BOOT_SERVICES_DATA) { + if (is_reserve_region(md)) { memblock_reserve(paddr, size); if (uefi_debug) pr_cont("*"); @@ -214,123 +212,6 @@ static __init void reserve_regions(void) set_bit(EFI_MEMMAP, &efi.flags); } - -static u64 __init free_one_region(u64 start, u64 end) -{ - u64 size = end - start; - - if (uefi_debug) - pr_info(" EFI freeing: 0x%012llx-0x%012llx\n", start, end - 1); - - free_bootmem_late(start, size); - return size; -} - -static u64 __init free_region(u64 start, u64 end) -{ - u64 map_start, map_end, total = 0; - - if (end <= start) - return total; - - map_start = (u64)memmap.phys_map; - map_end = PAGE_ALIGN(map_start + (memmap.map_end - memmap.map)); - map_start &= PAGE_MASK; - - if (start < map_end && end > map_start) { - /* region overlaps UEFI memmap */ - if (start < map_start) - total += free_one_region(start, map_start); - - if (map_end < end) - total += free_one_region(map_end, end); - } else - total += free_one_region(start, end); - - return total; -} - -static void __init free_boot_services(void) -{ - u64 total_freed = 0; - u64 keep_end, free_start, free_end; - efi_memory_desc_t *md; - - /* - * If kernel uses larger pages than UEFI, we have to be careful - * not to inadvertantly free memory we want to keep if there is - * overlap at the kernel page size alignment. We do not want to - * free is_reserve_region() memory nor the UEFI memmap itself. - * - * The memory map is sorted, so we keep track of the end of - * any previous region we want to keep, remember any region - * we want to free and defer freeing it until we encounter - * the next region we want to keep. This way, before freeing - * it, we can clip it as needed to avoid freeing memory we - * want to keep for UEFI. - */ - - keep_end = 0; - free_start = 0; - - for_each_efi_memory_desc(&memmap, md) { - u64 paddr, npages, size; - - if (is_reserve_region(md)) { - /* - * We don't want to free any memory from this region. - */ - if (free_start) { - /* adjust free_end then free region */ - if (free_end > md->phys_addr) - free_end -= PAGE_SIZE; - total_freed += free_region(free_start, free_end); - free_start = 0; - } - keep_end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT); - continue; - } - - if (md->type != EFI_BOOT_SERVICES_CODE && - md->type != EFI_BOOT_SERVICES_DATA) { - /* no need to free this region */ - continue; - } - - /* - * We want to free memory from this region. - */ - paddr = md->phys_addr; - npages = md->num_pages; - memrange_efi_to_native(&paddr, &npages); - size = npages << PAGE_SHIFT; - - if (free_start) { - if (paddr <= free_end) - free_end = paddr + size; - else { - total_freed += free_region(free_start, free_end); - free_start = paddr; - free_end = paddr + size; - } - } else { - free_start = paddr; - free_end = paddr + size; - } - if (free_start < keep_end) { - free_start += PAGE_SIZE; - if (free_start >= free_end) - free_start = 0; - } - } - if (free_start) - total_freed += free_region(free_start, free_end); - - if (total_freed) - pr_info("Freed 0x%llx bytes of EFI boot services memory", - total_freed); -} - void __init efi_init(void) { struct efi_fdt_params params; @@ -402,8 +283,6 @@ static int __init arm64_enable_runtime_services(void) } set_bit(EFI_SYSTEM_TABLES, &efi.flags); - free_boot_services(); - if (!efi_enabled(EFI_VIRTMAP)) { pr_err("No UEFI virtual mapping was installed -- runtime services will not be available\n"); return -1; -- cgit v0.10.2 From 9679be103108926cfe9e6fd2f6829cefa77e47b0 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 20 Oct 2014 16:41:38 +0200 Subject: arm64/efi: remove idmap manipulations from UEFI code Now that we have moved the call to SetVirtualAddressMap() to the stub, UEFI has no use for the ID map, so we can drop the code that installs ID mappings for UEFI memory regions. Acked-by: Leif Lindholm Acked-by: Will Deacon Tested-by: Leif Lindholm Signed-off-by: Ard Biesheuvel diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h index effef37..7baf2cc 100644 --- a/arch/arm64/include/asm/efi.h +++ b/arch/arm64/include/asm/efi.h @@ -6,11 +6,9 @@ #ifdef CONFIG_EFI extern void efi_init(void); -extern void efi_idmap_init(void); extern void efi_virtmap_init(void); #else #define efi_init() -#define efi_idmap_init() #define efi_virtmap_init() #endif diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index 5fd40c4..3d31176 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -31,8 +31,6 @@ extern void paging_init(void); extern void setup_mm_for_reboot(void); extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt); extern void init_mem_pgprot(void); -/* create an identity mapping for memory (or io if map_io is true) */ -extern void create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io); extern void create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys, unsigned long virt, phys_addr_t size, pgprot_t prot); diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index 4a5d734..a98415b 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -54,27 +54,6 @@ static int __init is_normal_ram(efi_memory_desc_t *md) return 0; } -static void __init efi_setup_idmap(void) -{ - struct memblock_region *r; - efi_memory_desc_t *md; - u64 paddr, npages, size; - - for_each_memblock(memory, r) - create_id_mapping(r->base, r->size, 0); - - /* map runtime io spaces */ - for_each_efi_memory_desc(&memmap, md) { - if (!(md->attribute & EFI_MEMORY_RUNTIME) || is_normal_ram(md)) - continue; - paddr = md->phys_addr; - npages = md->num_pages; - memrange_efi_to_native(&paddr, &npages); - size = npages << PAGE_SHIFT; - create_id_mapping(paddr, size, 1); - } -} - /* * Translate a EFI virtual address into a physical address: this is necessary, * as some data members of the EFI system table are virtually remapped after @@ -236,16 +215,6 @@ void __init efi_init(void) reserve_regions(); } -void __init efi_idmap_init(void) -{ - if (!efi_enabled(EFI_BOOT)) - return; - - /* boot time idmap_pg_dir is incomplete, so fill in missing parts */ - efi_setup_idmap(); - early_memunmap(memmap.map, memmap.map_end - memmap.map); -} - /* * Enable the UEFI Runtime Services if all prerequisites are in place, i.e., * non-early mapping of the UEFI system table and virtual mappings for all @@ -386,4 +355,5 @@ void __init efi_virtmap_init(void) create_pgd_mapping(&efi_mm, paddr, md->virt_addr, size, prot); } set_bit(EFI_VIRTMAP, &efi.flags); + early_memunmap(memmap.map, memmap.map_end - memmap.map); } diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index beac818..199d1b7 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -402,7 +402,6 @@ void __init setup_arch(char **cmdline_p) request_standard_resources(); efi_virtmap_init(); - efi_idmap_init(); early_ioremap_reset(); unflatten_device_tree(); diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 3f3d5aa..3286385 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -271,18 +271,6 @@ static void __init create_mapping(phys_addr_t phys, unsigned long virt, size, PAGE_KERNEL_EXEC); } -void __init create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io) -{ - if ((addr >> PGDIR_SHIFT) >= ARRAY_SIZE(idmap_pg_dir)) { - pr_warn("BUG: not creating id mapping for %pa\n", &addr); - return; - } - __create_mapping(&init_mm, &idmap_pg_dir[pgd_index(addr)], - addr, addr, size, - map_io ? __pgprot(PROT_DEVICE_nGnRE) - : PAGE_KERNEL_EXEC); -} - void __init create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys, unsigned long virt, phys_addr_t size, pgprot_t prot) -- cgit v0.10.2 From a18f36453e0788d2d285a62b85c7c668ec119a64 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Fri, 9 Jan 2015 18:30:13 +0100 Subject: ARM: 8266/1: Remove early stack deallocation from restore_user_regs Currently restore_user_regs deallocates the SVC stack early in its execution and relies on no exception being taken between the deallocation and the registers being restored. The introduction of a default FIQ handler that also uses the SVC stack breaks this assumption and can result in corrupted register state. This patch works around the problem by removing the early stack deallocation and using r2 as a temporary instead. I have not found a way to do this without introducing an extra mov instruction to the macro. Signed-off-by: Daniel Thompson Signed-off-by: Russell King diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S index 4176df7..1a0045a 100644 --- a/arch/arm/kernel/entry-header.S +++ b/arch/arm/kernel/entry-header.S @@ -253,21 +253,22 @@ .endm .macro restore_user_regs, fast = 0, offset = 0 - ldr r1, [sp, #\offset + S_PSR] @ get calling cpsr - ldr lr, [sp, #\offset + S_PC]! @ get pc + mov r2, sp + ldr r1, [r2, #\offset + S_PSR] @ get calling cpsr + ldr lr, [r2, #\offset + S_PC]! @ get pc msr spsr_cxsf, r1 @ save in spsr_svc #if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_32v6K) @ We must avoid clrex due to Cortex-A15 erratum #830321 - strex r1, r2, [sp] @ clear the exclusive monitor + strex r1, r2, [r2] @ clear the exclusive monitor #endif .if \fast - ldmdb sp, {r1 - lr}^ @ get calling r1 - lr + ldmdb r2, {r1 - lr}^ @ get calling r1 - lr .else - ldmdb sp, {r0 - lr}^ @ get calling r0 - lr + ldmdb r2, {r0 - lr}^ @ get calling r0 - lr .endif mov r0, r0 @ ARMv5T and earlier require a nop @ after ldm {}^ - add sp, sp, #S_FRAME_SIZE - S_PC + add sp, sp, #\offset + S_FRAME_SIZE movs pc, lr @ return & move spsr_svc into cpsr .endm -- cgit v0.10.2 From 2d9ed7406fd24987b75f78fea8290202d8108f34 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Mon, 5 Jan 2015 15:58:54 +0100 Subject: ARM: 8255/1: perf: Prevent wraparound during overflow If the overflow threshold for a counter is set above or near the 0xffffffff boundary then the kernel may lose track of the overflow causing only events that occur *after* the overflow to be recorded. Specifically the problem occurs when the value of the performance counter overtakes its original programmed value due to wrap around. Typical solutions to this problem are either to avoid programming in values likely to be overtaken or to treat the overflow bit as the 33rd bit of the counter. Its somewhat fiddly to refactor the code to correctly handle the 33rd bit during irqsave sections (context switches for example) so instead we take the simpler approach of avoiding values likely to be overtaken. We set the limit to half of max_period because this matches the limit imposed in __hw_perf_event_init(). This causes a doubling of the interrupt rate for large threshold values, however even with a very fast counter ticking at 4GHz the interrupt rate would only be ~1Hz. Signed-off-by: Daniel Thompson Acked-by: Will Deacon Signed-off-by: Russell King diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index f7c65ad..557e128 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -116,8 +116,14 @@ int armpmu_event_set_period(struct perf_event *event) ret = 1; } - if (left > (s64)armpmu->max_period) - left = armpmu->max_period; + /* + * Limit the maximum period to prevent the counter value + * from overtaking the one we are about to program. In + * effect we are reducing max_period to account for + * interrupt latency (and we are being very conservative). + */ + if (left > (armpmu->max_period >> 1)) + left = armpmu->max_period >> 1; local64_set(&hwc->prev_count, (u64)-left); -- cgit v0.10.2 From cb9627806ce898c436dc74252718e4a757b33bc3 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 12 Jan 2015 09:21:24 -0800 Subject: Bluetooth: Add defintions for HCI Read Stored Link Key command This patch adds the missing commmand structure and command complete structure for the HCI Read Store Link Key command. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index d0bca31..91cd9d3 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -855,6 +855,17 @@ struct hci_cp_set_event_flt { #define HCI_CONN_SETUP_AUTO_OFF 0x01 #define HCI_CONN_SETUP_AUTO_ON 0x02 +#define HCI_OP_READ_STORED_LINK_KEY 0x0c0d +struct hci_cp_read_stored_link_key { + bdaddr_t bdaddr; + __u8 read_all; +} __packed; +struct hci_rp_read_stored_link_key { + __u8 status; + __u8 max_keys; + __u8 num_keys; +} __packed; + #define HCI_OP_DELETE_STORED_LINK_KEY 0x0c12 struct hci_cp_delete_stored_link_key { bdaddr_t bdaddr; -- cgit v0.10.2 From c2f0f979276fc4911cef5da2fc113f0daeda3ebc Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 12 Jan 2015 09:21:25 -0800 Subject: Bluetooth: Handle command complete event for HCI Read Stored Link Keys When the HCI Read Stored Link Keys command completes it gives useful information of the current stored keys and maximum keys a controller can actually store. So process this event and store these information in hci_dev structure. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 89f4e3c..1f21fe4 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -205,6 +205,8 @@ struct hci_dev { __u16 lmp_subver; __u16 voice_setting; __u8 num_iac; + __u8 stored_max_keys; + __u8 stored_num_keys; __u8 io_capability; __s8 inq_tx_power; __u16 page_scan_interval; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0881efd..b1580da 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -214,6 +214,24 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) hci_bdaddr_list_clear(&hdev->le_white_list); } +static void hci_cc_read_stored_link_key(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_read_stored_link_key *rp = (void *)skb->data; + struct hci_cp_read_stored_link_key *sent; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + sent = hci_sent_cmd_data(hdev, HCI_OP_READ_STORED_LINK_KEY); + if (!sent) + return; + + if (!rp->status && sent->read_all == 0x01) { + hdev->stored_max_keys = rp->max_keys; + hdev->stored_num_keys = rp->num_keys; + } +} + static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -2714,6 +2732,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_reset(hdev, skb); break; + case HCI_OP_READ_STORED_LINK_KEY: + hci_cc_read_stored_link_key(hdev, skb); + break; + case HCI_OP_WRITE_LOCAL_NAME: hci_cc_write_local_name(hdev, skb); break; -- cgit v0.10.2 From 48ce62c4fae7e817d9018020345bbe30a6b9c446 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 12 Jan 2015 09:21:26 -0800 Subject: Bluetooth: Read stored link key information when powering on controller The information about max stored link keys and current stored link keys should be read at controller initialization. So issue HCI Read Stored Link Key command with BDADDR_ANY and read_all flag set to 0x01 to retrieve this information. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ba0d1fd..96572a4 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -731,6 +731,14 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) hci_setup_event_mask(req); + if (hdev->commands[6] & 0x20) { + struct hci_cp_read_stored_link_key cp; + + bacpy(&cp.bdaddr, BDADDR_ANY); + cp.read_all = 0x01; + hci_req_add(req, HCI_OP_READ_STORED_LINK_KEY, sizeof(cp), &cp); + } + /* Some Broadcom based Bluetooth controllers do not support the * Delete Stored Link Key command. They are clearly indicating its * absence in the bit mask of supported commands. -- cgit v0.10.2 From 039d4e410c06ed446566105a0a1c6ce9cad6b56b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 12 Jan 2015 09:21:27 -0800 Subject: Bluetooth: Add missing response structure for HCI Delete Stored Link Key This patch adds this missing structure for processing the result of the HCI Delete Stored Link Key command. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 91cd9d3..8e54f82 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -871,6 +871,10 @@ struct hci_cp_delete_stored_link_key { bdaddr_t bdaddr; __u8 delete_all; } __packed; +struct hci_rp_delete_stored_link_key { + __u8 status; + __u8 num_keys; +} __packed; #define HCI_MAX_NAME_LENGTH 248 -- cgit v0.10.2 From a93661203641eb5e8aa9bbed8d9689e5d5dbe6b1 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 12 Jan 2015 09:21:28 -0800 Subject: Bluetooth: Process result of HCI Delete Stored Link Key command When the HCI Delete Stored Link Key command completes, then update the value of current stored keys in hci_dev structure. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index b1580da..a58845e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -232,6 +232,22 @@ static void hci_cc_read_stored_link_key(struct hci_dev *hdev, } } +static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_delete_stored_link_key *rp = (void *)skb->data; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (rp->status) + return; + + if (rp->num_keys <= hdev->stored_num_keys) + hdev->stored_num_keys -= rp->num_keys; + else + hdev->stored_num_keys = 0; +} + static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -2736,6 +2752,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_read_stored_link_key(hdev, skb); break; + case HCI_OP_DELETE_STORED_LINK_KEY: + hci_cc_delete_stored_link_key(hdev, skb); + break; + case HCI_OP_WRITE_LOCAL_NAME: hci_cc_write_local_name(hdev, skb); break; -- cgit v0.10.2 From 3ec7a176fa81107c20f1693c64873c11a6cd8fa4 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 8 Jan 2015 13:52:12 +0300 Subject: net: eth: xgene: devm_ioremap() returns NULL on error devm_ioremap() returns NULL on failure, it doesn't return an ERR_PTR. Fixes: de7b5b3d790a ('net: eth: xgene: change APM X-Gene SoC platform ethernet to support ACPI') Signed-off-by: Dan Carpenter Acked-by: Iyappan Subramanian Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 1e56bf3..02add38 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -804,9 +804,9 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) return -ENODEV; } pdata->base_addr = devm_ioremap(dev, res->start, resource_size(res)); - if (IS_ERR(pdata->base_addr)) { + if (!pdata->base_addr) { dev_err(dev, "Unable to retrieve ENET Port CSR region\n"); - return PTR_ERR(pdata->base_addr); + return -ENOMEM; } res = platform_get_resource(pdev, IORESOURCE_MEM, RES_RING_CSR); @@ -816,9 +816,9 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) } pdata->ring_csr_addr = devm_ioremap(dev, res->start, resource_size(res)); - if (IS_ERR(pdata->ring_csr_addr)) { + if (!pdata->ring_csr_addr) { dev_err(dev, "Unable to retrieve ENET Ring CSR region\n"); - return PTR_ERR(pdata->ring_csr_addr); + return -ENOMEM; } res = platform_get_resource(pdev, IORESOURCE_MEM, RES_RING_CMD); @@ -828,9 +828,9 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) } pdata->ring_cmd_addr = devm_ioremap(dev, res->start, resource_size(res)); - if (IS_ERR(pdata->ring_cmd_addr)) { + if (!pdata->ring_cmd_addr) { dev_err(dev, "Unable to retrieve ENET Ring command region\n"); - return PTR_ERR(pdata->ring_cmd_addr); + return -ENOMEM; } ret = platform_get_irq(pdev, 0); -- cgit v0.10.2 From 20d14a5d377a6265c2e89986d052f79dff922217 Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Thu, 8 Jan 2015 16:13:07 +0100 Subject: tg3: move init/deinit from open/close to probe/remove Move init and deinit of PTP support from open/close functions to probe/remove funcs to avoid removing/re-adding of associated PTP device(s) during ifup/ifdown. v2: tg3_ptp_init call moved to correct place (thx. Prashant) Signed-off-by: Ivan Vecera Acked-by: Prashant Sreedharan Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 553dcd8..356bd5b 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -11556,11 +11556,7 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq, tg3_flag_set(tp, INIT_COMPLETE); tg3_enable_ints(tp); - if (init) - tg3_ptp_init(tp); - else - tg3_ptp_resume(tp); - + tg3_ptp_resume(tp); tg3_full_unlock(tp); @@ -11681,13 +11677,6 @@ static int tg3_open(struct net_device *dev) pci_set_power_state(tp->pdev, PCI_D3hot); } - if (tg3_flag(tp, PTP_CAPABLE)) { - tp->ptp_clock = ptp_clock_register(&tp->ptp_info, - &tp->pdev->dev); - if (IS_ERR(tp->ptp_clock)) - tp->ptp_clock = NULL; - } - return err; } @@ -11701,8 +11690,6 @@ static int tg3_close(struct net_device *dev) return -EAGAIN; } - tg3_ptp_fini(tp); - tg3_stop(tp); /* Clear stats across close / open calls */ @@ -17880,6 +17867,14 @@ static int tg3_init_one(struct pci_dev *pdev, goto err_out_apeunmap; } + if (tg3_flag(tp, PTP_CAPABLE)) { + tg3_ptp_init(tp); + tp->ptp_clock = ptp_clock_register(&tp->ptp_info, + &tp->pdev->dev); + if (IS_ERR(tp->ptp_clock)) + tp->ptp_clock = NULL; + } + netdev_info(dev, "Tigon3 [partno(%s) rev %04x] (%s) MAC address %pM\n", tp->board_part_number, tg3_chip_rev_id(tp), @@ -17955,6 +17950,8 @@ static void tg3_remove_one(struct pci_dev *pdev) if (dev) { struct tg3 *tp = netdev_priv(dev); + tg3_ptp_fini(tp); + release_firmware(tp->fw); tg3_reset_task_cancel(tp); -- cgit v0.10.2 From eee2f04b801ebc95da16f91fef029a083ba1a216 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 8 Jan 2015 11:29:18 -0500 Subject: packet: make packet too small warning match condition The expression in ll_header_truncated() tests less than or equal, but the warning prints less than. Update the warning. Reported-by: Jouni Malinen Signed-off-by: Willem de Bruijn Acked-by: Daniel Borkmann Signed-off-by: David S. Miller diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 6880f34..0f02668 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2102,7 +2102,7 @@ static bool ll_header_truncated(const struct net_device *dev, int len) { /* net device doesn't like empty head */ if (unlikely(len <= dev->hard_header_len)) { - net_warn_ratelimited("%s: packet size is too short (%d < %d)\n", + net_warn_ratelimited("%s: packet size is too short (%d <= %d)\n", current->comm, len, dev->hard_header_len); return true; } -- cgit v0.10.2 From 3bf3947526c1053ddf2523f261395d682718f56c Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Thu, 8 Jan 2015 12:31:18 -0800 Subject: vxlan: Improve support for header flags This patch cleans up the header flags of VXLAN in anticipation of defining some new ones: - Move header related definitions from vxlan.c to vxlan.h - Change VXLAN_FLAGS to be VXLAN_HF_VNI (only currently defined flag) - Move check for unknown flags to after we find vxlan_sock, this assumes that some flags may be processed based on tunnel configuration - Add a comment about why the stack treating unknown set flags as an error instead of ignoring them Signed-off-by: Tom Herbert Signed-off-by: David S. Miller diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 2ab0922..3a18d8e 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -61,12 +61,6 @@ #define FDB_AGE_DEFAULT 300 /* 5 min */ #define FDB_AGE_INTERVAL (10 * HZ) /* rescan interval */ -#define VXLAN_N_VID (1u << 24) -#define VXLAN_VID_MASK (VXLAN_N_VID - 1) -#define VXLAN_HLEN (sizeof(struct udphdr) + sizeof(struct vxlanhdr)) - -#define VXLAN_FLAGS 0x08000000 /* struct vxlanhdr.vx_flags required value. */ - /* UDP port for VXLAN traffic. * The IANA assigned port is 4789, but the Linux default is 8472 * for compatibility with early adopters. @@ -1095,18 +1089,21 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) { struct vxlan_sock *vs; struct vxlanhdr *vxh; + u32 flags, vni; /* Need Vxlan and inner Ethernet header to be present */ if (!pskb_may_pull(skb, VXLAN_HLEN)) goto error; - /* Return packets with reserved bits set */ vxh = (struct vxlanhdr *)(udp_hdr(skb) + 1); - if (vxh->vx_flags != htonl(VXLAN_FLAGS) || - (vxh->vx_vni & htonl(0xff))) { - netdev_dbg(skb->dev, "invalid vxlan flags=%#x vni=%#x\n", - ntohl(vxh->vx_flags), ntohl(vxh->vx_vni)); - goto error; + flags = ntohl(vxh->vx_flags); + vni = ntohl(vxh->vx_vni); + + if (flags & VXLAN_HF_VNI) { + flags &= ~VXLAN_HF_VNI; + } else { + /* VNI flag always required to be set */ + goto bad_flags; } if (iptunnel_pull_header(skb, VXLAN_HLEN, htons(ETH_P_TEB))) @@ -1116,6 +1113,19 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) if (!vs) goto drop; + if (flags || (vni & 0xff)) { + /* If there are any unprocessed flags remaining treat + * this as a malformed packet. This behavior diverges from + * VXLAN RFC (RFC7348) which stipulates that bits in reserved + * in reserved fields are to be ignored. The approach here + * maintains compatbility with previous stack code, and also + * is more robust and provides a little more security in + * adding extensions to VXLAN. + */ + + goto bad_flags; + } + vs->rcv(vs, skb, vxh->vx_vni); return 0; @@ -1124,6 +1134,10 @@ drop: kfree_skb(skb); return 0; +bad_flags: + netdev_dbg(skb->dev, "invalid vxlan flags=%#x vni=%#x\n", + ntohl(vxh->vx_flags), ntohl(vxh->vx_vni)); + error: /* Return non vxlan pkt */ return 1; @@ -1563,7 +1577,7 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs, } vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh)); - vxh->vx_flags = htonl(VXLAN_FLAGS); + vxh->vx_flags = htonl(VXLAN_HF_VNI); vxh->vx_vni = vni; skb_set_inner_protocol(skb, htons(ETH_P_TEB)); @@ -1607,7 +1621,7 @@ int vxlan_xmit_skb(struct vxlan_sock *vs, return -ENOMEM; vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh)); - vxh->vx_flags = htonl(VXLAN_FLAGS); + vxh->vx_flags = htonl(VXLAN_HF_VNI); vxh->vx_vni = vni; skb_set_inner_protocol(skb, htons(ETH_P_TEB)); diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 903461a..a0d8073 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -17,6 +17,13 @@ struct vxlanhdr { __be32 vx_vni; }; +/* VXLAN header flags. */ +#define VXLAN_HF_VNI 0x08000000 + +#define VXLAN_N_VID (1u << 24) +#define VXLAN_VID_MASK (VXLAN_N_VID - 1) +#define VXLAN_HLEN (sizeof(struct udphdr) + sizeof(struct vxlanhdr)) + struct vxlan_sock; typedef void (vxlan_rcv_t)(struct vxlan_sock *vh, struct sk_buff *skb, __be32 key); -- cgit v0.10.2 From aa2e092671c84b149ba0f9ad7185ace357fb4a33 Mon Sep 17 00:00:00 2001 From: hayeswang Date: Fri, 9 Jan 2015 10:26:35 +0800 Subject: r8152: call rtl_start_rx after netif_carrier_on Remove rtl_start_rx() from rtl_enable() and put it after calling netif_carrier_on(). Signed-off-by: Hayes Wang Signed-off-by: David S. Miller diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 57ec23e..cd93388 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -2059,7 +2059,7 @@ static int rtl_enable(struct r8152 *tp) rxdy_gated_en(tp, false); - return rtl_start_rx(tp); + return 0; } static int rtl8152_enable(struct r8152 *tp) @@ -2874,6 +2874,7 @@ static void set_carrier(struct r8152 *tp) tp->rtl_ops.enable(tp); set_bit(RTL8152_SET_RX_MODE, &tp->flags); netif_carrier_on(netdev); + rtl_start_rx(tp); } } else { if (tp->speed & LINK_STATUS) { -- cgit v0.10.2 From ef827a5b578c4255043a0c0032c88799fb4a774b Mon Sep 17 00:00:00 2001 From: hayeswang Date: Fri, 9 Jan 2015 10:26:36 +0800 Subject: r8152: check the status before submitting rx Don't submit the rx if the device is unplugged, stopped, or linking down. Signed-off-by: Hayes Wang Signed-off-by: David S. Miller diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index cd93388..b23426e 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -1789,6 +1789,11 @@ int r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags) { int ret; + /* The rx would be stopped, so skip submitting */ + if (test_bit(RTL8152_UNPLUG, &tp->flags) || + !test_bit(WORK_ENABLE, &tp->flags) || !netif_carrier_ok(tp->netdev)) + return 0; + usb_fill_bulk_urb(agg->urb, tp->udev, usb_rcvbulkpipe(tp->udev, 1), agg->head, agg_buf_sz, (usb_complete_t)read_bulk_callback, agg); -- cgit v0.10.2 From f902e8812ef657c6cf744ac25e21865217155460 Mon Sep 17 00:00:00 2001 From: Toshiaki Makita Date: Fri, 9 Jan 2015 14:16:40 +0900 Subject: bridge: Add ability to enable TSO Currently a bridge device turns off TSO feature if no bridge ports support it. We can always enable it, since packets can be segmented on ports by software as well as on the bridge device. This will reduce the number of packets processed in the bridge. Signed-off-by: Toshiaki Makita Signed-off-by: David S. Miller diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index ed307db..81e49fb 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -424,6 +424,7 @@ netdev_features_t br_features_recompute(struct net_bridge *br, features = netdev_increment_features(features, p->dev->features, mask); } + features = netdev_add_tso_features(features, mask); return features; } -- cgit v0.10.2 From 6c53e938a81c0b31f9f6a31690c3be601aa8fa60 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Thu, 8 Jan 2015 21:38:15 -0800 Subject: iw_cxgb4/cxgb4/cxgb4i: Cleanup register defines/MACROS related to CM CPL messages This patch cleanups all macros/register define related to connection management CPL messages that are defined in t4_msg.h and the affected files Signed-off-by: Anish Bhatt Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 9edc200..8787279 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -235,19 +235,19 @@ static void release_tid(struct c4iw_rdev *rdev, u32 hwtid, struct sk_buff *skb) static void set_emss(struct c4iw_ep *ep, u16 opt) { - ep->emss = ep->com.dev->rdev.lldi.mtus[GET_TCPOPT_MSS(opt)] - + ep->emss = ep->com.dev->rdev.lldi.mtus[TCPOPT_MSS_G(opt)] - ((AF_INET == ep->com.remote_addr.ss_family) ? sizeof(struct iphdr) : sizeof(struct ipv6hdr)) - sizeof(struct tcphdr); ep->mss = ep->emss; - if (GET_TCPOPT_TSTAMP(opt)) + if (TCPOPT_TSTAMP_G(opt)) ep->emss -= round_up(TCPOLEN_TIMESTAMP, 4); if (ep->emss < 128) ep->emss = 128; if (ep->emss & 7) PDBG("Warning: misaligned mtu idx %u mss %u emss=%u\n", - GET_TCPOPT_MSS(opt), ep->mss, ep->emss); - PDBG("%s mss_idx %u mss %u emss=%u\n", __func__, GET_TCPOPT_MSS(opt), + TCPOPT_MSS_G(opt), ep->mss, ep->emss); + PDBG("%s mss_idx %u mss %u emss=%u\n", __func__, TCPOPT_MSS_G(opt), ep->mss, ep->emss); } @@ -652,24 +652,24 @@ static int send_connect(struct c4iw_ep *ep) if (win > RCV_BUFSIZ_M) win = RCV_BUFSIZ_M; - opt0 = (nocong ? NO_CONG(1) : 0) | + opt0 = (nocong ? NO_CONG_F : 0) | KEEP_ALIVE_F | - DELACK(1) | + DELACK_F | WND_SCALE_V(wscale) | MSS_IDX_V(mtu_idx) | L2T_IDX_V(ep->l2t->idx) | TX_CHAN_V(ep->tx_chan) | SMAC_SEL_V(ep->smac_idx) | - DSCP(ep->tos) | + DSCP_V(ep->tos) | ULP_MODE_V(ULP_MODE_TCPDDP) | RCV_BUFSIZ_V(win); opt2 = RX_CHANNEL_V(0) | - CCTRL_ECN(enable_ecn) | + CCTRL_ECN_V(enable_ecn) | RSS_QUEUE_VALID_F | RSS_QUEUE_V(ep->rss_qid); if (enable_tcp_timestamps) - opt2 |= TSTAMPS_EN(1); + opt2 |= TSTAMPS_EN_F; if (enable_tcp_sack) - opt2 |= SACK_EN(1); + opt2 |= SACK_EN_F; if (wscale && enable_tcp_window_scaling) opt2 |= WND_SCALE_EN_F; if (is_t5(ep->com.dev->rdev.lldi.adapter_type)) { @@ -1042,7 +1042,7 @@ static int act_establish(struct c4iw_dev *dev, struct sk_buff *skb) struct c4iw_ep *ep; struct cpl_act_establish *req = cplhdr(skb); unsigned int tid = GET_TID(req); - unsigned int atid = GET_TID_TID(ntohl(req->tos_atid)); + unsigned int atid = TID_TID_G(ntohl(req->tos_atid)); struct tid_info *t = dev->rdev.lldi.tids; ep = lookup_atid(t, atid); @@ -1751,7 +1751,7 @@ static void send_fw_act_open_req(struct c4iw_ep *ep, unsigned int atid) skb = get_skb(NULL, sizeof(*req), GFP_KERNEL); req = (struct fw_ofld_connection_wr *)__skb_put(skb, sizeof(*req)); memset(req, 0, sizeof(*req)); - req->op_compl = htonl(V_WR_OP(FW_OFLD_CONNECTION_WR)); + req->op_compl = htonl(WR_OP_V(FW_OFLD_CONNECTION_WR)); req->len16_pkd = htonl(FW_WR_LEN16_V(DIV_ROUND_UP(sizeof(*req), 16))); req->le.filter = cpu_to_be32(cxgb4_select_ntuple( ep->com.dev->rdev.lldi.ports[0], @@ -1782,27 +1782,27 @@ static void send_fw_act_open_req(struct c4iw_ep *ep, unsigned int atid) if (win > RCV_BUFSIZ_M) win = RCV_BUFSIZ_M; - req->tcb.opt0 = (__force __be64) (TCAM_BYPASS(1) | - (nocong ? NO_CONG(1) : 0) | + req->tcb.opt0 = (__force __be64) (TCAM_BYPASS_F | + (nocong ? NO_CONG_F : 0) | KEEP_ALIVE_F | - DELACK(1) | + DELACK_F | WND_SCALE_V(wscale) | MSS_IDX_V(mtu_idx) | L2T_IDX_V(ep->l2t->idx) | TX_CHAN_V(ep->tx_chan) | SMAC_SEL_V(ep->smac_idx) | - DSCP(ep->tos) | + DSCP_V(ep->tos) | ULP_MODE_V(ULP_MODE_TCPDDP) | RCV_BUFSIZ_V(win)); - req->tcb.opt2 = (__force __be32) (PACE(1) | - TX_QUEUE(ep->com.dev->rdev.lldi.tx_modq[ep->tx_chan]) | + req->tcb.opt2 = (__force __be32) (PACE_V(1) | + TX_QUEUE_V(ep->com.dev->rdev.lldi.tx_modq[ep->tx_chan]) | RX_CHANNEL_V(0) | - CCTRL_ECN(enable_ecn) | + CCTRL_ECN_V(enable_ecn) | RSS_QUEUE_VALID_F | RSS_QUEUE_V(ep->rss_qid)); if (enable_tcp_timestamps) - req->tcb.opt2 |= (__force __be32)TSTAMPS_EN(1); + req->tcb.opt2 |= (__force __be32)TSTAMPS_EN_F; if (enable_tcp_sack) - req->tcb.opt2 |= (__force __be32)SACK_EN(1); + req->tcb.opt2 |= (__force __be32)SACK_EN_F; if (wscale && enable_tcp_window_scaling) req->tcb.opt2 |= (__force __be32)WND_SCALE_EN_F; req->tcb.opt0 = cpu_to_be64((__force u64)req->tcb.opt0); @@ -2023,10 +2023,10 @@ static int act_open_rpl(struct c4iw_dev *dev, struct sk_buff *skb) { struct c4iw_ep *ep; struct cpl_act_open_rpl *rpl = cplhdr(skb); - unsigned int atid = GET_TID_TID(GET_AOPEN_ATID( - ntohl(rpl->atid_status))); + unsigned int atid = TID_TID_G(AOPEN_ATID_G( + ntohl(rpl->atid_status))); struct tid_info *t = dev->rdev.lldi.tids; - int status = GET_AOPEN_STATUS(ntohl(rpl->atid_status)); + int status = AOPEN_STATUS_G(ntohl(rpl->atid_status)); struct sockaddr_in *la; struct sockaddr_in *ra; struct sockaddr_in6 *la6; @@ -2064,7 +2064,7 @@ static int act_open_rpl(struct c4iw_dev *dev, struct sk_buff *skb) if (ep->com.local_addr.ss_family == AF_INET && dev->rdev.lldi.enable_fw_ofld_conn) { send_fw_act_open_req(ep, - GET_TID_TID(GET_AOPEN_ATID( + TID_TID_G(AOPEN_ATID_G( ntohl(rpl->atid_status)))); return 0; } @@ -2181,24 +2181,24 @@ static void accept_cr(struct c4iw_ep *ep, struct sk_buff *skb, win = ep->rcv_win >> 10; if (win > RCV_BUFSIZ_M) win = RCV_BUFSIZ_M; - opt0 = (nocong ? NO_CONG(1) : 0) | + opt0 = (nocong ? NO_CONG_F : 0) | KEEP_ALIVE_F | - DELACK(1) | + DELACK_F | WND_SCALE_V(wscale) | MSS_IDX_V(mtu_idx) | L2T_IDX_V(ep->l2t->idx) | TX_CHAN_V(ep->tx_chan) | SMAC_SEL_V(ep->smac_idx) | - DSCP(ep->tos >> 2) | + DSCP_V(ep->tos >> 2) | ULP_MODE_V(ULP_MODE_TCPDDP) | RCV_BUFSIZ_V(win); opt2 = RX_CHANNEL_V(0) | RSS_QUEUE_VALID_F | RSS_QUEUE_V(ep->rss_qid); if (enable_tcp_timestamps && req->tcpopt.tstamp) - opt2 |= TSTAMPS_EN(1); + opt2 |= TSTAMPS_EN_F; if (enable_tcp_sack && req->tcpopt.sack) - opt2 |= SACK_EN(1); + opt2 |= SACK_EN_F; if (wscale && enable_tcp_window_scaling) opt2 |= WND_SCALE_EN_F; if (enable_ecn) { @@ -2208,7 +2208,7 @@ static void accept_cr(struct c4iw_ep *ep, struct sk_buff *skb, tcph = (const void *)(req + 1) + G_ETH_HDR_LEN(hlen) + G_IP_HDR_LEN(hlen); if (tcph->ece && tcph->cwr) - opt2 |= CCTRL_ECN(1); + opt2 |= CCTRL_ECN_V(1); } if (is_t5(ep->com.dev->rdev.lldi.adapter_type)) { u32 isn = (prandom_u32() & ~7UL) - 1; @@ -2277,7 +2277,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) { struct c4iw_ep *child_ep = NULL, *parent_ep; struct cpl_pass_accept_req *req = cplhdr(skb); - unsigned int stid = GET_POPEN_TID(ntohl(req->tos_stid)); + unsigned int stid = PASS_OPEN_TID_G(ntohl(req->tos_stid)); struct tid_info *t = dev->rdev.lldi.tids; unsigned int hwtid = GET_TID(req); struct dst_entry *dst; @@ -2310,14 +2310,14 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) ntohs(peer_port), peer_mss); dst = find_route(dev, *(__be32 *)local_ip, *(__be32 *)peer_ip, local_port, peer_port, - GET_POPEN_TOS(ntohl(req->tos_stid))); + PASS_OPEN_TOS_G(ntohl(req->tos_stid))); } else { PDBG("%s parent ep %p hwtid %u laddr %pI6 raddr %pI6 lport %d rport %d peer_mss %d\n" , __func__, parent_ep, hwtid, local_ip, peer_ip, ntohs(local_port), ntohs(peer_port), peer_mss); dst = find_route6(dev, local_ip, peer_ip, local_port, peer_port, - PASS_OPEN_TOS(ntohl(req->tos_stid)), + PASS_OPEN_TOS_G(ntohl(req->tos_stid)), ((struct sockaddr_in6 *) &parent_ep->com.local_addr)->sin6_scope_id); } @@ -2375,7 +2375,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) } c4iw_get_ep(&parent_ep->com); child_ep->parent_ep = parent_ep; - child_ep->tos = GET_POPEN_TOS(ntohl(req->tos_stid)); + child_ep->tos = PASS_OPEN_TOS_G(ntohl(req->tos_stid)); child_ep->dst = dst; child_ep->hwtid = hwtid; @@ -3516,8 +3516,8 @@ static void build_cpl_pass_accept_req(struct sk_buff *skb, int stid , u8 tos) V_ETH_HDR_LEN(G_RX_ETHHDR_LEN(eth_hdr_len))); req->vlan = (__force __be16) vlantag; req->len = (__force __be16) len; - req->tos_stid = cpu_to_be32(PASS_OPEN_TID(stid) | - PASS_OPEN_TOS(tos)); + req->tos_stid = cpu_to_be32(PASS_OPEN_TID_V(stid) | + PASS_OPEN_TOS_V(tos)); req->tcpopt.mss = htons(tmp_opt.mss_clamp); if (tmp_opt.wscale_ok) req->tcpopt.wsf = tmp_opt.snd_wscale; @@ -3542,7 +3542,7 @@ static void send_fw_pass_open_req(struct c4iw_dev *dev, struct sk_buff *skb, req_skb = alloc_skb(sizeof(struct fw_ofld_connection_wr), GFP_KERNEL); req = (struct fw_ofld_connection_wr *)__skb_put(req_skb, sizeof(*req)); memset(req, 0, sizeof(*req)); - req->op_compl = htonl(V_WR_OP(FW_OFLD_CONNECTION_WR) | FW_WR_COMPL_F); + req->op_compl = htonl(WR_OP_V(FW_OFLD_CONNECTION_WR) | FW_WR_COMPL_F); req->len16_pkd = htonl(FW_WR_LEN16_V(DIV_ROUND_UP(sizeof(*req), 16))); req->le.version_cpl = htonl(FW_OFLD_CONNECTION_WR_CPL_F); req->le.filter = (__force __be32) filter; @@ -3556,7 +3556,7 @@ static void send_fw_pass_open_req(struct c4iw_dev *dev, struct sk_buff *skb, htonl(FW_OFLD_CONNECTION_WR_T_STATE_V(TCP_SYN_RECV) | FW_OFLD_CONNECTION_WR_RCV_SCALE_V(cpl->tcpopt.wsf) | FW_OFLD_CONNECTION_WR_ASTID_V( - GET_PASS_OPEN_TID(ntohl(cpl->tos_stid)))); + PASS_OPEN_TID_G(ntohl(cpl->tos_stid)))); /* * We store the qid in opt2 which will be used by the firmware diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 6de41cc..2897f95 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -3416,8 +3416,8 @@ int cxgb4_create_server(const struct net_device *dev, unsigned int stid, req->peer_ip = htonl(0); chan = rxq_to_chan(&adap->sge, queue); req->opt0 = cpu_to_be64(TX_CHAN_V(chan)); - req->opt1 = cpu_to_be64(CONN_POLICY_ASK | - SYN_RSS_ENABLE | SYN_RSS_QUEUE(queue)); + req->opt1 = cpu_to_be64(CONN_POLICY_V(CPL_CONN_POLICY_ASK) | + SYN_RSS_ENABLE_F | SYN_RSS_QUEUE_V(queue)); ret = t4_mgmt_tx(adap, skb); return net_xmit_eval(ret); } @@ -3459,8 +3459,8 @@ int cxgb4_create_server6(const struct net_device *dev, unsigned int stid, req->peer_ip_lo = cpu_to_be64(0); chan = rxq_to_chan(&adap->sge, queue); req->opt0 = cpu_to_be64(TX_CHAN_V(chan)); - req->opt1 = cpu_to_be64(CONN_POLICY_ASK | - SYN_RSS_ENABLE | SYN_RSS_QUEUE(queue)); + req->opt1 = cpu_to_be64(CONN_POLICY_V(CPL_CONN_POLICY_ASK) | + SYN_RSS_ENABLE_F | SYN_RSS_QUEUE_V(queue)); ret = t4_mgmt_tx(adap, skb); return net_xmit_eval(ret); } diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.c b/drivers/net/ethernet/chelsio/cxgb4/l2t.c index 5ae1445..dea984b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/l2t.c +++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.c @@ -151,7 +151,7 @@ static int write_l2e(struct adapter *adap, struct l2t_entry *e, int sync) OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, e->idx | (sync ? F_SYNC_WR : 0) | - TID_QID(adap->sge.fw_evtq.abs_id))); + TID_QID_V(adap->sge.fw_evtq.abs_id))); req->params = htons(L2T_W_PORT(e->lport) | L2T_W_NOREPLY(!sync)); req->l2t_idx = htons(e->idx); req->vlan = htons(e->vlan); diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h index 0f89f68..15e7206 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h @@ -124,6 +124,13 @@ enum CPL_error { }; enum { + CPL_CONN_POLICY_AUTO = 0, + CPL_CONN_POLICY_ASK = 1, + CPL_CONN_POLICY_FILTER = 2, + CPL_CONN_POLICY_DENY = 3 +}; + +enum { ULP_MODE_NONE = 0, ULP_MODE_ISCSI = 2, ULP_MODE_RDMA = 4, @@ -160,16 +167,28 @@ union opcode_tid { u8 opcode; }; -#define CPL_OPCODE(x) ((x) << 24) -#define G_CPL_OPCODE(x) (((x) >> 24) & 0xFF) -#define MK_OPCODE_TID(opcode, tid) (CPL_OPCODE(opcode) | (tid)) +#define CPL_OPCODE_S 24 +#define CPL_OPCODE_V(x) ((x) << CPL_OPCODE_S) +#define CPL_OPCODE_G(x) (((x) >> CPL_OPCODE_S) & 0xFF) +#define TID_G(x) ((x) & 0xFFFFFF) + +/* tid is assumed to be 24-bits */ +#define MK_OPCODE_TID(opcode, tid) (CPL_OPCODE_V(opcode) | (tid)) + #define OPCODE_TID(cmd) ((cmd)->ot.opcode_tid) -#define GET_TID(cmd) (ntohl(OPCODE_TID(cmd)) & 0xFFFFFF) + +/* extract the TID from a CPL command */ +#define GET_TID(cmd) (TID_G(be32_to_cpu(OPCODE_TID(cmd)))) /* partitioning of TID fields that also carry a queue id */ -#define GET_TID_TID(x) ((x) & 0x3fff) -#define GET_TID_QID(x) (((x) >> 14) & 0x3ff) -#define TID_QID(x) ((x) << 14) +#define TID_TID_S 0 +#define TID_TID_M 0x3fff +#define TID_TID_G(x) (((x) >> TID_TID_S) & TID_TID_M) + +#define TID_QID_S 14 +#define TID_QID_M 0x3ff +#define TID_QID_V(x) ((x) << TID_QID_S) +#define TID_QID_G(x) (((x) >> TID_QID_S) & TID_QID_M) struct rss_header { u8 opcode; @@ -199,8 +218,8 @@ struct work_request_hdr { }; /* wr_hi fields */ -#define S_WR_OP 24 -#define V_WR_OP(x) ((__u64)(x) << S_WR_OP) +#define WR_OP_S 24 +#define WR_OP_V(x) ((__u64)(x) << WR_OP_S) #define WR_HDR struct work_request_hdr wr @@ -270,17 +289,42 @@ struct cpl_pass_open_req { __be32 local_ip; __be32 peer_ip; __be64 opt0; -#define NO_CONG(x) ((x) << 4) -#define DELACK(x) ((x) << 5) -#define DSCP(x) ((x) << 22) -#define TCAM_BYPASS(x) ((u64)(x) << 48) -#define NAGLE(x) ((u64)(x) << 49) __be64 opt1; -#define SYN_RSS_ENABLE (1 << 0) -#define SYN_RSS_QUEUE(x) ((x) << 2) -#define CONN_POLICY_ASK (1 << 22) }; +/* option 0 fields */ +#define NO_CONG_S 4 +#define NO_CONG_V(x) ((x) << NO_CONG_S) +#define NO_CONG_F NO_CONG_V(1U) + +#define DELACK_S 5 +#define DELACK_V(x) ((x) << DELACK_S) +#define DELACK_F DELACK_V(1U) + +#define DSCP_S 22 +#define DSCP_M 0x3F +#define DSCP_V(x) ((x) << DSCP_S) +#define DSCP_G(x) (((x) >> DSCP_S) & DSCP_M) + +#define TCAM_BYPASS_S 48 +#define TCAM_BYPASS_V(x) ((__u64)(x) << TCAM_BYPASS_S) +#define TCAM_BYPASS_F TCAM_BYPASS_V(1ULL) + +#define NAGLE_S 49 +#define NAGLE_V(x) ((__u64)(x) << NAGLE_S) +#define NAGLE_F NAGLE_V(1ULL) + +/* option 1 fields */ +#define SYN_RSS_ENABLE_S 0 +#define SYN_RSS_ENABLE_V(x) ((x) << SYN_RSS_ENABLE_S) +#define SYN_RSS_ENABLE_F SYN_RSS_ENABLE_V(1U) + +#define SYN_RSS_QUEUE_S 2 +#define SYN_RSS_QUEUE_V(x) ((x) << SYN_RSS_QUEUE_S) + +#define CONN_POLICY_S 22 +#define CONN_POLICY_V(x) ((x) << CONN_POLICY_S) + struct cpl_pass_open_req6 { WR_HDR; union opcode_tid ot; @@ -304,16 +348,37 @@ struct cpl_pass_accept_rpl { WR_HDR; union opcode_tid ot; __be32 opt2; -#define RX_COALESCE_VALID(x) ((x) << 11) -#define RX_COALESCE(x) ((x) << 12) -#define PACE(x) ((x) << 16) -#define TX_QUEUE(x) ((x) << 23) -#define CCTRL_ECN(x) ((x) << 27) -#define TSTAMPS_EN(x) ((x) << 29) -#define SACK_EN(x) ((x) << 30) __be64 opt0; }; +/* option 2 fields */ +#define RX_COALESCE_VALID_S 11 +#define RX_COALESCE_VALID_V(x) ((x) << RX_COALESCE_VALID_S) +#define RX_COALESCE_VALID_F RX_COALESCE_VALID_V(1U) + +#define RX_COALESCE_S 12 +#define RX_COALESCE_V(x) ((x) << RX_COALESCE_S) + +#define PACE_S 16 +#define PACE_V(x) ((x) << PACE_S) + +#define TX_QUEUE_S 23 +#define TX_QUEUE_M 0x7 +#define TX_QUEUE_V(x) ((x) << TX_QUEUE_S) +#define TX_QUEUE_G(x) (((x) >> TX_QUEUE_S) & TX_QUEUE_M) + +#define CCTRL_ECN_S 27 +#define CCTRL_ECN_V(x) ((x) << CCTRL_ECN_S) +#define CCTRL_ECN_F CCTRL_ECN_V(1U) + +#define TSTAMPS_EN_S 29 +#define TSTAMPS_EN_V(x) ((x) << TSTAMPS_EN_S) +#define TSTAMPS_EN_F TSTAMPS_EN_V(1U) + +#define SACK_EN_S 30 +#define SACK_EN_V(x) ((x) << SACK_EN_S) +#define SACK_EN_F SACK_EN_V(1U) + struct cpl_t5_pass_accept_rpl { WR_HDR; union opcode_tid ot; @@ -384,30 +449,61 @@ struct cpl_t5_act_open_req6 { struct cpl_act_open_rpl { union opcode_tid ot; __be32 atid_status; -#define GET_AOPEN_STATUS(x) ((x) & 0xff) -#define GET_AOPEN_ATID(x) (((x) >> 8) & 0xffffff) }; +/* cpl_act_open_rpl.atid_status fields */ +#define AOPEN_STATUS_S 0 +#define AOPEN_STATUS_M 0xFF +#define AOPEN_STATUS_G(x) (((x) >> AOPEN_STATUS_S) & AOPEN_STATUS_M) + +#define AOPEN_ATID_S 8 +#define AOPEN_ATID_M 0xFFFFFF +#define AOPEN_ATID_G(x) (((x) >> AOPEN_ATID_S) & AOPEN_ATID_M) + struct cpl_pass_establish { union opcode_tid ot; __be32 rsvd; __be32 tos_stid; -#define PASS_OPEN_TID(x) ((x) << 0) -#define PASS_OPEN_TOS(x) ((x) << 24) -#define GET_PASS_OPEN_TID(x) (((x) >> 0) & 0xFFFFFF) -#define GET_POPEN_TID(x) ((x) & 0xffffff) -#define GET_POPEN_TOS(x) (((x) >> 24) & 0xff) __be16 mac_idx; __be16 tcp_opt; -#define GET_TCPOPT_WSCALE_OK(x) (((x) >> 5) & 1) -#define GET_TCPOPT_SACK(x) (((x) >> 6) & 1) -#define GET_TCPOPT_TSTAMP(x) (((x) >> 7) & 1) -#define GET_TCPOPT_SND_WSCALE(x) (((x) >> 8) & 0xf) -#define GET_TCPOPT_MSS(x) (((x) >> 12) & 0xf) __be32 snd_isn; __be32 rcv_isn; }; +/* cpl_pass_establish.tos_stid fields */ +#define PASS_OPEN_TID_S 0 +#define PASS_OPEN_TID_M 0xFFFFFF +#define PASS_OPEN_TID_V(x) ((x) << PASS_OPEN_TID_S) +#define PASS_OPEN_TID_G(x) (((x) >> PASS_OPEN_TID_S) & PASS_OPEN_TID_M) + +#define PASS_OPEN_TOS_S 24 +#define PASS_OPEN_TOS_M 0xFF +#define PASS_OPEN_TOS_V(x) ((x) << PASS_OPEN_TOS_S) +#define PASS_OPEN_TOS_G(x) (((x) >> PASS_OPEN_TOS_S) & PASS_OPEN_TOS_M) + +/* cpl_pass_establish.tcp_opt fields (also applies to act_open_establish) */ +#define TCPOPT_WSCALE_OK_S 5 +#define TCPOPT_WSCALE_OK_M 0x1 +#define TCPOPT_WSCALE_OK_G(x) \ + (((x) >> TCPOPT_WSCALE_OK_S) & TCPOPT_WSCALE_OK_M) + +#define TCPOPT_SACK_S 6 +#define TCPOPT_SACK_M 0x1 +#define TCPOPT_SACK_G(x) (((x) >> TCPOPT_SACK_S) & TCPOPT_SACK_M) + +#define TCPOPT_TSTAMP_S 7 +#define TCPOPT_TSTAMP_M 0x1 +#define TCPOPT_TSTAMP_G(x) (((x) >> TCPOPT_TSTAMP_S) & TCPOPT_TSTAMP_M) + +#define TCPOPT_SND_WSCALE_S 8 +#define TCPOPT_SND_WSCALE_M 0xF +#define TCPOPT_SND_WSCALE_G(x) \ + (((x) >> TCPOPT_SND_WSCALE_S) & TCPOPT_SND_WSCALE_M) + +#define TCPOPT_MSS_S 12 +#define TCPOPT_MSS_M 0xF +#define TCPOPT_MSS_G(x) (((x) >> TCPOPT_MSS_S) & TCPOPT_MSS_M) + struct cpl_act_establish { union opcode_tid ot; __be32 rsvd; diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index 7bfbacd..4591d93 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -450,7 +450,7 @@ static int fwevtq_handler(struct sge_rspq *rspq, const __be64 *rsp, /* FW can send EGR_UPDATEs encapsulated in a CPL_FW4_MSG. */ const struct cpl_sge_egr_update *p = (void *)(rsp + 3); - opcode = G_CPL_OPCODE(ntohl(p->opcode_qid)); + opcode = CPL_OPCODE_G(ntohl(p->opcode_qid)); if (opcode != CPL_SGE_EGR_UPDATE) { dev_err(adapter->pdev_dev, "unexpected FW4/CPL %#x on FW event queue\n" , opcode); diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c index a83d2ce..c3f2b97 100644 --- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c +++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c @@ -704,7 +704,7 @@ static void do_act_establish(struct cxgbi_device *cdev, struct sk_buff *skb) struct cpl_act_establish *req = (struct cpl_act_establish *)skb->data; unsigned short tcp_opt = ntohs(req->tcp_opt); unsigned int tid = GET_TID(req); - unsigned int atid = GET_TID_TID(ntohl(req->tos_atid)); + unsigned int atid = TID_TID_G(ntohl(req->tos_atid)); struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(cdev); struct tid_info *t = lldi->tids; u32 rcv_isn = be32_to_cpu(req->rcv_isn); @@ -752,15 +752,15 @@ static void do_act_establish(struct cxgbi_device *cdev, struct sk_buff *skb) if (cxgb4i_rcv_win > (RCV_BUFSIZ_MASK << 10)) csk->rcv_wup -= cxgb4i_rcv_win - (RCV_BUFSIZ_MASK << 10); - csk->advmss = lldi->mtus[GET_TCPOPT_MSS(tcp_opt)] - 40; - if (GET_TCPOPT_TSTAMP(tcp_opt)) + csk->advmss = lldi->mtus[TCPOPT_MSS_G(tcp_opt)] - 40; + if (TCPOPT_TSTAMP_G(tcp_opt)) csk->advmss -= 12; if (csk->advmss < 128) csk->advmss = 128; log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, "csk 0x%p, mss_idx %u, advmss %u.\n", - csk, GET_TCPOPT_MSS(tcp_opt), csk->advmss); + csk, TCPOPT_MSS_G(tcp_opt), csk->advmss); cxgbi_sock_established(csk, ntohl(req->snd_isn), ntohs(req->tcp_opt)); @@ -856,8 +856,8 @@ static void do_act_open_rpl(struct cxgbi_device *cdev, struct sk_buff *skb) struct cpl_act_open_rpl *rpl = (struct cpl_act_open_rpl *)skb->data; unsigned int tid = GET_TID(rpl); unsigned int atid = - GET_TID_TID(GET_AOPEN_ATID(be32_to_cpu(rpl->atid_status))); - unsigned int status = GET_AOPEN_STATUS(be32_to_cpu(rpl->atid_status)); + TID_TID_G(AOPEN_ATID_G(be32_to_cpu(rpl->atid_status))); + unsigned int status = AOPEN_STATUS_G(be32_to_cpu(rpl->atid_status)); struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(cdev); struct tid_info *t = lldi->tids; -- cgit v0.10.2 From bdc590b99f6002faeb12c1c810cbbf8ac4481f70 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Thu, 8 Jan 2015 21:38:16 -0800 Subject: iw_cxgb4/cxgb4/cxgb4vf/cxgb4i/csiostor: Cleanup register defines/macros related to all other cpl messages This patch cleanups all other macros/register define related to CPL messages that are defined in t4_msg.h and the affected files Signed-off-by: Anish Bhatt Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 8787279..694e030 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -3501,19 +3501,19 @@ static void build_cpl_pass_accept_req(struct sk_buff *skb, int stid , u8 tos) req = (struct cpl_pass_accept_req *)__skb_push(skb, sizeof(*req)); memset(req, 0, sizeof(*req)); req->l2info = cpu_to_be16(V_SYN_INTF(intf) | - V_SYN_MAC_IDX(G_RX_MACIDX( + V_SYN_MAC_IDX(RX_MACIDX_G( (__force int) htonl(l2info))) | F_SYN_XACT_MATCH); eth_hdr_len = is_t4(dev->rdev.lldi.adapter_type) ? - G_RX_ETHHDR_LEN((__force int) htonl(l2info)) : - G_RX_T5_ETHHDR_LEN((__force int) htonl(l2info)); - req->hdr_len = cpu_to_be32(V_SYN_RX_CHAN(G_RX_CHAN( + RX_ETHHDR_LEN_G((__force int)htonl(l2info)) : + RX_T5_ETHHDR_LEN_G((__force int)htonl(l2info)); + req->hdr_len = cpu_to_be32(V_SYN_RX_CHAN(RX_CHAN_G( (__force int) htonl(l2info))) | - V_TCP_HDR_LEN(G_RX_TCPHDR_LEN( + V_TCP_HDR_LEN(RX_TCPHDR_LEN_G( (__force int) htons(hdr_len))) | - V_IP_HDR_LEN(G_RX_IPHDR_LEN( + V_IP_HDR_LEN(RX_IPHDR_LEN_G( (__force int) htons(hdr_len))) | - V_ETH_HDR_LEN(G_RX_ETHHDR_LEN(eth_hdr_len))); + V_ETH_HDR_LEN(RX_ETHHDR_LEN_G(eth_hdr_len))); req->vlan = (__force __be16) vlantag; req->len = (__force __be16) len; req->tos_stid = cpu_to_be32(PASS_OPEN_TID_V(stid) | @@ -3613,7 +3613,7 @@ static int rx_pkt(struct c4iw_dev *dev, struct sk_buff *skb) struct neighbour *neigh; /* Drop all non-SYN packets */ - if (!(cpl->l2info & cpu_to_be32(F_RXF_SYN))) + if (!(cpl->l2info & cpu_to_be32(RXF_SYN_F))) goto reject; /* @@ -3635,8 +3635,8 @@ static int rx_pkt(struct c4iw_dev *dev, struct sk_buff *skb) } eth_hdr_len = is_t4(dev->rdev.lldi.adapter_type) ? - G_RX_ETHHDR_LEN(htonl(cpl->l2info)) : - G_RX_T5_ETHHDR_LEN(htonl(cpl->l2info)); + RX_ETHHDR_LEN_G(htonl(cpl->l2info)) : + RX_T5_ETHHDR_LEN_G(htonl(cpl->l2info)); if (eth_hdr_len == ETH_HLEN) { eh = (struct ethhdr *)(req + 1); iph = (struct iphdr *)(eh + 1); diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c index cb43c22..b9dc9fc 100644 --- a/drivers/infiniband/hw/cxgb4/mem.c +++ b/drivers/infiniband/hw/cxgb4/mem.c @@ -86,14 +86,14 @@ static int _c4iw_write_mem_dma_aligned(struct c4iw_rdev *rdev, u32 addr, req->wr.wr_lo = wait ? (__force __be64)(unsigned long) &wr_wait : 0L; req->wr.wr_mid = cpu_to_be32(FW_WR_LEN16_V(DIV_ROUND_UP(wr_len, 16))); req->cmd = cpu_to_be32(ULPTX_CMD_V(ULP_TX_MEM_WRITE)); - req->cmd |= cpu_to_be32(V_T5_ULP_MEMIO_ORDER(1)); + req->cmd |= cpu_to_be32(T5_ULP_MEMIO_ORDER_V(1)); req->dlen = cpu_to_be32(ULP_MEMIO_DATA_LEN_V(len>>5)); req->len16 = cpu_to_be32(DIV_ROUND_UP(wr_len-sizeof(req->wr), 16)); req->lock_addr = cpu_to_be32(ULP_MEMIO_ADDR_V(addr)); sgl = (struct ulptx_sgl *)(req + 1); sgl->cmd_nsge = cpu_to_be32(ULPTX_CMD_V(ULP_TX_SC_DSGL) | - ULPTX_NSGE(1)); + ULPTX_NSGE_V(1)); sgl->len0 = cpu_to_be32(len); sgl->addr0 = cpu_to_be64(data); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 2897f95..23ae0b7 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -672,7 +672,7 @@ static void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl) if (idx >= adap->tids.ftid_base && nidx < (adap->tids.nftids + adap->tids.nsftids)) { idx = nidx; - ret = GET_TCB_COOKIE(rpl->cookie); + ret = TCB_COOKIE_G(rpl->cookie); f = &adap->tids.ftid_tab[idx]; if (ret == FW_FILTER_WR_FLT_DELETED) { @@ -724,7 +724,7 @@ static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp, if (likely(opcode == CPL_SGE_EGR_UPDATE)) { const struct cpl_sge_egr_update *p = (void *)rsp; - unsigned int qid = EGR_QID(ntohl(p->opcode_qid)); + unsigned int qid = EGR_QID_G(ntohl(p->opcode_qid)); struct sge_txq *txq; txq = q->adap->sge.egr_map[qid - q->adap->sge.egr_start]; @@ -3483,8 +3483,8 @@ int cxgb4_remove_server(const struct net_device *dev, unsigned int stid, req = (struct cpl_close_listsvr_req *)__skb_put(skb, sizeof(*req)); INIT_TP_WR(req, 0); OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_CLOSE_LISTSRV_REQ, stid)); - req->reply_ctrl = htons(NO_REPLY(0) | (ipv6 ? LISTSVR_IPV6(1) : - LISTSVR_IPV6(0)) | QUEUENO(queue)); + req->reply_ctrl = htons(NO_REPLY_V(0) | (ipv6 ? LISTSVR_IPV6_V(1) : + LISTSVR_IPV6_V(0)) | QUEUENO_V(queue)); ret = t4_mgmt_tx(adap, skb); return net_xmit_eval(ret); } diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.c b/drivers/net/ethernet/chelsio/cxgb4/l2t.c index dea984b..252efc2 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/l2t.c +++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.c @@ -152,7 +152,7 @@ static int write_l2e(struct adapter *adap, struct l2t_entry *e, int sync) OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, e->idx | (sync ? F_SYNC_WR : 0) | TID_QID_V(adap->sge.fw_evtq.abs_id))); - req->params = htons(L2T_W_PORT(e->lport) | L2T_W_NOREPLY(!sync)); + req->params = htons(L2T_W_PORT_V(e->lport) | L2T_W_NOREPLY_V(!sync)); req->l2t_idx = htons(e->idx); req->vlan = htons(e->vlan); if (e->neigh && !(e->neigh->dev->flags & IFF_LOOPBACK)) diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index d7c301c..a79fa6a 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -821,7 +821,8 @@ static void write_sgl(const struct sk_buff *skb, struct sge_txq *q, sgl->addr0 = cpu_to_be64(addr[1]); } - sgl->cmd_nsge = htonl(ULPTX_CMD_V(ULP_TX_SC_DSGL) | ULPTX_NSGE(nfrags)); + sgl->cmd_nsge = htonl(ULPTX_CMD_V(ULP_TX_SC_DSGL) | + ULPTX_NSGE_V(nfrags)); if (likely(--nfrags == 0)) return; /* @@ -1761,7 +1762,7 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, pkt = (const struct cpl_rx_pkt *)rsp; csum_ok = pkt->csum_calc && !pkt->err_vec && (q->netdev->features & NETIF_F_RXCSUM); - if ((pkt->l2info & htonl(RXF_TCP)) && + if ((pkt->l2info & htonl(RXF_TCP_F)) && (q->netdev->features & NETIF_F_GRO) && csum_ok && !pkt->ip_frag) { do_gro(rxq, si, pkt); return 0; @@ -1783,11 +1784,11 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, rxq->stats.pkts++; - if (csum_ok && (pkt->l2info & htonl(RXF_UDP | RXF_TCP))) { + if (csum_ok && (pkt->l2info & htonl(RXF_UDP_F | RXF_TCP_F))) { if (!pkt->ip_frag) { skb->ip_summed = CHECKSUM_UNNECESSARY; rxq->stats.rx_cso++; - } else if (pkt->l2info & htonl(RXF_IP)) { + } else if (pkt->l2info & htonl(RXF_IP_F)) { __sum16 c = (__force __sum16)pkt->csum; skb->csum = csum_unfold(c); skb->ip_summed = CHECKSUM_COMPLETE; diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h index 15e7206..0fb975e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h @@ -518,24 +518,39 @@ struct cpl_get_tcb { WR_HDR; union opcode_tid ot; __be16 reply_ctrl; -#define QUEUENO(x) ((x) << 0) -#define REPLY_CHAN(x) ((x) << 14) -#define NO_REPLY(x) ((x) << 15) __be16 cookie; }; +/* cpl_get_tcb.reply_ctrl fields */ +#define QUEUENO_S 0 +#define QUEUENO_V(x) ((x) << QUEUENO_S) + +#define REPLY_CHAN_S 14 +#define REPLY_CHAN_V(x) ((x) << REPLY_CHAN_S) +#define REPLY_CHAN_F REPLY_CHAN_V(1U) + +#define NO_REPLY_S 15 +#define NO_REPLY_V(x) ((x) << NO_REPLY_S) +#define NO_REPLY_F NO_REPLY_V(1U) + struct cpl_set_tcb_field { WR_HDR; union opcode_tid ot; __be16 reply_ctrl; __be16 word_cookie; -#define TCB_WORD(x) ((x) << 0) -#define TCB_COOKIE(x) ((x) << 5) -#define GET_TCB_COOKIE(x) (((x) >> 5) & 7) __be64 mask; __be64 val; }; +/* cpl_set_tcb_field.word_cookie fields */ +#define TCB_WORD_S 0 +#define TCB_WORD(x) ((x) << TCB_WORD_S) + +#define TCB_COOKIE_S 5 +#define TCB_COOKIE_M 0x7 +#define TCB_COOKIE_V(x) ((x) << TCB_COOKIE_S) +#define TCB_COOKIE_G(x) (((x) >> TCB_COOKIE_S) & TCB_COOKIE_M) + struct cpl_set_tcb_rpl { union opcode_tid ot; __be16 rsvd; @@ -562,10 +577,14 @@ struct cpl_close_listsvr_req { WR_HDR; union opcode_tid ot; __be16 reply_ctrl; -#define LISTSVR_IPV6(x) ((x) << 14) __be16 rsvd; }; +/* additional cpl_close_listsvr_req.reply_ctrl field */ +#define LISTSVR_IPV6_S 14 +#define LISTSVR_IPV6_V(x) ((x) << LISTSVR_IPV6_S) +#define LISTSVR_IPV6_F LISTSVR_IPV6_V(1U) + struct cpl_close_listsvr_rpl { union opcode_tid ot; u8 rsvd[3]; @@ -661,6 +680,34 @@ struct cpl_tx_pkt_lso_core { /* encapsulated CPL (TX_PKT, TX_PKT_XT or TX_DATA) follows here */ }; +/* cpl_tx_pkt_lso_core.lso_ctrl fields */ +#define LSO_TCPHDR_LEN_S 0 +#define LSO_TCPHDR_LEN_V(x) ((x) << LSO_TCPHDR_LEN_S) + +#define LSO_IPHDR_LEN_S 4 +#define LSO_IPHDR_LEN_V(x) ((x) << LSO_IPHDR_LEN_S) + +#define LSO_ETHHDR_LEN_S 16 +#define LSO_ETHHDR_LEN_V(x) ((x) << LSO_ETHHDR_LEN_S) + +#define LSO_IPV6_S 20 +#define LSO_IPV6_V(x) ((x) << LSO_IPV6_S) +#define LSO_IPV6_F LSO_IPV6_V(1U) + +#define LSO_LAST_SLICE_S 22 +#define LSO_LAST_SLICE_V(x) ((x) << LSO_LAST_SLICE_S) +#define LSO_LAST_SLICE_F LSO_LAST_SLICE_V(1U) + +#define LSO_FIRST_SLICE_S 23 +#define LSO_FIRST_SLICE_V(x) ((x) << LSO_FIRST_SLICE_S) +#define LSO_FIRST_SLICE_F LSO_FIRST_SLICE_V(1U) + +#define LSO_OPCODE_S 24 +#define LSO_OPCODE_V(x) ((x) << LSO_OPCODE_S) + +#define LSO_T5_XFER_SIZE_S 0 +#define LSO_T5_XFER_SIZE_V(x) ((x) << LSO_T5_XFER_SIZE_S) + struct cpl_tx_pkt_lso { WR_HDR; struct cpl_tx_pkt_lso_core c; @@ -670,8 +717,6 @@ struct cpl_tx_pkt_lso { struct cpl_iscsi_hdr { union opcode_tid ot; __be16 pdu_len_ddp; -#define ISCSI_PDU_LEN(x) ((x) & 0x7FFF) -#define ISCSI_DDP (1 << 15) __be16 len; __be32 seq; __be16 urg; @@ -679,6 +724,16 @@ struct cpl_iscsi_hdr { u8 status; }; +/* cpl_iscsi_hdr.pdu_len_ddp fields */ +#define ISCSI_PDU_LEN_S 0 +#define ISCSI_PDU_LEN_M 0x7FFF +#define ISCSI_PDU_LEN_V(x) ((x) << ISCSI_PDU_LEN_S) +#define ISCSI_PDU_LEN_G(x) (((x) >> ISCSI_PDU_LEN_S) & ISCSI_PDU_LEN_M) + +#define ISCSI_DDP_S 15 +#define ISCSI_DDP_V(x) ((x) << ISCSI_DDP_S) +#define ISCSI_DDP_F ISCSI_DDP_V(1U) + struct cpl_rx_data { union opcode_tid ot; __be16 rsvd; @@ -735,49 +790,61 @@ struct cpl_rx_pkt { __be16 vlan; __be16 len; __be32 l2info; -#define RXF_UDP (1 << 22) -#define RXF_TCP (1 << 23) -#define RXF_IP (1 << 24) -#define RXF_IP6 (1 << 25) __be16 hdr_len; __be16 err_vec; }; +#define RXF_UDP_S 22 +#define RXF_UDP_V(x) ((x) << RXF_UDP_S) +#define RXF_UDP_F RXF_UDP_V(1U) + +#define RXF_TCP_S 23 +#define RXF_TCP_V(x) ((x) << RXF_TCP_S) +#define RXF_TCP_F RXF_TCP_V(1U) + +#define RXF_IP_S 24 +#define RXF_IP_V(x) ((x) << RXF_IP_S) +#define RXF_IP_F RXF_IP_V(1U) + +#define RXF_IP6_S 25 +#define RXF_IP6_V(x) ((x) << RXF_IP6_S) +#define RXF_IP6_F RXF_IP6_V(1U) + /* rx_pkt.l2info fields */ -#define S_RX_ETHHDR_LEN 0 -#define M_RX_ETHHDR_LEN 0x1F -#define V_RX_ETHHDR_LEN(x) ((x) << S_RX_ETHHDR_LEN) -#define G_RX_ETHHDR_LEN(x) (((x) >> S_RX_ETHHDR_LEN) & M_RX_ETHHDR_LEN) - -#define S_RX_T5_ETHHDR_LEN 0 -#define M_RX_T5_ETHHDR_LEN 0x3F -#define V_RX_T5_ETHHDR_LEN(x) ((x) << S_RX_T5_ETHHDR_LEN) -#define G_RX_T5_ETHHDR_LEN(x) (((x) >> S_RX_T5_ETHHDR_LEN) & M_RX_T5_ETHHDR_LEN) - -#define S_RX_MACIDX 8 -#define M_RX_MACIDX 0x1FF -#define V_RX_MACIDX(x) ((x) << S_RX_MACIDX) -#define G_RX_MACIDX(x) (((x) >> S_RX_MACIDX) & M_RX_MACIDX) - -#define S_RXF_SYN 21 -#define V_RXF_SYN(x) ((x) << S_RXF_SYN) -#define F_RXF_SYN V_RXF_SYN(1U) - -#define S_RX_CHAN 28 -#define M_RX_CHAN 0xF -#define V_RX_CHAN(x) ((x) << S_RX_CHAN) -#define G_RX_CHAN(x) (((x) >> S_RX_CHAN) & M_RX_CHAN) +#define RX_ETHHDR_LEN_S 0 +#define RX_ETHHDR_LEN_M 0x1F +#define RX_ETHHDR_LEN_V(x) ((x) << RX_ETHHDR_LEN_S) +#define RX_ETHHDR_LEN_G(x) (((x) >> RX_ETHHDR_LEN_S) & RX_ETHHDR_LEN_M) + +#define RX_T5_ETHHDR_LEN_S 0 +#define RX_T5_ETHHDR_LEN_M 0x3F +#define RX_T5_ETHHDR_LEN_V(x) ((x) << RX_T5_ETHHDR_LEN_S) +#define RX_T5_ETHHDR_LEN_G(x) (((x) >> RX_T5_ETHHDR_LEN_S) & RX_T5_ETHHDR_LEN_M) + +#define RX_MACIDX_S 8 +#define RX_MACIDX_M 0x1FF +#define RX_MACIDX_V(x) ((x) << RX_MACIDX_S) +#define RX_MACIDX_G(x) (((x) >> RX_MACIDX_S) & RX_MACIDX_M) + +#define RXF_SYN_S 21 +#define RXF_SYN_V(x) ((x) << RXF_SYN_S) +#define RXF_SYN_F RXF_SYN_V(1U) + +#define RX_CHAN_S 28 +#define RX_CHAN_M 0xF +#define RX_CHAN_V(x) ((x) << RX_CHAN_S) +#define RX_CHAN_G(x) (((x) >> RX_CHAN_S) & RX_CHAN_M) /* rx_pkt.hdr_len fields */ -#define S_RX_TCPHDR_LEN 0 -#define M_RX_TCPHDR_LEN 0x3F -#define V_RX_TCPHDR_LEN(x) ((x) << S_RX_TCPHDR_LEN) -#define G_RX_TCPHDR_LEN(x) (((x) >> S_RX_TCPHDR_LEN) & M_RX_TCPHDR_LEN) +#define RX_TCPHDR_LEN_S 0 +#define RX_TCPHDR_LEN_M 0x3F +#define RX_TCPHDR_LEN_V(x) ((x) << RX_TCPHDR_LEN_S) +#define RX_TCPHDR_LEN_G(x) (((x) >> RX_TCPHDR_LEN_S) & RX_TCPHDR_LEN_M) -#define S_RX_IPHDR_LEN 6 -#define M_RX_IPHDR_LEN 0x3FF -#define V_RX_IPHDR_LEN(x) ((x) << S_RX_IPHDR_LEN) -#define G_RX_IPHDR_LEN(x) (((x) >> S_RX_IPHDR_LEN) & M_RX_IPHDR_LEN) +#define RX_IPHDR_LEN_S 6 +#define RX_IPHDR_LEN_M 0x3FF +#define RX_IPHDR_LEN_V(x) ((x) << RX_IPHDR_LEN_S) +#define RX_IPHDR_LEN_G(x) (((x) >> RX_IPHDR_LEN_S) & RX_IPHDR_LEN_M) struct cpl_trace_pkt { u8 opcode; @@ -826,14 +893,22 @@ struct cpl_l2t_write_req { WR_HDR; union opcode_tid ot; __be16 params; -#define L2T_W_INFO(x) ((x) << 2) -#define L2T_W_PORT(x) ((x) << 8) -#define L2T_W_NOREPLY(x) ((x) << 15) __be16 l2t_idx; __be16 vlan; u8 dst_mac[6]; }; +/* cpl_l2t_write_req.params fields */ +#define L2T_W_INFO_S 2 +#define L2T_W_INFO_V(x) ((x) << L2T_W_INFO_S) + +#define L2T_W_PORT_S 8 +#define L2T_W_PORT_V(x) ((x) << L2T_W_PORT_S) + +#define L2T_W_NOREPLY_S 15 +#define L2T_W_NOREPLY_V(x) ((x) << L2T_W_NOREPLY_S) +#define L2T_W_NOREPLY_F L2T_W_NOREPLY_V(1U) + struct cpl_l2t_write_rpl { union opcode_tid ot; u8 status; @@ -848,11 +923,15 @@ struct cpl_rdma_terminate { struct cpl_sge_egr_update { __be32 opcode_qid; -#define EGR_QID(x) ((x) & 0x1FFFF) __be16 cidx; __be16 pidx; }; +/* cpl_sge_egr_update.ot fields */ +#define EGR_QID_S 0 +#define EGR_QID_M 0x1FFFF +#define EGR_QID_G(x) (((x) >> EGR_QID_S) & EGR_QID_M) + /* cpl_fw*.type values */ enum { FW_TYPE_CMD_RPL = 0, @@ -945,22 +1024,30 @@ struct ulptx_sge_pair { struct ulptx_sgl { __be32 cmd_nsge; -#define ULPTX_NSGE(x) ((x) << 0) -#define ULPTX_MORE (1U << 23) __be32 len0; __be64 addr0; struct ulptx_sge_pair sge[0]; }; +#define ULPTX_NSGE_S 0 +#define ULPTX_NSGE_V(x) ((x) << ULPTX_NSGE_S) + +#define ULPTX_MORE_S 23 +#define ULPTX_MORE_V(x) ((x) << ULPTX_MORE_S) +#define ULPTX_MORE_F ULPTX_MORE_V(1U) + struct ulp_mem_io { WR_HDR; __be32 cmd; __be32 len16; /* command length */ __be32 dlen; /* data length in 32-byte units */ __be32 lock_addr; -#define ULP_MEMIO_LOCK(x) ((x) << 31) }; +#define ULP_MEMIO_LOCK_S 31 +#define ULP_MEMIO_LOCK_V(x) ((x) << ULP_MEMIO_LOCK_S) +#define ULP_MEMIO_LOCK_F ULP_MEMIO_LOCK_V(1U) + /* additional ulp_mem_io.cmd fields */ #define ULP_MEMIO_ORDER_S 23 #define ULP_MEMIO_ORDER_V(x) ((x) << ULP_MEMIO_ORDER_S) @@ -970,13 +1057,9 @@ struct ulp_mem_io { #define T5_ULP_MEMIO_IMM_V(x) ((x) << T5_ULP_MEMIO_IMM_S) #define T5_ULP_MEMIO_IMM_F T5_ULP_MEMIO_IMM_V(1U) -#define S_T5_ULP_MEMIO_IMM 23 -#define V_T5_ULP_MEMIO_IMM(x) ((x) << S_T5_ULP_MEMIO_IMM) -#define F_T5_ULP_MEMIO_IMM V_T5_ULP_MEMIO_IMM(1U) - -#define S_T5_ULP_MEMIO_ORDER 22 -#define V_T5_ULP_MEMIO_ORDER(x) ((x) << S_T5_ULP_MEMIO_ORDER) -#define F_T5_ULP_MEMIO_ORDER V_T5_ULP_MEMIO_ORDER(1U) +#define T5_ULP_MEMIO_ORDER_S 22 +#define T5_ULP_MEMIO_ORDER_V(x) ((x) << T5_ULP_MEMIO_ORDER_S) +#define T5_ULP_MEMIO_ORDER_F T5_ULP_MEMIO_ORDER_V(1U) /* ulp_mem_io.lock_addr fields */ #define ULP_MEMIO_ADDR_S 0 diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index 4591d93..710e5e2 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -471,7 +471,7 @@ static int fwevtq_handler(struct sge_rspq *rspq, const __be64 *rsp, * free TX Queue Descriptors ... */ const struct cpl_sge_egr_update *p = cpl; - unsigned int qid = EGR_QID(be32_to_cpu(p->opcode_qid)); + unsigned int qid = EGR_QID_G(be32_to_cpu(p->opcode_qid)); struct sge *s = &adapter->sge; struct sge_txq *tq; struct sge_eth_txq *txq; diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c index ef4da3e..4424277 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c @@ -926,7 +926,7 @@ static void write_sgl(const struct sk_buff *skb, struct sge_txq *tq, } sgl->cmd_nsge = htonl(ULPTX_CMD_V(ULP_TX_SC_DSGL) | - ULPTX_NSGE(nfrags)); + ULPTX_NSGE_V(nfrags)); if (likely(--nfrags == 0)) return; /* @@ -1604,7 +1604,7 @@ int t4vf_ethrx_handler(struct sge_rspq *rspq, const __be64 *rsp, * If this is a good TCP packet and we have Generic Receive Offload * enabled, handle the packet in the GRO path. */ - if ((pkt->l2info & cpu_to_be32(RXF_TCP)) && + if ((pkt->l2info & cpu_to_be32(RXF_TCP_F)) && (rspq->netdev->features & NETIF_F_GRO) && csum_ok && !pkt->ip_frag) { do_gro(rxq, gl, pkt); @@ -1626,7 +1626,7 @@ int t4vf_ethrx_handler(struct sge_rspq *rspq, const __be64 *rsp, rxq->stats.pkts++; if (csum_ok && !pkt->err_vec && - (be32_to_cpu(pkt->l2info) & (RXF_UDP|RXF_TCP))) { + (be32_to_cpu(pkt->l2info) & (RXF_UDP_F | RXF_TCP_F))) { if (!pkt->ip_frag) skb->ip_summed = CHECKSUM_UNNECESSARY; else { diff --git a/drivers/scsi/csiostor/csio_lnode.c b/drivers/scsi/csiostor/csio_lnode.c index 87f9280..c00b2ff 100644 --- a/drivers/scsi/csiostor/csio_lnode.c +++ b/drivers/scsi/csiostor/csio_lnode.c @@ -1758,7 +1758,7 @@ csio_ln_mgmt_submit_wr(struct csio_mgmtm *mgmtm, struct csio_ioreq *io_req, else { /* Program DSGL to dma payload */ dsgl.cmd_nsge = htonl(ULPTX_CMD_V(ULP_TX_SC_DSGL) | - ULPTX_MORE | ULPTX_NSGE(1)); + ULPTX_MORE_F | ULPTX_NSGE_V(1)); dsgl.len0 = cpu_to_be32(pld_len); dsgl.addr0 = cpu_to_be64(pld->paddr); csio_wr_copy_to_wrp(&dsgl, &wrp, ALIGN(wr_off, 8), diff --git a/drivers/scsi/csiostor/csio_scsi.c b/drivers/scsi/csiostor/csio_scsi.c index 3987284..2c4562d 100644 --- a/drivers/scsi/csiostor/csio_scsi.c +++ b/drivers/scsi/csiostor/csio_scsi.c @@ -298,8 +298,8 @@ csio_scsi_init_ultptx_dsgl(struct csio_hw *hw, struct csio_ioreq *req, struct csio_dma_buf *dma_buf; struct scsi_cmnd *scmnd = csio_scsi_cmnd(req); - sgl->cmd_nsge = htonl(ULPTX_CMD_V(ULP_TX_SC_DSGL) | ULPTX_MORE | - ULPTX_NSGE(req->nsge)); + sgl->cmd_nsge = htonl(ULPTX_CMD_V(ULP_TX_SC_DSGL) | ULPTX_MORE_F | + ULPTX_NSGE_V(req->nsge)); /* Now add the data SGLs */ if (likely(!req->dcopy)) { scsi_for_each_sg(scmnd, sgel, req->nsge, i) { diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c index c3f2b97..37d7191 100644 --- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c +++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c @@ -1112,7 +1112,7 @@ static void do_rx_iscsi_hdr(struct cxgbi_device *cdev, struct sk_buff *skb) hlen = ntohs(cpl->len); dlen = ntohl(*(unsigned int *)(bhs + 4)) & 0xFFFFFF; - plen = ISCSI_PDU_LEN(pdu_len_ddp); + plen = ISCSI_PDU_LEN_G(pdu_len_ddp); if (is_t4(lldi->adapter_type)) plen -= 40; @@ -1619,7 +1619,7 @@ static int ddp_setup_conn_pgidx(struct cxgbi_sock *csk, unsigned int tid, req = (struct cpl_set_tcb_field *)skb->head; INIT_TP_WR(req, csk->tid); OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, csk->tid)); - req->reply_ctrl = htons(NO_REPLY(reply) | QUEUENO(csk->rss_qid)); + req->reply_ctrl = htons(NO_REPLY_V(reply) | QUEUENO_V(csk->rss_qid)); req->word_cookie = htons(0); req->mask = cpu_to_be64(0x3 << 8); req->val = cpu_to_be64(pg_idx << 8); @@ -1651,7 +1651,7 @@ static int ddp_setup_conn_digest(struct cxgbi_sock *csk, unsigned int tid, req = (struct cpl_set_tcb_field *)skb->head; INIT_TP_WR(req, tid); OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, tid)); - req->reply_ctrl = htons(NO_REPLY(reply) | QUEUENO(csk->rss_qid)); + req->reply_ctrl = htons(NO_REPLY_V(reply) | QUEUENO_V(csk->rss_qid)); req->word_cookie = htons(0); req->mask = cpu_to_be64(0x3 << 4); req->val = cpu_to_be64(((hcrc ? ULP_CRC_HEADER : 0) | -- cgit v0.10.2 From a248ce068f560ece6b72eb75b54380e3d800cbab Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Mon, 12 Jan 2015 12:42:34 -0800 Subject: staging: line6: toneport.c: Fix for possible null pointer dereference The NULL check was done to late, and there it was a risk of a possible null pointer dereference. This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/toneport.c b/drivers/staging/line6/toneport.c index 6943715..660dc3f 100644 --- a/drivers/staging/line6/toneport.c +++ b/drivers/staging/line6/toneport.c @@ -433,12 +433,16 @@ void line6_toneport_reset_resume(struct usb_line6_toneport *toneport) void line6_toneport_disconnect(struct usb_interface *interface) { struct usb_line6_toneport *toneport; + struct snd_line6_pcm *line6pcm; u16 idProduct; if (interface == NULL) return; toneport = usb_get_intfdata(interface); + if (NULL == toneport) + return; + del_timer_sync(&toneport->timer); idProduct = le16_to_cpu(toneport->line6.usbdev->descriptor.idProduct); @@ -447,13 +451,10 @@ void line6_toneport_disconnect(struct usb_interface *interface) device_remove_file(&interface->dev, &dev_attr_led_green); } - if (toneport != NULL) { - struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm; - - if (line6pcm != NULL) { - line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); - line6_pcm_disconnect(line6pcm); - } + line6pcm = toneport->line6.line6pcm; + if (line6pcm != NULL) { + line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); + line6_pcm_disconnect(line6pcm); } toneport_destruct(interface); -- cgit v0.10.2 From 690ea44ede52e3e45eeab3b0e9704d7c3b258ae5 Mon Sep 17 00:00:00 2001 From: Jonas Lundqvist Date: Mon, 12 Jan 2015 12:42:35 -0800 Subject: Staging: line6: remove spaces before commas. Fix three space prohibited errors in pcm.h found by checkpatch.pl. Signed-off-by: Jonas Lundqvist Reviewed-by: Jeremiah Mahler Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/pcm.h b/drivers/staging/line6/pcm.h index 6aa0d46..5d87934 100644 --- a/drivers/staging/line6/pcm.h +++ b/drivers/staging/line6/pcm.h @@ -145,21 +145,21 @@ enum { LINE6_BIT_PCM_IMPULSE_PLAYBACK_BUFFER | #endif LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER | - LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER , + LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER, LINE6_BITS_PLAYBACK_STREAM = #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE LINE6_BIT_PCM_IMPULSE_PLAYBACK_STREAM | #endif LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM | - LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM , + LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM, LINE6_BITS_CAPTURE_BUFFER = #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE LINE6_BIT_PCM_IMPULSE_CAPTURE_BUFFER | #endif LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER | - LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER , + LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER, LINE6_BITS_CAPTURE_STREAM = #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE -- cgit v0.10.2 From 5c3396f909aa02d0b7337258c78bd872510e837d Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:36 -0800 Subject: staging: line6: Remove `device_bit' from properties The `device_bit' member was no longer used as of commit 2807904441d4 (staging: line6: drop MIDI parameter sysfs attrs). Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index 503b2d7..15f3bc4 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -57,32 +57,32 @@ static const struct usb_device_id line6_id_table[] = { MODULE_DEVICE_TABLE(usb, line6_id_table); -#define L6PROP(dev_bit, dev_id, dev_name, dev_cap)\ - {.device_bit = LINE6_BIT_##dev_bit, .id = dev_id,\ +#define L6PROP(dev_id, dev_name, dev_cap)\ + {.id = dev_id,\ .name = dev_name, .capabilities = LINE6_BIT_##dev_cap} /* *INDENT-OFF* */ static const struct line6_properties line6_properties_table[] = { - L6PROP(BASSPODXT, "BassPODxt", "BassPODxt", CTRL_PCM_HW), - L6PROP(BASSPODXTLIVE, "BassPODxtLive", "BassPODxt Live", CTRL_PCM_HW), - L6PROP(BASSPODXTPRO, "BassPODxtPro", "BassPODxt Pro", CTRL_PCM_HW), - L6PROP(GUITARPORT, "GuitarPort", "GuitarPort", PCM), - L6PROP(POCKETPOD, "PocketPOD", "Pocket POD", CONTROL), - L6PROP(PODHD300, "PODHD300", "POD HD300", CTRL_PCM_HW), - L6PROP(PODHD400, "PODHD400", "POD HD400", CTRL_PCM_HW), - L6PROP(PODHD500, "PODHD500", "POD HD500", CTRL_PCM_HW), - L6PROP(PODSTUDIO_GX, "PODStudioGX", "POD Studio GX", PCM), - L6PROP(PODSTUDIO_UX1, "PODStudioUX1", "POD Studio UX1", PCM), - L6PROP(PODSTUDIO_UX2, "PODStudioUX2", "POD Studio UX2", PCM), - L6PROP(PODX3, "PODX3", "POD X3", PCM), - L6PROP(PODX3LIVE, "PODX3Live", "POD X3 Live", PCM), - L6PROP(PODXT, "PODxt", "PODxt", CTRL_PCM_HW), - L6PROP(PODXTLIVE, "PODxtLive", "PODxt Live", CTRL_PCM_HW), - L6PROP(PODXTPRO, "PODxtPro", "PODxt Pro", CTRL_PCM_HW), - L6PROP(TONEPORT_GX, "TonePortGX", "TonePort GX", PCM), - L6PROP(TONEPORT_UX1, "TonePortUX1", "TonePort UX1", PCM), - L6PROP(TONEPORT_UX2, "TonePortUX2", "TonePort UX2", PCM), - L6PROP(VARIAX, "Variax", "Variax Workbench", CONTROL), + L6PROP("BassPODxt", "BassPODxt", CTRL_PCM_HW), + L6PROP("BassPODxtLive", "BassPODxt Live", CTRL_PCM_HW), + L6PROP("BassPODxtPro", "BassPODxt Pro", CTRL_PCM_HW), + L6PROP("GuitarPort", "GuitarPort", PCM), + L6PROP("PocketPOD", "Pocket POD", CONTROL), + L6PROP("PODHD300", "POD HD300", CTRL_PCM_HW), + L6PROP("PODHD400", "POD HD400", CTRL_PCM_HW), + L6PROP("PODHD500", "POD HD500", CTRL_PCM_HW), + L6PROP("PODStudioGX", "POD Studio GX", PCM), + L6PROP("PODStudioUX1", "POD Studio UX1", PCM), + L6PROP("PODStudioUX2", "POD Studio UX2", PCM), + L6PROP("PODX3", "POD X3", PCM), + L6PROP("PODX3Live", "POD X3 Live", PCM), + L6PROP("PODxt", "PODxt", CTRL_PCM_HW), + L6PROP("PODxtLive", "PODxt Live", CTRL_PCM_HW), + L6PROP("PODxtPro", "PODxt Pro", CTRL_PCM_HW), + L6PROP("TonePortGX", "TonePort GX", PCM), + L6PROP("TonePortUX1", "TonePort UX1", PCM), + L6PROP("TonePortUX2", "TonePort UX2", PCM), + L6PROP("Variax", "Variax Workbench", CONTROL), }; /* *INDENT-ON* */ diff --git a/drivers/staging/line6/driver.h b/drivers/staging/line6/driver.h index 16e3fc2..1cc7532 100644 --- a/drivers/staging/line6/driver.h +++ b/drivers/staging/line6/driver.h @@ -76,11 +76,6 @@ static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4; */ struct line6_properties { /** - Bit identifying this device in the line6usb driver. - */ - int device_bit; - - /** Card id string (maximum 16 characters). This can be used to address the device in ALSA programs as "default:CARD=" diff --git a/drivers/staging/line6/pcm.h b/drivers/staging/line6/pcm.h index 5d87934..4f60823 100644 --- a/drivers/staging/line6/pcm.h +++ b/drivers/staging/line6/pcm.h @@ -98,6 +98,8 @@ enum { LINE6_INDEX_PAUSE_PLAYBACK, LINE6_INDEX_PREPARED, +#define LINE6_BIT(x) LINE6_BIT_ ## x = 1 << LINE6_INDEX_ ## x + /* individual bit masks: */ LINE6_BIT(PCM_ALSA_PLAYBACK_BUFFER), LINE6_BIT(PCM_ALSA_PLAYBACK_STREAM), diff --git a/drivers/staging/line6/usbdefs.h b/drivers/staging/line6/usbdefs.h index 2d1cc47..2bc2fe7 100644 --- a/drivers/staging/line6/usbdefs.h +++ b/drivers/staging/line6/usbdefs.h @@ -40,65 +40,6 @@ #define LINE6_DEVID_TONEPORT_UX2 0x4142 #define LINE6_DEVID_VARIAX 0x534d -#define LINE6_BIT(x) LINE6_BIT_ ## x = 1 << LINE6_INDEX_ ## x - -enum { - LINE6_INDEX_BASSPODXT, - LINE6_INDEX_BASSPODXTLIVE, - LINE6_INDEX_BASSPODXTPRO, - LINE6_INDEX_GUITARPORT, - LINE6_INDEX_POCKETPOD, - LINE6_INDEX_PODHD300, - LINE6_INDEX_PODHD400, - LINE6_INDEX_PODHD500, - LINE6_INDEX_PODSTUDIO_GX, - LINE6_INDEX_PODSTUDIO_UX1, - LINE6_INDEX_PODSTUDIO_UX2, - LINE6_INDEX_PODX3, - LINE6_INDEX_PODX3LIVE, - LINE6_INDEX_PODXT, - LINE6_INDEX_PODXTLIVE, - LINE6_INDEX_PODXTPRO, - LINE6_INDEX_TONEPORT_GX, - LINE6_INDEX_TONEPORT_UX1, - LINE6_INDEX_TONEPORT_UX2, - LINE6_INDEX_VARIAX, - - LINE6_BIT(BASSPODXT), - LINE6_BIT(BASSPODXTLIVE), - LINE6_BIT(BASSPODXTPRO), - LINE6_BIT(GUITARPORT), - LINE6_BIT(POCKETPOD), - LINE6_BIT(PODHD300), - LINE6_BIT(PODHD400), - LINE6_BIT(PODHD500), - LINE6_BIT(PODSTUDIO_GX), - LINE6_BIT(PODSTUDIO_UX1), - LINE6_BIT(PODSTUDIO_UX2), - LINE6_BIT(PODX3), - LINE6_BIT(PODX3LIVE), - LINE6_BIT(PODXT), - LINE6_BIT(PODXTLIVE), - LINE6_BIT(PODXTPRO), - LINE6_BIT(TONEPORT_GX), - LINE6_BIT(TONEPORT_UX1), - LINE6_BIT(TONEPORT_UX2), - LINE6_BIT(VARIAX), - - LINE6_BITS_PRO = LINE6_BIT_BASSPODXTPRO | LINE6_BIT_PODXTPRO, - LINE6_BITS_LIVE = LINE6_BIT_BASSPODXTLIVE | LINE6_BIT_PODXTLIVE | - LINE6_BIT_PODX3LIVE, - LINE6_BITS_PODXTALL = LINE6_BIT_PODXT | LINE6_BIT_PODXTLIVE | - LINE6_BIT_PODXTPRO, - LINE6_BITS_PODX3ALL = LINE6_BIT_PODX3 | LINE6_BIT_PODX3LIVE, - LINE6_BITS_PODHDALL = LINE6_BIT_PODHD300 | - LINE6_BIT_PODHD400 | - LINE6_BIT_PODHD500, - LINE6_BITS_BASSPODXTALL = LINE6_BIT_BASSPODXT | - LINE6_BIT_BASSPODXTLIVE | - LINE6_BIT_BASSPODXTPRO -}; - /* device supports settings parameter via USB */ #define LINE6_BIT_CONTROL (1 << 0) /* device supports PCM input/output via USB */ -- cgit v0.10.2 From c667ee1bac76f52d6fa3bd5430f471213b0e755f Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:37 -0800 Subject: staging: line6: Remove line6_pod_transmit_paramter() This function was no longer used as of commit 2807904441d4 (staging: line6: drop MIDI parameter sysfs attrs). Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/pod.c b/drivers/staging/line6/pod.c index 44f4b2f..7b4ec92 100644 --- a/drivers/staging/line6/pod.c +++ b/drivers/staging/line6/pod.c @@ -159,15 +159,6 @@ void line6_pod_process_message(struct usb_line6_pod *pod) } /* - Transmit PODxt Pro control parameter. -*/ -void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param, - u8 value) -{ - line6_transmit_parameter(&pod->line6, param, value); -} - -/* Send system parameter (from integer). */ static int pod_set_system_param_int(struct usb_line6_pod *pod, int value, diff --git a/drivers/staging/line6/pod.h b/drivers/staging/line6/pod.h index 3e3f167..397d94c 100644 --- a/drivers/staging/line6/pod.h +++ b/drivers/staging/line6/pod.h @@ -96,7 +96,5 @@ extern void line6_pod_disconnect(struct usb_interface *interface); extern int line6_pod_init(struct usb_interface *interface, struct usb_line6_pod *pod); extern void line6_pod_process_message(struct usb_line6_pod *pod); -extern void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param, - u8 value); #endif -- cgit v0.10.2 From e45bfe5e7c6d8563cad171df052218baa6226fc2 Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:38 -0800 Subject: staging: line6: Remove unsupported X3 devices Support for these devices appears to have never been completed. Remove them from the device table along with a minimal amount of supporting code. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index 15f3bc4..e40400b 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -43,8 +43,6 @@ static const struct usb_device_id line6_id_table[] = { {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_GX)}, {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_UX1)}, {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_UX2)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3LIVE)}, {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXT)}, {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXTLIVE)}, {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXTPRO)}, @@ -74,8 +72,6 @@ static const struct line6_properties line6_properties_table[] = { L6PROP("PODStudioGX", "POD Studio GX", PCM), L6PROP("PODStudioUX1", "POD Studio UX1", PCM), L6PROP("PODStudioUX2", "POD Studio UX2", PCM), - L6PROP("PODX3", "POD X3", PCM), - L6PROP("PODX3Live", "POD X3 Live", PCM), L6PROP("PODxt", "PODxt", CTRL_PCM_HW), L6PROP("PODxtLive", "PODxt Live", CTRL_PCM_HW), L6PROP("PODxtPro", "PODxt Pro", CTRL_PCM_HW), @@ -673,8 +669,6 @@ static int line6_probe(struct usb_interface *interface, break; case LINE6_DEVID_PODHD500: - case LINE6_DEVID_PODX3: - case LINE6_DEVID_PODX3LIVE: switch (interface_number) { case 0: alternate = 1; @@ -765,14 +759,6 @@ static int line6_probe(struct usb_interface *interface, ep_write = 0x02; break; - case LINE6_DEVID_PODX3: - case LINE6_DEVID_PODX3LIVE: - /* currently unused! */ - size = sizeof(struct usb_line6_pod); - ep_read = 0x81; - ep_write = 0x01; - break; - case LINE6_DEVID_PODSTUDIO_GX: case LINE6_DEVID_PODSTUDIO_UX1: case LINE6_DEVID_PODSTUDIO_UX2: @@ -898,8 +884,6 @@ static int line6_probe(struct usb_interface *interface, case LINE6_DEVID_BASSPODXTLIVE: case LINE6_DEVID_BASSPODXTPRO: case LINE6_DEVID_POCKETPOD: - case LINE6_DEVID_PODX3: - case LINE6_DEVID_PODX3LIVE: case LINE6_DEVID_PODXT: case LINE6_DEVID_PODXTPRO: ret = line6_pod_init(interface, (struct usb_line6_pod *)line6); @@ -971,14 +955,6 @@ static int line6_probe(struct usb_interface *interface, dev_info(&interface->dev, "Line6 %s now attached\n", line6->properties->name); - switch (product) { - case LINE6_DEVID_PODX3: - case LINE6_DEVID_PODX3LIVE: - dev_info(&interface->dev, - "NOTE: the Line6 %s is detected, but not yet supported\n", - line6->properties->name); - } - /* increment reference counters: */ usb_get_intf(interface); usb_get_dev(usbdev); @@ -1026,8 +1002,6 @@ static void line6_disconnect(struct usb_interface *interface) case LINE6_DEVID_BASSPODXTLIVE: case LINE6_DEVID_BASSPODXTPRO: case LINE6_DEVID_POCKETPOD: - case LINE6_DEVID_PODX3: - case LINE6_DEVID_PODX3LIVE: case LINE6_DEVID_PODXT: case LINE6_DEVID_PODXTPRO: line6_pod_disconnect(interface); diff --git a/drivers/staging/line6/pcm.c b/drivers/staging/line6/pcm.c index a3136b1..076c87b 100644 --- a/drivers/staging/line6/pcm.c +++ b/drivers/staging/line6/pcm.c @@ -442,8 +442,6 @@ int line6_init_pcm(struct usb_line6 *line6, break; case LINE6_DEVID_PODHD500: - case LINE6_DEVID_PODX3: - case LINE6_DEVID_PODX3LIVE: ep_read = 0x86; ep_write = 0x02; break; diff --git a/drivers/staging/line6/usbdefs.h b/drivers/staging/line6/usbdefs.h index 2bc2fe7..06bf909 100644 --- a/drivers/staging/line6/usbdefs.h +++ b/drivers/staging/line6/usbdefs.h @@ -30,8 +30,6 @@ #define LINE6_DEVID_PODSTUDIO_GX 0x4153 #define LINE6_DEVID_PODSTUDIO_UX1 0x4150 #define LINE6_DEVID_PODSTUDIO_UX2 0x4151 -#define LINE6_DEVID_PODX3 0x414a -#define LINE6_DEVID_PODX3LIVE 0x414b #define LINE6_DEVID_PODXT 0x5044 #define LINE6_DEVID_PODXTLIVE 0x4650 #define LINE6_DEVID_PODXTPRO 0x5050 -- cgit v0.10.2 From daf54a59f37cb156f4d3044810ac4fe2fdb2b208 Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:39 -0800 Subject: staging: line6: Cleanup device table Wrap USB_DEVICE to avoid repeating the Line 6 vendor ID. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index e40400b..a263bce 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -30,27 +30,29 @@ #define DRIVER_DESC "Line6 USB Driver" #define DRIVER_VERSION "0.9.1beta" DRIVER_REVISION +#define LINE6_DEVICE(prod) USB_DEVICE(LINE6_VENDOR_ID, prod) + /* table of devices that work with this driver */ static const struct usb_device_id line6_id_table[] = { - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXT)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXTLIVE)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXTPRO)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_GUITARPORT)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_POCKETPOD)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODHD300)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODHD400)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODHD500)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_GX)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_UX1)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_UX2)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXT)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXTLIVE)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXTPRO)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_GX)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_UX1)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_UX2)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_VARIAX)}, - {}, + { LINE6_DEVICE(LINE6_DEVID_BASSPODXT) }, + { LINE6_DEVICE(LINE6_DEVID_BASSPODXTLIVE) }, + { LINE6_DEVICE(LINE6_DEVID_BASSPODXTPRO) }, + { LINE6_DEVICE(LINE6_DEVID_GUITARPORT) }, + { LINE6_DEVICE(LINE6_DEVID_POCKETPOD) }, + { LINE6_DEVICE(LINE6_DEVID_PODHD300) }, + { LINE6_DEVICE(LINE6_DEVID_PODHD400) }, + { LINE6_DEVICE(LINE6_DEVID_PODHD500) }, + { LINE6_DEVICE(LINE6_DEVID_PODSTUDIO_GX) }, + { LINE6_DEVICE(LINE6_DEVID_PODSTUDIO_UX1) }, + { LINE6_DEVICE(LINE6_DEVID_PODSTUDIO_UX2) }, + { LINE6_DEVICE(LINE6_DEVID_PODXT) }, + { LINE6_DEVICE(LINE6_DEVID_PODXTLIVE) }, + { LINE6_DEVICE(LINE6_DEVID_PODXTPRO) }, + { LINE6_DEVICE(LINE6_DEVID_TONEPORT_GX) }, + { LINE6_DEVICE(LINE6_DEVID_TONEPORT_UX1) }, + { LINE6_DEVICE(LINE6_DEVID_TONEPORT_UX2) }, + { LINE6_DEVICE(LINE6_DEVID_VARIAX) }, + {} }; MODULE_DEVICE_TABLE(usb, line6_id_table); -- cgit v0.10.2 From 410dca8d99ae508078c90414b19f1758c1b4d6fd Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:40 -0800 Subject: staging: line6: Define a device type enum Define an enum containing the supported devices and associate each entry in the device table to the respective value. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index a263bce..2797e41 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -34,24 +34,42 @@ /* table of devices that work with this driver */ static const struct usb_device_id line6_id_table[] = { - { LINE6_DEVICE(LINE6_DEVID_BASSPODXT) }, - { LINE6_DEVICE(LINE6_DEVID_BASSPODXTLIVE) }, - { LINE6_DEVICE(LINE6_DEVID_BASSPODXTPRO) }, - { LINE6_DEVICE(LINE6_DEVID_GUITARPORT) }, - { LINE6_DEVICE(LINE6_DEVID_POCKETPOD) }, - { LINE6_DEVICE(LINE6_DEVID_PODHD300) }, - { LINE6_DEVICE(LINE6_DEVID_PODHD400) }, - { LINE6_DEVICE(LINE6_DEVID_PODHD500) }, - { LINE6_DEVICE(LINE6_DEVID_PODSTUDIO_GX) }, - { LINE6_DEVICE(LINE6_DEVID_PODSTUDIO_UX1) }, - { LINE6_DEVICE(LINE6_DEVID_PODSTUDIO_UX2) }, - { LINE6_DEVICE(LINE6_DEVID_PODXT) }, - { LINE6_DEVICE(LINE6_DEVID_PODXTLIVE) }, - { LINE6_DEVICE(LINE6_DEVID_PODXTPRO) }, - { LINE6_DEVICE(LINE6_DEVID_TONEPORT_GX) }, - { LINE6_DEVICE(LINE6_DEVID_TONEPORT_UX1) }, - { LINE6_DEVICE(LINE6_DEVID_TONEPORT_UX2) }, - { LINE6_DEVICE(LINE6_DEVID_VARIAX) }, + { LINE6_DEVICE(LINE6_DEVID_BASSPODXT), + .driver_info = LINE6_BASSPODXT }, + { LINE6_DEVICE(LINE6_DEVID_BASSPODXTLIVE), + .driver_info = LINE6_BASSPODXTLIVE }, + { LINE6_DEVICE(LINE6_DEVID_BASSPODXTPRO), + .driver_info = LINE6_BASSPODXTPRO }, + { LINE6_DEVICE(LINE6_DEVID_GUITARPORT), + .driver_info = LINE6_GUITARPORT }, + { LINE6_DEVICE(LINE6_DEVID_POCKETPOD), + .driver_info = LINE6_POCKETPOD }, + { LINE6_DEVICE(LINE6_DEVID_PODHD300), + .driver_info = LINE6_PODHD300 }, + { LINE6_DEVICE(LINE6_DEVID_PODHD400), + .driver_info = LINE6_PODHD400 }, + { LINE6_DEVICE(LINE6_DEVID_PODHD500), + .driver_info = LINE6_PODHD500 }, + { LINE6_DEVICE(LINE6_DEVID_PODSTUDIO_GX), + .driver_info = LINE6_PODSTUDIO_GX }, + { LINE6_DEVICE(LINE6_DEVID_PODSTUDIO_UX1), + .driver_info = LINE6_PODSTUDIO_UX1 }, + { LINE6_DEVICE(LINE6_DEVID_PODSTUDIO_UX2), + .driver_info = LINE6_PODSTUDIO_UX2 }, + { LINE6_DEVICE(LINE6_DEVID_PODXT), + .driver_info = LINE6_PODXT }, + { LINE6_DEVICE(LINE6_DEVID_PODXTLIVE), + .driver_info = LINE6_PODXTLIVE }, + { LINE6_DEVICE(LINE6_DEVID_PODXTPRO), + .driver_info = LINE6_PODXTPRO }, + { LINE6_DEVICE(LINE6_DEVID_TONEPORT_GX), + .driver_info = LINE6_TONEPORT_GX }, + { LINE6_DEVICE(LINE6_DEVID_TONEPORT_UX1), + .driver_info = LINE6_TONEPORT_UX1 }, + { LINE6_DEVICE(LINE6_DEVID_TONEPORT_UX2), + .driver_info = LINE6_TONEPORT_UX2 }, + { LINE6_DEVICE(LINE6_DEVID_VARIAX), + .driver_info = LINE6_VARIAX }, {} }; diff --git a/drivers/staging/line6/driver.h b/drivers/staging/line6/driver.h index 1cc7532..8fb4a9c 100644 --- a/drivers/staging/line6/driver.h +++ b/drivers/staging/line6/driver.h @@ -20,6 +20,27 @@ #define DRIVER_NAME "line6usb" +enum line6_device_type { + LINE6_BASSPODXT, + LINE6_BASSPODXTLIVE, + LINE6_BASSPODXTPRO, + LINE6_GUITARPORT, + LINE6_POCKETPOD, + LINE6_PODHD300, + LINE6_PODHD400, + LINE6_PODHD500, + LINE6_PODSTUDIO_GX, + LINE6_PODSTUDIO_UX1, + LINE6_PODSTUDIO_UX2, + LINE6_PODXT, + LINE6_PODXTLIVE, + LINE6_PODXTPRO, + LINE6_TONEPORT_GX, + LINE6_TONEPORT_UX1, + LINE6_TONEPORT_UX2, + LINE6_VARIAX +}; + #define LINE6_TIMEOUT 1 #define LINE6_BUFSIZE_LISTEN 32 #define LINE6_MESSAGE_MAXLEN 256 -- cgit v0.10.2 From c33a20b71daf7e7d0d5746200621867ae44d09e9 Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:41 -0800 Subject: staging: line6: Index properties array with device type The current logic uses the index of the matched entry from the device table as an offset to the corresponding properties entry. The values of the new `line6_device_type' enum are ordered such that they can be used as an index into either of these arrays. Drop the device entry lookup logic and use the device type (via the .driver_info member) instead. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index 2797e41..c090b2b 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -624,7 +624,7 @@ static void line6_destruct(struct usb_interface *interface) static int line6_probe(struct usb_interface *interface, const struct usb_device_id *id) { - int devtype; + enum line6_device_type devtype; struct usb_device *usbdev; struct usb_line6 *line6; const struct line6_properties *properties; @@ -646,20 +646,7 @@ static int line6_probe(struct usb_interface *interface, goto err_put; } - /* check vendor and product id */ - for (devtype = ARRAY_SIZE(line6_id_table) - 1; devtype--;) { - u16 idVendor = le16_to_cpu(usbdev->descriptor.idVendor); - u16 idProduct = le16_to_cpu(usbdev->descriptor.idProduct); - - if (idVendor == line6_id_table[devtype].idVendor && - idProduct == line6_id_table[devtype].idProduct) - break; - } - - if (devtype < 0) { - ret = -ENODEV; - goto err_put; - } + devtype = id->driver_info; /* initialize device info: */ properties = &line6_properties_table[devtype]; -- cgit v0.10.2 From a23a8bff1535ddf2f7b9f358f3eb47973d757c54 Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:42 -0800 Subject: staging: line6: Key off of device type The driver currently uses the device's idProduct as input to several switch statements. In some cases this is not sufficiently granular and the interface number must be taken into account. Store the device type in `usb_line6' and key off of it instead. New types can then be added that map to specific interfaces on the device so that this conditional logic can be flattened out. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index c090b2b..81d5a27 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -363,23 +363,23 @@ static void line6_data_received(struct urb *urb) line6->message_length = done; line6_midi_receive(line6, line6->buffer_message, done); - switch (le16_to_cpu(line6->usbdev->descriptor.idProduct)) { - case LINE6_DEVID_BASSPODXT: - case LINE6_DEVID_BASSPODXTLIVE: - case LINE6_DEVID_BASSPODXTPRO: - case LINE6_DEVID_PODXT: - case LINE6_DEVID_PODXTPRO: - case LINE6_DEVID_POCKETPOD: + switch (line6->type) { + case LINE6_BASSPODXT: + case LINE6_BASSPODXTLIVE: + case LINE6_BASSPODXTPRO: + case LINE6_PODXT: + case LINE6_PODXTPRO: + case LINE6_POCKETPOD: line6_pod_process_message((struct usb_line6_pod *) line6); break; - case LINE6_DEVID_PODHD300: - case LINE6_DEVID_PODHD400: - case LINE6_DEVID_PODHD500: + case LINE6_PODHD300: + case LINE6_PODHD400: + case LINE6_PODHD500: break; /* let userspace handle MIDI */ - case LINE6_DEVID_PODXTLIVE: + case LINE6_PODXTLIVE: switch (line6->interface_number) { case PODXTLIVE_INTERFACE_POD: line6_pod_process_message((struct usb_line6_pod @@ -399,7 +399,7 @@ static void line6_data_received(struct urb *urb) } break; - case LINE6_DEVID_VARIAX: + case LINE6_VARIAX: line6_variax_process_message((struct usb_line6_variax *) line6); break; @@ -629,7 +629,6 @@ static int line6_probe(struct usb_interface *interface, struct usb_line6 *line6; const struct line6_properties *properties; int interface_number, alternate = 0; - int product; int size = 0; int ep_read = 0, ep_write = 0; int ret; @@ -651,19 +650,18 @@ static int line6_probe(struct usb_interface *interface, /* initialize device info: */ properties = &line6_properties_table[devtype]; dev_info(&interface->dev, "Line6 %s found\n", properties->name); - product = le16_to_cpu(usbdev->descriptor.idProduct); /* query interface number */ interface_number = interface->cur_altsetting->desc.bInterfaceNumber; - switch (product) { - case LINE6_DEVID_BASSPODXTLIVE: - case LINE6_DEVID_PODXTLIVE: - case LINE6_DEVID_VARIAX: + switch (devtype) { + case LINE6_BASSPODXTLIVE: + case LINE6_PODXTLIVE: + case LINE6_VARIAX: alternate = 1; break; - case LINE6_DEVID_POCKETPOD: + case LINE6_POCKETPOD: switch (interface_number) { case 0: return -ENODEV; /* this interface has no endpoints */ @@ -675,7 +673,7 @@ static int line6_probe(struct usb_interface *interface, } break; - case LINE6_DEVID_PODHD500: + case LINE6_PODHD500: switch (interface_number) { case 0: alternate = 1; @@ -688,25 +686,25 @@ static int line6_probe(struct usb_interface *interface, } break; - case LINE6_DEVID_BASSPODXT: - case LINE6_DEVID_BASSPODXTPRO: - case LINE6_DEVID_PODXT: - case LINE6_DEVID_PODXTPRO: - case LINE6_DEVID_PODHD300: - case LINE6_DEVID_PODHD400: + case LINE6_BASSPODXT: + case LINE6_BASSPODXTPRO: + case LINE6_PODXT: + case LINE6_PODXTPRO: + case LINE6_PODHD300: + case LINE6_PODHD400: alternate = 5; break; - case LINE6_DEVID_GUITARPORT: - case LINE6_DEVID_PODSTUDIO_GX: - case LINE6_DEVID_PODSTUDIO_UX1: - case LINE6_DEVID_TONEPORT_GX: - case LINE6_DEVID_TONEPORT_UX1: + case LINE6_GUITARPORT: + case LINE6_PODSTUDIO_GX: + case LINE6_PODSTUDIO_UX1: + case LINE6_TONEPORT_GX: + case LINE6_TONEPORT_UX1: alternate = 2; /* 1..4 seem to be ok */ break; - case LINE6_DEVID_TONEPORT_UX2: - case LINE6_DEVID_PODSTUDIO_UX2: + case LINE6_TONEPORT_UX2: + case LINE6_PODSTUDIO_UX2: switch (interface_number) { case 0: /* defaults to 44.1kHz, 16-bit */ @@ -735,49 +733,49 @@ static int line6_probe(struct usb_interface *interface, goto err_put; } - /* initialize device data based on product id: */ - switch (product) { - case LINE6_DEVID_BASSPODXT: - case LINE6_DEVID_BASSPODXTLIVE: - case LINE6_DEVID_BASSPODXTPRO: - case LINE6_DEVID_PODXT: - case LINE6_DEVID_PODXTPRO: + /* initialize device data based on device: */ + switch (devtype) { + case LINE6_BASSPODXT: + case LINE6_BASSPODXTLIVE: + case LINE6_BASSPODXTPRO: + case LINE6_PODXT: + case LINE6_PODXTPRO: size = sizeof(struct usb_line6_pod); ep_read = 0x84; ep_write = 0x03; break; - case LINE6_DEVID_PODHD300: - case LINE6_DEVID_PODHD400: + case LINE6_PODHD300: + case LINE6_PODHD400: size = sizeof(struct usb_line6_podhd); ep_read = 0x84; ep_write = 0x03; break; - case LINE6_DEVID_PODHD500: + case LINE6_PODHD500: size = sizeof(struct usb_line6_podhd); ep_read = 0x81; ep_write = 0x01; break; - case LINE6_DEVID_POCKETPOD: + case LINE6_POCKETPOD: size = sizeof(struct usb_line6_pod); ep_read = 0x82; ep_write = 0x02; break; - case LINE6_DEVID_PODSTUDIO_GX: - case LINE6_DEVID_PODSTUDIO_UX1: - case LINE6_DEVID_PODSTUDIO_UX2: - case LINE6_DEVID_TONEPORT_GX: - case LINE6_DEVID_TONEPORT_UX1: - case LINE6_DEVID_TONEPORT_UX2: - case LINE6_DEVID_GUITARPORT: + case LINE6_PODSTUDIO_GX: + case LINE6_PODSTUDIO_UX1: + case LINE6_PODSTUDIO_UX2: + case LINE6_TONEPORT_GX: + case LINE6_TONEPORT_UX1: + case LINE6_TONEPORT_UX2: + case LINE6_GUITARPORT: size = sizeof(struct usb_line6_toneport); /* these don't have a control channel */ break; - case LINE6_DEVID_PODXTLIVE: + case LINE6_PODXTLIVE: switch (interface_number) { case PODXTLIVE_INTERFACE_POD: size = sizeof(struct usb_line6_pod); @@ -797,7 +795,7 @@ static int line6_probe(struct usb_interface *interface, } break; - case LINE6_DEVID_VARIAX: + case LINE6_VARIAX: size = sizeof(struct usb_line6_variax); ep_read = 0x82; ep_write = 0x01; @@ -829,7 +827,7 @@ static int line6_probe(struct usb_interface *interface, line6->ifcdev = &interface->dev; line6->ep_control_read = ep_read; line6->ep_control_write = ep_write; - line6->product = product; + line6->type = devtype; /* get data from endpoint descriptor (see usb_maxpacket): */ { @@ -885,25 +883,25 @@ static int line6_probe(struct usb_interface *interface, } } - /* initialize device data based on product id: */ - switch (product) { - case LINE6_DEVID_BASSPODXT: - case LINE6_DEVID_BASSPODXTLIVE: - case LINE6_DEVID_BASSPODXTPRO: - case LINE6_DEVID_POCKETPOD: - case LINE6_DEVID_PODXT: - case LINE6_DEVID_PODXTPRO: + /* initialize device data based on device: */ + switch (devtype) { + case LINE6_BASSPODXT: + case LINE6_BASSPODXTLIVE: + case LINE6_BASSPODXTPRO: + case LINE6_POCKETPOD: + case LINE6_PODXT: + case LINE6_PODXTPRO: ret = line6_pod_init(interface, (struct usb_line6_pod *)line6); break; - case LINE6_DEVID_PODHD300: - case LINE6_DEVID_PODHD400: - case LINE6_DEVID_PODHD500: + case LINE6_PODHD300: + case LINE6_PODHD400: + case LINE6_PODHD500: ret = line6_podhd_init(interface, (struct usb_line6_podhd *)line6); break; - case LINE6_DEVID_PODXTLIVE: + case LINE6_PODXTLIVE: switch (interface_number) { case PODXTLIVE_INTERFACE_POD: ret = @@ -926,19 +924,19 @@ static int line6_probe(struct usb_interface *interface, break; - case LINE6_DEVID_VARIAX: + case LINE6_VARIAX: ret = line6_variax_init(interface, (struct usb_line6_variax *)line6); break; - case LINE6_DEVID_PODSTUDIO_GX: - case LINE6_DEVID_PODSTUDIO_UX1: - case LINE6_DEVID_PODSTUDIO_UX2: - case LINE6_DEVID_TONEPORT_GX: - case LINE6_DEVID_TONEPORT_UX1: - case LINE6_DEVID_TONEPORT_UX2: - case LINE6_DEVID_GUITARPORT: + case LINE6_PODSTUDIO_GX: + case LINE6_PODSTUDIO_UX1: + case LINE6_PODSTUDIO_UX2: + case LINE6_TONEPORT_GX: + case LINE6_TONEPORT_UX1: + case LINE6_TONEPORT_UX2: + case LINE6_GUITARPORT: ret = line6_toneport_init(interface, (struct usb_line6_toneport *)line6); @@ -1004,23 +1002,23 @@ static void line6_disconnect(struct usb_interface *interface) dev_err(line6->ifcdev, "driver bug: inconsistent usb device\n"); - switch (le16_to_cpu(line6->usbdev->descriptor.idProduct)) { - case LINE6_DEVID_BASSPODXT: - case LINE6_DEVID_BASSPODXTLIVE: - case LINE6_DEVID_BASSPODXTPRO: - case LINE6_DEVID_POCKETPOD: - case LINE6_DEVID_PODXT: - case LINE6_DEVID_PODXTPRO: + switch (line6->type) { + case LINE6_BASSPODXT: + case LINE6_BASSPODXTLIVE: + case LINE6_BASSPODXTPRO: + case LINE6_POCKETPOD: + case LINE6_PODXT: + case LINE6_PODXTPRO: line6_pod_disconnect(interface); break; - case LINE6_DEVID_PODHD300: - case LINE6_DEVID_PODHD400: - case LINE6_DEVID_PODHD500: + case LINE6_PODHD300: + case LINE6_PODHD400: + case LINE6_PODHD500: line6_podhd_disconnect(interface); break; - case LINE6_DEVID_PODXTLIVE: + case LINE6_PODXTLIVE: switch (interface_number) { case PODXTLIVE_INTERFACE_POD: line6_pod_disconnect(interface); @@ -1033,17 +1031,17 @@ static void line6_disconnect(struct usb_interface *interface) break; - case LINE6_DEVID_VARIAX: + case LINE6_VARIAX: line6_variax_disconnect(interface); break; - case LINE6_DEVID_PODSTUDIO_GX: - case LINE6_DEVID_PODSTUDIO_UX1: - case LINE6_DEVID_PODSTUDIO_UX2: - case LINE6_DEVID_TONEPORT_GX: - case LINE6_DEVID_TONEPORT_UX1: - case LINE6_DEVID_TONEPORT_UX2: - case LINE6_DEVID_GUITARPORT: + case LINE6_PODSTUDIO_GX: + case LINE6_PODSTUDIO_UX1: + case LINE6_PODSTUDIO_UX2: + case LINE6_TONEPORT_GX: + case LINE6_TONEPORT_UX1: + case LINE6_TONEPORT_UX2: + case LINE6_GUITARPORT: line6_toneport_disconnect(interface); break; @@ -1107,15 +1105,18 @@ static int line6_reset_resume(struct usb_interface *interface) { struct usb_line6 *line6 = usb_get_intfdata(interface); - switch (le16_to_cpu(line6->usbdev->descriptor.idProduct)) { - case LINE6_DEVID_PODSTUDIO_GX: - case LINE6_DEVID_PODSTUDIO_UX1: - case LINE6_DEVID_PODSTUDIO_UX2: - case LINE6_DEVID_TONEPORT_GX: - case LINE6_DEVID_TONEPORT_UX1: - case LINE6_DEVID_TONEPORT_UX2: - case LINE6_DEVID_GUITARPORT: + switch (line6->type) { + case LINE6_PODSTUDIO_GX: + case LINE6_PODSTUDIO_UX1: + case LINE6_PODSTUDIO_UX2: + case LINE6_TONEPORT_GX: + case LINE6_TONEPORT_UX1: + case LINE6_TONEPORT_UX2: + case LINE6_GUITARPORT: line6_toneport_reset_resume((struct usb_line6_toneport *)line6); + + default: + break; } return line6_resume(interface); diff --git a/drivers/staging/line6/driver.h b/drivers/staging/line6/driver.h index 8fb4a9c..c536795 100644 --- a/drivers/staging/line6/driver.h +++ b/drivers/staging/line6/driver.h @@ -126,9 +126,9 @@ struct usb_line6 { struct usb_device *usbdev; /** - Product id. + Device type. */ - int product; + enum line6_device_type type; /** Properties. diff --git a/drivers/staging/line6/pcm.c b/drivers/staging/line6/pcm.c index 076c87b..86c7bcb 100644 --- a/drivers/staging/line6/pcm.c +++ b/drivers/staging/line6/pcm.c @@ -427,37 +427,37 @@ int line6_init_pcm(struct usb_line6 *line6, if (!(line6->properties->capabilities & LINE6_BIT_PCM)) return 0; /* skip PCM initialization and report success */ - /* initialize PCM subsystem based on product id: */ - switch (line6->product) { - case LINE6_DEVID_BASSPODXT: - case LINE6_DEVID_BASSPODXTLIVE: - case LINE6_DEVID_BASSPODXTPRO: - case LINE6_DEVID_PODXT: - case LINE6_DEVID_PODXTLIVE: - case LINE6_DEVID_PODXTPRO: - case LINE6_DEVID_PODHD300: - case LINE6_DEVID_PODHD400: + /* initialize PCM subsystem based on device: */ + switch (line6->type) { + case LINE6_BASSPODXT: + case LINE6_BASSPODXTLIVE: + case LINE6_BASSPODXTPRO: + case LINE6_PODXT: + case LINE6_PODXTLIVE: + case LINE6_PODXTPRO: + case LINE6_PODHD300: + case LINE6_PODHD400: ep_read = 0x82; ep_write = 0x01; break; - case LINE6_DEVID_PODHD500: + case LINE6_PODHD500: ep_read = 0x86; ep_write = 0x02; break; - case LINE6_DEVID_POCKETPOD: + case LINE6_POCKETPOD: ep_read = 0x82; ep_write = 0x02; break; - case LINE6_DEVID_GUITARPORT: - case LINE6_DEVID_PODSTUDIO_GX: - case LINE6_DEVID_PODSTUDIO_UX1: - case LINE6_DEVID_PODSTUDIO_UX2: - case LINE6_DEVID_TONEPORT_GX: - case LINE6_DEVID_TONEPORT_UX1: - case LINE6_DEVID_TONEPORT_UX2: + case LINE6_GUITARPORT: + case LINE6_PODSTUDIO_GX: + case LINE6_PODSTUDIO_UX1: + case LINE6_PODSTUDIO_UX2: + case LINE6_TONEPORT_GX: + case LINE6_TONEPORT_UX1: + case LINE6_TONEPORT_UX2: ep_read = 0x82; ep_write = 0x01; break; diff --git a/drivers/staging/line6/toneport.c b/drivers/staging/line6/toneport.c index 660dc3f..9e5cee1 100644 --- a/drivers/staging/line6/toneport.c +++ b/drivers/staging/line6/toneport.c @@ -97,11 +97,11 @@ static const struct { {"Inst & Mic", 0x0901} }; -static bool toneport_has_led(short product) +static bool toneport_has_led(enum line6_device_type type) { return - (product == LINE6_DEVID_GUITARPORT) || - (product == LINE6_DEVID_TONEPORT_GX); + (type == LINE6_GUITARPORT) || + (type == LINE6_TONEPORT_GX); /* add your device here if you are missing support for the LEDs */ } @@ -310,7 +310,6 @@ static void toneport_setup(struct usb_line6_toneport *toneport) int ticks; struct usb_line6 *line6 = &toneport->line6; struct usb_device *usbdev = line6->usbdev; - u16 idProduct = le16_to_cpu(usbdev->descriptor.idProduct); /* sync time on device with host: */ ticks = (int)get_seconds(); @@ -320,17 +319,19 @@ static void toneport_setup(struct usb_line6_toneport *toneport) toneport_send_cmd(usbdev, 0x0301, 0x0000); /* initialize source select: */ - switch (le16_to_cpu(usbdev->descriptor.idProduct)) { - case LINE6_DEVID_TONEPORT_UX1: - case LINE6_DEVID_TONEPORT_UX2: - case LINE6_DEVID_PODSTUDIO_UX1: - case LINE6_DEVID_PODSTUDIO_UX2: + switch (line6->type) { + case LINE6_TONEPORT_UX1: + case LINE6_TONEPORT_UX2: + case LINE6_PODSTUDIO_UX1: + case LINE6_PODSTUDIO_UX2: toneport_send_cmd(usbdev, toneport_source_info[toneport->source].code, 0x0000); + default: + break; } - if (toneport_has_led(idProduct)) + if (toneport_has_led(line6->type)) toneport_update_led(&usbdev->dev); } @@ -342,8 +343,6 @@ static int toneport_try_init(struct usb_interface *interface, { int err; struct usb_line6 *line6 = &toneport->line6; - struct usb_device *usbdev = line6->usbdev; - u16 idProduct = le16_to_cpu(usbdev->descriptor.idProduct); if ((interface == NULL) || (toneport == NULL)) return -ENODEV; @@ -366,17 +365,20 @@ static int toneport_try_init(struct usb_interface *interface, return err; /* register source select control: */ - switch (le16_to_cpu(usbdev->descriptor.idProduct)) { - case LINE6_DEVID_TONEPORT_UX1: - case LINE6_DEVID_TONEPORT_UX2: - case LINE6_DEVID_PODSTUDIO_UX1: - case LINE6_DEVID_PODSTUDIO_UX2: + switch (line6->type) { + case LINE6_TONEPORT_UX1: + case LINE6_TONEPORT_UX2: + case LINE6_PODSTUDIO_UX1: + case LINE6_PODSTUDIO_UX2: err = snd_ctl_add(line6->card, snd_ctl_new1(&toneport_control_source, line6->line6pcm)); if (err < 0) return err; + + default: + break; } /* register audio system: */ @@ -387,7 +389,7 @@ static int toneport_try_init(struct usb_interface *interface, line6_read_serial_number(line6, &toneport->serial_number); line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1); - if (toneport_has_led(idProduct)) { + if (toneport_has_led(line6->type)) { CHECK_RETURN(device_create_file (&interface->dev, &dev_attr_led_red)); CHECK_RETURN(device_create_file @@ -434,7 +436,6 @@ void line6_toneport_disconnect(struct usb_interface *interface) { struct usb_line6_toneport *toneport; struct snd_line6_pcm *line6pcm; - u16 idProduct; if (interface == NULL) return; @@ -444,9 +445,8 @@ void line6_toneport_disconnect(struct usb_interface *interface) return; del_timer_sync(&toneport->timer); - idProduct = le16_to_cpu(toneport->line6.usbdev->descriptor.idProduct); - if (toneport_has_led(idProduct)) { + if (toneport_has_led(toneport->line6.type)) { device_remove_file(&interface->dev, &dev_attr_led_red); device_remove_file(&interface->dev, &dev_attr_led_green); } -- cgit v0.10.2 From f45be7dcc9e50b254eab2bd6373dda0b382e5ca1 Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:43 -0800 Subject: staging: line6: Remove idVendor and idProduct macros These are now only used to build the device table. Each entry in this table is already clearly documented as to what device it maps to so the macros become unnecessary indirection. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index 81d5a27..6dc8a0d 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -30,46 +30,28 @@ #define DRIVER_DESC "Line6 USB Driver" #define DRIVER_VERSION "0.9.1beta" DRIVER_REVISION -#define LINE6_DEVICE(prod) USB_DEVICE(LINE6_VENDOR_ID, prod) +#define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod) /* table of devices that work with this driver */ static const struct usb_device_id line6_id_table[] = { - { LINE6_DEVICE(LINE6_DEVID_BASSPODXT), - .driver_info = LINE6_BASSPODXT }, - { LINE6_DEVICE(LINE6_DEVID_BASSPODXTLIVE), - .driver_info = LINE6_BASSPODXTLIVE }, - { LINE6_DEVICE(LINE6_DEVID_BASSPODXTPRO), - .driver_info = LINE6_BASSPODXTPRO }, - { LINE6_DEVICE(LINE6_DEVID_GUITARPORT), - .driver_info = LINE6_GUITARPORT }, - { LINE6_DEVICE(LINE6_DEVID_POCKETPOD), - .driver_info = LINE6_POCKETPOD }, - { LINE6_DEVICE(LINE6_DEVID_PODHD300), - .driver_info = LINE6_PODHD300 }, - { LINE6_DEVICE(LINE6_DEVID_PODHD400), - .driver_info = LINE6_PODHD400 }, - { LINE6_DEVICE(LINE6_DEVID_PODHD500), - .driver_info = LINE6_PODHD500 }, - { LINE6_DEVICE(LINE6_DEVID_PODSTUDIO_GX), - .driver_info = LINE6_PODSTUDIO_GX }, - { LINE6_DEVICE(LINE6_DEVID_PODSTUDIO_UX1), - .driver_info = LINE6_PODSTUDIO_UX1 }, - { LINE6_DEVICE(LINE6_DEVID_PODSTUDIO_UX2), - .driver_info = LINE6_PODSTUDIO_UX2 }, - { LINE6_DEVICE(LINE6_DEVID_PODXT), - .driver_info = LINE6_PODXT }, - { LINE6_DEVICE(LINE6_DEVID_PODXTLIVE), - .driver_info = LINE6_PODXTLIVE }, - { LINE6_DEVICE(LINE6_DEVID_PODXTPRO), - .driver_info = LINE6_PODXTPRO }, - { LINE6_DEVICE(LINE6_DEVID_TONEPORT_GX), - .driver_info = LINE6_TONEPORT_GX }, - { LINE6_DEVICE(LINE6_DEVID_TONEPORT_UX1), - .driver_info = LINE6_TONEPORT_UX1 }, - { LINE6_DEVICE(LINE6_DEVID_TONEPORT_UX2), - .driver_info = LINE6_TONEPORT_UX2 }, - { LINE6_DEVICE(LINE6_DEVID_VARIAX), - .driver_info = LINE6_VARIAX }, + { LINE6_DEVICE(0x4250), .driver_info = LINE6_BASSPODXT }, + { LINE6_DEVICE(0x4642), .driver_info = LINE6_BASSPODXTLIVE }, + { LINE6_DEVICE(0x4252), .driver_info = LINE6_BASSPODXTPRO }, + { LINE6_DEVICE(0x4750), .driver_info = LINE6_GUITARPORT }, + { LINE6_DEVICE(0x5051), .driver_info = LINE6_POCKETPOD }, + { LINE6_DEVICE(0x5057), .driver_info = LINE6_PODHD300 }, + { LINE6_DEVICE(0x5058), .driver_info = LINE6_PODHD400 }, + { LINE6_DEVICE(0x414D), .driver_info = LINE6_PODHD500 }, + { LINE6_DEVICE(0x4153), .driver_info = LINE6_PODSTUDIO_GX }, + { LINE6_DEVICE(0x4150), .driver_info = LINE6_PODSTUDIO_UX1 }, + { LINE6_DEVICE(0x4151), .driver_info = LINE6_PODSTUDIO_UX2 }, + { LINE6_DEVICE(0x5044), .driver_info = LINE6_PODXT }, + { LINE6_DEVICE(0x4650), .driver_info = LINE6_PODXTLIVE }, + { LINE6_DEVICE(0x5050), .driver_info = LINE6_PODXTPRO }, + { LINE6_DEVICE(0x4147), .driver_info = LINE6_TONEPORT_GX }, + { LINE6_DEVICE(0x4141), .driver_info = LINE6_TONEPORT_UX1 }, + { LINE6_DEVICE(0x4142), .driver_info = LINE6_TONEPORT_UX2 }, + { LINE6_DEVICE(0x534d), .driver_info = LINE6_VARIAX }, {} }; diff --git a/drivers/staging/line6/usbdefs.h b/drivers/staging/line6/usbdefs.h index 06bf909..c897dba 100644 --- a/drivers/staging/line6/usbdefs.h +++ b/drivers/staging/line6/usbdefs.h @@ -12,32 +12,8 @@ #ifndef USBDEFS_H #define USBDEFS_H -#define LINE6_VENDOR_ID 0x0e41 - #define USB_INTERVALS_PER_SECOND 1000 -/* - Device ids. -*/ -#define LINE6_DEVID_BASSPODXT 0x4250 -#define LINE6_DEVID_BASSPODXTLIVE 0x4642 -#define LINE6_DEVID_BASSPODXTPRO 0x4252 -#define LINE6_DEVID_GUITARPORT 0x4750 -#define LINE6_DEVID_POCKETPOD 0x5051 -#define LINE6_DEVID_PODHD300 0x5057 -#define LINE6_DEVID_PODHD400 0x5058 -#define LINE6_DEVID_PODHD500 0x414D -#define LINE6_DEVID_PODSTUDIO_GX 0x4153 -#define LINE6_DEVID_PODSTUDIO_UX1 0x4150 -#define LINE6_DEVID_PODSTUDIO_UX2 0x4151 -#define LINE6_DEVID_PODXT 0x5044 -#define LINE6_DEVID_PODXTLIVE 0x4650 -#define LINE6_DEVID_PODXTPRO 0x5050 -#define LINE6_DEVID_TONEPORT_GX 0x4147 -#define LINE6_DEVID_TONEPORT_UX1 0x4141 -#define LINE6_DEVID_TONEPORT_UX2 0x4142 -#define LINE6_DEVID_VARIAX 0x534d - /* device supports settings parameter via USB */ #define LINE6_BIT_CONTROL (1 << 0) /* device supports PCM input/output via USB */ -- cgit v0.10.2 From 5a8584742586373cd749335b4f7bcb983f39d34e Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:44 -0800 Subject: staging: line6: Remove useless comments Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index 6dc8a0d..acde205 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -61,7 +61,6 @@ MODULE_DEVICE_TABLE(usb, line6_id_table); {.id = dev_id,\ .name = dev_name, .capabilities = LINE6_BIT_##dev_cap} -/* *INDENT-OFF* */ static const struct line6_properties line6_properties_table[] = { L6PROP("BassPODxt", "BassPODxt", CTRL_PCM_HW), L6PROP("BassPODxtLive", "BassPODxt Live", CTRL_PCM_HW), @@ -82,7 +81,6 @@ static const struct line6_properties line6_properties_table[] = { L6PROP("TonePortUX2", "TonePort UX2", PCM), L6PROP("Variax", "Variax Workbench", CONTROL), }; -/* *INDENT-ON* */ /* This is Line6's MIDI manufacturer ID. -- cgit v0.10.2 From 4cb1a4ae4afb36234bb0521b282d4861e12ff80b Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:45 -0800 Subject: staging: line6: Rename capability macros Including "BIT" in the macro name is pointless. Replace with "CAP" to provide some context for what its value represents. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index acde205..4ec87a3 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -59,7 +59,7 @@ MODULE_DEVICE_TABLE(usb, line6_id_table); #define L6PROP(dev_id, dev_name, dev_cap)\ {.id = dev_id,\ - .name = dev_name, .capabilities = LINE6_BIT_##dev_cap} + .name = dev_name, .capabilities = LINE6_CAP_##dev_cap} static const struct line6_properties line6_properties_table[] = { L6PROP("BassPODxt", "BassPODxt", CTRL_PCM_HW), @@ -830,7 +830,7 @@ static int line6_probe(struct usb_interface *interface, usb_set_intfdata(interface, line6); - if (properties->capabilities & LINE6_BIT_CONTROL) { + if (properties->capabilities & LINE6_CAP_CONTROL) { /* initialize USB buffers: */ line6->buffer_listen = kmalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL); @@ -1052,7 +1052,7 @@ static int line6_suspend(struct usb_interface *interface, pm_message_t message) snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot); - if (line6->properties->capabilities & LINE6_BIT_CONTROL) + if (line6->properties->capabilities & LINE6_CAP_CONTROL) line6_stop_listen(line6); if (line6pcm != NULL) { @@ -1071,7 +1071,7 @@ static int line6_resume(struct usb_interface *interface) { struct usb_line6 *line6 = usb_get_intfdata(interface); - if (line6->properties->capabilities & LINE6_BIT_CONTROL) + if (line6->properties->capabilities & LINE6_CAP_CONTROL) line6_start_listen(line6); snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0); diff --git a/drivers/staging/line6/midi.c b/drivers/staging/line6/midi.c index 1ac343b..c453485 100644 --- a/drivers/staging/line6/midi.c +++ b/drivers/staging/line6/midi.c @@ -279,7 +279,7 @@ int line6_init_midi(struct usb_line6 *line6) int err; struct snd_line6_midi *line6midi; - if (!(line6->properties->capabilities & LINE6_BIT_CONTROL)) { + if (!(line6->properties->capabilities & LINE6_CAP_CONTROL)) { /* skip MIDI initialization and report success */ return 0; } diff --git a/drivers/staging/line6/pcm.c b/drivers/staging/line6/pcm.c index 86c7bcb..e09772f 100644 --- a/drivers/staging/line6/pcm.c +++ b/drivers/staging/line6/pcm.c @@ -424,7 +424,7 @@ int line6_init_pcm(struct usb_line6 *line6, int ep_read = 0, ep_write = 0; struct snd_line6_pcm *line6pcm; - if (!(line6->properties->capabilities & LINE6_BIT_PCM)) + if (!(line6->properties->capabilities & LINE6_CAP_PCM)) return 0; /* skip PCM initialization and report success */ /* initialize PCM subsystem based on device: */ diff --git a/drivers/staging/line6/playback.c b/drivers/staging/line6/playback.c index 2ca8900..54b7f60 100644 --- a/drivers/staging/line6/playback.c +++ b/drivers/staging/line6/playback.c @@ -261,7 +261,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) #endif if (! (line6pcm->line6-> - properties->capabilities & LINE6_BIT_HWMON) + properties->capabilities & LINE6_CAP_HWMON) && (line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM) && (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM)) add_monitor_signal(urb_out, line6pcm->prev_fbuf, diff --git a/drivers/staging/line6/pod.c b/drivers/staging/line6/pod.c index 7b4ec92..0fb1788 100644 --- a/drivers/staging/line6/pod.c +++ b/drivers/staging/line6/pod.c @@ -396,7 +396,7 @@ static int pod_try_init(struct usb_interface *interface, handler. */ - if (pod->line6.properties->capabilities & LINE6_BIT_CONTROL) { + if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) { pod->monitor_level = POD_SYSTEM_INVALID; /* initiate startup procedure: */ diff --git a/drivers/staging/line6/usbdefs.h b/drivers/staging/line6/usbdefs.h index c897dba..d6e46ee 100644 --- a/drivers/staging/line6/usbdefs.h +++ b/drivers/staging/line6/usbdefs.h @@ -15,15 +15,15 @@ #define USB_INTERVALS_PER_SECOND 1000 /* device supports settings parameter via USB */ -#define LINE6_BIT_CONTROL (1 << 0) +#define LINE6_CAP_CONTROL (1 << 0) /* device supports PCM input/output via USB */ -#define LINE6_BIT_PCM (1 << 1) +#define LINE6_CAP_PCM (1 << 1) /* device support hardware monitoring */ -#define LINE6_BIT_HWMON (1 << 2) +#define LINE6_CAP_HWMON (1 << 2) -#define LINE6_BIT_CTRL_PCM_HW (LINE6_BIT_CONTROL | \ - LINE6_BIT_PCM | \ - LINE6_BIT_HWMON) +#define LINE6_CAP_CTRL_PCM_HW (LINE6_CAP_CONTROL | \ + LINE6_CAP_PCM | \ + LINE6_CAP_HWMON) #define LINE6_FALLBACK_INTERVAL 10 #define LINE6_FALLBACK_MAXPACKETSIZE 16 -- cgit v0.10.2 From 4d947546c3fb6bc426a7527cf6f8ddb8368fbbc7 Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:46 -0800 Subject: staging: line6: Use explicit indexes when defining properties Specify the index of the properties entry explicitly to define this structure more robustly. Also, drop the `L6PROP' macro in favor of initializing each member explicitly on its own line since horizontal space is limited and more attributes will be added later. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index 4ec87a3..c988b78 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -57,29 +57,97 @@ static const struct usb_device_id line6_id_table[] = { MODULE_DEVICE_TABLE(usb, line6_id_table); -#define L6PROP(dev_id, dev_name, dev_cap)\ - {.id = dev_id,\ - .name = dev_name, .capabilities = LINE6_CAP_##dev_cap} - static const struct line6_properties line6_properties_table[] = { - L6PROP("BassPODxt", "BassPODxt", CTRL_PCM_HW), - L6PROP("BassPODxtLive", "BassPODxt Live", CTRL_PCM_HW), - L6PROP("BassPODxtPro", "BassPODxt Pro", CTRL_PCM_HW), - L6PROP("GuitarPort", "GuitarPort", PCM), - L6PROP("PocketPOD", "Pocket POD", CONTROL), - L6PROP("PODHD300", "POD HD300", CTRL_PCM_HW), - L6PROP("PODHD400", "POD HD400", CTRL_PCM_HW), - L6PROP("PODHD500", "POD HD500", CTRL_PCM_HW), - L6PROP("PODStudioGX", "POD Studio GX", PCM), - L6PROP("PODStudioUX1", "POD Studio UX1", PCM), - L6PROP("PODStudioUX2", "POD Studio UX2", PCM), - L6PROP("PODxt", "PODxt", CTRL_PCM_HW), - L6PROP("PODxtLive", "PODxt Live", CTRL_PCM_HW), - L6PROP("PODxtPro", "PODxt Pro", CTRL_PCM_HW), - L6PROP("TonePortGX", "TonePort GX", PCM), - L6PROP("TonePortUX1", "TonePort UX1", PCM), - L6PROP("TonePortUX2", "TonePort UX2", PCM), - L6PROP("Variax", "Variax Workbench", CONTROL), + [LINE6_BASSPODXT] = { + .id = "BassPODxt", + .name = "BassPODxt", + .capabilities = LINE6_CAP_CTRL_PCM_HW, + }, + [LINE6_BASSPODXTLIVE] = { + .id = "BassPODxtLive", + .name = "BassPODxt Live", + .capabilities = LINE6_CAP_CTRL_PCM_HW, + }, + [LINE6_BASSPODXTPRO] = { + .id = "BassPODxtPro", + .name = "BassPODxt Pro", + .capabilities = LINE6_CAP_CTRL_PCM_HW, + }, + [LINE6_GUITARPORT] = { + .id = "GuitarPort", + .name = "GuitarPort", + .capabilities = LINE6_CAP_PCM, + }, + [LINE6_POCKETPOD] = { + .id = "PocketPOD", + .name = "Pocket POD", + .capabilities = LINE6_CAP_CONTROL, + }, + [LINE6_PODHD300] = { + .id = "PODHD300", + .name = "POD HD300", + .capabilities = LINE6_CAP_CTRL_PCM_HW, + }, + [LINE6_PODHD400] = { + .id = "PODHD400", + .name = "POD HD400", + .capabilities = LINE6_CAP_CTRL_PCM_HW, + }, + [LINE6_PODHD500] = { + .id = "PODHD500", + .name = "POD HD500", + .capabilities = LINE6_CAP_CTRL_PCM_HW, + }, + [LINE6_PODSTUDIO_GX] = { + .id = "PODStudioGX", + .name = "POD Studio GX", + .capabilities = LINE6_CAP_PCM, + }, + [LINE6_PODSTUDIO_UX1] = { + .id = "PODStudioUX1", + .name = "POD Studio UX1", + .capabilities = LINE6_CAP_PCM, + }, + [LINE6_PODSTUDIO_UX2] = { + .id = "PODStudioUX2", + .name = "POD Studio UX2", + .capabilities = LINE6_CAP_PCM, + }, + [LINE6_PODXT] = { + .id = "PODxt", + .name = "PODxt", + .capabilities = LINE6_CAP_CTRL_PCM_HW, + }, + [LINE6_PODXTLIVE] = { + .id = "PODxtLive", + .name = "PODxt Live", + .capabilities = LINE6_CAP_CTRL_PCM_HW, + }, + [LINE6_PODXTPRO] = { + .id = "PODxtPro", + .name = "PODxt Pro", + .capabilities = LINE6_CAP_CTRL_PCM_HW, + }, + [LINE6_TONEPORT_GX] = { + .id = "TonePortGX", + .name = "TonePort GX", + .capabilities = LINE6_CAP_PCM, + }, + [LINE6_TONEPORT_UX1] = { + .id = "TonePortUX1", + .name = "TonePort UX1", + .capabilities = LINE6_CAP_PCM, + }, + [LINE6_TONEPORT_UX2] = { + .id = "TonePortUX2", + .name = "TonePort UX2", + .capabilities = LINE6_CAP_PCM, + }, + [LINE6_VARIAX] = { + .id = "Variax", + .name = "Variax Workbench", + .capabilities = LINE6_CAP_CONTROL, + } }; /* -- cgit v0.10.2 From 7b3e4d47ca372c4e54f0a1f2e7cffca0b9f9c070 Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:47 -0800 Subject: staging: line6: List out capabilities individually The `LINE6_CAP_CTRL_PCM_HW' macro combines three capabilities to save horizontal space when defining the properties entries. Now that these are no longer limited to single lines this is not such a concern. Specify capabilities individually when defining each property for better clarity. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index c988b78..6fecc1b 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -61,92 +61,110 @@ static const struct line6_properties line6_properties_table[] = { [LINE6_BASSPODXT] = { .id = "BassPODxt", .name = "BassPODxt", - .capabilities = LINE6_CAP_CTRL_PCM_HW, + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, }, [LINE6_BASSPODXTLIVE] = { .id = "BassPODxtLive", .name = "BassPODxt Live", - .capabilities = LINE6_CAP_CTRL_PCM_HW, + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, }, [LINE6_BASSPODXTPRO] = { .id = "BassPODxtPro", .name = "BassPODxt Pro", - .capabilities = LINE6_CAP_CTRL_PCM_HW, + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, }, [LINE6_GUITARPORT] = { .id = "GuitarPort", .name = "GuitarPort", - .capabilities = LINE6_CAP_PCM, + .capabilities = LINE6_CAP_PCM, }, [LINE6_POCKETPOD] = { .id = "PocketPOD", .name = "Pocket POD", - .capabilities = LINE6_CAP_CONTROL, + .capabilities = LINE6_CAP_CONTROL, }, [LINE6_PODHD300] = { .id = "PODHD300", .name = "POD HD300", - .capabilities = LINE6_CAP_CTRL_PCM_HW, + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, }, [LINE6_PODHD400] = { .id = "PODHD400", .name = "POD HD400", - .capabilities = LINE6_CAP_CTRL_PCM_HW, + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, }, [LINE6_PODHD500] = { .id = "PODHD500", .name = "POD HD500", - .capabilities = LINE6_CAP_CTRL_PCM_HW, + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, }, [LINE6_PODSTUDIO_GX] = { .id = "PODStudioGX", .name = "POD Studio GX", - .capabilities = LINE6_CAP_PCM, + .capabilities = LINE6_CAP_PCM, }, [LINE6_PODSTUDIO_UX1] = { .id = "PODStudioUX1", .name = "POD Studio UX1", - .capabilities = LINE6_CAP_PCM, + .capabilities = LINE6_CAP_PCM, }, [LINE6_PODSTUDIO_UX2] = { .id = "PODStudioUX2", .name = "POD Studio UX2", - .capabilities = LINE6_CAP_PCM, + .capabilities = LINE6_CAP_PCM, }, [LINE6_PODXT] = { .id = "PODxt", .name = "PODxt", - .capabilities = LINE6_CAP_CTRL_PCM_HW, + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, }, [LINE6_PODXTLIVE] = { .id = "PODxtLive", .name = "PODxt Live", - .capabilities = LINE6_CAP_CTRL_PCM_HW, + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, }, [LINE6_PODXTPRO] = { .id = "PODxtPro", .name = "PODxt Pro", - .capabilities = LINE6_CAP_CTRL_PCM_HW, + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, }, [LINE6_TONEPORT_GX] = { .id = "TonePortGX", .name = "TonePort GX", - .capabilities = LINE6_CAP_PCM, + .capabilities = LINE6_CAP_PCM, }, [LINE6_TONEPORT_UX1] = { .id = "TonePortUX1", .name = "TonePort UX1", - .capabilities = LINE6_CAP_PCM, + .capabilities = LINE6_CAP_PCM, }, [LINE6_TONEPORT_UX2] = { .id = "TonePortUX2", .name = "TonePort UX2", - .capabilities = LINE6_CAP_PCM, + .capabilities = LINE6_CAP_PCM, }, [LINE6_VARIAX] = { .id = "Variax", .name = "Variax Workbench", - .capabilities = LINE6_CAP_CONTROL, + .capabilities = LINE6_CAP_CONTROL, } }; diff --git a/drivers/staging/line6/usbdefs.h b/drivers/staging/line6/usbdefs.h index d6e46ee..f4d080e 100644 --- a/drivers/staging/line6/usbdefs.h +++ b/drivers/staging/line6/usbdefs.h @@ -21,10 +21,6 @@ /* device support hardware monitoring */ #define LINE6_CAP_HWMON (1 << 2) -#define LINE6_CAP_CTRL_PCM_HW (LINE6_CAP_CONTROL | \ - LINE6_CAP_PCM | \ - LINE6_CAP_HWMON) - #define LINE6_FALLBACK_INTERVAL 10 #define LINE6_FALLBACK_MAXPACKETSIZE 16 -- cgit v0.10.2 From 7ad07310d57dec80d32572479f58decb6a8529e0 Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:48 -0800 Subject: staging: line6: Split out PODxt Live interfaces The PODxt Live device has both a POD and a Variax interface. Add device type entries for each of these. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index 6fecc1b..cb96029 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -31,6 +31,7 @@ #define DRIVER_VERSION "0.9.1beta" DRIVER_REVISION #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod) +#define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n) /* table of devices that work with this driver */ static const struct usb_device_id line6_id_table[] = { @@ -46,7 +47,8 @@ static const struct usb_device_id line6_id_table[] = { { LINE6_DEVICE(0x4150), .driver_info = LINE6_PODSTUDIO_UX1 }, { LINE6_DEVICE(0x4151), .driver_info = LINE6_PODSTUDIO_UX2 }, { LINE6_DEVICE(0x5044), .driver_info = LINE6_PODXT }, - { LINE6_DEVICE(0x4650), .driver_info = LINE6_PODXTLIVE }, + { LINE6_IF_NUM(0x4650, 0), .driver_info = LINE6_PODXTLIVE_POD }, + { LINE6_IF_NUM(0x4650, 1), .driver_info = LINE6_PODXTLIVE_VARIAX }, { LINE6_DEVICE(0x5050), .driver_info = LINE6_PODXTPRO }, { LINE6_DEVICE(0x4147), .driver_info = LINE6_TONEPORT_GX }, { LINE6_DEVICE(0x4141), .driver_info = LINE6_TONEPORT_UX1 }, @@ -132,7 +134,14 @@ static const struct line6_properties line6_properties_table[] = { | LINE6_CAP_PCM | LINE6_CAP_HWMON, }, - [LINE6_PODXTLIVE] = { + [LINE6_PODXTLIVE_POD] = { + .id = "PODxtLive", + .name = "PODxt Live", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, + }, + [LINE6_PODXTLIVE_VARIAX] = { .id = "PODxtLive", .name = "PODxt Live", .capabilities = LINE6_CAP_CONTROL @@ -445,24 +454,15 @@ static void line6_data_received(struct urb *urb) case LINE6_PODHD500: break; /* let userspace handle MIDI */ - case LINE6_PODXTLIVE: - switch (line6->interface_number) { - case PODXTLIVE_INTERFACE_POD: - line6_pod_process_message((struct usb_line6_pod + case LINE6_PODXTLIVE_POD: + line6_pod_process_message((struct usb_line6_pod *)line6); - break; - - case PODXTLIVE_INTERFACE_VARIAX: - line6_variax_process_message((struct - usb_line6_variax - *)line6); - break; - - default: - dev_err(line6->ifcdev, - "PODxt Live interface %d not supported\n", - line6->interface_number); - } + break; + + case LINE6_PODXTLIVE_VARIAX: + line6_variax_process_message((struct + usb_line6_variax + *)line6); break; case LINE6_VARIAX: @@ -722,7 +722,8 @@ static int line6_probe(struct usb_interface *interface, switch (devtype) { case LINE6_BASSPODXTLIVE: - case LINE6_PODXTLIVE: + case LINE6_PODXTLIVE_POD: + case LINE6_PODXTLIVE_VARIAX: case LINE6_VARIAX: alternate = 1; break; @@ -841,24 +842,16 @@ static int line6_probe(struct usb_interface *interface, /* these don't have a control channel */ break; - case LINE6_PODXTLIVE: - switch (interface_number) { - case PODXTLIVE_INTERFACE_POD: - size = sizeof(struct usb_line6_pod); - ep_read = 0x84; - ep_write = 0x03; - break; - - case PODXTLIVE_INTERFACE_VARIAX: - size = sizeof(struct usb_line6_variax); - ep_read = 0x86; - ep_write = 0x05; - break; + case LINE6_PODXTLIVE_POD: + size = sizeof(struct usb_line6_pod); + ep_read = 0x84; + ep_write = 0x03; + break; - default: - ret = -ENODEV; - goto err_put; - } + case LINE6_PODXTLIVE_VARIAX: + size = sizeof(struct usb_line6_variax); + ep_read = 0x86; + ep_write = 0x05; break; case LINE6_VARIAX: @@ -887,7 +880,6 @@ static int line6_probe(struct usb_interface *interface, } /* store basic data: */ - line6->interface_number = interface_number; line6->properties = properties; line6->usbdev = usbdev; line6->ifcdev = &interface->dev; @@ -967,27 +959,16 @@ static int line6_probe(struct usb_interface *interface, (struct usb_line6_podhd *)line6); break; - case LINE6_PODXTLIVE: - switch (interface_number) { - case PODXTLIVE_INTERFACE_POD: - ret = - line6_pod_init(interface, - (struct usb_line6_pod *)line6); - break; - - case PODXTLIVE_INTERFACE_VARIAX: - ret = - line6_variax_init(interface, - (struct usb_line6_variax *)line6); - break; - - default: - dev_err(&interface->dev, - "PODxt Live interface %d not supported\n", - interface_number); - ret = -ENODEV; - } + case LINE6_PODXTLIVE_POD: + ret = + line6_pod_init(interface, + (struct usb_line6_pod *)line6); + break; + case LINE6_PODXTLIVE_VARIAX: + ret = + line6_variax_init(interface, + (struct usb_line6_variax *)line6); break; case LINE6_VARIAX: @@ -1084,17 +1065,12 @@ static void line6_disconnect(struct usb_interface *interface) line6_podhd_disconnect(interface); break; - case LINE6_PODXTLIVE: - switch (interface_number) { - case PODXTLIVE_INTERFACE_POD: - line6_pod_disconnect(interface); - break; - - case PODXTLIVE_INTERFACE_VARIAX: - line6_variax_disconnect(interface); - break; - } + case LINE6_PODXTLIVE_POD: + line6_pod_disconnect(interface); + break; + case LINE6_PODXTLIVE_VARIAX: + line6_variax_disconnect(interface); break; case LINE6_VARIAX: diff --git a/drivers/staging/line6/driver.h b/drivers/staging/line6/driver.h index c536795..085aa44 100644 --- a/drivers/staging/line6/driver.h +++ b/drivers/staging/line6/driver.h @@ -33,7 +33,8 @@ enum line6_device_type { LINE6_PODSTUDIO_UX1, LINE6_PODSTUDIO_UX2, LINE6_PODXT, - LINE6_PODXTLIVE, + LINE6_PODXTLIVE_POD, + LINE6_PODXTLIVE_VARIAX, LINE6_PODXTPRO, LINE6_TONEPORT_GX, LINE6_TONEPORT_UX1, @@ -136,11 +137,6 @@ struct usb_line6 { const struct line6_properties *properties; /** - Interface number. - */ - int interface_number; - - /** Interval (ms). */ int interval; diff --git a/drivers/staging/line6/pcm.c b/drivers/staging/line6/pcm.c index e09772f..d09d1ea 100644 --- a/drivers/staging/line6/pcm.c +++ b/drivers/staging/line6/pcm.c @@ -433,7 +433,8 @@ int line6_init_pcm(struct usb_line6 *line6, case LINE6_BASSPODXTLIVE: case LINE6_BASSPODXTPRO: case LINE6_PODXT: - case LINE6_PODXTLIVE: + case LINE6_PODXTLIVE_POD: + case LINE6_PODXTLIVE_VARIAX: case LINE6_PODXTPRO: case LINE6_PODHD300: case LINE6_PODHD400: diff --git a/drivers/staging/line6/pod.h b/drivers/staging/line6/pod.h index 397d94c..91fd4c5 100644 --- a/drivers/staging/line6/pod.h +++ b/drivers/staging/line6/pod.h @@ -21,12 +21,6 @@ #include "driver.h" /* - PODxt Live interfaces -*/ -#define PODXTLIVE_INTERFACE_POD 0 -#define PODXTLIVE_INTERFACE_VARIAX 1 - -/* Locate name in binary program dump */ #define POD_NAME_OFFSET 0 -- cgit v0.10.2 From 951dd316119ca5fb7eedcb1950d9e19154e7c332 Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:49 -0800 Subject: staging: line6: Split out POD HD500 interfaces The driver uses a different altsetting depending on the interface. Add device type entries for each of these. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index cb96029..e97e2cb 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -42,7 +42,8 @@ static const struct usb_device_id line6_id_table[] = { { LINE6_DEVICE(0x5051), .driver_info = LINE6_POCKETPOD }, { LINE6_DEVICE(0x5057), .driver_info = LINE6_PODHD300 }, { LINE6_DEVICE(0x5058), .driver_info = LINE6_PODHD400 }, - { LINE6_DEVICE(0x414D), .driver_info = LINE6_PODHD500 }, + { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 }, + { LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 }, { LINE6_DEVICE(0x4153), .driver_info = LINE6_PODSTUDIO_GX }, { LINE6_DEVICE(0x4150), .driver_info = LINE6_PODSTUDIO_UX1 }, { LINE6_DEVICE(0x4151), .driver_info = LINE6_PODSTUDIO_UX2 }, @@ -105,7 +106,14 @@ static const struct line6_properties line6_properties_table[] = { | LINE6_CAP_PCM | LINE6_CAP_HWMON, }, - [LINE6_PODHD500] = { + [LINE6_PODHD500_0] = { + .id = "PODHD500", + .name = "POD HD500", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, + }, + [LINE6_PODHD500_1] = { .id = "PODHD500", .name = "POD HD500", .capabilities = LINE6_CAP_CONTROL @@ -451,7 +459,8 @@ static void line6_data_received(struct urb *urb) case LINE6_PODHD300: case LINE6_PODHD400: - case LINE6_PODHD500: + case LINE6_PODHD500_0: + case LINE6_PODHD500_1: break; /* let userspace handle MIDI */ case LINE6_PODXTLIVE_POD: @@ -740,17 +749,12 @@ static int line6_probe(struct usb_interface *interface, } break; - case LINE6_PODHD500: - switch (interface_number) { - case 0: - alternate = 1; - break; - case 1: - alternate = 0; - break; - default: - MISSING_CASE; - } + case LINE6_PODHD500_0: + alternate = 1; + break; + + case LINE6_PODHD500_1: + alternate = 0; break; case LINE6_BASSPODXT: @@ -819,7 +823,8 @@ static int line6_probe(struct usb_interface *interface, ep_write = 0x03; break; - case LINE6_PODHD500: + case LINE6_PODHD500_0: + case LINE6_PODHD500_1: size = sizeof(struct usb_line6_podhd); ep_read = 0x81; ep_write = 0x01; @@ -954,7 +959,8 @@ static int line6_probe(struct usb_interface *interface, case LINE6_PODHD300: case LINE6_PODHD400: - case LINE6_PODHD500: + case LINE6_PODHD500_0: + case LINE6_PODHD500_1: ret = line6_podhd_init(interface, (struct usb_line6_podhd *)line6); break; @@ -1061,7 +1067,8 @@ static void line6_disconnect(struct usb_interface *interface) case LINE6_PODHD300: case LINE6_PODHD400: - case LINE6_PODHD500: + case LINE6_PODHD500_0: + case LINE6_PODHD500_1: line6_podhd_disconnect(interface); break; diff --git a/drivers/staging/line6/driver.h b/drivers/staging/line6/driver.h index 085aa44..9d6b351 100644 --- a/drivers/staging/line6/driver.h +++ b/drivers/staging/line6/driver.h @@ -28,7 +28,8 @@ enum line6_device_type { LINE6_POCKETPOD, LINE6_PODHD300, LINE6_PODHD400, - LINE6_PODHD500, + LINE6_PODHD500_0, + LINE6_PODHD500_1, LINE6_PODSTUDIO_GX, LINE6_PODSTUDIO_UX1, LINE6_PODSTUDIO_UX2, diff --git a/drivers/staging/line6/pcm.c b/drivers/staging/line6/pcm.c index d09d1ea..d8450af 100644 --- a/drivers/staging/line6/pcm.c +++ b/drivers/staging/line6/pcm.c @@ -442,7 +442,8 @@ int line6_init_pcm(struct usb_line6 *line6, ep_write = 0x01; break; - case LINE6_PODHD500: + case LINE6_PODHD500_0: + case LINE6_PODHD500_1: ep_read = 0x86; ep_write = 0x02; break; -- cgit v0.10.2 From 3a3eae6c18e3b90311e6372a0df1371d193db2a1 Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:50 -0800 Subject: staging: line6: Filter on Pocket POD interface The driver only supports interface 1 of the Pocket POD. Use the device table to filter on this. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index e97e2cb..8b03bc0 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -39,7 +39,7 @@ static const struct usb_device_id line6_id_table[] = { { LINE6_DEVICE(0x4642), .driver_info = LINE6_BASSPODXTLIVE }, { LINE6_DEVICE(0x4252), .driver_info = LINE6_BASSPODXTPRO }, { LINE6_DEVICE(0x4750), .driver_info = LINE6_GUITARPORT }, - { LINE6_DEVICE(0x5051), .driver_info = LINE6_POCKETPOD }, + { LINE6_IF_NUM(0x5051, 1), .driver_info = LINE6_POCKETPOD }, { LINE6_DEVICE(0x5057), .driver_info = LINE6_PODHD300 }, { LINE6_DEVICE(0x5058), .driver_info = LINE6_PODHD400 }, { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 }, @@ -738,15 +738,7 @@ static int line6_probe(struct usb_interface *interface, break; case LINE6_POCKETPOD: - switch (interface_number) { - case 0: - return -ENODEV; /* this interface has no endpoints */ - case 1: - alternate = 0; - break; - default: - MISSING_CASE; - } + alternate = 0; break; case LINE6_PODHD500_0: -- cgit v0.10.2 From b98a8115f7cb5c8342606eb9a3cb1d808d01efb7 Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:51 -0800 Subject: staging: line6: Filter on UX2 interfaces The driver only supports interface 0 of the TonePort UX2 and POD Studio UX2 devices. Use the device table to filter on this. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index 8b03bc0..f04ff80 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -46,14 +46,14 @@ static const struct usb_device_id line6_id_table[] = { { LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 }, { LINE6_DEVICE(0x4153), .driver_info = LINE6_PODSTUDIO_GX }, { LINE6_DEVICE(0x4150), .driver_info = LINE6_PODSTUDIO_UX1 }, - { LINE6_DEVICE(0x4151), .driver_info = LINE6_PODSTUDIO_UX2 }, + { LINE6_IF_NUM(0x4151, 0), .driver_info = LINE6_PODSTUDIO_UX2 }, { LINE6_DEVICE(0x5044), .driver_info = LINE6_PODXT }, { LINE6_IF_NUM(0x4650, 0), .driver_info = LINE6_PODXTLIVE_POD }, { LINE6_IF_NUM(0x4650, 1), .driver_info = LINE6_PODXTLIVE_VARIAX }, { LINE6_DEVICE(0x5050), .driver_info = LINE6_PODXTPRO }, { LINE6_DEVICE(0x4147), .driver_info = LINE6_TONEPORT_GX }, { LINE6_DEVICE(0x4141), .driver_info = LINE6_TONEPORT_UX1 }, - { LINE6_DEVICE(0x4142), .driver_info = LINE6_TONEPORT_UX2 }, + { LINE6_IF_NUM(0x4142, 0), .driver_info = LINE6_TONEPORT_UX2 }, { LINE6_DEVICE(0x534d), .driver_info = LINE6_VARIAX }, {} }; @@ -768,20 +768,8 @@ static int line6_probe(struct usb_interface *interface, case LINE6_TONEPORT_UX2: case LINE6_PODSTUDIO_UX2: - switch (interface_number) { - case 0: - /* defaults to 44.1kHz, 16-bit */ - alternate = 2; - break; - case 1: - /* don't know yet what this is ... - alternate = 1; - break; - */ - return -ENODEV; - default: - MISSING_CASE; - } + /* defaults to 44.1kHz, 16-bit */ + alternate = 2; break; default: -- cgit v0.10.2 From 7b9584fa1c0be583a8981763dba7ef9f4d1fe67b Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:52 -0800 Subject: staging: line6: Move altsetting to properties The device type can now be used to determine the altsetting for the interface. Drop the conditional logic and make this value a property. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index f04ff80..0150470 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -67,6 +67,7 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_PCM | LINE6_CAP_HWMON, + .altsetting = 5, }, [LINE6_BASSPODXTLIVE] = { .id = "BassPODxtLive", @@ -74,6 +75,7 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_PCM | LINE6_CAP_HWMON, + .altsetting = 1, }, [LINE6_BASSPODXTPRO] = { .id = "BassPODxtPro", @@ -81,16 +83,19 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_PCM | LINE6_CAP_HWMON, + .altsetting = 5, }, [LINE6_GUITARPORT] = { .id = "GuitarPort", .name = "GuitarPort", .capabilities = LINE6_CAP_PCM, + .altsetting = 2, /* 1..4 seem to be ok */ }, [LINE6_POCKETPOD] = { .id = "PocketPOD", .name = "Pocket POD", .capabilities = LINE6_CAP_CONTROL, + .altsetting = 0, }, [LINE6_PODHD300] = { .id = "PODHD300", @@ -98,6 +103,7 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_PCM | LINE6_CAP_HWMON, + .altsetting = 5, }, [LINE6_PODHD400] = { .id = "PODHD400", @@ -105,6 +111,7 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_PCM | LINE6_CAP_HWMON, + .altsetting = 5, }, [LINE6_PODHD500_0] = { .id = "PODHD500", @@ -112,6 +119,7 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_PCM | LINE6_CAP_HWMON, + .altsetting = 1, }, [LINE6_PODHD500_1] = { .id = "PODHD500", @@ -119,21 +127,25 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_PCM | LINE6_CAP_HWMON, + .altsetting = 1, }, [LINE6_PODSTUDIO_GX] = { .id = "PODStudioGX", .name = "POD Studio GX", .capabilities = LINE6_CAP_PCM, + .altsetting = 2, /* 1..4 seem to be ok */ }, [LINE6_PODSTUDIO_UX1] = { .id = "PODStudioUX1", .name = "POD Studio UX1", .capabilities = LINE6_CAP_PCM, + .altsetting = 2, /* 1..4 seem to be ok */ }, [LINE6_PODSTUDIO_UX2] = { .id = "PODStudioUX2", .name = "POD Studio UX2", .capabilities = LINE6_CAP_PCM, + .altsetting = 2, /* defaults to 44.1kHz, 16-bit */ }, [LINE6_PODXT] = { .id = "PODxt", @@ -141,6 +153,7 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_PCM | LINE6_CAP_HWMON, + .altsetting = 5, }, [LINE6_PODXTLIVE_POD] = { .id = "PODxtLive", @@ -148,6 +161,7 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_PCM | LINE6_CAP_HWMON, + .altsetting = 1, }, [LINE6_PODXTLIVE_VARIAX] = { .id = "PODxtLive", @@ -155,6 +169,7 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_PCM | LINE6_CAP_HWMON, + .altsetting = 1, }, [LINE6_PODXTPRO] = { .id = "PODxtPro", @@ -162,26 +177,31 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_PCM | LINE6_CAP_HWMON, + .altsetting = 5, }, [LINE6_TONEPORT_GX] = { .id = "TonePortGX", .name = "TonePort GX", .capabilities = LINE6_CAP_PCM, + .altsetting = 2, /* 1..4 seem to be ok */ }, [LINE6_TONEPORT_UX1] = { .id = "TonePortUX1", .name = "TonePort UX1", .capabilities = LINE6_CAP_PCM, + .altsetting = 2, /* 1..4 seem to be ok */ }, [LINE6_TONEPORT_UX2] = { .id = "TonePortUX2", .name = "TonePort UX2", .capabilities = LINE6_CAP_PCM, + .altsetting = 2, /* defaults to 44.1kHz, 16-bit */ }, [LINE6_VARIAX] = { .id = "Variax", .name = "Variax Workbench", .capabilities = LINE6_CAP_CONTROL, + .altsetting = 1, } }; @@ -703,7 +723,7 @@ static int line6_probe(struct usb_interface *interface, struct usb_device *usbdev; struct usb_line6 *line6; const struct line6_properties *properties; - int interface_number, alternate = 0; + int interface_number; int size = 0; int ep_read = 0, ep_write = 0; int ret; @@ -729,56 +749,8 @@ static int line6_probe(struct usb_interface *interface, /* query interface number */ interface_number = interface->cur_altsetting->desc.bInterfaceNumber; - switch (devtype) { - case LINE6_BASSPODXTLIVE: - case LINE6_PODXTLIVE_POD: - case LINE6_PODXTLIVE_VARIAX: - case LINE6_VARIAX: - alternate = 1; - break; - - case LINE6_POCKETPOD: - alternate = 0; - break; - - case LINE6_PODHD500_0: - alternate = 1; - break; - - case LINE6_PODHD500_1: - alternate = 0; - break; - - case LINE6_BASSPODXT: - case LINE6_BASSPODXTPRO: - case LINE6_PODXT: - case LINE6_PODXTPRO: - case LINE6_PODHD300: - case LINE6_PODHD400: - alternate = 5; - break; - - case LINE6_GUITARPORT: - case LINE6_PODSTUDIO_GX: - case LINE6_PODSTUDIO_UX1: - case LINE6_TONEPORT_GX: - case LINE6_TONEPORT_UX1: - alternate = 2; /* 1..4 seem to be ok */ - break; - - case LINE6_TONEPORT_UX2: - case LINE6_PODSTUDIO_UX2: - /* defaults to 44.1kHz, 16-bit */ - alternate = 2; - break; - - default: - MISSING_CASE; - ret = -ENODEV; - goto err_put; - } - - ret = usb_set_interface(usbdev, interface_number, alternate); + ret = usb_set_interface(usbdev, interface_number, + properties->altsetting); if (ret < 0) { dev_err(&interface->dev, "set_interface failed\n"); goto err_put; diff --git a/drivers/staging/line6/driver.h b/drivers/staging/line6/driver.h index 9d6b351..97d6be1 100644 --- a/drivers/staging/line6/driver.h +++ b/drivers/staging/line6/driver.h @@ -115,6 +115,8 @@ struct line6_properties { line6usb driver. */ int capabilities; + + int altsetting; }; /** -- cgit v0.10.2 From 9e165be72f49b2de0cb859304a1eb7e6e26da1e1 Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:53 -0800 Subject: staging: line6: Move control endpoints to properties The device type can now be used to determine the addresses of the control endpoints for the interface. Drop the conditional logic and make these values properties. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index 0150470..40ec57c 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -68,6 +68,8 @@ static const struct line6_properties line6_properties_table[] = { | LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 5, + .ep_ctrl_r = 0x84, + .ep_ctrl_w = 0x03, }, [LINE6_BASSPODXTLIVE] = { .id = "BassPODxtLive", @@ -76,6 +78,8 @@ static const struct line6_properties line6_properties_table[] = { | LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 1, + .ep_ctrl_r = 0x84, + .ep_ctrl_w = 0x03, }, [LINE6_BASSPODXTPRO] = { .id = "BassPODxtPro", @@ -84,18 +88,23 @@ static const struct line6_properties line6_properties_table[] = { | LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 5, + .ep_ctrl_r = 0x84, + .ep_ctrl_w = 0x03, }, [LINE6_GUITARPORT] = { .id = "GuitarPort", .name = "GuitarPort", .capabilities = LINE6_CAP_PCM, .altsetting = 2, /* 1..4 seem to be ok */ + /* no control channel */ }, [LINE6_POCKETPOD] = { .id = "PocketPOD", .name = "Pocket POD", .capabilities = LINE6_CAP_CONTROL, .altsetting = 0, + .ep_ctrl_r = 0x82, + .ep_ctrl_w = 0x02, }, [LINE6_PODHD300] = { .id = "PODHD300", @@ -104,6 +113,8 @@ static const struct line6_properties line6_properties_table[] = { | LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 5, + .ep_ctrl_r = 0x84, + .ep_ctrl_w = 0x03, }, [LINE6_PODHD400] = { .id = "PODHD400", @@ -112,6 +123,8 @@ static const struct line6_properties line6_properties_table[] = { | LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 5, + .ep_ctrl_r = 0x84, + .ep_ctrl_w = 0x03, }, [LINE6_PODHD500_0] = { .id = "PODHD500", @@ -120,6 +133,8 @@ static const struct line6_properties line6_properties_table[] = { | LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 1, + .ep_ctrl_r = 0x81, + .ep_ctrl_w = 0x01, }, [LINE6_PODHD500_1] = { .id = "PODHD500", @@ -128,24 +143,29 @@ static const struct line6_properties line6_properties_table[] = { | LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 1, + .ep_ctrl_r = 0x81, + .ep_ctrl_w = 0x01, }, [LINE6_PODSTUDIO_GX] = { .id = "PODStudioGX", .name = "POD Studio GX", .capabilities = LINE6_CAP_PCM, .altsetting = 2, /* 1..4 seem to be ok */ + /* no control channel */ }, [LINE6_PODSTUDIO_UX1] = { .id = "PODStudioUX1", .name = "POD Studio UX1", .capabilities = LINE6_CAP_PCM, .altsetting = 2, /* 1..4 seem to be ok */ + /* no control channel */ }, [LINE6_PODSTUDIO_UX2] = { .id = "PODStudioUX2", .name = "POD Studio UX2", .capabilities = LINE6_CAP_PCM, .altsetting = 2, /* defaults to 44.1kHz, 16-bit */ + /* no control channel */ }, [LINE6_PODXT] = { .id = "PODxt", @@ -154,6 +174,8 @@ static const struct line6_properties line6_properties_table[] = { | LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 5, + .ep_ctrl_r = 0x84, + .ep_ctrl_w = 0x03, }, [LINE6_PODXTLIVE_POD] = { .id = "PODxtLive", @@ -162,6 +184,8 @@ static const struct line6_properties line6_properties_table[] = { | LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 1, + .ep_ctrl_r = 0x84, + .ep_ctrl_w = 0x03, }, [LINE6_PODXTLIVE_VARIAX] = { .id = "PODxtLive", @@ -170,6 +194,8 @@ static const struct line6_properties line6_properties_table[] = { | LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 1, + .ep_ctrl_r = 0x86, + .ep_ctrl_w = 0x05, }, [LINE6_PODXTPRO] = { .id = "PODxtPro", @@ -178,30 +204,37 @@ static const struct line6_properties line6_properties_table[] = { | LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 5, + .ep_ctrl_r = 0x84, + .ep_ctrl_w = 0x03, }, [LINE6_TONEPORT_GX] = { .id = "TonePortGX", .name = "TonePort GX", .capabilities = LINE6_CAP_PCM, .altsetting = 2, /* 1..4 seem to be ok */ + /* no control channel */ }, [LINE6_TONEPORT_UX1] = { .id = "TonePortUX1", .name = "TonePort UX1", .capabilities = LINE6_CAP_PCM, .altsetting = 2, /* 1..4 seem to be ok */ + /* no control channel */ }, [LINE6_TONEPORT_UX2] = { .id = "TonePortUX2", .name = "TonePort UX2", .capabilities = LINE6_CAP_PCM, .altsetting = 2, /* defaults to 44.1kHz, 16-bit */ + /* no control channel */ }, [LINE6_VARIAX] = { .id = "Variax", .name = "Variax Workbench", .capabilities = LINE6_CAP_CONTROL, .altsetting = 1, + .ep_ctrl_r = 0x82, + .ep_ctrl_w = 0x01, } }; @@ -245,9 +278,9 @@ static int line6_start_listen(struct usb_line6 *line6) int err; usb_fill_int_urb(line6->urb_listen, line6->usbdev, - usb_rcvintpipe(line6->usbdev, line6->ep_control_read), - line6->buffer_listen, LINE6_BUFSIZE_LISTEN, - line6_data_received, line6, line6->interval); + usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r), + line6->buffer_listen, LINE6_BUFSIZE_LISTEN, + line6_data_received, line6, line6->interval); line6->urb_listen->actual_length = 0; err = usb_submit_urb(line6->urb_listen, GFP_ATOMIC); return err; @@ -277,7 +310,7 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, retval = usb_interrupt_msg(line6->usbdev, usb_sndintpipe(line6->usbdev, - line6->ep_control_write), + line6->properties->ep_ctrl_w), (char *)frag_buf, frag_size, &partial, LINE6_TIMEOUT * HZ); @@ -319,9 +352,9 @@ static int line6_send_raw_message_async_part(struct message *msg, int bytes = min(msg->size - done, line6->max_packet_size); usb_fill_int_urb(urb, line6->usbdev, - usb_sndintpipe(line6->usbdev, line6->ep_control_write), - (char *)msg->buffer + done, bytes, - line6_async_request_sent, msg, line6->interval); + usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w), + (char *)msg->buffer + done, bytes, + line6_async_request_sent, msg, line6->interval); msg->done += bytes; retval = usb_submit_urb(urb, GFP_ATOMIC); @@ -525,7 +558,7 @@ int line6_send_program(struct usb_line6 *line6, u8 value) retval = usb_interrupt_msg(line6->usbdev, usb_sndintpipe(line6->usbdev, - line6->ep_control_write), + line6->properties->ep_ctrl_w), buffer, 2, &partial, LINE6_TIMEOUT * HZ); if (retval) @@ -555,7 +588,7 @@ int line6_transmit_parameter(struct usb_line6 *line6, int param, u8 value) retval = usb_interrupt_msg(line6->usbdev, usb_sndintpipe(line6->usbdev, - line6->ep_control_write), + line6->properties->ep_ctrl_w), buffer, 3, &partial, LINE6_TIMEOUT * HZ); if (retval) @@ -725,7 +758,6 @@ static int line6_probe(struct usb_interface *interface, const struct line6_properties *properties; int interface_number; int size = 0; - int ep_read = 0, ep_write = 0; int ret; if (interface == NULL) @@ -764,28 +796,20 @@ static int line6_probe(struct usb_interface *interface, case LINE6_PODXT: case LINE6_PODXTPRO: size = sizeof(struct usb_line6_pod); - ep_read = 0x84; - ep_write = 0x03; break; case LINE6_PODHD300: case LINE6_PODHD400: size = sizeof(struct usb_line6_podhd); - ep_read = 0x84; - ep_write = 0x03; break; case LINE6_PODHD500_0: case LINE6_PODHD500_1: size = sizeof(struct usb_line6_podhd); - ep_read = 0x81; - ep_write = 0x01; break; case LINE6_POCKETPOD: size = sizeof(struct usb_line6_pod); - ep_read = 0x82; - ep_write = 0x02; break; case LINE6_PODSTUDIO_GX: @@ -796,25 +820,18 @@ static int line6_probe(struct usb_interface *interface, case LINE6_TONEPORT_UX2: case LINE6_GUITARPORT: size = sizeof(struct usb_line6_toneport); - /* these don't have a control channel */ break; case LINE6_PODXTLIVE_POD: size = sizeof(struct usb_line6_pod); - ep_read = 0x84; - ep_write = 0x03; break; case LINE6_PODXTLIVE_VARIAX: size = sizeof(struct usb_line6_variax); - ep_read = 0x86; - ep_write = 0x05; break; case LINE6_VARIAX: size = sizeof(struct usb_line6_variax); - ep_read = 0x82; - ep_write = 0x01; break; default: @@ -840,15 +857,13 @@ static int line6_probe(struct usb_interface *interface, line6->properties = properties; line6->usbdev = usbdev; line6->ifcdev = &interface->dev; - line6->ep_control_read = ep_read; - line6->ep_control_write = ep_write; line6->type = devtype; /* get data from endpoint descriptor (see usb_maxpacket): */ { struct usb_host_endpoint *ep; - unsigned epnum = - usb_pipeendpoint(usb_rcvintpipe(usbdev, ep_read)); + unsigned pipe = usb_rcvintpipe(usbdev, properties->ep_ctrl_r); + unsigned epnum = usb_pipeendpoint(pipe); ep = usbdev->ep_in[epnum]; if (ep != NULL) { diff --git a/drivers/staging/line6/driver.h b/drivers/staging/line6/driver.h index 97d6be1..a0555f4 100644 --- a/drivers/staging/line6/driver.h +++ b/drivers/staging/line6/driver.h @@ -117,6 +117,9 @@ struct line6_properties { int capabilities; int altsetting; + + unsigned ep_ctrl_r; + unsigned ep_ctrl_w; }; /** @@ -171,16 +174,6 @@ struct usb_line6 { struct snd_line6_midi *line6midi; /** - USB endpoint for listening to control commands. - */ - int ep_control_read; - - /** - USB endpoint for writing control commands. - */ - int ep_control_write; - - /** URB for listening to PODxt Pro control endpoint. */ struct urb *urb_listen; diff --git a/drivers/staging/line6/midi.c b/drivers/staging/line6/midi.c index c453485..c9d725a 100644 --- a/drivers/staging/line6/midi.c +++ b/drivers/staging/line6/midi.c @@ -136,7 +136,7 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data, usb_fill_int_urb(urb, line6->usbdev, usb_sndbulkpipe(line6->usbdev, - line6->ep_control_write), + line6->properties->ep_ctrl_w), transfer_buffer, length, midi_sent, line6, line6->interval); urb->actual_length = 0; -- cgit v0.10.2 From b95d2e408bf94ad73d022e36937b38335890af8f Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:54 -0800 Subject: staging: line6: Remove stale Pocket POD PCM endpoints Commit 1027f476f507 (staging: line6: sync with upstream) removed PCM from the Pocket POD capabilities but left the endpoint configuration. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/pcm.c b/drivers/staging/line6/pcm.c index d8450af..19aa927 100644 --- a/drivers/staging/line6/pcm.c +++ b/drivers/staging/line6/pcm.c @@ -448,11 +448,6 @@ int line6_init_pcm(struct usb_line6 *line6, ep_write = 0x02; break; - case LINE6_POCKETPOD: - ep_read = 0x82; - ep_write = 0x02; - break; - case LINE6_GUITARPORT: case LINE6_PODSTUDIO_GX: case LINE6_PODSTUDIO_UX1: -- cgit v0.10.2 From 16d603d32d3437abd1867c7671ed2763bd3aaf0d Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:55 -0800 Subject: staging: line6: Move audio endpoints to properties The device type can now be used to determine the addresses of the audio endpoints for the interface. Drop the conditional logic and make these values properties. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/capture.c b/drivers/staging/line6/capture.c index e6ca631..f24c7c5 100644 --- a/drivers/staging/line6/capture.c +++ b/drivers/staging/line6/capture.c @@ -400,6 +400,7 @@ struct snd_pcm_ops snd_line6_capture_ops = { int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm) { + struct usb_line6 *line6 = line6pcm->line6; int i; /* create audio URBs and fill in constant values: */ @@ -411,14 +412,14 @@ int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm) usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); if (urb == NULL) { - dev_err(line6pcm->line6->ifcdev, "Out of memory\n"); + dev_err(line6->ifcdev, "Out of memory\n"); return -ENOMEM; } - urb->dev = line6pcm->line6->usbdev; + urb->dev = line6->usbdev; urb->pipe = - usb_rcvisocpipe(line6pcm->line6->usbdev, - line6pcm->ep_audio_read & + usb_rcvisocpipe(line6->usbdev, + line6->properties->ep_audio_r & USB_ENDPOINT_NUMBER_MASK); urb->transfer_flags = URB_ISO_ASAP; urb->start_frame = -1; diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index 40ec57c..4bfef21 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -70,6 +70,8 @@ static const struct line6_properties line6_properties_table[] = { .altsetting = 5, .ep_ctrl_r = 0x84, .ep_ctrl_w = 0x03, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, }, [LINE6_BASSPODXTLIVE] = { .id = "BassPODxtLive", @@ -80,6 +82,8 @@ static const struct line6_properties line6_properties_table[] = { .altsetting = 1, .ep_ctrl_r = 0x84, .ep_ctrl_w = 0x03, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, }, [LINE6_BASSPODXTPRO] = { .id = "BassPODxtPro", @@ -90,6 +94,8 @@ static const struct line6_properties line6_properties_table[] = { .altsetting = 5, .ep_ctrl_r = 0x84, .ep_ctrl_w = 0x03, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, }, [LINE6_GUITARPORT] = { .id = "GuitarPort", @@ -97,6 +103,8 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_PCM, .altsetting = 2, /* 1..4 seem to be ok */ /* no control channel */ + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, }, [LINE6_POCKETPOD] = { .id = "PocketPOD", @@ -105,6 +113,7 @@ static const struct line6_properties line6_properties_table[] = { .altsetting = 0, .ep_ctrl_r = 0x82, .ep_ctrl_w = 0x02, + /* no audio channel */ }, [LINE6_PODHD300] = { .id = "PODHD300", @@ -115,6 +124,8 @@ static const struct line6_properties line6_properties_table[] = { .altsetting = 5, .ep_ctrl_r = 0x84, .ep_ctrl_w = 0x03, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, }, [LINE6_PODHD400] = { .id = "PODHD400", @@ -125,6 +136,8 @@ static const struct line6_properties line6_properties_table[] = { .altsetting = 5, .ep_ctrl_r = 0x84, .ep_ctrl_w = 0x03, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, }, [LINE6_PODHD500_0] = { .id = "PODHD500", @@ -135,6 +148,8 @@ static const struct line6_properties line6_properties_table[] = { .altsetting = 1, .ep_ctrl_r = 0x81, .ep_ctrl_w = 0x01, + .ep_audio_r = 0x86, + .ep_audio_w = 0x02, }, [LINE6_PODHD500_1] = { .id = "PODHD500", @@ -145,6 +160,8 @@ static const struct line6_properties line6_properties_table[] = { .altsetting = 1, .ep_ctrl_r = 0x81, .ep_ctrl_w = 0x01, + .ep_audio_r = 0x86, + .ep_audio_w = 0x02, }, [LINE6_PODSTUDIO_GX] = { .id = "PODStudioGX", @@ -152,6 +169,8 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_PCM, .altsetting = 2, /* 1..4 seem to be ok */ /* no control channel */ + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, }, [LINE6_PODSTUDIO_UX1] = { .id = "PODStudioUX1", @@ -159,6 +178,8 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_PCM, .altsetting = 2, /* 1..4 seem to be ok */ /* no control channel */ + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, }, [LINE6_PODSTUDIO_UX2] = { .id = "PODStudioUX2", @@ -166,6 +187,8 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_PCM, .altsetting = 2, /* defaults to 44.1kHz, 16-bit */ /* no control channel */ + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, }, [LINE6_PODXT] = { .id = "PODxt", @@ -176,6 +199,8 @@ static const struct line6_properties line6_properties_table[] = { .altsetting = 5, .ep_ctrl_r = 0x84, .ep_ctrl_w = 0x03, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, }, [LINE6_PODXTLIVE_POD] = { .id = "PODxtLive", @@ -186,6 +211,8 @@ static const struct line6_properties line6_properties_table[] = { .altsetting = 1, .ep_ctrl_r = 0x84, .ep_ctrl_w = 0x03, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, }, [LINE6_PODXTLIVE_VARIAX] = { .id = "PODxtLive", @@ -196,6 +223,8 @@ static const struct line6_properties line6_properties_table[] = { .altsetting = 1, .ep_ctrl_r = 0x86, .ep_ctrl_w = 0x05, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, }, [LINE6_PODXTPRO] = { .id = "PODxtPro", @@ -206,6 +235,8 @@ static const struct line6_properties line6_properties_table[] = { .altsetting = 5, .ep_ctrl_r = 0x84, .ep_ctrl_w = 0x03, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, }, [LINE6_TONEPORT_GX] = { .id = "TonePortGX", @@ -213,6 +244,8 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_PCM, .altsetting = 2, /* 1..4 seem to be ok */ /* no control channel */ + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, }, [LINE6_TONEPORT_UX1] = { .id = "TonePortUX1", @@ -220,6 +253,8 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_PCM, .altsetting = 2, /* 1..4 seem to be ok */ /* no control channel */ + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, }, [LINE6_TONEPORT_UX2] = { .id = "TonePortUX2", @@ -227,6 +262,8 @@ static const struct line6_properties line6_properties_table[] = { .capabilities = LINE6_CAP_PCM, .altsetting = 2, /* defaults to 44.1kHz, 16-bit */ /* no control channel */ + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, }, [LINE6_VARIAX] = { .id = "Variax", @@ -235,6 +272,7 @@ static const struct line6_properties line6_properties_table[] = { .altsetting = 1, .ep_ctrl_r = 0x82, .ep_ctrl_w = 0x01, + /* no audio channel */ } }; diff --git a/drivers/staging/line6/driver.h b/drivers/staging/line6/driver.h index a0555f4..a4bde71 100644 --- a/drivers/staging/line6/driver.h +++ b/drivers/staging/line6/driver.h @@ -120,6 +120,8 @@ struct line6_properties { unsigned ep_ctrl_r; unsigned ep_ctrl_w; + unsigned ep_audio_r; + unsigned ep_audio_w; }; /** diff --git a/drivers/staging/line6/pcm.c b/drivers/staging/line6/pcm.c index 19aa927..6d4e5cd 100644 --- a/drivers/staging/line6/pcm.c +++ b/drivers/staging/line6/pcm.c @@ -421,55 +421,13 @@ int line6_init_pcm(struct usb_line6 *line6, }; int err; - int ep_read = 0, ep_write = 0; + unsigned ep_read = line6->properties->ep_audio_r; + unsigned ep_write = line6->properties->ep_audio_w; struct snd_line6_pcm *line6pcm; if (!(line6->properties->capabilities & LINE6_CAP_PCM)) return 0; /* skip PCM initialization and report success */ - /* initialize PCM subsystem based on device: */ - switch (line6->type) { - case LINE6_BASSPODXT: - case LINE6_BASSPODXTLIVE: - case LINE6_BASSPODXTPRO: - case LINE6_PODXT: - case LINE6_PODXTLIVE_POD: - case LINE6_PODXTLIVE_VARIAX: - case LINE6_PODXTPRO: - case LINE6_PODHD300: - case LINE6_PODHD400: - ep_read = 0x82; - ep_write = 0x01; - break; - - case LINE6_PODHD500_0: - case LINE6_PODHD500_1: - ep_read = 0x86; - ep_write = 0x02; - break; - - case LINE6_GUITARPORT: - case LINE6_PODSTUDIO_GX: - case LINE6_PODSTUDIO_UX1: - case LINE6_PODSTUDIO_UX2: - case LINE6_TONEPORT_GX: - case LINE6_TONEPORT_UX1: - case LINE6_TONEPORT_UX2: - ep_read = 0x82; - ep_write = 0x01; - break; - - /* this is for interface_number == 1: - case LINE6_DEVID_TONEPORT_UX2: - case LINE6_DEVID_PODSTUDIO_UX2: - ep_read = 0x87; - ep_write = 0x00; - break; */ - - default: - MISSING_CASE; - } - line6pcm = kzalloc(sizeof(*line6pcm), GFP_KERNEL); if (line6pcm == NULL) @@ -478,8 +436,6 @@ int line6_init_pcm(struct usb_line6 *line6, line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255; line6pcm->volume_monitor = 255; line6pcm->line6 = line6; - line6pcm->ep_audio_read = ep_read; - line6pcm->ep_audio_write = ep_write; /* Read and write buffers are sized identically, so choose minimum */ line6pcm->max_packet_size = min( diff --git a/drivers/staging/line6/pcm.h b/drivers/staging/line6/pcm.h index 4f60823..7315e81 100644 --- a/drivers/staging/line6/pcm.h +++ b/drivers/staging/line6/pcm.h @@ -297,16 +297,6 @@ struct snd_line6_pcm { int max_packet_size; /** - USB endpoint for listening to audio data. - */ - int ep_audio_read; - - /** - USB endpoint for writing audio data. - */ - int ep_audio_write; - - /** Bit mask of active capture URBs. */ unsigned long active_urb_in; diff --git a/drivers/staging/line6/playback.c b/drivers/staging/line6/playback.c index 54b7f60..da2e3b8 100644 --- a/drivers/staging/line6/playback.c +++ b/drivers/staging/line6/playback.c @@ -560,6 +560,7 @@ struct snd_pcm_ops snd_line6_playback_ops = { int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm) { + struct usb_line6 *line6 = line6pcm->line6; int i; /* create audio URBs and fill in constant values: */ @@ -571,14 +572,14 @@ int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm) usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); if (urb == NULL) { - dev_err(line6pcm->line6->ifcdev, "Out of memory\n"); + dev_err(line6->ifcdev, "Out of memory\n"); return -ENOMEM; } - urb->dev = line6pcm->line6->usbdev; + urb->dev = line6->usbdev; urb->pipe = - usb_sndisocpipe(line6pcm->line6->usbdev, - line6pcm->ep_audio_write & + usb_sndisocpipe(line6->usbdev, + line6->properties->ep_audio_w & USB_ENDPOINT_NUMBER_MASK); urb->transfer_flags = URB_ISO_ASAP; urb->start_frame = -1; -- cgit v0.10.2 From a221dd453a311eb55c1f1ee6322947d99d9b6170 Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:56 -0800 Subject: staging: line6: Pass *_init() `usb_line6' pointers Casting the `struct usb_line6' pointer at the call point makes the code difficult to read. This is substantially cleaned up by moving the cast into the callees. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index 4bfef21..08f8051 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -959,33 +959,26 @@ static int line6_probe(struct usb_interface *interface, case LINE6_POCKETPOD: case LINE6_PODXT: case LINE6_PODXTPRO: - ret = line6_pod_init(interface, (struct usb_line6_pod *)line6); + ret = line6_pod_init(interface, line6); break; case LINE6_PODHD300: case LINE6_PODHD400: case LINE6_PODHD500_0: case LINE6_PODHD500_1: - ret = line6_podhd_init(interface, - (struct usb_line6_podhd *)line6); + ret = line6_podhd_init(interface, line6); break; case LINE6_PODXTLIVE_POD: - ret = - line6_pod_init(interface, - (struct usb_line6_pod *)line6); + ret = line6_pod_init(interface, line6); break; case LINE6_PODXTLIVE_VARIAX: - ret = - line6_variax_init(interface, - (struct usb_line6_variax *)line6); + ret = line6_variax_init(interface, line6); break; case LINE6_VARIAX: - ret = - line6_variax_init(interface, - (struct usb_line6_variax *)line6); + ret = line6_variax_init(interface, line6); break; case LINE6_PODSTUDIO_GX: @@ -995,9 +988,7 @@ static int line6_probe(struct usb_interface *interface, case LINE6_TONEPORT_UX1: case LINE6_TONEPORT_UX2: case LINE6_GUITARPORT: - ret = - line6_toneport_init(interface, - (struct usb_line6_toneport *)line6); + ret = line6_toneport_init(interface, line6); break; default: diff --git a/drivers/staging/line6/pod.c b/drivers/staging/line6/pod.c index 0fb1788..9292b72 100644 --- a/drivers/staging/line6/pod.c +++ b/drivers/staging/line6/pod.c @@ -353,10 +353,10 @@ static int pod_create_files2(struct device *dev) Try to init POD device. */ static int pod_try_init(struct usb_interface *interface, - struct usb_line6_pod *pod) + struct usb_line6 *line6) { int err; - struct usb_line6 *line6 = &pod->line6; + struct usb_line6_pod *pod = (struct usb_line6_pod *) line6; init_timer(&pod->startup_timer); INIT_WORK(&pod->startup_work, pod_startup4); @@ -409,9 +409,9 @@ static int pod_try_init(struct usb_interface *interface, /* Init POD device (and clean up in case of failure). */ -int line6_pod_init(struct usb_interface *interface, struct usb_line6_pod *pod) +int line6_pod_init(struct usb_interface *interface, struct usb_line6 *line6) { - int err = pod_try_init(interface, pod); + int err = pod_try_init(interface, line6); if (err < 0) pod_destruct(interface); diff --git a/drivers/staging/line6/pod.h b/drivers/staging/line6/pod.h index 91fd4c5..cf6c75cd 100644 --- a/drivers/staging/line6/pod.h +++ b/drivers/staging/line6/pod.h @@ -88,7 +88,7 @@ struct usb_line6_pod { extern void line6_pod_disconnect(struct usb_interface *interface); extern int line6_pod_init(struct usb_interface *interface, - struct usb_line6_pod *pod); + struct usb_line6 *line6); extern void line6_pod_process_message(struct usb_line6_pod *pod); #endif diff --git a/drivers/staging/line6/podhd.c b/drivers/staging/line6/podhd.c index 7ef4543..3bb942e 100644 --- a/drivers/staging/line6/podhd.c +++ b/drivers/staging/line6/podhd.c @@ -121,9 +121,9 @@ static int podhd_try_init(struct usb_interface *interface, /* Init POD HD device (and clean up in case of failure). */ -int line6_podhd_init(struct usb_interface *interface, - struct usb_line6_podhd *podhd) +int line6_podhd_init(struct usb_interface *interface, struct usb_line6 *line6) { + struct usb_line6_podhd *podhd = (struct usb_line6_podhd *) line6; int err = podhd_try_init(interface, podhd); if (err < 0) diff --git a/drivers/staging/line6/podhd.h b/drivers/staging/line6/podhd.h index 652f740..b7d9568 100644 --- a/drivers/staging/line6/podhd.h +++ b/drivers/staging/line6/podhd.h @@ -25,6 +25,6 @@ struct usb_line6_podhd { extern void line6_podhd_disconnect(struct usb_interface *interface); extern int line6_podhd_init(struct usb_interface *interface, - struct usb_line6_podhd *podhd); + struct usb_line6 *line6); #endif /* PODHD_H */ diff --git a/drivers/staging/line6/toneport.c b/drivers/staging/line6/toneport.c index 9e5cee1..de91910 100644 --- a/drivers/staging/line6/toneport.c +++ b/drivers/staging/line6/toneport.c @@ -339,10 +339,10 @@ static void toneport_setup(struct usb_line6_toneport *toneport) Try to init Toneport device. */ static int toneport_try_init(struct usb_interface *interface, - struct usb_line6_toneport *toneport) + struct usb_line6 *line6) { int err; - struct usb_line6 *line6 = &toneport->line6; + struct usb_line6_toneport *toneport = (struct usb_line6_toneport *) line6; if ((interface == NULL) || (toneport == NULL)) return -ENODEV; @@ -411,9 +411,9 @@ static int toneport_try_init(struct usb_interface *interface, Init Toneport device (and clean up in case of failure). */ int line6_toneport_init(struct usb_interface *interface, - struct usb_line6_toneport *toneport) + struct usb_line6 *line6) { - int err = toneport_try_init(interface, toneport); + int err = toneport_try_init(interface, line6); if (err < 0) toneport_destruct(interface); diff --git a/drivers/staging/line6/toneport.h b/drivers/staging/line6/toneport.h index 8576b72..af2b4e0 100644 --- a/drivers/staging/line6/toneport.h +++ b/drivers/staging/line6/toneport.h @@ -46,7 +46,7 @@ struct usb_line6_toneport { extern void line6_toneport_disconnect(struct usb_interface *interface); extern int line6_toneport_init(struct usb_interface *interface, - struct usb_line6_toneport *toneport); + struct usb_line6 *line6); extern void line6_toneport_reset_resume(struct usb_line6_toneport *toneport); #endif diff --git a/drivers/staging/line6/variax.c b/drivers/staging/line6/variax.c index ae2be99..f5b618b 100644 --- a/drivers/staging/line6/variax.c +++ b/drivers/staging/line6/variax.c @@ -174,8 +174,9 @@ static void variax_destruct(struct usb_interface *interface) Try to init workbench device. */ static int variax_try_init(struct usb_interface *interface, - struct usb_line6_variax *variax) + struct usb_line6 *line6) { + struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; int err; init_timer(&variax->startup_timer1); @@ -212,10 +213,9 @@ static int variax_try_init(struct usb_interface *interface, /* Init workbench device (and clean up in case of failure). */ -int line6_variax_init(struct usb_interface *interface, - struct usb_line6_variax *variax) +int line6_variax_init(struct usb_interface *interface, struct usb_line6 *line6) { - int err = variax_try_init(interface, variax); + int err = variax_try_init(interface, line6); if (err < 0) variax_destruct(interface); diff --git a/drivers/staging/line6/variax.h b/drivers/staging/line6/variax.h index 24de796..9bf1464 100644 --- a/drivers/staging/line6/variax.h +++ b/drivers/staging/line6/variax.h @@ -66,7 +66,7 @@ struct usb_line6_variax { extern void line6_variax_disconnect(struct usb_interface *interface); extern int line6_variax_init(struct usb_interface *interface, - struct usb_line6_variax *variax); + struct usb_line6 *line6); extern void line6_variax_process_message(struct usb_line6_variax *variax); #endif -- cgit v0.10.2 From 1cad3e8dcac92ee07c576e93d1b54c8308e2fa80 Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:57 -0800 Subject: staging: line6: Pass *_process_message() `usb_line6' pointers Casting the `struct usb_line6' pointer at the call point makes the code difficult to read. This is substantially cleaned up by moving the cast into the callees. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index 08f8051..369e60e 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -544,8 +544,7 @@ static void line6_data_received(struct urb *urb) case LINE6_PODXT: case LINE6_PODXTPRO: case LINE6_POCKETPOD: - line6_pod_process_message((struct usb_line6_pod *) - line6); + line6_pod_process_message(line6); break; case LINE6_PODHD300: @@ -555,19 +554,15 @@ static void line6_data_received(struct urb *urb) break; /* let userspace handle MIDI */ case LINE6_PODXTLIVE_POD: - line6_pod_process_message((struct usb_line6_pod - *)line6); + line6_pod_process_message(line6); break; case LINE6_PODXTLIVE_VARIAX: - line6_variax_process_message((struct - usb_line6_variax - *)line6); + line6_variax_process_message(line6); break; case LINE6_VARIAX: - line6_variax_process_message((struct usb_line6_variax *) - line6); + line6_variax_process_message(line6); break; default: diff --git a/drivers/staging/line6/pod.c b/drivers/staging/line6/pod.c index 9292b72..aa8977d 100644 --- a/drivers/staging/line6/pod.c +++ b/drivers/staging/line6/pod.c @@ -131,8 +131,9 @@ static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, /* Process a completely received message. */ -void line6_pod_process_message(struct usb_line6_pod *pod) +void line6_pod_process_message(struct usb_line6 *line6) { + struct usb_line6_pod *pod = (struct usb_line6_pod *) line6; const unsigned char *buf = pod->line6.buffer_message; if (memcmp(buf, pod_version_header, sizeof(pod_version_header)) == 0) { diff --git a/drivers/staging/line6/pod.h b/drivers/staging/line6/pod.h index cf6c75cd..984a00b 100644 --- a/drivers/staging/line6/pod.h +++ b/drivers/staging/line6/pod.h @@ -89,6 +89,6 @@ struct usb_line6_pod { extern void line6_pod_disconnect(struct usb_interface *interface); extern int line6_pod_init(struct usb_interface *interface, struct usb_line6 *line6); -extern void line6_pod_process_message(struct usb_line6_pod *pod); +extern void line6_pod_process_message(struct usb_line6 *line6); #endif diff --git a/drivers/staging/line6/variax.c b/drivers/staging/line6/variax.c index f5b618b..4d41994 100644 --- a/drivers/staging/line6/variax.c +++ b/drivers/staging/line6/variax.c @@ -130,8 +130,9 @@ static void variax_startup6(struct work_struct *work) /* Process a completely received message. */ -void line6_variax_process_message(struct usb_line6_variax *variax) +void line6_variax_process_message(struct usb_line6 *line6) { + struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; const unsigned char *buf = variax->line6.buffer_message; switch (buf[0]) { diff --git a/drivers/staging/line6/variax.h b/drivers/staging/line6/variax.h index 9bf1464..7d445ff 100644 --- a/drivers/staging/line6/variax.h +++ b/drivers/staging/line6/variax.h @@ -67,6 +67,6 @@ struct usb_line6_variax { extern void line6_variax_disconnect(struct usb_interface *interface); extern int line6_variax_init(struct usb_interface *interface, struct usb_line6 *line6); -extern void line6_variax_process_message(struct usb_line6_variax *variax); +extern void line6_variax_process_message(struct usb_line6 *line6); #endif -- cgit v0.10.2 From 01f6b2bc6a40efa2d29b213e636b9208a8126318 Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:58 -0800 Subject: staging: line6: Call *_process_message() via pointer Which *_process_message() function (if any) to call when data is received is known at initialization. Add a function pointer to the `usb_line6' struct and use to call into the appropriate logic instead of evaluating the conditional logic for each message. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index 369e60e..f7629cb 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -537,37 +537,8 @@ static void line6_data_received(struct urb *urb) line6->message_length = done; line6_midi_receive(line6, line6->buffer_message, done); - switch (line6->type) { - case LINE6_BASSPODXT: - case LINE6_BASSPODXTLIVE: - case LINE6_BASSPODXTPRO: - case LINE6_PODXT: - case LINE6_PODXTPRO: - case LINE6_POCKETPOD: - line6_pod_process_message(line6); - break; - - case LINE6_PODHD300: - case LINE6_PODHD400: - case LINE6_PODHD500_0: - case LINE6_PODHD500_1: - break; /* let userspace handle MIDI */ - - case LINE6_PODXTLIVE_POD: - line6_pod_process_message(line6); - break; - - case LINE6_PODXTLIVE_VARIAX: - line6_variax_process_message(line6); - break; - - case LINE6_VARIAX: - line6_variax_process_message(line6); - break; - - default: - MISSING_CASE; - } + if (line6->process_message) + line6->process_message(line6); } line6_start_listen(line6); diff --git a/drivers/staging/line6/driver.h b/drivers/staging/line6/driver.h index a4bde71..220813f 100644 --- a/drivers/staging/line6/driver.h +++ b/drivers/staging/line6/driver.h @@ -194,6 +194,8 @@ struct usb_line6 { Length of message to be processed. */ int message_length; + + void (*process_message)(struct usb_line6 *); }; extern char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, diff --git a/drivers/staging/line6/pod.c b/drivers/staging/line6/pod.c index aa8977d..79dcff4 100644 --- a/drivers/staging/line6/pod.c +++ b/drivers/staging/line6/pod.c @@ -131,7 +131,7 @@ static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, /* Process a completely received message. */ -void line6_pod_process_message(struct usb_line6 *line6) +static void line6_pod_process_message(struct usb_line6 *line6) { struct usb_line6_pod *pod = (struct usb_line6_pod *) line6; const unsigned char *buf = pod->line6.buffer_message; @@ -359,6 +359,8 @@ static int pod_try_init(struct usb_interface *interface, int err; struct usb_line6_pod *pod = (struct usb_line6_pod *) line6; + line6->process_message = line6_pod_process_message; + init_timer(&pod->startup_timer); INIT_WORK(&pod->startup_work, pod_startup4); diff --git a/drivers/staging/line6/pod.h b/drivers/staging/line6/pod.h index 984a00b..0d78ca7 100644 --- a/drivers/staging/line6/pod.h +++ b/drivers/staging/line6/pod.h @@ -89,6 +89,5 @@ struct usb_line6_pod { extern void line6_pod_disconnect(struct usb_interface *interface); extern int line6_pod_init(struct usb_interface *interface, struct usb_line6 *line6); -extern void line6_pod_process_message(struct usb_line6 *line6); #endif diff --git a/drivers/staging/line6/variax.c b/drivers/staging/line6/variax.c index 4d41994..ccb1f68 100644 --- a/drivers/staging/line6/variax.c +++ b/drivers/staging/line6/variax.c @@ -130,7 +130,7 @@ static void variax_startup6(struct work_struct *work) /* Process a completely received message. */ -void line6_variax_process_message(struct usb_line6 *line6) +static void line6_variax_process_message(struct usb_line6 *line6) { struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; const unsigned char *buf = variax->line6.buffer_message; @@ -180,6 +180,8 @@ static int variax_try_init(struct usb_interface *interface, struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; int err; + line6->process_message = line6_variax_process_message; + init_timer(&variax->startup_timer1); init_timer(&variax->startup_timer2); INIT_WORK(&variax->startup_work, variax_startup6); diff --git a/drivers/staging/line6/variax.h b/drivers/staging/line6/variax.h index 7d445ff..f21ff20 100644 --- a/drivers/staging/line6/variax.h +++ b/drivers/staging/line6/variax.h @@ -67,6 +67,5 @@ struct usb_line6_variax { extern void line6_variax_disconnect(struct usb_interface *interface); extern int line6_variax_init(struct usb_interface *interface, struct usb_line6 *line6); -extern void line6_variax_process_message(struct usb_line6 *line6); #endif -- cgit v0.10.2 From a46c467251353109683d353826208a17aec9383e Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:42:59 -0800 Subject: staging: line6: Call *_disconnect() via pointer Which *_disconnect() to call on disconnect is known at initialization. Add a function pointer to the `usb_line6' struct and use to call into the appropriate logic instead of evaluating the conditional logic. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index f7629cb..fc852f6 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -1017,48 +1017,7 @@ static void line6_disconnect(struct usb_interface *interface) dev_err(line6->ifcdev, "driver bug: inconsistent usb device\n"); - switch (line6->type) { - case LINE6_BASSPODXT: - case LINE6_BASSPODXTLIVE: - case LINE6_BASSPODXTPRO: - case LINE6_POCKETPOD: - case LINE6_PODXT: - case LINE6_PODXTPRO: - line6_pod_disconnect(interface); - break; - - case LINE6_PODHD300: - case LINE6_PODHD400: - case LINE6_PODHD500_0: - case LINE6_PODHD500_1: - line6_podhd_disconnect(interface); - break; - - case LINE6_PODXTLIVE_POD: - line6_pod_disconnect(interface); - break; - - case LINE6_PODXTLIVE_VARIAX: - line6_variax_disconnect(interface); - break; - - case LINE6_VARIAX: - line6_variax_disconnect(interface); - break; - - case LINE6_PODSTUDIO_GX: - case LINE6_PODSTUDIO_UX1: - case LINE6_PODSTUDIO_UX2: - case LINE6_TONEPORT_GX: - case LINE6_TONEPORT_UX1: - case LINE6_TONEPORT_UX2: - case LINE6_GUITARPORT: - line6_toneport_disconnect(interface); - break; - - default: - MISSING_CASE; - } + line6->disconnect(interface); dev_info(&interface->dev, "Line6 %s now disconnected\n", line6->properties->name); diff --git a/drivers/staging/line6/driver.h b/drivers/staging/line6/driver.h index 220813f..ad203f1 100644 --- a/drivers/staging/line6/driver.h +++ b/drivers/staging/line6/driver.h @@ -196,6 +196,7 @@ struct usb_line6 { int message_length; void (*process_message)(struct usb_line6 *); + void (*disconnect)(struct usb_interface *); }; extern char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, diff --git a/drivers/staging/line6/pod.c b/drivers/staging/line6/pod.c index 79dcff4..b9af5cf 100644 --- a/drivers/staging/line6/pod.c +++ b/drivers/staging/line6/pod.c @@ -360,6 +360,7 @@ static int pod_try_init(struct usb_interface *interface, struct usb_line6_pod *pod = (struct usb_line6_pod *) line6; line6->process_message = line6_pod_process_message; + line6->disconnect = line6_pod_disconnect; init_timer(&pod->startup_timer); INIT_WORK(&pod->startup_work, pod_startup4); diff --git a/drivers/staging/line6/podhd.c b/drivers/staging/line6/podhd.c index 3bb942e..a57fbce 100644 --- a/drivers/staging/line6/podhd.c +++ b/drivers/staging/line6/podhd.c @@ -98,6 +98,8 @@ static int podhd_try_init(struct usb_interface *interface, if ((interface == NULL) || (podhd == NULL)) return -ENODEV; + line6->disconnect = line6_podhd_disconnect; + /* initialize audio system: */ err = line6_init_audio(line6); if (err < 0) diff --git a/drivers/staging/line6/toneport.c b/drivers/staging/line6/toneport.c index de91910..5c462b7 100644 --- a/drivers/staging/line6/toneport.c +++ b/drivers/staging/line6/toneport.c @@ -347,6 +347,8 @@ static int toneport_try_init(struct usb_interface *interface, if ((interface == NULL) || (toneport == NULL)) return -ENODEV; + line6->disconnect = line6_toneport_disconnect; + /* initialize audio system: */ err = line6_init_audio(line6); if (err < 0) diff --git a/drivers/staging/line6/variax.c b/drivers/staging/line6/variax.c index ccb1f68..ca25b41 100644 --- a/drivers/staging/line6/variax.c +++ b/drivers/staging/line6/variax.c @@ -181,6 +181,7 @@ static int variax_try_init(struct usb_interface *interface, int err; line6->process_message = line6_variax_process_message; + line6->disconnect = line6_variax_disconnect; init_timer(&variax->startup_timer1); init_timer(&variax->startup_timer2); -- cgit v0.10.2 From d29b854fe9036a505af373ac485b2110ebae6ccd Mon Sep 17 00:00:00 2001 From: Chris Rorvick Date: Mon, 12 Jan 2015 12:43:00 -0800 Subject: staging: line6: Make *_disconnect() functions static Remove declarations from the header and move the definitions up in the source so they need not be forward declared. Signed-off-by: Chris Rorvick Reviewed-by: Stefan Hajnoczi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/drivers/staging/line6/pod.c b/drivers/staging/line6/pod.c index b9af5cf..85a4363 100644 --- a/drivers/staging/line6/pod.c +++ b/drivers/staging/line6/pod.c @@ -338,6 +338,35 @@ static void pod_destruct(struct usb_interface *interface) } /* + POD device disconnected. +*/ +static void line6_pod_disconnect(struct usb_interface *interface) +{ + struct usb_line6_pod *pod; + + if (interface == NULL) + return; + pod = usb_get_intfdata(interface); + + if (pod != NULL) { + struct snd_line6_pcm *line6pcm = pod->line6.line6pcm; + struct device *dev = &interface->dev; + + if (line6pcm != NULL) + line6_pcm_disconnect(line6pcm); + + if (dev != NULL) { + /* remove sysfs entries: */ + device_remove_file(dev, &dev_attr_device_id); + device_remove_file(dev, &dev_attr_firmware_version); + device_remove_file(dev, &dev_attr_serial_number); + } + } + + pod_destruct(interface); +} + +/* Create sysfs entries. */ static int pod_create_files2(struct device *dev) @@ -422,32 +451,3 @@ int line6_pod_init(struct usb_interface *interface, struct usb_line6 *line6) return err; } - -/* - POD device disconnected. -*/ -void line6_pod_disconnect(struct usb_interface *interface) -{ - struct usb_line6_pod *pod; - - if (interface == NULL) - return; - pod = usb_get_intfdata(interface); - - if (pod != NULL) { - struct snd_line6_pcm *line6pcm = pod->line6.line6pcm; - struct device *dev = &interface->dev; - - if (line6pcm != NULL) - line6_pcm_disconnect(line6pcm); - - if (dev != NULL) { - /* remove sysfs entries: */ - device_remove_file(dev, &dev_attr_device_id); - device_remove_file(dev, &dev_attr_firmware_version); - device_remove_file(dev, &dev_attr_serial_number); - } - } - - pod_destruct(interface); -} diff --git a/drivers/staging/line6/pod.h b/drivers/staging/line6/pod.h index 0d78ca7..87a8f0f 100644 --- a/drivers/staging/line6/pod.h +++ b/drivers/staging/line6/pod.h @@ -86,7 +86,6 @@ struct usb_line6_pod { int device_id; }; -extern void line6_pod_disconnect(struct usb_interface *interface); extern int line6_pod_init(struct usb_interface *interface, struct usb_line6 *line6); diff --git a/drivers/staging/line6/podhd.c b/drivers/staging/line6/podhd.c index a57fbce..27c5402 100644 --- a/drivers/staging/line6/podhd.c +++ b/drivers/staging/line6/podhd.c @@ -87,6 +87,27 @@ static void podhd_destruct(struct usb_interface *interface) } /* + POD HD device disconnected. +*/ +static void line6_podhd_disconnect(struct usb_interface *interface) +{ + struct usb_line6_podhd *podhd; + + if (interface == NULL) + return; + podhd = usb_get_intfdata(interface); + + if (podhd != NULL) { + struct snd_line6_pcm *line6pcm = podhd->line6.line6pcm; + + if (line6pcm != NULL) + line6_pcm_disconnect(line6pcm); + } + + podhd_destruct(interface); +} + +/* Try to init POD HD device. */ static int podhd_try_init(struct usb_interface *interface, @@ -133,24 +154,3 @@ int line6_podhd_init(struct usb_interface *interface, struct usb_line6 *line6) return err; } - -/* - POD HD device disconnected. -*/ -void line6_podhd_disconnect(struct usb_interface *interface) -{ - struct usb_line6_podhd *podhd; - - if (interface == NULL) - return; - podhd = usb_get_intfdata(interface); - - if (podhd != NULL) { - struct snd_line6_pcm *line6pcm = podhd->line6.line6pcm; - - if (line6pcm != NULL) - line6_pcm_disconnect(line6pcm); - } - - podhd_destruct(interface); -} diff --git a/drivers/staging/line6/podhd.h b/drivers/staging/line6/podhd.h index b7d9568..a14f711 100644 --- a/drivers/staging/line6/podhd.h +++ b/drivers/staging/line6/podhd.h @@ -23,7 +23,6 @@ struct usb_line6_podhd { struct usb_line6 line6; }; -extern void line6_podhd_disconnect(struct usb_interface *interface); extern int line6_podhd_init(struct usb_interface *interface, struct usb_line6 *line6); diff --git a/drivers/staging/line6/toneport.c b/drivers/staging/line6/toneport.c index 5c462b7..aae78d8 100644 --- a/drivers/staging/line6/toneport.c +++ b/drivers/staging/line6/toneport.c @@ -336,6 +336,39 @@ static void toneport_setup(struct usb_line6_toneport *toneport) } /* + Toneport device disconnected. +*/ +static void line6_toneport_disconnect(struct usb_interface *interface) +{ + struct usb_line6_toneport *toneport; + u16 idProduct; + + if (interface == NULL) + return; + + toneport = usb_get_intfdata(interface); + del_timer_sync(&toneport->timer); + idProduct = le16_to_cpu(toneport->line6.usbdev->descriptor.idProduct); + + if (toneport_has_led(idProduct)) { + device_remove_file(&interface->dev, &dev_attr_led_red); + device_remove_file(&interface->dev, &dev_attr_led_green); + } + + if (toneport != NULL) { + struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm; + + if (line6pcm != NULL) { + line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); + line6_pcm_disconnect(line6pcm); + } + } + + toneport_destruct(interface); +} + + +/* Try to init Toneport device. */ static int toneport_try_init(struct usb_interface *interface, @@ -430,34 +463,3 @@ void line6_toneport_reset_resume(struct usb_line6_toneport *toneport) { toneport_setup(toneport); } - -/* - Toneport device disconnected. -*/ -void line6_toneport_disconnect(struct usb_interface *interface) -{ - struct usb_line6_toneport *toneport; - struct snd_line6_pcm *line6pcm; - - if (interface == NULL) - return; - - toneport = usb_get_intfdata(interface); - if (NULL == toneport) - return; - - del_timer_sync(&toneport->timer); - - if (toneport_has_led(toneport->line6.type)) { - device_remove_file(&interface->dev, &dev_attr_led_red); - device_remove_file(&interface->dev, &dev_attr_led_green); - } - - line6pcm = toneport->line6.line6pcm; - if (line6pcm != NULL) { - line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); - line6_pcm_disconnect(line6pcm); - } - - toneport_destruct(interface); -} diff --git a/drivers/staging/line6/toneport.h b/drivers/staging/line6/toneport.h index af2b4e0..8cb1442 100644 --- a/drivers/staging/line6/toneport.h +++ b/drivers/staging/line6/toneport.h @@ -44,7 +44,6 @@ struct usb_line6_toneport { struct timer_list timer; }; -extern void line6_toneport_disconnect(struct usb_interface *interface); extern int line6_toneport_init(struct usb_interface *interface, struct usb_line6 *line6); extern void line6_toneport_reset_resume(struct usb_line6_toneport *toneport); diff --git a/drivers/staging/line6/variax.c b/drivers/staging/line6/variax.c index ca25b41..b4a41b0 100644 --- a/drivers/staging/line6/variax.c +++ b/drivers/staging/line6/variax.c @@ -172,6 +172,17 @@ static void variax_destruct(struct usb_interface *interface) } /* + Workbench device disconnected. +*/ +static void line6_variax_disconnect(struct usb_interface *interface) +{ + if (interface == NULL) + return; + + variax_destruct(interface); +} + +/* Try to init workbench device. */ static int variax_try_init(struct usb_interface *interface, @@ -226,14 +237,3 @@ int line6_variax_init(struct usb_interface *interface, struct usb_line6 *line6) return err; } - -/* - Workbench device disconnected. -*/ -void line6_variax_disconnect(struct usb_interface *interface) -{ - if (interface == NULL) - return; - - variax_destruct(interface); -} diff --git a/drivers/staging/line6/variax.h b/drivers/staging/line6/variax.h index f21ff20..dfb94e5 100644 --- a/drivers/staging/line6/variax.h +++ b/drivers/staging/line6/variax.h @@ -64,7 +64,6 @@ struct usb_line6_variax { int startup_progress; }; -extern void line6_variax_disconnect(struct usb_interface *interface); extern int line6_variax_init(struct usb_interface *interface, struct usb_line6 *line6); -- cgit v0.10.2 From 703068eee6dde2ab318048f976b6ebd7fd239a78 Mon Sep 17 00:00:00 2001 From: Jon Maloy Date: Fri, 9 Jan 2015 15:26:58 +0800 Subject: tipc: fix bug in broadcast retransmit code In commit 58dc55f25631178ee74cd27185956a8f7dcb3e32 ("tipc: use generic SKB list APIs to manage link transmission queue") we replace all list traversal loops with the macros skb_queue_walk() or skb_queue_walk_safe(). While the previous loops were based on the assumption that the list was NULL-terminated, the standard macros stop when the iterator reaches the list head, which is non-NULL. In the function bclink_retransmit_pkt() this macro replacement has lead to a bug. When we receive a BCAST STATE_MSG we unconditionally call the function bclink_retransmit_pkt(), whether there really is anything to retransmit or not, assuming that the sequence number comparisons will lead to the correct behavior. However, if the transmission queue is empty, or if there are no eligible buffers in the transmission queue, we will by mistake pass the list head pointer to the function tipc_link_retransmit(). Since the list head is not a valid sk_buff, this leads to a crash. In this commit we fix this by only calling tipc_link_retransmit() if we actually found eligible buffers in the transmission queue. Reviewed-by: Ying Xue Signed-off-by: Jon Maloy Signed-off-by: David S. Miller diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index 96ceefe..a9e174f 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -220,10 +220,11 @@ static void bclink_retransmit_pkt(u32 after, u32 to) struct sk_buff *skb; skb_queue_walk(&bcl->outqueue, skb) { - if (more(buf_seqno(skb), after)) + if (more(buf_seqno(skb), after)) { + tipc_link_retransmit(bcl, skb, mod(to - after)); break; + } } - tipc_link_retransmit(bcl, skb, mod(to - after)); } /** -- cgit v0.10.2 From 6b8326ed14683f641e1c4149197f23a48c7cee36 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Fri, 9 Jan 2015 15:26:59 +0800 Subject: tipc: remove tipc_core_start/stop routines Remove redundant wrapper functions like tipc_core_start() and tipc_core_stop(), and directly move them to their callers, such as tipc_init() and tipc_exit(), having us clearly know what are really done in both initialization and deinitialzation functions. Signed-off-by: Ying Xue Tested-by: Tero Aho Reviewed-by: Jon Maloy Signed-off-by: David S. Miller diff --git a/net/tipc/core.c b/net/tipc/core.c index 71b2ada..10bc0bf 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -75,28 +75,21 @@ struct sk_buff *tipc_buf_acquire(u32 size) return skb; } -/** - * tipc_core_stop - switch TIPC from SINGLE NODE to NOT RUNNING mode - */ -static void tipc_core_stop(void) -{ - tipc_net_stop(); - tipc_bearer_cleanup(); - tipc_netlink_stop(); - tipc_subscr_stop(); - tipc_nametbl_stop(); - tipc_socket_stop(); - tipc_unregister_sysctl(); - tipc_sk_rht_destroy(); -} - -/** - * tipc_core_start - switch TIPC from NOT RUNNING to SINGLE NODE mode - */ -static int tipc_core_start(void) +static int __init tipc_init(void) { int err; + pr_info("Activated (version " TIPC_MOD_VER ")\n"); + + tipc_own_addr = 0; + tipc_net_id = 4711; + + sysctl_tipc_rmem[0] = TIPC_CONN_OVERLOAD_LIMIT >> 4 << + TIPC_LOW_IMPORTANCE; + sysctl_tipc_rmem[1] = TIPC_CONN_OVERLOAD_LIMIT >> 4 << + TIPC_CRITICAL_IMPORTANCE; + sysctl_tipc_rmem[2] = TIPC_CONN_OVERLOAD_LIMIT; + get_random_bytes(&tipc_random, sizeof(tipc_random)); err = tipc_sk_rht_init(); @@ -127,6 +120,7 @@ static int tipc_core_start(void) if (err) goto out_bearer; + pr_info("Started in single node mode\n"); return 0; out_bearer: tipc_subscr_stop(); @@ -141,35 +135,21 @@ out_netlink: out_nametbl: tipc_sk_rht_destroy(); out_reftbl: + pr_err("Unable to start in single node mode\n"); return err; } -static int __init tipc_init(void) -{ - int res; - - pr_info("Activated (version " TIPC_MOD_VER ")\n"); - - tipc_own_addr = 0; - tipc_net_id = 4711; - - sysctl_tipc_rmem[0] = TIPC_CONN_OVERLOAD_LIMIT >> 4 << - TIPC_LOW_IMPORTANCE; - sysctl_tipc_rmem[1] = TIPC_CONN_OVERLOAD_LIMIT >> 4 << - TIPC_CRITICAL_IMPORTANCE; - sysctl_tipc_rmem[2] = TIPC_CONN_OVERLOAD_LIMIT; - - res = tipc_core_start(); - if (res) - pr_err("Unable to start in single node mode\n"); - else - pr_info("Started in single node mode\n"); - return res; -} - static void __exit tipc_exit(void) { - tipc_core_stop(); + tipc_net_stop(); + tipc_bearer_cleanup(); + tipc_netlink_stop(); + tipc_subscr_stop(); + tipc_nametbl_stop(); + tipc_socket_stop(); + tipc_unregister_sysctl(); + tipc_sk_rht_destroy(); + pr_info("Deactivated\n"); } -- cgit v0.10.2 From 2f55c43788df7358be8c6e78ae2a3d3268e7afb6 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Fri, 9 Jan 2015 15:27:00 +0800 Subject: tipc: remove unnecessary wrapper functions of kernel timer APIs Not only some wrapper function like k_term_timer() is empty, but also some others including k_start_timer() and k_cancel_timer() don't return back any value to its caller, what's more, there is no any component in the kernel world to do such thing. Therefore, these timer interfaces defined in tipc module should be purged. Signed-off-by: Ying Xue Tested-by: Tero Aho Reviewed-by: Jon Maloy Signed-off-by: David S. Miller diff --git a/net/tipc/core.h b/net/tipc/core.h index 56fe422..d570689 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -107,73 +107,6 @@ void tipc_unregister_sysctl(void); #endif /* - * TIPC timer code - */ -typedef void (*Handler) (unsigned long); - -/** - * k_init_timer - initialize a timer - * @timer: pointer to timer structure - * @routine: pointer to routine to invoke when timer expires - * @argument: value to pass to routine when timer expires - * - * Timer must be initialized before use (and terminated when no longer needed). - */ -static inline void k_init_timer(struct timer_list *timer, Handler routine, - unsigned long argument) -{ - setup_timer(timer, routine, argument); -} - -/** - * k_start_timer - start a timer - * @timer: pointer to timer structure - * @msec: time to delay (in ms) - * - * Schedules a previously initialized timer for later execution. - * If timer is already running, the new timeout overrides the previous request. - * - * To ensure the timer doesn't expire before the specified delay elapses, - * the amount of delay is rounded up when converting to the jiffies - * then an additional jiffy is added to account for the fact that - * the starting time may be in the middle of the current jiffy. - */ -static inline void k_start_timer(struct timer_list *timer, unsigned long msec) -{ - mod_timer(timer, jiffies + msecs_to_jiffies(msec) + 1); -} - -/** - * k_cancel_timer - cancel a timer - * @timer: pointer to timer structure - * - * Cancels a previously initialized timer. - * Can be called safely even if the timer is already inactive. - * - * WARNING: Must not be called when holding locks required by the timer's - * timeout routine, otherwise deadlock can occur on SMP systems! - */ -static inline void k_cancel_timer(struct timer_list *timer) -{ - del_timer_sync(timer); -} - -/** - * k_term_timer - terminate a timer - * @timer: pointer to timer structure - * - * Prevents further use of a previously initialized timer. - * - * WARNING: Caller must ensure timer isn't currently running. - * - * (Do not "enhance" this routine to automatically cancel an active timer, - * otherwise deadlock can arise when a timeout routine calls k_term_timer.) - */ -static inline void k_term_timer(struct timer_list *timer) -{ -} - -/* * TIPC message buffer code * * TIPC message buffer headroom reserves space for the worst-case diff --git a/net/tipc/discover.c b/net/tipc/discover.c index aa722a4..1a3a985 100644 --- a/net/tipc/discover.c +++ b/net/tipc/discover.c @@ -38,10 +38,14 @@ #include "link.h" #include "discover.h" -#define TIPC_LINK_REQ_INIT 125 /* min delay during bearer start up */ -#define TIPC_LINK_REQ_FAST 1000 /* max delay if bearer has no links */ -#define TIPC_LINK_REQ_SLOW 60000 /* max delay if bearer has links */ -#define TIPC_LINK_REQ_INACTIVE 0xffffffff /* indicates no timer in use */ +/* min delay during bearer start up */ +#define TIPC_LINK_REQ_INIT msecs_to_jiffies(125) +/* max delay if bearer has no links */ +#define TIPC_LINK_REQ_FAST msecs_to_jiffies(1000) +/* max delay if bearer has links */ +#define TIPC_LINK_REQ_SLOW msecs_to_jiffies(60000) +/* indicates no timer in use */ +#define TIPC_LINK_REQ_INACTIVE 0xffffffff /** @@ -63,7 +67,7 @@ struct tipc_link_req { spinlock_t lock; struct sk_buff *buf; struct timer_list timer; - unsigned int timer_intv; + unsigned long timer_intv; }; /** @@ -265,7 +269,7 @@ static void disc_update(struct tipc_link_req *req) if ((req->timer_intv == TIPC_LINK_REQ_INACTIVE) || (req->timer_intv > TIPC_LINK_REQ_FAST)) { req->timer_intv = TIPC_LINK_REQ_INIT; - k_start_timer(&req->timer, req->timer_intv); + mod_timer(&req->timer, jiffies + req->timer_intv); } } } @@ -295,12 +299,13 @@ void tipc_disc_remove_dest(struct tipc_link_req *req) /** * disc_timeout - send a periodic link setup request - * @req: ptr to link request structure + * @data: ptr to link request structure * * Called whenever a link setup request timer associated with a bearer expires. */ -static void disc_timeout(struct tipc_link_req *req) +static void disc_timeout(unsigned long data) { + struct tipc_link_req *req = (struct tipc_link_req *)data; int max_delay; spin_lock_bh(&req->lock); @@ -329,7 +334,7 @@ static void disc_timeout(struct tipc_link_req *req) if (req->timer_intv > max_delay) req->timer_intv = max_delay; - k_start_timer(&req->timer, req->timer_intv); + mod_timer(&req->timer, jiffies + req->timer_intv); exit: spin_unlock_bh(&req->lock); } @@ -363,8 +368,8 @@ int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest) req->num_nodes = 0; req->timer_intv = TIPC_LINK_REQ_INIT; spin_lock_init(&req->lock); - k_init_timer(&req->timer, (Handler)disc_timeout, (unsigned long)req); - k_start_timer(&req->timer, req->timer_intv); + setup_timer(&req->timer, disc_timeout, (unsigned long)req); + mod_timer(&req->timer, jiffies + req->timer_intv); b_ptr->link_req = req; tipc_bearer_send(req->bearer_id, req->buf, &req->dest); return 0; @@ -376,8 +381,7 @@ int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest) */ void tipc_disc_delete(struct tipc_link_req *req) { - k_cancel_timer(&req->timer); - k_term_timer(&req->timer); + del_timer_sync(&req->timer); kfree_skb(req->buf); kfree(req); } @@ -397,7 +401,7 @@ void tipc_disc_reset(struct tipc_bearer *b_ptr) req->domain = b_ptr->domain; req->num_nodes = 0; req->timer_intv = TIPC_LINK_REQ_INIT; - k_start_timer(&req->timer, req->timer_intv); + mod_timer(&req->timer, jiffies + req->timer_intv); tipc_bearer_send(req->bearer_id, req->buf, &req->dest); spin_unlock_bh(&req->lock); } diff --git a/net/tipc/link.c b/net/tipc/link.c index 082c3b5..f2531a8 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -106,7 +106,7 @@ static void link_handle_out_of_seq_msg(struct tipc_link *l_ptr, static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf); static int tipc_link_tunnel_rcv(struct tipc_node *n_ptr, struct sk_buff **buf); -static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tolerance); +static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tol); static void link_state_event(struct tipc_link *l_ptr, u32 event); static void link_reset_statistics(struct tipc_link *l_ptr); static void link_print(struct tipc_link *l_ptr, const char *str); @@ -169,8 +169,9 @@ int tipc_link_is_active(struct tipc_link *l_ptr) * link_timeout - handle expiration of link timer * @l_ptr: pointer to link */ -static void link_timeout(struct tipc_link *l_ptr) +static void link_timeout(unsigned long data) { + struct tipc_link *l_ptr = (struct tipc_link *)data; struct sk_buff *skb; tipc_node_lock(l_ptr->owner); @@ -217,9 +218,9 @@ static void link_timeout(struct tipc_link *l_ptr) tipc_node_unlock(l_ptr->owner); } -static void link_set_timer(struct tipc_link *l_ptr, u32 time) +static void link_set_timer(struct tipc_link *link, unsigned long time) { - k_start_timer(&l_ptr->timer, time); + mod_timer(&link->timer, jiffies + time); } /** @@ -299,8 +300,7 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr, tipc_node_attach_link(n_ptr, l_ptr); - k_init_timer(&l_ptr->timer, (Handler)link_timeout, - (unsigned long)l_ptr); + setup_timer(&l_ptr->timer, link_timeout, (unsigned long)l_ptr); link_state_event(l_ptr, STARTING_EVT); @@ -479,7 +479,7 @@ static void link_activate(struct tipc_link *l_ptr) static void link_state_event(struct tipc_link *l_ptr, unsigned int event) { struct tipc_link *other; - u32 cont_intv = l_ptr->continuity_interval; + unsigned long cont_intv = l_ptr->cont_intv; if (l_ptr->flags & LINK_STOPPED) return; @@ -1880,15 +1880,16 @@ void tipc_link_bundle_rcv(struct sk_buff *buf) kfree_skb(buf); } -static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tolerance) +static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tol) { - if ((tolerance < TIPC_MIN_LINK_TOL) || (tolerance > TIPC_MAX_LINK_TOL)) + unsigned long intv = ((tol / 4) > 500) ? 500 : tol / 4; + + if ((tol < TIPC_MIN_LINK_TOL) || (tol > TIPC_MAX_LINK_TOL)) return; - l_ptr->tolerance = tolerance; - l_ptr->continuity_interval = - ((tolerance / 4) > 500) ? 500 : tolerance / 4; - l_ptr->abort_limit = tolerance / (l_ptr->continuity_interval / 4); + l_ptr->tolerance = tol; + l_ptr->cont_intv = msecs_to_jiffies(intv); + l_ptr->abort_limit = tol / (jiffies_to_msecs(l_ptr->cont_intv) / 4); } void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window) diff --git a/net/tipc/link.h b/net/tipc/link.h index 55812e8..15ca850 100644 --- a/net/tipc/link.h +++ b/net/tipc/link.h @@ -105,7 +105,7 @@ struct tipc_stats { * @peer_bearer_id: bearer id used by link's peer endpoint * @bearer_id: local bearer id used by link * @tolerance: minimum link continuity loss needed to reset link [in ms] - * @continuity_interval: link continuity testing interval [in ms] + * @cont_intv: link continuity testing interval * @abort_limit: # of unacknowledged continuity probes needed to reset link * @state: current state of link FSM * @fsm_msg_cnt: # of protocol messages link FSM has sent in current state @@ -146,7 +146,7 @@ struct tipc_link { u32 peer_bearer_id; u32 bearer_id; u32 tolerance; - u32 continuity_interval; + unsigned long cont_intv; u32 abort_limit; int state; u32 fsm_msg_cnt; diff --git a/net/tipc/msg.h b/net/tipc/msg.h index d5c83d7..1a52f7c 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -77,12 +77,10 @@ #define TIPC_MEDIA_ADDR_OFFSET 5 - struct tipc_msg { __be32 hdr[15]; }; - static inline u32 msg_word(struct tipc_msg *m, u32 pos) { return ntohl(m->hdr[pos]); diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 701f31b..e16197e 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -47,7 +47,7 @@ #define SS_READY -2 /* socket is connectionless */ #define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */ -#define CONN_PROBING_INTERVAL 3600000 /* [ms] => 1 h */ +#define CONN_PROBING_INTERVAL msecs_to_jiffies(3600000) /* [ms] => 1 h */ #define TIPC_FWD_MSG 1 #define TIPC_CONN_OK 0 #define TIPC_CONN_PROBING 1 @@ -68,7 +68,7 @@ * @publications: list of publications for port * @pub_count: total # of publications port has made during its lifetime * @probing_state: - * @probing_interval: + * @probing_intv: * @timer: * @port: port - interacts with 'sk' and with the rest of the TIPC stack * @peer_name: the peer of the connection, if any @@ -93,7 +93,7 @@ struct tipc_sock { struct list_head publications; u32 pub_count; u32 probing_state; - u32 probing_interval; + unsigned long probing_intv; struct timer_list timer; uint conn_timeout; atomic_t dupl_rcvcnt; @@ -361,7 +361,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock, return -EINVAL; } msg_set_origport(msg, tsk->portid); - k_init_timer(&tsk->timer, (Handler)tipc_sk_timeout, tsk->portid); + setup_timer(&tsk->timer, tipc_sk_timeout, tsk->portid); sk->sk_backlog_rcv = tipc_backlog_rcv; sk->sk_rcvbuf = sysctl_tipc_rmem[1]; sk->sk_data_ready = tipc_data_ready; @@ -511,7 +511,7 @@ static int tipc_release(struct socket *sock) } tipc_sk_withdraw(tsk, 0, NULL); - k_cancel_timer(&tsk->timer); + del_timer_sync(&tsk->timer); tipc_sk_remove(tsk); if (tsk->connected) { skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, @@ -522,7 +522,6 @@ static int tipc_release(struct socket *sock) tipc_link_xmit_skb(skb, dnode, tsk->portid); tipc_node_remove_conn(dnode, tsk->portid); } - k_term_timer(&tsk->timer); /* Discard any remaining (connection-based) messages in receive queue */ __skb_queue_purge(&sk->sk_receive_queue); @@ -1139,10 +1138,10 @@ static void tipc_sk_finish_conn(struct tipc_sock *tsk, u32 peer_port, msg_set_lookup_scope(msg, 0); msg_set_hdr_sz(msg, SHORT_H_SIZE); - tsk->probing_interval = CONN_PROBING_INTERVAL; + tsk->probing_intv = CONN_PROBING_INTERVAL; tsk->probing_state = TIPC_CONN_OK; tsk->connected = 1; - k_start_timer(&tsk->timer, tsk->probing_interval); + mod_timer(&tsk->timer, jiffies + tsk->probing_intv); tipc_node_add_conn(peer_node, tsk->portid, peer_port); tsk->max_pkt = tipc_node_get_mtu(peer_node, tsk->portid); } @@ -2128,7 +2127,7 @@ static void tipc_sk_timeout(unsigned long portid) 0, peer_node, tipc_own_addr, peer_port, portid, TIPC_OK); tsk->probing_state = TIPC_CONN_PROBING; - k_start_timer(&tsk->timer, tsk->probing_interval); + mod_timer(&tsk->timer, jiffies + tsk->probing_intv); } bh_unlock_sock(sk); if (skb) diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c index 0344206..e6cb959 100644 --- a/net/tipc/subscr.c +++ b/net/tipc/subscr.c @@ -141,8 +141,9 @@ void tipc_subscr_report_overlap(struct tipc_subscription *sub, u32 found_lower, subscr_send_event(sub, found_lower, found_upper, event, port_ref, node); } -static void subscr_timeout(struct tipc_subscription *sub) +static void subscr_timeout(unsigned long data) { + struct tipc_subscription *sub = (struct tipc_subscription *)data; struct tipc_subscriber *subscriber = sub->subscriber; /* The spin lock per subscriber is used to protect its members */ @@ -167,7 +168,6 @@ static void subscr_timeout(struct tipc_subscription *sub) TIPC_SUBSCR_TIMEOUT, 0, 0); /* Now destroy subscription */ - k_term_timer(&sub->timer); kfree(sub); atomic_dec(&subscription_count); } @@ -207,8 +207,7 @@ static void subscr_release(struct tipc_subscriber *subscriber) subscription_list) { if (sub->timeout != TIPC_WAIT_FOREVER) { spin_unlock_bh(&subscriber->lock); - k_cancel_timer(&sub->timer); - k_term_timer(&sub->timer); + del_timer_sync(&sub->timer); spin_lock_bh(&subscriber->lock); } subscr_del(sub); @@ -250,8 +249,7 @@ static void subscr_cancel(struct tipc_subscr *s, if (sub->timeout != TIPC_WAIT_FOREVER) { sub->timeout = TIPC_WAIT_FOREVER; spin_unlock_bh(&subscriber->lock); - k_cancel_timer(&sub->timer); - k_term_timer(&sub->timer); + del_timer_sync(&sub->timer); spin_lock_bh(&subscriber->lock); } subscr_del(sub); @@ -296,7 +294,7 @@ static int subscr_subscribe(struct tipc_subscr *s, sub->seq.type = htohl(s->seq.type, swap); sub->seq.lower = htohl(s->seq.lower, swap); sub->seq.upper = htohl(s->seq.upper, swap); - sub->timeout = htohl(s->timeout, swap); + sub->timeout = msecs_to_jiffies(htohl(s->timeout, swap)); sub->filter = htohl(s->filter, swap); if ((!(sub->filter & TIPC_SUB_PORTS) == !(sub->filter & TIPC_SUB_SERVICE)) || @@ -311,9 +309,8 @@ static int subscr_subscribe(struct tipc_subscr *s, memcpy(&sub->evt.s, s, sizeof(struct tipc_subscr)); atomic_inc(&subscription_count); if (sub->timeout != TIPC_WAIT_FOREVER) { - k_init_timer(&sub->timer, - (Handler)subscr_timeout, (unsigned long)sub); - k_start_timer(&sub->timer, sub->timeout); + setup_timer(&sub->timer, subscr_timeout, (unsigned long)sub); + mod_timer(&sub->timer, jiffies + sub->timeout); } *sub_p = sub; return 0; diff --git a/net/tipc/subscr.h b/net/tipc/subscr.h index 393e417..ab01713 100644 --- a/net/tipc/subscr.h +++ b/net/tipc/subscr.h @@ -58,7 +58,7 @@ struct tipc_subscriber; struct tipc_subscription { struct tipc_subscriber *subscriber; struct tipc_name_seq seq; - u32 timeout; + unsigned long timeout; u32 filter; struct timer_list timer; struct list_head nameseq_list; -- cgit v0.10.2 From 859fc7c0cedca0f84dac471fa31e9512259e1ecd Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Fri, 9 Jan 2015 15:27:01 +0800 Subject: tipc: cleanup core.c and core.h files Only the works of initializing and shutting down tipc module are done in core.h and core.c files, so all stuffs which are not closely associated with the two tasks should be moved to appropriate places. Signed-off-by: Ying Xue Tested-by: Tero Aho Reviewed-by: Jon Maloy Signed-off-by: David S. Miller diff --git a/net/tipc/addr.h b/net/tipc/addr.h index a74acf9..60b00ab 100644 --- a/net/tipc/addr.h +++ b/net/tipc/addr.h @@ -37,8 +37,6 @@ #ifndef _TIPC_ADDR_H #define _TIPC_ADDR_H -#include "core.h" - #define TIPC_ZONE_MASK 0xff000000u #define TIPC_CLUSTER_MASK 0xfffff000u diff --git a/net/tipc/config.h b/net/tipc/config.h index 47b1bf1..a41a41c 100644 --- a/net/tipc/config.h +++ b/net/tipc/config.h @@ -37,10 +37,10 @@ #ifndef _TIPC_CONFIG_H #define _TIPC_CONFIG_H -/* ---------------------------------------------------------------------- */ - #include "link.h" +#define ULTRA_STRING_MAX_LEN 32768 + struct sk_buff *tipc_cfg_reply_alloc(int payload_size); int tipc_cfg_append_tlv(struct sk_buff *buf, int tlv_type, void *tlv_data, int tlv_data_size); diff --git a/net/tipc/core.c b/net/tipc/core.c index 10bc0bf..b6ec3d7 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -52,29 +52,6 @@ u32 tipc_own_addr __read_mostly; int tipc_net_id __read_mostly; int sysctl_tipc_rmem[3] __read_mostly; /* min/default/max */ -/** - * tipc_buf_acquire - creates a TIPC message buffer - * @size: message size (including TIPC header) - * - * Returns a new buffer with data pointers set to the specified size. - * - * NOTE: Headroom is reserved to allow prepending of a data link header. - * There may also be unrequested tailroom present at the buffer's end. - */ -struct sk_buff *tipc_buf_acquire(u32 size) -{ - struct sk_buff *skb; - unsigned int buf_size = (BUF_HEADROOM + size + 3) & ~3u; - - skb = alloc_skb_fclone(buf_size, GFP_ATOMIC); - if (skb) { - skb_reserve(skb, BUF_HEADROOM); - skb_put(skb, size); - skb->next = NULL; - } - return skb; -} - static int __init tipc_init(void) { int err; diff --git a/net/tipc/core.h b/net/tipc/core.h index d570689..a5b3140 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -60,20 +60,9 @@ #define TIPC_MOD_VER "2.0.0" -#define ULTRA_STRING_MAX_LEN 32768 -#define TIPC_MAX_SUBSCRIPTIONS 65535 -#define TIPC_MAX_PUBLICATIONS 65535 - -struct tipc_msg; /* msg.h */ - int tipc_snprintf(char *buf, int len, const char *fmt, ...); /* - * TIPC-specific error codes - */ -#define ELINKCONG EAGAIN /* link congestion <=> resource unavailable */ - -/* * Global configuration variables */ extern u32 tipc_own_addr __read_mostly; @@ -86,18 +75,6 @@ extern int sysctl_tipc_named_timeout __read_mostly; */ extern int tipc_random __read_mostly; -/* - * Routines available to privileged subsystems - */ -int tipc_netlink_start(void); -void tipc_netlink_stop(void); -int tipc_socket_init(void); -void tipc_socket_stop(void); -int tipc_sock_create_local(int type, struct socket **res); -void tipc_sock_release_local(struct socket *sock); -int tipc_sock_accept_local(struct socket *sock, struct socket **newsock, - int flags); - #ifdef CONFIG_SYSCTL int tipc_register_sysctl(void); void tipc_unregister_sysctl(void); @@ -106,34 +83,4 @@ void tipc_unregister_sysctl(void); #define tipc_unregister_sysctl() #endif -/* - * TIPC message buffer code - * - * TIPC message buffer headroom reserves space for the worst-case - * link-level device header (in case the message is sent off-node). - * - * Note: Headroom should be a multiple of 4 to ensure the TIPC header fields - * are word aligned for quicker access - */ -#define BUF_HEADROOM LL_MAX_HEADER - -struct tipc_skb_cb { - void *handle; - struct sk_buff *tail; - bool deferred; - bool wakeup_pending; - bool bundling; - u16 chain_sz; - u16 chain_imp; -}; - -#define TIPC_SKB_CB(__skb) ((struct tipc_skb_cb *)&((__skb)->cb[0])) - -static inline struct tipc_msg *buf_msg(struct sk_buff *skb) -{ - return (struct tipc_msg *)skb->data; -} - -struct sk_buff *tipc_buf_acquire(u32 size); - #endif diff --git a/net/tipc/link.h b/net/tipc/link.h index 15ca850..e96c318 100644 --- a/net/tipc/link.h +++ b/net/tipc/link.h @@ -41,6 +41,10 @@ #include "msg.h" #include "node.h" +/* TIPC-specific error codes +*/ +#define ELINKCONG EAGAIN /* link congestion <=> resource unavailable */ + /* Out-of-range value for link sequence numbers */ #define INVALID_LINK_SEQ 0x10000 diff --git a/net/tipc/msg.c b/net/tipc/msg.c index a687b30..35523fb 100644 --- a/net/tipc/msg.c +++ b/net/tipc/msg.c @@ -46,6 +46,29 @@ static unsigned int align(unsigned int i) return (i + 3) & ~3u; } +/** + * tipc_buf_acquire - creates a TIPC message buffer + * @size: message size (including TIPC header) + * + * Returns a new buffer with data pointers set to the specified size. + * + * NOTE: Headroom is reserved to allow prepending of a data link header. + * There may also be unrequested tailroom present at the buffer's end. + */ +struct sk_buff *tipc_buf_acquire(u32 size) +{ + struct sk_buff *skb; + unsigned int buf_size = (BUF_HEADROOM + size + 3) & ~3u; + + skb = alloc_skb_fclone(buf_size, GFP_ATOMIC); + if (skb) { + skb_reserve(skb, BUF_HEADROOM); + skb_put(skb, size); + skb->next = NULL; + } + return skb; +} + void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, u32 hsize, u32 destnode) { diff --git a/net/tipc/msg.h b/net/tipc/msg.h index 1a52f7c..0065a2e 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -77,10 +77,38 @@ #define TIPC_MEDIA_ADDR_OFFSET 5 +/** + * TIPC message buffer code + * + * TIPC message buffer headroom reserves space for the worst-case + * link-level device header (in case the message is sent off-node). + * + * Note: Headroom should be a multiple of 4 to ensure the TIPC header fields + * are word aligned for quicker access + */ +#define BUF_HEADROOM LL_MAX_HEADER + +struct tipc_skb_cb { + void *handle; + struct sk_buff *tail; + bool deferred; + bool wakeup_pending; + bool bundling; + u16 chain_sz; + u16 chain_imp; +}; + +#define TIPC_SKB_CB(__skb) ((struct tipc_skb_cb *)&((__skb)->cb[0])) + struct tipc_msg { __be32 hdr[15]; }; +static inline struct tipc_msg *buf_msg(struct sk_buff *skb) +{ + return (struct tipc_msg *)skb->data; +} + static inline u32 msg_word(struct tipc_msg *m, u32 pos) { return ntohl(m->hdr[pos]); @@ -719,27 +747,20 @@ static inline u32 msg_tot_origport(struct tipc_msg *m) return msg_origport(m); } +struct sk_buff *tipc_buf_acquire(u32 size); bool tipc_msg_reverse(struct sk_buff *buf, u32 *dnode, int err); - int tipc_msg_eval(struct sk_buff *buf, u32 *dnode); - void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, u32 hsize, u32 destnode); - struct sk_buff *tipc_msg_create(uint user, uint type, uint hdr_sz, uint data_sz, u32 dnode, u32 onode, u32 dport, u32 oport, int errcode); - int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf); - bool tipc_msg_bundle(struct sk_buff_head *list, struct sk_buff *skb, u32 mtu); - bool tipc_msg_make_bundle(struct sk_buff_head *list, struct sk_buff *skb, u32 mtu, u32 dnode); - int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset, int dsz, int mtu, struct sk_buff_head *list); - struct sk_buff *tipc_msg_reassemble(struct sk_buff_head *list); #endif diff --git a/net/tipc/netlink.h b/net/tipc/netlink.h index 1425c68..ae2f2d9 100644 --- a/net/tipc/netlink.h +++ b/net/tipc/netlink.h @@ -45,4 +45,7 @@ struct tipc_nl_msg { u32 seq; }; +int tipc_netlink_start(void); +void tipc_netlink_stop(void); + #endif diff --git a/net/tipc/server.c b/net/tipc/server.c index a538a02..869eb09 100644 --- a/net/tipc/server.c +++ b/net/tipc/server.c @@ -35,6 +35,7 @@ #include "server.h" #include "core.h" +#include "socket.h" #include /* Number of messages to send before rescheduling */ diff --git a/net/tipc/server.h b/net/tipc/server.h index be817b0..87bc05c 100644 --- a/net/tipc/server.h +++ b/net/tipc/server.h @@ -36,7 +36,8 @@ #ifndef _TIPC_SERVER_H #define _TIPC_SERVER_H -#include "core.h" +#include +#include #define TIPC_SERVER_NAME_LEN 32 diff --git a/net/tipc/socket.h b/net/tipc/socket.h index c7d46d06..46bc370 100644 --- a/net/tipc/socket.h +++ b/net/tipc/socket.h @@ -42,6 +42,13 @@ #define TIPC_FLOWCTRL_WIN (TIPC_CONNACK_INTV * 2) #define TIPC_CONN_OVERLOAD_LIMIT ((TIPC_FLOWCTRL_WIN * 2 + 1) * \ SKB_TRUESIZE(TIPC_MAX_USER_MSG_SIZE)) + +int tipc_socket_init(void); +void tipc_socket_stop(void); +int tipc_sock_create_local(int type, struct socket **res); +void tipc_sock_release_local(struct socket *sock); +int tipc_sock_accept_local(struct socket *sock, struct socket **newsock, + int flags); int tipc_sk_rcv(struct sk_buff *buf); struct sk_buff *tipc_sk_socks_show(void); void tipc_sk_mcast_rcv(struct sk_buff *buf); diff --git a/net/tipc/subscr.h b/net/tipc/subscr.h index ab01713..0d39589 100644 --- a/net/tipc/subscr.h +++ b/net/tipc/subscr.h @@ -39,6 +39,9 @@ #include "server.h" +#define TIPC_MAX_SUBSCRIPTIONS 65535 +#define TIPC_MAX_PUBLICATIONS 65535 + struct tipc_subscription; struct tipc_subscriber; -- cgit v0.10.2 From f2f2a96a20d52d65aa79bd4019af43bbfb0e1528 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Fri, 9 Jan 2015 15:27:02 +0800 Subject: tipc: feed tipc sock pointer to tipc_sk_timeout routine In order to make tipc socket table aware of namespace, a networking namespace instance must be passed to tipc_sk_lookup(), allowing it to look up tipc socket instance with a given port ID from a concrete socket table. However, as now tipc_sk_timeout() only has one port ID parameter and is not namespace aware, it's unable to obtain a correct socket instance through tipc_sk_lookup() just with a port ID, especially after namespace is completely supported. If port ID is replaced with socket instance as tipc_sk_timeout()'s parameter, it's unnecessary to look up socket table. But as the timer handler - tipc_sk_timeout() is run asynchronously, socket reference must be held before its timer is launched, and must be carefully checked to identify whether the socket reference needs to be put or not when its timer is terminated. Signed-off-by: Ying Xue Tested-by: Tero Aho Reviewed-by: Jon Maloy Signed-off-by: David S. Miller diff --git a/net/tipc/socket.c b/net/tipc/socket.c index e16197e..c58f66b 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -110,7 +110,7 @@ static void tipc_write_space(struct sock *sk); static int tipc_release(struct socket *sock); static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags); static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p); -static void tipc_sk_timeout(unsigned long portid); +static void tipc_sk_timeout(unsigned long data); static int tipc_sk_publish(struct tipc_sock *tsk, uint scope, struct tipc_name_seq const *seq); static int tipc_sk_withdraw(struct tipc_sock *tsk, uint scope, @@ -361,7 +361,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock, return -EINVAL; } msg_set_origport(msg, tsk->portid); - setup_timer(&tsk->timer, tipc_sk_timeout, tsk->portid); + setup_timer(&tsk->timer, tipc_sk_timeout, (unsigned long)tsk); sk->sk_backlog_rcv = tipc_backlog_rcv; sk->sk_rcvbuf = sysctl_tipc_rmem[1]; sk->sk_data_ready = tipc_data_ready; @@ -475,7 +475,7 @@ static int tipc_release(struct socket *sock) struct sock *sk = sock->sk; struct tipc_sock *tsk; struct sk_buff *skb; - u32 dnode; + u32 dnode, probing_state; /* * Exit if socket isn't fully initialized (occurs when a failed accept() @@ -511,7 +511,9 @@ static int tipc_release(struct socket *sock) } tipc_sk_withdraw(tsk, 0, NULL); - del_timer_sync(&tsk->timer); + probing_state = tsk->probing_state; + if (del_timer_sync(&tsk->timer) && probing_state != TIPC_CONN_PROBING) + sock_put(sk); tipc_sk_remove(tsk); if (tsk->connected) { skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, @@ -1141,7 +1143,8 @@ static void tipc_sk_finish_conn(struct tipc_sock *tsk, u32 peer_port, tsk->probing_intv = CONN_PROBING_INTERVAL; tsk->probing_state = TIPC_CONN_OK; tsk->connected = 1; - mod_timer(&tsk->timer, jiffies + tsk->probing_intv); + if (!mod_timer(&tsk->timer, jiffies + tsk->probing_intv)) + sock_hold(&tsk->sk); tipc_node_add_conn(peer_node, tsk->portid, peer_port); tsk->max_pkt = tipc_node_get_mtu(peer_node, tsk->portid); } @@ -2096,18 +2099,13 @@ restart: return res; } -static void tipc_sk_timeout(unsigned long portid) +static void tipc_sk_timeout(unsigned long data) { - struct tipc_sock *tsk; - struct sock *sk; + struct tipc_sock *tsk = (struct tipc_sock *)data; + struct sock *sk = &tsk->sk; struct sk_buff *skb = NULL; u32 peer_port, peer_node; - tsk = tipc_sk_lookup(portid); - if (!tsk) - return; - - sk = &tsk->sk; bh_lock_sock(sk); if (!tsk->connected) { bh_unlock_sock(sk); @@ -2120,18 +2118,19 @@ static void tipc_sk_timeout(unsigned long portid) /* Previous probe not answered -> self abort */ skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, SHORT_H_SIZE, 0, tipc_own_addr, - peer_node, portid, peer_port, + peer_node, tsk->portid, peer_port, TIPC_ERR_NO_PORT); } else { skb = tipc_msg_create(CONN_MANAGER, CONN_PROBE, INT_H_SIZE, 0, peer_node, tipc_own_addr, - peer_port, portid, TIPC_OK); + peer_port, tsk->portid, TIPC_OK); tsk->probing_state = TIPC_CONN_PROBING; - mod_timer(&tsk->timer, jiffies + tsk->probing_intv); + if (!mod_timer(&tsk->timer, jiffies + tsk->probing_intv)) + sock_hold(sk); } bh_unlock_sock(sk); if (skb) - tipc_link_xmit_skb(skb, peer_node, portid); + tipc_link_xmit_skb(skb, peer_node, tsk->portid); exit: sock_put(sk); } -- cgit v0.10.2 From 54fef04ad05f15984082c225fe47ce6af8ea1c5c Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Fri, 9 Jan 2015 15:27:03 +0800 Subject: tipc: remove unused tipc_link_get_max_pkt routine Signed-off-by: Ying Xue Tested-by: Tero Aho Reviewed-by: Jon Maloy Signed-off-by: David S. Miller diff --git a/net/tipc/link.c b/net/tipc/link.c index f2531a8..f231058 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -2267,33 +2267,6 @@ struct sk_buff *tipc_link_cmd_show_stats(const void *req_tlv_area, int req_tlv_s return buf; } -/** - * tipc_link_get_max_pkt - get maximum packet size to use when sending to destination - * @dest: network address of destination node - * @selector: used to select from set of active links - * - * If no active link can be found, uses default maximum packet size. - */ -u32 tipc_link_get_max_pkt(u32 dest, u32 selector) -{ - struct tipc_node *n_ptr; - struct tipc_link *l_ptr; - u32 res = MAX_PKT_DEFAULT; - - if (dest == tipc_own_addr) - return MAX_MSG_SIZE; - - n_ptr = tipc_node_find(dest); - if (n_ptr) { - tipc_node_lock(n_ptr); - l_ptr = n_ptr->active_links[selector & 1]; - if (l_ptr) - res = l_ptr->max_pkt; - tipc_node_unlock(n_ptr); - } - return res; -} - static void link_print(struct tipc_link *l_ptr, const char *str) { struct tipc_bearer *b_ptr; diff --git a/net/tipc/link.h b/net/tipc/link.h index e96c318..692268d 100644 --- a/net/tipc/link.h +++ b/net/tipc/link.h @@ -220,7 +220,6 @@ void tipc_link_reset_list(unsigned int bearer_id); int tipc_link_xmit_skb(struct sk_buff *skb, u32 dest, u32 selector); int tipc_link_xmit(struct sk_buff_head *list, u32 dest, u32 selector); int __tipc_link_xmit(struct tipc_link *link, struct sk_buff_head *list); -u32 tipc_link_get_max_pkt(u32 dest, u32 selector); void tipc_link_bundle_rcv(struct sk_buff *buf); void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int prob, u32 gap, u32 tolerance, u32 priority, u32 acked_mtu); -- cgit v0.10.2 From c93d3baa24095887005647984cff5de8c63d3611 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Fri, 9 Jan 2015 15:27:04 +0800 Subject: tipc: involve namespace infrastructure Involve namespace infrastructure, make the "tipc_net_id" global variable aware of per namespace, and rename it to "net_id". In order that the conversion can be successfully done, an instance of networking namespace must be passed to relevant functions, allowing them to access the "net_id" variable of per namespace. Signed-off-by: Ying Xue Tested-by: Tero Aho Reviewed-by: Jon Maloy Signed-off-by: David S. Miller diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index a9e174f..f982311 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -327,9 +327,11 @@ exit: * * RCU and node lock set */ -void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent) +void tipc_bclink_update_link_state(struct net *net, struct tipc_node *n_ptr, + u32 last_sent) { struct sk_buff *buf; + struct tipc_net *tn = net_generic(net, tipc_net_id); /* Ignore "stale" link state info */ if (less_eq(last_sent, n_ptr->bclink.last_in)) @@ -362,7 +364,7 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent) tipc_msg_init(msg, BCAST_PROTOCOL, STATE_MSG, INT_H_SIZE, n_ptr->addr); msg_set_non_seq(msg, 1); - msg_set_mc_netid(msg, tipc_net_id); + msg_set_mc_netid(msg, tn->net_id); msg_set_bcast_ack(msg, n_ptr->bclink.last_in); msg_set_bcgap_after(msg, n_ptr->bclink.last_in); msg_set_bcgap_to(msg, to); @@ -476,8 +478,9 @@ static void bclink_accept_pkt(struct tipc_node *node, u32 seqno) * * RCU is locked, no other locks set */ -void tipc_bclink_rcv(struct sk_buff *buf) +void tipc_bclink_rcv(struct net *net, struct sk_buff *buf) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_msg *msg = buf_msg(buf); struct tipc_node *node; u32 next_in; @@ -485,7 +488,7 @@ void tipc_bclink_rcv(struct sk_buff *buf) int deferred = 0; /* Screen out unwanted broadcast messages */ - if (msg_mc_netid(msg) != tipc_net_id) + if (msg_mc_netid(msg) != tn->net_id) goto exit; node = tipc_node_find(msg_prevnode(msg)); @@ -638,6 +641,8 @@ static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1, { int bp_index; struct tipc_msg *msg = buf_msg(buf); + struct net *net = sock_net(buf->sk); + struct tipc_net *tn = net_generic(net, tipc_net_id); /* Prepare broadcast link message for reliable transmission, * if first time trying to send it; @@ -647,7 +652,7 @@ static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1, if (likely(!msg_non_seq(buf_msg(buf)))) { bcbuf_set_acks(buf, bclink->bcast_nodes.count); msg_set_non_seq(msg, 1); - msg_set_mc_netid(msg, tipc_net_id); + msg_set_mc_netid(msg, tn->net_id); bcl->stats.sent_info++; if (WARN_ON(!bclink->bcast_nodes.count)) { diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h index 644d791..a5fd224 100644 --- a/net/tipc/bcast.h +++ b/net/tipc/bcast.h @@ -91,10 +91,11 @@ void tipc_bclink_add_node(u32 addr); void tipc_bclink_remove_node(u32 addr); struct tipc_node *tipc_bclink_retransmit_to(void); void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked); -void tipc_bclink_rcv(struct sk_buff *buf); +void tipc_bclink_rcv(struct net *net, struct sk_buff *buf); u32 tipc_bclink_get_last_sent(void); u32 tipc_bclink_acks_missing(struct tipc_node *n_ptr); -void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent); +void tipc_bclink_update_link_state(struct net *net, struct tipc_node *n_ptr, + u32 last_sent); int tipc_bclink_stats(char *stats_buf, const u32 buf_size); int tipc_bclink_reset_stats(void); int tipc_bclink_set_queue_limits(u32 limit); diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 463db5b..cdd3033 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -260,7 +260,8 @@ void tipc_bearer_remove_dest(u32 bearer_id, u32 dest) /** * tipc_enable_bearer - enable bearer with the given name */ -int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority) +int tipc_enable_bearer(struct net *net, const char *name, u32 disc_domain, + u32 priority) { struct tipc_bearer *b_ptr; struct tipc_media *m_ptr; @@ -361,7 +362,7 @@ restart: b_ptr->net_plane = bearer_id + 'A'; b_ptr->priority = priority; - res = tipc_disc_create(b_ptr, &b_ptr->bcast_addr); + res = tipc_disc_create(net, b_ptr, &b_ptr->bcast_addr); if (res) { bearer_disable(b_ptr, false); pr_warn("Bearer <%s> rejected, discovery object creation failed\n", @@ -380,11 +381,11 @@ restart: /** * tipc_reset_bearer - Reset all links established over this bearer */ -static int tipc_reset_bearer(struct tipc_bearer *b_ptr) +static int tipc_reset_bearer(struct net *net, struct tipc_bearer *b_ptr) { pr_info("Resetting bearer <%s>\n", b_ptr->name); tipc_link_reset_list(b_ptr->identity); - tipc_disc_reset(b_ptr); + tipc_disc_reset(net, b_ptr); return 0; } @@ -539,17 +540,12 @@ static int tipc_l2_rcv_msg(struct sk_buff *buf, struct net_device *dev, { struct tipc_bearer *b_ptr; - if (!net_eq(dev_net(dev), &init_net)) { - kfree_skb(buf); - return NET_RX_DROP; - } - rcu_read_lock(); b_ptr = rcu_dereference_rtnl(dev->tipc_ptr); if (likely(b_ptr)) { if (likely(buf->pkt_type <= PACKET_BROADCAST)) { buf->next = NULL; - tipc_rcv(buf, b_ptr); + tipc_rcv(dev_net(dev), buf, b_ptr); rcu_read_unlock(); return NET_RX_SUCCESS; } @@ -572,11 +568,9 @@ static int tipc_l2_rcv_msg(struct sk_buff *buf, struct net_device *dev, static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt, void *ptr) { - struct tipc_bearer *b_ptr; struct net_device *dev = netdev_notifier_info_to_dev(ptr); - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; + struct net *net = dev_net(dev); + struct tipc_bearer *b_ptr; b_ptr = rtnl_dereference(dev->tipc_ptr); if (!b_ptr) @@ -590,12 +584,12 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt, break; case NETDEV_DOWN: case NETDEV_CHANGEMTU: - tipc_reset_bearer(b_ptr); + tipc_reset_bearer(net, b_ptr); break; case NETDEV_CHANGEADDR: b_ptr->media->raw2addr(b_ptr, &b_ptr->addr, (char *)dev->dev_addr); - tipc_reset_bearer(b_ptr); + tipc_reset_bearer(net, b_ptr); break; case NETDEV_UNREGISTER: case NETDEV_CHANGENAME: @@ -808,6 +802,7 @@ int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info) int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info) { + struct net *net = genl_info_net(info); int err; char *bearer; struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1]; @@ -847,7 +842,7 @@ int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info) } rtnl_lock(); - err = tipc_enable_bearer(bearer, domain, prio); + err = tipc_enable_bearer(net, bearer, domain, prio); if (err) { rtnl_unlock(); return err; diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h index 2c1230a..43f683a 100644 --- a/net/tipc/bearer.h +++ b/net/tipc/bearer.h @@ -165,8 +165,9 @@ extern struct tipc_bearer __rcu *bearer_list[]; * TIPC routines available to supported media types */ -void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *tb_ptr); -int tipc_enable_bearer(const char *bearer_name, u32 disc_domain, u32 priority); +void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr); +int tipc_enable_bearer(struct net *net, const char *bearer_name, + u32 disc_domain, u32 priority); int tipc_disable_bearer(const char *name); /* diff --git a/net/tipc/config.c b/net/tipc/config.c index 0b3a90e..28d4272 100644 --- a/net/tipc/config.c +++ b/net/tipc/config.c @@ -134,7 +134,7 @@ static struct sk_buff *tipc_show_stats(void) return buf; } -static struct sk_buff *cfg_enable_bearer(void) +static struct sk_buff *cfg_enable_bearer(struct net *net) { struct tipc_bearer_config *args; @@ -142,7 +142,7 @@ static struct sk_buff *cfg_enable_bearer(void) return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); args = (struct tipc_bearer_config *)TLV_DATA(req_tlv_area); - if (tipc_enable_bearer(args->name, + if (tipc_enable_bearer(net, args->name, ntohl(args->disc_domain), ntohl(args->priority))) return tipc_cfg_reply_error_string("unable to enable bearer"); @@ -161,7 +161,7 @@ static struct sk_buff *cfg_disable_bearer(void) return tipc_cfg_reply_none(); } -static struct sk_buff *cfg_set_own_addr(void) +static struct sk_buff *cfg_set_own_addr(struct net *net) { u32 addr; @@ -177,20 +177,21 @@ static struct sk_buff *cfg_set_own_addr(void) if (tipc_own_addr) return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED " (cannot change node address once assigned)"); - if (!tipc_net_start(addr)) + if (!tipc_net_start(net, addr)) return tipc_cfg_reply_none(); return tipc_cfg_reply_error_string("cannot change to network mode"); } -static struct sk_buff *cfg_set_netid(void) +static struct sk_buff *cfg_set_netid(struct net *net) { + struct tipc_net *tn = net_generic(net, tipc_net_id); u32 value; if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); - if (value == tipc_net_id) + if (value == tn->net_id) return tipc_cfg_reply_none(); if (value < 1 || value > 9999) return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE @@ -198,14 +199,16 @@ static struct sk_buff *cfg_set_netid(void) if (tipc_own_addr) return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED " (cannot change network id once TIPC has joined a network)"); - tipc_net_id = value; + tn->net_id = value; return tipc_cfg_reply_none(); } -struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area, - int request_space, int reply_headroom) +struct sk_buff *tipc_cfg_do_cmd(struct net *net, u32 orig_node, u16 cmd, + const void *request_area, int request_space, + int reply_headroom) { struct sk_buff *rep_tlv_buf; + struct tipc_net *tn = net_generic(net, tipc_net_id); rtnl_lock(); @@ -261,19 +264,19 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area rep_tlv_buf = tipc_link_cmd_config(req_tlv_area, req_tlv_space, cmd); break; case TIPC_CMD_ENABLE_BEARER: - rep_tlv_buf = cfg_enable_bearer(); + rep_tlv_buf = cfg_enable_bearer(net); break; case TIPC_CMD_DISABLE_BEARER: rep_tlv_buf = cfg_disable_bearer(); break; case TIPC_CMD_SET_NODE_ADDR: - rep_tlv_buf = cfg_set_own_addr(); + rep_tlv_buf = cfg_set_own_addr(net); break; case TIPC_CMD_SET_NETID: - rep_tlv_buf = cfg_set_netid(); + rep_tlv_buf = cfg_set_netid(net); break; case TIPC_CMD_GET_NETID: - rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_net_id); + rep_tlv_buf = tipc_cfg_reply_unsigned(tn->net_id); break; case TIPC_CMD_NOT_NET_ADMIN: rep_tlv_buf = diff --git a/net/tipc/config.h b/net/tipc/config.h index a41a41c..9e9b575 100644 --- a/net/tipc/config.h +++ b/net/tipc/config.h @@ -61,7 +61,7 @@ static inline struct sk_buff *tipc_cfg_reply_ultra_string(char *string) return tipc_cfg_reply_string_type(TIPC_TLV_ULTRA_STRING, string); } -struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, +struct sk_buff *tipc_cfg_do_cmd(struct net *net, u32 orig_node, u16 cmd, const void *req_tlv_area, int req_tlv_space, int headroom); #endif diff --git a/net/tipc/core.c b/net/tipc/core.c index b6ec3d7..a230248 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -52,6 +52,26 @@ u32 tipc_own_addr __read_mostly; int tipc_net_id __read_mostly; int sysctl_tipc_rmem[3] __read_mostly; /* min/default/max */ +static int __net_init tipc_init_net(struct net *net) +{ + struct tipc_net *tn = net_generic(net, tipc_net_id); + + tn->net_id = 4711; + + return 0; +} + +static void __net_exit tipc_exit_net(struct net *net) +{ +} + +static struct pernet_operations tipc_net_ops = { + .init = tipc_init_net, + .exit = tipc_exit_net, + .id = &tipc_net_id, + .size = sizeof(struct tipc_net), +}; + static int __init tipc_init(void) { int err; @@ -59,7 +79,6 @@ static int __init tipc_init(void) pr_info("Activated (version " TIPC_MOD_VER ")\n"); tipc_own_addr = 0; - tipc_net_id = 4711; sysctl_tipc_rmem[0] = TIPC_CONN_OVERLOAD_LIMIT >> 4 << TIPC_LOW_IMPORTANCE; @@ -69,6 +88,10 @@ static int __init tipc_init(void) get_random_bytes(&tipc_random, sizeof(tipc_random)); + err = register_pernet_subsys(&tipc_net_ops); + if (err) + goto out_pernet; + err = tipc_sk_rht_init(); if (err) goto out_reftbl; @@ -112,12 +135,15 @@ out_netlink: out_nametbl: tipc_sk_rht_destroy(); out_reftbl: + unregister_pernet_subsys(&tipc_net_ops); +out_pernet: pr_err("Unable to start in single node mode\n"); return err; } static void __exit tipc_exit(void) { + unregister_pernet_subsys(&tipc_net_ops); tipc_net_stop(); tipc_bearer_cleanup(); tipc_netlink_stop(); diff --git a/net/tipc/core.h b/net/tipc/core.h index a5b3140..106e815 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -57,6 +57,7 @@ #include #include #include +#include #define TIPC_MOD_VER "2.0.0" @@ -75,6 +76,10 @@ extern int sysctl_tipc_named_timeout __read_mostly; */ extern int tipc_random __read_mostly; +struct tipc_net { + int net_id; +}; + #ifdef CONFIG_SYSCTL int tipc_register_sysctl(void); void tipc_unregister_sysctl(void); diff --git a/net/tipc/discover.c b/net/tipc/discover.c index 1a3a985..246a237 100644 --- a/net/tipc/discover.c +++ b/net/tipc/discover.c @@ -72,12 +72,14 @@ struct tipc_link_req { /** * tipc_disc_init_msg - initialize a link setup message + * @net: the applicable net namespace * @type: message type (request or response) * @b_ptr: ptr to bearer issuing message */ -static void tipc_disc_init_msg(struct sk_buff *buf, u32 type, +static void tipc_disc_init_msg(struct net *net, struct sk_buff *buf, u32 type, struct tipc_bearer *b_ptr) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_msg *msg; u32 dest_domain = b_ptr->domain; @@ -86,7 +88,7 @@ static void tipc_disc_init_msg(struct sk_buff *buf, u32 type, msg_set_non_seq(msg, 1); msg_set_node_sig(msg, tipc_random); msg_set_dest_domain(msg, dest_domain); - msg_set_bc_netid(msg, tipc_net_id); + msg_set_bc_netid(msg, tn->net_id); b_ptr->media->addr2msg(msg_media_addr(msg), &b_ptr->addr); } @@ -111,11 +113,14 @@ static void disc_dupl_alert(struct tipc_bearer *b_ptr, u32 node_addr, /** * tipc_disc_rcv - handle incoming discovery message (request or response) + * @net: the applicable net namespace * @buf: buffer containing message * @bearer: bearer that message arrived on */ -void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *bearer) +void tipc_disc_rcv(struct net *net, struct sk_buff *buf, + struct tipc_bearer *bearer) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_node *node; struct tipc_link *link; struct tipc_media_addr maddr; @@ -137,7 +142,7 @@ void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *bearer) kfree_skb(buf); /* Ensure message from node is valid and communication is permitted */ - if (net_id != tipc_net_id) + if (net_id != tn->net_id) return; if (maddr.broadcast) return; @@ -248,7 +253,7 @@ void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *bearer) if (respond && (mtyp == DSC_REQ_MSG)) { rbuf = tipc_buf_acquire(INT_H_SIZE); if (rbuf) { - tipc_disc_init_msg(rbuf, DSC_RESP_MSG, bearer); + tipc_disc_init_msg(net, rbuf, DSC_RESP_MSG, bearer); tipc_bearer_send(bearer->identity, rbuf, &maddr); kfree_skb(rbuf); } @@ -341,13 +346,15 @@ exit: /** * tipc_disc_create - create object to send periodic link setup requests + * @net: the applicable net namespace * @b_ptr: ptr to bearer issuing requests * @dest: destination address for request messages * @dest_domain: network domain to which links can be established * * Returns 0 if successful, otherwise -errno. */ -int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest) +int tipc_disc_create(struct net *net, struct tipc_bearer *b_ptr, + struct tipc_media_addr *dest) { struct tipc_link_req *req; @@ -361,7 +368,7 @@ int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest) return -ENOMEM; } - tipc_disc_init_msg(req->buf, DSC_REQ_MSG, b_ptr); + tipc_disc_init_msg(net, req->buf, DSC_REQ_MSG, b_ptr); memcpy(&req->dest, dest, sizeof(*dest)); req->bearer_id = b_ptr->identity; req->domain = b_ptr->domain; @@ -388,15 +395,16 @@ void tipc_disc_delete(struct tipc_link_req *req) /** * tipc_disc_reset - reset object to send periodic link setup requests + * @net: the applicable net namespace * @b_ptr: ptr to bearer issuing requests * @dest_domain: network domain to which links can be established */ -void tipc_disc_reset(struct tipc_bearer *b_ptr) +void tipc_disc_reset(struct net *net, struct tipc_bearer *b_ptr) { struct tipc_link_req *req = b_ptr->link_req; spin_lock_bh(&req->lock); - tipc_disc_init_msg(req->buf, DSC_REQ_MSG, b_ptr); + tipc_disc_init_msg(net, req->buf, DSC_REQ_MSG, b_ptr); req->bearer_id = b_ptr->identity; req->domain = b_ptr->domain; req->num_nodes = 0; diff --git a/net/tipc/discover.h b/net/tipc/discover.h index 515b573..c9b1277 100644 --- a/net/tipc/discover.h +++ b/net/tipc/discover.h @@ -39,11 +39,13 @@ struct tipc_link_req; -int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest); +int tipc_disc_create(struct net *net, struct tipc_bearer *b_ptr, + struct tipc_media_addr *dest); void tipc_disc_delete(struct tipc_link_req *req); -void tipc_disc_reset(struct tipc_bearer *b_ptr); +void tipc_disc_reset(struct net *net, struct tipc_bearer *b_ptr); void tipc_disc_add_dest(struct tipc_link_req *req); void tipc_disc_remove_dest(struct tipc_link_req *req); -void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *b_ptr); +void tipc_disc_rcv(struct net *net, struct sk_buff *buf, + struct tipc_bearer *b_ptr); #endif diff --git a/net/tipc/link.c b/net/tipc/link.c index f231058..248813c 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -101,10 +101,12 @@ static const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = { */ #define START_CHANGEOVER 100000u -static void link_handle_out_of_seq_msg(struct tipc_link *l_ptr, +static void link_handle_out_of_seq_msg(struct net *net, + struct tipc_link *l_ptr, struct sk_buff *buf); -static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf); -static int tipc_link_tunnel_rcv(struct tipc_node *n_ptr, +static void tipc_link_proto_rcv(struct net *net, struct tipc_link *l_ptr, + struct sk_buff *buf); +static int tipc_link_tunnel_rcv(struct net *net, struct tipc_node *n_ptr, struct sk_buff **buf); static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tol); static void link_state_event(struct tipc_link *l_ptr, u32 event); @@ -113,7 +115,8 @@ static void link_print(struct tipc_link *l_ptr, const char *str); static void tipc_link_sync_xmit(struct tipc_link *l); static void tipc_link_sync_rcv(struct tipc_node *n, struct sk_buff *buf); static int tipc_link_input(struct tipc_link *l, struct sk_buff *buf); -static int tipc_link_prepare_input(struct tipc_link *l, struct sk_buff **buf); +static int tipc_link_prepare_input(struct net *net, struct tipc_link *l, + struct sk_buff **buf); /* * Simple link routines @@ -1063,13 +1066,14 @@ static int link_recv_buf_validate(struct sk_buff *buf) /** * tipc_rcv - process TIPC packets/messages arriving from off-node + * @net: net namespace handler * @skb: TIPC packet * @b_ptr: pointer to bearer message arrived on * * Invoked with no locks held. Bearer pointer must point to a valid bearer * structure (i.e. cannot be NULL), but bearer can be inactive. */ -void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *b_ptr) +void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr) { struct sk_buff_head head; struct tipc_node *n_ptr; @@ -1096,9 +1100,9 @@ void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *b_ptr) if (unlikely(msg_non_seq(msg))) { if (msg_user(msg) == LINK_CONFIG) - tipc_disc_rcv(skb, b_ptr); + tipc_disc_rcv(net, skb, b_ptr); else - tipc_bclink_rcv(skb); + tipc_bclink_rcv(net, skb); continue; } @@ -1159,7 +1163,7 @@ void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *b_ptr) /* Process the incoming packet */ if (unlikely(!link_working_working(l_ptr))) { if (msg_user(msg) == LINK_PROTOCOL) { - tipc_link_proto_rcv(l_ptr, skb); + tipc_link_proto_rcv(net, l_ptr, skb); link_retrieve_defq(l_ptr, &head); tipc_node_unlock(n_ptr); continue; @@ -1179,7 +1183,7 @@ void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *b_ptr) /* Link is now in state WORKING_WORKING */ if (unlikely(seq_no != mod(l_ptr->next_in_no))) { - link_handle_out_of_seq_msg(l_ptr, skb); + link_handle_out_of_seq_msg(net, l_ptr, skb); link_retrieve_defq(l_ptr, &head); tipc_node_unlock(n_ptr); continue; @@ -1193,7 +1197,7 @@ void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *b_ptr) tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, 0, 0, 0, 0); } - if (tipc_link_prepare_input(l_ptr, &skb)) { + if (tipc_link_prepare_input(net, l_ptr, &skb)) { tipc_node_unlock(n_ptr); continue; } @@ -1216,7 +1220,8 @@ discard: * * Node lock must be held */ -static int tipc_link_prepare_input(struct tipc_link *l, struct sk_buff **buf) +static int tipc_link_prepare_input(struct net *net, struct tipc_link *l, + struct sk_buff **buf) { struct tipc_node *n; struct tipc_msg *msg; @@ -1226,7 +1231,7 @@ static int tipc_link_prepare_input(struct tipc_link *l, struct sk_buff **buf) msg = buf_msg(*buf); switch (msg_user(msg)) { case CHANGEOVER_PROTOCOL: - if (tipc_link_tunnel_rcv(n, buf)) + if (tipc_link_tunnel_rcv(net, n, buf)) res = 0; break; case MSG_FRAGMENTER: @@ -1325,13 +1330,14 @@ u32 tipc_link_defer_pkt(struct sk_buff_head *list, struct sk_buff *skb) /* * link_handle_out_of_seq_msg - handle arrival of out-of-sequence packet */ -static void link_handle_out_of_seq_msg(struct tipc_link *l_ptr, +static void link_handle_out_of_seq_msg(struct net *net, + struct tipc_link *l_ptr, struct sk_buff *buf) { u32 seq_no = buf_seqno(buf); if (likely(msg_user(buf_msg(buf)) == LINK_PROTOCOL)) { - tipc_link_proto_rcv(l_ptr, buf); + tipc_link_proto_rcv(net, l_ptr, buf); return; } @@ -1455,7 +1461,8 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg, * Note that network plane id propagates through the network, and may * change at any time. The node with lowest address rules */ -static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf) +static void tipc_link_proto_rcv(struct net *net, struct tipc_link *l_ptr, + struct sk_buff *buf) { u32 rec_gap = 0; u32 max_pkt_info; @@ -1571,7 +1578,7 @@ static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf) /* Protocol message before retransmits, reduce loss risk */ if (l_ptr->owner->bclink.recv_permitted) - tipc_bclink_update_link_state(l_ptr->owner, + tipc_bclink_update_link_state(net, l_ptr->owner, msg_last_bcast(msg)); if (rec_gap || (msg_probe(msg))) { @@ -1748,7 +1755,7 @@ static struct sk_buff *buf_extract(struct sk_buff *skb, u32 from_pos) /* tipc_link_dup_rcv(): Receive a tunnelled DUPLICATE_MSG packet. * Owner node is locked. */ -static void tipc_link_dup_rcv(struct tipc_link *l_ptr, +static void tipc_link_dup_rcv(struct net *net, struct tipc_link *l_ptr, struct sk_buff *t_buf) { struct sk_buff *buf; @@ -1763,7 +1770,7 @@ static void tipc_link_dup_rcv(struct tipc_link *l_ptr, } /* Add buffer to deferred queue, if applicable: */ - link_handle_out_of_seq_msg(l_ptr, buf); + link_handle_out_of_seq_msg(net, l_ptr, buf); } /* tipc_link_failover_rcv(): Receive a tunnelled ORIGINAL_MSG packet @@ -1817,7 +1824,7 @@ exit: * returned to the active link for delivery upwards. * Owner node is locked. */ -static int tipc_link_tunnel_rcv(struct tipc_node *n_ptr, +static int tipc_link_tunnel_rcv(struct net *net, struct tipc_node *n_ptr, struct sk_buff **buf) { struct sk_buff *t_buf = *buf; @@ -1835,7 +1842,7 @@ static int tipc_link_tunnel_rcv(struct tipc_node *n_ptr, goto exit; if (msg_type(t_msg) == DUPLICATE_MSG) - tipc_link_dup_rcv(l_ptr, t_buf); + tipc_link_dup_rcv(net, l_ptr, t_buf); else if (msg_type(t_msg) == ORIGINAL_MSG) *buf = tipc_link_failover_rcv(l_ptr, t_buf); else diff --git a/net/tipc/msg.c b/net/tipc/msg.c index 35523fb..a38f6a6 100644 --- a/net/tipc/msg.c +++ b/net/tipc/msg.c @@ -34,6 +34,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include #include "core.h" #include "msg.h" #include "addr.h" @@ -214,6 +215,7 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset, skb = tipc_buf_acquire(msz); if (unlikely(!skb)) return -ENOMEM; + skb_orphan(skb); __skb_queue_tail(list, skb); skb_copy_to_linear_data(skb, mhdr, mhsz); pktpos = skb->data + mhsz; @@ -234,6 +236,7 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset, skb = tipc_buf_acquire(pktmax); if (!skb) return -ENOMEM; + skb_orphan(skb); __skb_queue_tail(list, skb); pktpos = skb->data; skb_copy_to_linear_data(skb, &pkthdr, INT_H_SIZE); @@ -267,6 +270,7 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset, rc = -ENOMEM; goto error; } + skb_orphan(skb); __skb_queue_tail(list, skb); msg_set_type(&pkthdr, FRAGMENT); msg_set_size(&pkthdr, pktsz); diff --git a/net/tipc/net.c b/net/tipc/net.c index cf13df3..5ce9d62 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -108,8 +108,9 @@ static const struct nla_policy tipc_nl_net_policy[TIPC_NLA_NET_MAX + 1] = { * - A local spin_lock protecting the queue of subscriber events. */ -int tipc_net_start(u32 addr) +int tipc_net_start(struct net *net, u32 addr) { + struct tipc_net *tn = net_generic(net, tipc_net_id); char addr_string[16]; int res; @@ -125,7 +126,8 @@ int tipc_net_start(u32 addr) pr_info("Started in network mode\n"); pr_info("Own node address %s, network identity %u\n", - tipc_addr_string_fill(addr_string, tipc_own_addr), tipc_net_id); + tipc_addr_string_fill(addr_string, tipc_own_addr), + tn->net_id); return 0; } @@ -144,8 +146,9 @@ void tipc_net_stop(void) pr_info("Left network mode\n"); } -static int __tipc_nl_add_net(struct tipc_nl_msg *msg) +static int __tipc_nl_add_net(struct net *net, struct tipc_nl_msg *msg) { + struct tipc_net *tn = net_generic(net, tipc_net_id); void *hdr; struct nlattr *attrs; @@ -158,7 +161,7 @@ static int __tipc_nl_add_net(struct tipc_nl_msg *msg) if (!attrs) goto msg_full; - if (nla_put_u32(msg->skb, TIPC_NLA_NET_ID, tipc_net_id)) + if (nla_put_u32(msg->skb, TIPC_NLA_NET_ID, tn->net_id)) goto attr_msg_full; nla_nest_end(msg->skb, attrs); @@ -176,6 +179,7 @@ msg_full: int tipc_nl_net_dump(struct sk_buff *skb, struct netlink_callback *cb) { + struct net *net = sock_net(skb->sk); int err; int done = cb->args[0]; struct tipc_nl_msg msg; @@ -187,7 +191,7 @@ int tipc_nl_net_dump(struct sk_buff *skb, struct netlink_callback *cb) msg.portid = NETLINK_CB(cb->skb).portid; msg.seq = cb->nlh->nlmsg_seq; - err = __tipc_nl_add_net(&msg); + err = __tipc_nl_add_net(net, &msg); if (err) goto out; @@ -200,8 +204,10 @@ out: int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info) { - int err; + struct net *net = genl_info_net(info); + struct tipc_net *tn = net_generic(net, tipc_net_id); struct nlattr *attrs[TIPC_NLA_NET_MAX + 1]; + int err; if (!info->attrs[TIPC_NLA_NET]) return -EINVAL; @@ -223,7 +229,7 @@ int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info) if (val < 1 || val > 9999) return -EINVAL; - tipc_net_id = val; + tn->net_id = val; } if (attrs[TIPC_NLA_NET_ADDR]) { @@ -238,7 +244,7 @@ int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info) return -EINVAL; rtnl_lock(); - tipc_net_start(addr); + tipc_net_start(net, addr); rtnl_unlock(); } diff --git a/net/tipc/net.h b/net/tipc/net.h index a81c1b9..2c4812f 100644 --- a/net/tipc/net.h +++ b/net/tipc/net.h @@ -39,7 +39,7 @@ #include -int tipc_net_start(u32 addr); +int tipc_net_start(struct net *net, u32 addr); void tipc_net_stop(void); diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index b891e39..282b596 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -46,6 +46,7 @@ static int handle_cmd(struct sk_buff *skb, struct genl_info *info) { + struct net *net = genl_info_net(info); struct sk_buff *rep_buf; struct nlmsghdr *rep_nlh; struct nlmsghdr *req_nlh = info->nlhdr; @@ -58,10 +59,11 @@ static int handle_cmd(struct sk_buff *skb, struct genl_info *info) else cmd = req_userhdr->cmd; - rep_buf = tipc_cfg_do_cmd(req_userhdr->dest, cmd, - nlmsg_data(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN, - nlmsg_attrlen(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN), - hdr_space); + rep_buf = tipc_cfg_do_cmd(net, req_userhdr->dest, cmd, + nlmsg_data(req_nlh) + GENL_HDRLEN + + TIPC_GENL_HDRLEN, + nlmsg_attrlen(req_nlh, GENL_HDRLEN + + TIPC_GENL_HDRLEN), hdr_space); if (rep_buf) { skb_push(rep_buf, hdr_space); -- cgit v0.10.2 From f2f9800d4955a96d92896841d8ba9b04201deaa1 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Fri, 9 Jan 2015 15:27:05 +0800 Subject: tipc: make tipc node table aware of net namespace Global variables associated with node table are below: - node table list (node_htable) - node hash table list (tipc_node_list) - node table lock (node_list_lock) - node number counter (tipc_num_nodes) - node link number counter (tipc_num_links) To make node table support namespace, above global variables must be moved to tipc_net structure in order to keep secret for different namespaces. As a consequence, these variables are allocated and initialized when namespace is created, and deallocated when namespace is destroyed. After the change, functions associated with these variables have to utilize a namespace pointer to access them. So adding namespace pointer as a parameter of these functions is the major change made in the commit. Signed-off-by: Ying Xue Tested-by: Tero Aho Reviewed-by: Jon Maloy Signed-off-by: David S. Miller diff --git a/net/tipc/addr.h b/net/tipc/addr.h index 60b00ab..3e1f18e 100644 --- a/net/tipc/addr.h +++ b/net/tipc/addr.h @@ -40,6 +40,8 @@ #define TIPC_ZONE_MASK 0xff000000u #define TIPC_CLUSTER_MASK 0xfffff000u +extern u32 tipc_own_addr __read_mostly; + static inline u32 tipc_zone_mask(u32 addr) { return addr & TIPC_ZONE_MASK; diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index f982311..816c0e4 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -232,13 +232,12 @@ static void bclink_retransmit_pkt(u32 after, u32 to) * * Called with no locks taken */ -void tipc_bclink_wakeup_users(void) +void tipc_bclink_wakeup_users(struct net *net) { struct sk_buff *skb; while ((skb = skb_dequeue(&bclink->link.waiting_sks))) - tipc_sk_rcv(skb); - + tipc_sk_rcv(net, skb); } /** @@ -385,9 +384,9 @@ void tipc_bclink_update_link_state(struct net *net, struct tipc_node *n_ptr, * Delay any upcoming NACK by this node if another node has already * requested the first message this node is going to ask for. */ -static void bclink_peek_nack(struct tipc_msg *msg) +static void bclink_peek_nack(struct net *net, struct tipc_msg *msg) { - struct tipc_node *n_ptr = tipc_node_find(msg_destnode(msg)); + struct tipc_node *n_ptr = tipc_node_find(net, msg_destnode(msg)); if (unlikely(!n_ptr)) return; @@ -404,11 +403,12 @@ static void bclink_peek_nack(struct tipc_msg *msg) /* tipc_bclink_xmit - broadcast buffer chain to all nodes in cluster * and to identified node local sockets + * @net: the applicable net namespace * @list: chain of buffers containing message * Consumes the buffer chain, except when returning -ELINKCONG * Returns 0 if success, otherwise errno: -ELINKCONG,-EHOSTUNREACH,-EMSGSIZE */ -int tipc_bclink_xmit(struct sk_buff_head *list) +int tipc_bclink_xmit(struct net *net, struct sk_buff_head *list) { int rc = 0; int bc = 0; @@ -443,7 +443,7 @@ int tipc_bclink_xmit(struct sk_buff_head *list) /* Deliver message clone */ if (likely(!rc)) - tipc_sk_mcast_rcv(skb); + tipc_sk_mcast_rcv(net, skb); else kfree_skb(skb); @@ -491,7 +491,7 @@ void tipc_bclink_rcv(struct net *net, struct sk_buff *buf) if (msg_mc_netid(msg) != tn->net_id) goto exit; - node = tipc_node_find(msg_prevnode(msg)); + node = tipc_node_find(net, msg_prevnode(msg)); if (unlikely(!node)) goto exit; @@ -514,7 +514,7 @@ void tipc_bclink_rcv(struct net *net, struct sk_buff *buf) tipc_bclink_unlock(); } else { tipc_node_unlock(node); - bclink_peek_nack(msg); + bclink_peek_nack(net, msg); } goto exit; } @@ -532,7 +532,7 @@ receive: tipc_bclink_unlock(); tipc_node_unlock(node); if (likely(msg_mcast(msg))) - tipc_sk_mcast_rcv(buf); + tipc_sk_mcast_rcv(net, buf); else kfree_skb(buf); } else if (msg_user(msg) == MSG_BUNDLER) { @@ -542,7 +542,7 @@ receive: bcl->stats.recv_bundled += msg_msgcnt(msg); tipc_bclink_unlock(); tipc_node_unlock(node); - tipc_link_bundle_rcv(buf); + tipc_link_bundle_rcv(net, buf); } else if (msg_user(msg) == MSG_FRAGMENTER) { tipc_buf_append(&node->bclink.reasm_buf, &buf); if (unlikely(!buf && !node->bclink.reasm_buf)) @@ -563,7 +563,7 @@ receive: bclink_accept_pkt(node, seqno); tipc_bclink_unlock(); tipc_node_unlock(node); - tipc_named_rcv(buf); + tipc_named_rcv(net, buf); } else { tipc_bclink_lock(); bclink_accept_pkt(node, seqno); diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h index a5fd224..fd0d17a 100644 --- a/net/tipc/bcast.h +++ b/net/tipc/bcast.h @@ -101,8 +101,8 @@ int tipc_bclink_reset_stats(void); int tipc_bclink_set_queue_limits(u32 limit); void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action); uint tipc_bclink_get_mtu(void); -int tipc_bclink_xmit(struct sk_buff_head *list); -void tipc_bclink_wakeup_users(void); +int tipc_bclink_xmit(struct net *net, struct sk_buff_head *list); +void tipc_bclink_wakeup_users(struct net *net); int tipc_nl_add_bc_link(struct tipc_nl_msg *msg); #endif diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index cdd3033..a735c08 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -69,7 +69,8 @@ static const struct nla_policy tipc_nl_media_policy[TIPC_NLA_MEDIA_MAX + 1] = { struct tipc_bearer __rcu *bearer_list[MAX_BEARERS + 1]; -static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down); +static void bearer_disable(struct net *net, struct tipc_bearer *b_ptr, + bool shutting_down); /** * tipc_media_find - locates specified media object by name @@ -364,7 +365,7 @@ restart: res = tipc_disc_create(net, b_ptr, &b_ptr->bcast_addr); if (res) { - bearer_disable(b_ptr, false); + bearer_disable(net, b_ptr, false); pr_warn("Bearer <%s> rejected, discovery object creation failed\n", name); return -EINVAL; @@ -384,7 +385,7 @@ restart: static int tipc_reset_bearer(struct net *net, struct tipc_bearer *b_ptr) { pr_info("Resetting bearer <%s>\n", b_ptr->name); - tipc_link_reset_list(b_ptr->identity); + tipc_link_reset_list(net, b_ptr->identity); tipc_disc_reset(net, b_ptr); return 0; } @@ -394,14 +395,15 @@ static int tipc_reset_bearer(struct net *net, struct tipc_bearer *b_ptr) * * Note: This routine assumes caller holds RTNL lock. */ -static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down) +static void bearer_disable(struct net *net, struct tipc_bearer *b_ptr, + bool shutting_down) { u32 i; pr_info("Disabling bearer <%s>\n", b_ptr->name); b_ptr->media->disable_media(b_ptr); - tipc_link_delete_list(b_ptr->identity, shutting_down); + tipc_link_delete_list(net, b_ptr->identity, shutting_down); if (b_ptr->link_req) tipc_disc_delete(b_ptr->link_req); @@ -414,7 +416,7 @@ static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down) kfree_rcu(b_ptr, rcu); } -int tipc_disable_bearer(const char *name) +int tipc_disable_bearer(struct net *net, const char *name) { struct tipc_bearer *b_ptr; int res; @@ -424,7 +426,7 @@ int tipc_disable_bearer(const char *name) pr_warn("Attempt to disable unknown bearer <%s>\n", name); res = -EINVAL; } else { - bearer_disable(b_ptr, false); + bearer_disable(net, b_ptr, false); res = 0; } return res; @@ -593,7 +595,7 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt, break; case NETDEV_UNREGISTER: case NETDEV_CHANGENAME: - bearer_disable(b_ptr, false); + bearer_disable(dev_net(dev), b_ptr, false); break; } return NOTIFY_OK; @@ -626,7 +628,7 @@ void tipc_bearer_cleanup(void) dev_remove_pack(&tipc_packet_type); } -void tipc_bearer_stop(void) +void tipc_bearer_stop(struct net *net) { struct tipc_bearer *b_ptr; u32 i; @@ -634,7 +636,7 @@ void tipc_bearer_stop(void) for (i = 0; i < MAX_BEARERS; i++) { b_ptr = rtnl_dereference(bearer_list[i]); if (b_ptr) { - bearer_disable(b_ptr, true); + bearer_disable(net, b_ptr, true); bearer_list[i] = NULL; } } @@ -772,6 +774,7 @@ int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info) char *name; struct tipc_bearer *bearer; struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1]; + struct net *net = genl_info_net(info); if (!info->attrs[TIPC_NLA_BEARER]) return -EINVAL; @@ -794,7 +797,7 @@ int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } - bearer_disable(bearer, false); + bearer_disable(net, bearer, false); rtnl_unlock(); return 0; diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h index 43f683a..91a8eee 100644 --- a/net/tipc/bearer.h +++ b/net/tipc/bearer.h @@ -168,7 +168,7 @@ extern struct tipc_bearer __rcu *bearer_list[]; void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr); int tipc_enable_bearer(struct net *net, const char *bearer_name, u32 disc_domain, u32 priority); -int tipc_disable_bearer(const char *name); +int tipc_disable_bearer(struct net *net, const char *name); /* * Routines made available to TIPC by supported media types @@ -205,7 +205,7 @@ struct tipc_bearer *tipc_bearer_find(const char *name); struct tipc_media *tipc_media_find(const char *name); int tipc_bearer_setup(void); void tipc_bearer_cleanup(void); -void tipc_bearer_stop(void); +void tipc_bearer_stop(struct net *net); void tipc_bearer_send(u32 bearer_id, struct sk_buff *buf, struct tipc_media_addr *dest); diff --git a/net/tipc/config.c b/net/tipc/config.c index 28d4272..cf2d417 100644 --- a/net/tipc/config.c +++ b/net/tipc/config.c @@ -150,12 +150,12 @@ static struct sk_buff *cfg_enable_bearer(struct net *net) return tipc_cfg_reply_none(); } -static struct sk_buff *cfg_disable_bearer(void) +static struct sk_buff *cfg_disable_bearer(struct net *net) { if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_BEARER_NAME)) return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); - if (tipc_disable_bearer((char *)TLV_DATA(req_tlv_area))) + if (tipc_disable_bearer(net, (char *)TLV_DATA(req_tlv_area))) return tipc_cfg_reply_error_string("unable to disable bearer"); return tipc_cfg_reply_none(); @@ -232,16 +232,20 @@ struct sk_buff *tipc_cfg_do_cmd(struct net *net, u32 orig_node, u16 cmd, rep_tlv_buf = tipc_cfg_reply_none(); break; case TIPC_CMD_GET_NODES: - rep_tlv_buf = tipc_node_get_nodes(req_tlv_area, req_tlv_space); + rep_tlv_buf = tipc_node_get_nodes(net, req_tlv_area, + req_tlv_space); break; case TIPC_CMD_GET_LINKS: - rep_tlv_buf = tipc_node_get_links(req_tlv_area, req_tlv_space); + rep_tlv_buf = tipc_node_get_links(net, req_tlv_area, + req_tlv_space); break; case TIPC_CMD_SHOW_LINK_STATS: - rep_tlv_buf = tipc_link_cmd_show_stats(req_tlv_area, req_tlv_space); + rep_tlv_buf = tipc_link_cmd_show_stats(net, req_tlv_area, + req_tlv_space); break; case TIPC_CMD_RESET_LINK_STATS: - rep_tlv_buf = tipc_link_cmd_reset_stats(req_tlv_area, req_tlv_space); + rep_tlv_buf = tipc_link_cmd_reset_stats(net, req_tlv_area, + req_tlv_space); break; case TIPC_CMD_SHOW_NAME_TABLE: rep_tlv_buf = tipc_nametbl_get(req_tlv_area, req_tlv_space); @@ -261,13 +265,14 @@ struct sk_buff *tipc_cfg_do_cmd(struct net *net, u32 orig_node, u16 cmd, case TIPC_CMD_SET_LINK_TOL: case TIPC_CMD_SET_LINK_PRI: case TIPC_CMD_SET_LINK_WINDOW: - rep_tlv_buf = tipc_link_cmd_config(req_tlv_area, req_tlv_space, cmd); + rep_tlv_buf = tipc_link_cmd_config(net, req_tlv_area, + req_tlv_space, cmd); break; case TIPC_CMD_ENABLE_BEARER: rep_tlv_buf = cfg_enable_bearer(net); break; case TIPC_CMD_DISABLE_BEARER: - rep_tlv_buf = cfg_disable_bearer(); + rep_tlv_buf = cfg_disable_bearer(net); break; case TIPC_CMD_SET_NODE_ADDR: rep_tlv_buf = cfg_set_own_addr(net); diff --git a/net/tipc/core.c b/net/tipc/core.c index a230248..7b84439 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -57,12 +57,15 @@ static int __net_init tipc_init_net(struct net *net) struct tipc_net *tn = net_generic(net, tipc_net_id); tn->net_id = 4711; + INIT_LIST_HEAD(&tn->node_list); + spin_lock_init(&tn->node_list_lock); return 0; } static void __net_exit tipc_exit_net(struct net *net) { + tipc_net_stop(net); } static struct pernet_operations tipc_net_ops = { @@ -144,7 +147,6 @@ out_pernet: static void __exit tipc_exit(void) { unregister_pernet_subsys(&tipc_net_ops); - tipc_net_stop(); tipc_bearer_cleanup(); tipc_netlink_stop(); tipc_subscr_stop(); diff --git a/net/tipc/core.h b/net/tipc/core.h index 106e815..4fb1133 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -59,6 +59,8 @@ #include #include +#include "node.h" + #define TIPC_MOD_VER "2.0.0" int tipc_snprintf(char *buf, int len, const char *fmt, ...); @@ -78,6 +80,13 @@ extern int tipc_random __read_mostly; struct tipc_net { int net_id; + + /* Node table and node list */ + spinlock_t node_list_lock; + struct hlist_head node_htable[NODE_HTABLE_SIZE]; + struct list_head node_list; + u32 num_nodes; + u32 num_links; }; #ifdef CONFIG_SYSCTL diff --git a/net/tipc/discover.c b/net/tipc/discover.c index 246a237..f0fd8b4 100644 --- a/net/tipc/discover.c +++ b/net/tipc/discover.c @@ -162,9 +162,9 @@ void tipc_disc_rcv(struct net *net, struct sk_buff *buf, return; /* Locate, or if necessary, create, node: */ - node = tipc_node_find(onode); + node = tipc_node_find(net, onode); if (!node) - node = tipc_node_create(onode); + node = tipc_node_create(net, onode); if (!node) return; diff --git a/net/tipc/link.c b/net/tipc/link.c index 248813c..f650565 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -114,7 +114,8 @@ static void link_reset_statistics(struct tipc_link *l_ptr); static void link_print(struct tipc_link *l_ptr, const char *str); static void tipc_link_sync_xmit(struct tipc_link *l); static void tipc_link_sync_rcv(struct tipc_node *n, struct sk_buff *buf); -static int tipc_link_input(struct tipc_link *l, struct sk_buff *buf); +static int tipc_link_input(struct net *net, struct tipc_link *l, + struct sk_buff *buf); static int tipc_link_prepare_input(struct net *net, struct tipc_link *l, struct sk_buff **buf); @@ -310,13 +311,15 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr, return l_ptr; } -void tipc_link_delete_list(unsigned int bearer_id, bool shutting_down) +void tipc_link_delete_list(struct net *net, unsigned int bearer_id, + bool shutting_down) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_link *l_ptr; struct tipc_node *n_ptr; rcu_read_lock(); - list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) { + list_for_each_entry_rcu(n_ptr, &tn->node_list, list) { tipc_node_lock(n_ptr); l_ptr = n_ptr->links[bearer_id]; if (l_ptr) { @@ -451,13 +454,14 @@ void tipc_link_reset(struct tipc_link *l_ptr) link_reset_statistics(l_ptr); } -void tipc_link_reset_list(unsigned int bearer_id) +void tipc_link_reset_list(struct net *net, unsigned int bearer_id) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_link *l_ptr; struct tipc_node *n_ptr; rcu_read_lock(); - list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) { + list_for_each_entry_rcu(n_ptr, &tn->node_list, list) { tipc_node_lock(n_ptr); l_ptr = n_ptr->links[bearer_id]; if (l_ptr) @@ -773,16 +777,18 @@ static int __tipc_link_xmit_skb(struct tipc_link *link, struct sk_buff *skb) return __tipc_link_xmit(link, &head); } -int tipc_link_xmit_skb(struct sk_buff *skb, u32 dnode, u32 selector) +int tipc_link_xmit_skb(struct net *net, struct sk_buff *skb, u32 dnode, + u32 selector) { struct sk_buff_head head; skb2list(skb, &head); - return tipc_link_xmit(&head, dnode, selector); + return tipc_link_xmit(net, &head, dnode, selector); } /** * tipc_link_xmit() is the general link level function for message sending + * @net: the applicable net namespace * @list: chain of buffers containing message * @dsz: amount of user data to be sent * @dnode: address of destination node @@ -790,13 +796,14 @@ int tipc_link_xmit_skb(struct sk_buff *skb, u32 dnode, u32 selector) * Consumes the buffer chain, except when returning -ELINKCONG * Returns 0 if success, otherwise errno: -ELINKCONG,-EHOSTUNREACH,-EMSGSIZE */ -int tipc_link_xmit(struct sk_buff_head *list, u32 dnode, u32 selector) +int tipc_link_xmit(struct net *net, struct sk_buff_head *list, u32 dnode, + u32 selector) { struct tipc_link *link = NULL; struct tipc_node *node; int rc = -EHOSTUNREACH; - node = tipc_node_find(dnode); + node = tipc_node_find(net, dnode); if (node) { tipc_node_lock(node); link = node->active_links[selector & 1]; @@ -813,7 +820,7 @@ int tipc_link_xmit(struct sk_buff_head *list, u32 dnode, u32 selector) * buffer, we just need to dequeue one SKB buffer from the * head list. */ - return tipc_sk_rcv(__skb_dequeue(list)); + return tipc_sk_rcv(net, __skb_dequeue(list)); } __skb_queue_purge(list); @@ -1066,7 +1073,7 @@ static int link_recv_buf_validate(struct sk_buff *buf) /** * tipc_rcv - process TIPC packets/messages arriving from off-node - * @net: net namespace handler + * @net: the applicable net namespace * @skb: TIPC packet * @b_ptr: pointer to bearer message arrived on * @@ -1112,7 +1119,7 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr) goto discard; /* Locate neighboring node that sent message */ - n_ptr = tipc_node_find(msg_prevnode(msg)); + n_ptr = tipc_node_find(net, msg_prevnode(msg)); if (unlikely(!n_ptr)) goto discard; tipc_node_lock(n_ptr); @@ -1203,7 +1210,7 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr) } tipc_node_unlock(n_ptr); - if (tipc_link_input(l_ptr, skb) != 0) + if (tipc_link_input(net, l_ptr, skb) != 0) goto discard; continue; unlock_discard: @@ -1263,7 +1270,8 @@ static int tipc_link_prepare_input(struct net *net, struct tipc_link *l, /** * tipc_link_input - Deliver message too higher layers */ -static int tipc_link_input(struct tipc_link *l, struct sk_buff *buf) +static int tipc_link_input(struct net *net, struct tipc_link *l, + struct sk_buff *buf) { struct tipc_msg *msg = buf_msg(buf); int res = 0; @@ -1274,13 +1282,13 @@ static int tipc_link_input(struct tipc_link *l, struct sk_buff *buf) case TIPC_HIGH_IMPORTANCE: case TIPC_CRITICAL_IMPORTANCE: case CONN_MANAGER: - tipc_sk_rcv(buf); + tipc_sk_rcv(net, buf); break; case NAME_DISTRIBUTOR: - tipc_named_rcv(buf); + tipc_named_rcv(net, buf); break; case MSG_BUNDLER: - tipc_link_bundle_rcv(buf); + tipc_link_bundle_rcv(net, buf); break; default: res = -EINVAL; @@ -1855,7 +1863,7 @@ exit: /* * Bundler functionality: */ -void tipc_link_bundle_rcv(struct sk_buff *buf) +void tipc_link_bundle_rcv(struct net *net, struct sk_buff *buf) { u32 msgcount = msg_msgcnt(buf_msg(buf)); u32 pos = INT_H_SIZE; @@ -1872,13 +1880,13 @@ void tipc_link_bundle_rcv(struct sk_buff *buf) pos += align(msg_size(omsg)); if (msg_isdata(omsg)) { if (unlikely(msg_type(omsg) == TIPC_MCAST_MSG)) - tipc_sk_mcast_rcv(obuf); + tipc_sk_mcast_rcv(net, obuf); else - tipc_sk_rcv(obuf); + tipc_sk_rcv(net, obuf); } else if (msg_user(omsg) == CONN_MANAGER) { - tipc_sk_rcv(obuf); + tipc_sk_rcv(net, obuf); } else if (msg_user(omsg) == NAME_DISTRIBUTOR) { - tipc_named_rcv(obuf); + tipc_named_rcv(net, obuf); } else { pr_warn("Illegal bundled msg: %u\n", msg_user(omsg)); kfree_skb(obuf); @@ -1919,14 +1927,17 @@ void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window) } /* tipc_link_find_owner - locate owner node of link by link's name + * @net: the applicable net namespace * @name: pointer to link name string * @bearer_id: pointer to index in 'node->links' array where the link was found. * * Returns pointer to node owning the link, or 0 if no matching link is found. */ -static struct tipc_node *tipc_link_find_owner(const char *link_name, +static struct tipc_node *tipc_link_find_owner(struct net *net, + const char *link_name, unsigned int *bearer_id) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_link *l_ptr; struct tipc_node *n_ptr; struct tipc_node *found_node = NULL; @@ -1934,7 +1945,7 @@ static struct tipc_node *tipc_link_find_owner(const char *link_name, *bearer_id = 0; rcu_read_lock(); - list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) { + list_for_each_entry_rcu(n_ptr, &tn->node_list, list) { tipc_node_lock(n_ptr); for (i = 0; i < MAX_BEARERS; i++) { l_ptr = n_ptr->links[i]; @@ -1978,6 +1989,7 @@ static int link_value_is_valid(u16 cmd, u32 new_value) /** * link_cmd_set_value - change priority/tolerance/window for link/bearer/media + * @net: the applicable net namespace * @name: ptr to link, bearer, or media name * @new_value: new value of link, bearer, or media setting * @cmd: which link, bearer, or media attribute to set (TIPC_CMD_SET_LINK_*) @@ -1986,7 +1998,8 @@ static int link_value_is_valid(u16 cmd, u32 new_value) * * Returns 0 if value updated and negative value on error. */ -static int link_cmd_set_value(const char *name, u32 new_value, u16 cmd) +static int link_cmd_set_value(struct net *net, const char *name, u32 new_value, + u16 cmd) { struct tipc_node *node; struct tipc_link *l_ptr; @@ -1995,7 +2008,7 @@ static int link_cmd_set_value(const char *name, u32 new_value, u16 cmd) int bearer_id; int res = 0; - node = tipc_link_find_owner(name, &bearer_id); + node = tipc_link_find_owner(net, name, &bearer_id); if (node) { tipc_node_lock(node); l_ptr = node->links[bearer_id]; @@ -2063,8 +2076,8 @@ static int link_cmd_set_value(const char *name, u32 new_value, u16 cmd) return res; } -struct sk_buff *tipc_link_cmd_config(const void *req_tlv_area, int req_tlv_space, - u16 cmd) +struct sk_buff *tipc_link_cmd_config(struct net *net, const void *req_tlv_area, + int req_tlv_space, u16 cmd) { struct tipc_link_config *args; u32 new_value; @@ -2088,7 +2101,7 @@ struct sk_buff *tipc_link_cmd_config(const void *req_tlv_area, int req_tlv_space " (cannot change setting on broadcast link)"); } - res = link_cmd_set_value(args->name, new_value, cmd); + res = link_cmd_set_value(net, args->name, new_value, cmd); if (res) return tipc_cfg_reply_error_string("cannot change link setting"); @@ -2106,7 +2119,9 @@ static void link_reset_statistics(struct tipc_link *l_ptr) l_ptr->stats.recv_info = l_ptr->next_in_no; } -struct sk_buff *tipc_link_cmd_reset_stats(const void *req_tlv_area, int req_tlv_space) +struct sk_buff *tipc_link_cmd_reset_stats(struct net *net, + const void *req_tlv_area, + int req_tlv_space) { char *link_name; struct tipc_link *l_ptr; @@ -2122,7 +2137,7 @@ struct sk_buff *tipc_link_cmd_reset_stats(const void *req_tlv_area, int req_tlv_ return tipc_cfg_reply_error_string("link not found"); return tipc_cfg_reply_none(); } - node = tipc_link_find_owner(link_name, &bearer_id); + node = tipc_link_find_owner(net, link_name, &bearer_id); if (!node) return tipc_cfg_reply_error_string("link not found"); @@ -2147,13 +2162,15 @@ static u32 percent(u32 count, u32 total) /** * tipc_link_stats - print link statistics + * @net: the applicable net namespace * @name: link name * @buf: print buffer area * @buf_size: size of print buffer area * * Returns length of print buffer data string (or 0 if error) */ -static int tipc_link_stats(const char *name, char *buf, const u32 buf_size) +static int tipc_link_stats(struct net *net, const char *name, char *buf, + const u32 buf_size) { struct tipc_link *l; struct tipc_stats *s; @@ -2166,7 +2183,7 @@ static int tipc_link_stats(const char *name, char *buf, const u32 buf_size) if (!strcmp(name, tipc_bclink_name)) return tipc_bclink_stats(buf, buf_size); - node = tipc_link_find_owner(name, &bearer_id); + node = tipc_link_find_owner(net, name, &bearer_id); if (!node) return 0; @@ -2243,7 +2260,9 @@ static int tipc_link_stats(const char *name, char *buf, const u32 buf_size) return ret; } -struct sk_buff *tipc_link_cmd_show_stats(const void *req_tlv_area, int req_tlv_space) +struct sk_buff *tipc_link_cmd_show_stats(struct net *net, + const void *req_tlv_area, + int req_tlv_space) { struct sk_buff *buf; struct tlv_desc *rep_tlv; @@ -2261,7 +2280,7 @@ struct sk_buff *tipc_link_cmd_show_stats(const void *req_tlv_area, int req_tlv_s rep_tlv = (struct tlv_desc *)buf->data; pb = TLV_DATA(rep_tlv); pb_len = ULTRA_STRING_MAX_LEN; - str_len = tipc_link_stats((char *)TLV_DATA(req_tlv_area), + str_len = tipc_link_stats(net, (char *)TLV_DATA(req_tlv_area), pb, pb_len); if (!str_len) { kfree_skb(buf); @@ -2343,6 +2362,7 @@ int tipc_nl_link_set(struct sk_buff *skb, struct genl_info *info) struct tipc_link *link; struct tipc_node *node; struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1]; + struct net *net = genl_info_net(info); if (!info->attrs[TIPC_NLA_LINK]) return -EINVAL; @@ -2358,7 +2378,7 @@ int tipc_nl_link_set(struct sk_buff *skb, struct genl_info *info) name = nla_data(attrs[TIPC_NLA_LINK_NAME]); - node = tipc_link_find_owner(name, &bearer_id); + node = tipc_link_find_owner(net, name, &bearer_id); if (!node) return -EINVAL; @@ -2567,6 +2587,8 @@ static int __tipc_nl_add_node_links(struct tipc_nl_msg *msg, int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb) { + struct net *net = sock_net(skb->sk); + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_node *node; struct tipc_nl_msg msg; u32 prev_node = cb->args[0]; @@ -2584,7 +2606,7 @@ int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb) rcu_read_lock(); if (prev_node) { - node = tipc_node_find(prev_node); + node = tipc_node_find(net, prev_node); if (!node) { /* We never set seq or call nl_dump_check_consistent() * this means that setting prev_seq here will cause the @@ -2596,7 +2618,8 @@ int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb) goto out; } - list_for_each_entry_continue_rcu(node, &tipc_node_list, list) { + list_for_each_entry_continue_rcu(node, &tn->node_list, + list) { tipc_node_lock(node); err = __tipc_nl_add_node_links(&msg, node, &prev_link); tipc_node_unlock(node); @@ -2610,7 +2633,7 @@ int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb) if (err) goto out; - list_for_each_entry_rcu(node, &tipc_node_list, list) { + list_for_each_entry_rcu(node, &tn->node_list, list) { tipc_node_lock(node); err = __tipc_nl_add_node_links(&msg, node, &prev_link); tipc_node_unlock(node); @@ -2633,6 +2656,7 @@ out: int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info) { + struct net *net = genl_info_net(info); struct sk_buff *ans_skb; struct tipc_nl_msg msg; struct tipc_link *link; @@ -2645,7 +2669,7 @@ int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info) return -EINVAL; name = nla_data(info->attrs[TIPC_NLA_LINK_NAME]); - node = tipc_link_find_owner(name, &bearer_id); + node = tipc_link_find_owner(net, name, &bearer_id); if (!node) return -EINVAL; @@ -2687,6 +2711,7 @@ int tipc_nl_link_reset_stats(struct sk_buff *skb, struct genl_info *info) struct tipc_link *link; struct tipc_node *node; struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1]; + struct net *net = genl_info_net(info); if (!info->attrs[TIPC_NLA_LINK]) return -EINVAL; @@ -2709,7 +2734,7 @@ int tipc_nl_link_reset_stats(struct sk_buff *skb, struct genl_info *info) return 0; } - node = tipc_link_find_owner(link_name, &bearer_id); + node = tipc_link_find_owner(net, link_name, &bearer_id); if (!node) return -EINVAL; diff --git a/net/tipc/link.h b/net/tipc/link.h index 692268d..380e27e 100644 --- a/net/tipc/link.h +++ b/net/tipc/link.h @@ -200,27 +200,31 @@ struct tipc_port; struct tipc_link *tipc_link_create(struct tipc_node *n_ptr, struct tipc_bearer *b_ptr, const struct tipc_media_addr *media_addr); -void tipc_link_delete_list(unsigned int bearer_id, bool shutting_down); +void tipc_link_delete_list(struct net *net, unsigned int bearer_id, + bool shutting_down); void tipc_link_failover_send_queue(struct tipc_link *l_ptr); void tipc_link_dup_queue_xmit(struct tipc_link *l_ptr, struct tipc_link *dest); void tipc_link_reset_fragments(struct tipc_link *l_ptr); int tipc_link_is_up(struct tipc_link *l_ptr); int tipc_link_is_active(struct tipc_link *l_ptr); void tipc_link_purge_queues(struct tipc_link *l_ptr); -struct sk_buff *tipc_link_cmd_config(const void *req_tlv_area, - int req_tlv_space, - u16 cmd); -struct sk_buff *tipc_link_cmd_show_stats(const void *req_tlv_area, +struct sk_buff *tipc_link_cmd_config(struct net *net, const void *req_tlv_area, + int req_tlv_space, u16 cmd); +struct sk_buff *tipc_link_cmd_show_stats(struct net *net, + const void *req_tlv_area, int req_tlv_space); -struct sk_buff *tipc_link_cmd_reset_stats(const void *req_tlv_area, +struct sk_buff *tipc_link_cmd_reset_stats(struct net *net, + const void *req_tlv_area, int req_tlv_space); void tipc_link_reset_all(struct tipc_node *node); void tipc_link_reset(struct tipc_link *l_ptr); -void tipc_link_reset_list(unsigned int bearer_id); -int tipc_link_xmit_skb(struct sk_buff *skb, u32 dest, u32 selector); -int tipc_link_xmit(struct sk_buff_head *list, u32 dest, u32 selector); +void tipc_link_reset_list(struct net *net, unsigned int bearer_id); +int tipc_link_xmit_skb(struct net *net, struct sk_buff *skb, u32 dest, + u32 selector); +int tipc_link_xmit(struct net *net, struct sk_buff_head *list, u32 dest, + u32 selector); int __tipc_link_xmit(struct tipc_link *link, struct sk_buff_head *list); -void tipc_link_bundle_rcv(struct sk_buff *buf); +void tipc_link_bundle_rcv(struct net *net, struct sk_buff *buf); void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int prob, u32 gap, u32 tolerance, u32 priority, u32 acked_mtu); void tipc_link_push_packets(struct tipc_link *l_ptr); diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c index ba6083d..d40df58 100644 --- a/net/tipc/name_distr.c +++ b/net/tipc/name_distr.c @@ -81,14 +81,15 @@ static struct sk_buff *named_prepare_buf(u32 type, u32 size, u32 dest) return buf; } -void named_cluster_distribute(struct sk_buff *skb) +void named_cluster_distribute(struct net *net, struct sk_buff *skb) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct sk_buff *oskb; struct tipc_node *node; u32 dnode; rcu_read_lock(); - list_for_each_entry_rcu(node, &tipc_node_list, list) { + list_for_each_entry_rcu(node, &tn->node_list, list) { dnode = node->addr; if (in_own_node(dnode)) continue; @@ -98,7 +99,7 @@ void named_cluster_distribute(struct sk_buff *skb) if (!oskb) break; msg_set_destnode(buf_msg(oskb), dnode); - tipc_link_xmit_skb(oskb, dnode, dnode); + tipc_link_xmit_skb(net, oskb, dnode, dnode); } rcu_read_unlock(); @@ -160,13 +161,14 @@ struct sk_buff *tipc_named_withdraw(struct publication *publ) * @dnode: node to be updated * @pls: linked list of publication items to be packed into buffer chain */ -static void named_distribute(struct sk_buff_head *list, u32 dnode, - struct list_head *pls) +static void named_distribute(struct net *net, struct sk_buff_head *list, + u32 dnode, struct list_head *pls) { struct publication *publ; struct sk_buff *skb = NULL; struct distr_item *item = NULL; - uint msg_dsz = (tipc_node_get_mtu(dnode, 0) / ITEM_SIZE) * ITEM_SIZE; + uint msg_dsz = (tipc_node_get_mtu(net, dnode, 0) / ITEM_SIZE) * + ITEM_SIZE; uint msg_rem = msg_dsz; list_for_each_entry(publ, pls, local_list) { @@ -202,30 +204,31 @@ static void named_distribute(struct sk_buff_head *list, u32 dnode, /** * tipc_named_node_up - tell specified node about all publications by this node */ -void tipc_named_node_up(u32 dnode) +void tipc_named_node_up(struct net *net, u32 dnode) { struct sk_buff_head head; __skb_queue_head_init(&head); rcu_read_lock(); - named_distribute(&head, dnode, + named_distribute(net, &head, dnode, &tipc_nametbl->publ_list[TIPC_CLUSTER_SCOPE]); - named_distribute(&head, dnode, + named_distribute(net, &head, dnode, &tipc_nametbl->publ_list[TIPC_ZONE_SCOPE]); rcu_read_unlock(); - tipc_link_xmit(&head, dnode, dnode); + tipc_link_xmit(net, &head, dnode, dnode); } -static void tipc_publ_subscribe(struct publication *publ, u32 addr) +static void tipc_publ_subscribe(struct net *net, struct publication *publ, + u32 addr) { struct tipc_node *node; if (in_own_node(addr)) return; - node = tipc_node_find(addr); + node = tipc_node_find(net, addr); if (!node) { pr_warn("Node subscription rejected, unknown node 0x%x\n", addr); @@ -237,11 +240,12 @@ static void tipc_publ_subscribe(struct publication *publ, u32 addr) tipc_node_unlock(node); } -static void tipc_publ_unsubscribe(struct publication *publ, u32 addr) +static void tipc_publ_unsubscribe(struct net *net, struct publication *publ, + u32 addr) { struct tipc_node *node; - node = tipc_node_find(addr); + node = tipc_node_find(net, addr); if (!node) return; @@ -256,7 +260,7 @@ static void tipc_publ_unsubscribe(struct publication *publ, u32 addr) * Invoked for each publication issued by a newly failed node. * Removes publication structure from name table & deletes it. */ -static void tipc_publ_purge(struct publication *publ, u32 addr) +static void tipc_publ_purge(struct net *net, struct publication *publ, u32 addr) { struct publication *p; @@ -264,7 +268,7 @@ static void tipc_publ_purge(struct publication *publ, u32 addr) p = tipc_nametbl_remove_publ(publ->type, publ->lower, publ->node, publ->ref, publ->key); if (p) - tipc_publ_unsubscribe(p, addr); + tipc_publ_unsubscribe(net, p, addr); spin_unlock_bh(&tipc_nametbl_lock); if (p != publ) { @@ -277,12 +281,12 @@ static void tipc_publ_purge(struct publication *publ, u32 addr) kfree_rcu(p, rcu); } -void tipc_publ_notify(struct list_head *nsub_list, u32 addr) +void tipc_publ_notify(struct net *net, struct list_head *nsub_list, u32 addr) { struct publication *publ, *tmp; list_for_each_entry_safe(publ, tmp, nsub_list, nodesub_list) - tipc_publ_purge(publ, addr); + tipc_publ_purge(net, publ, addr); } /** @@ -292,7 +296,8 @@ void tipc_publ_notify(struct list_head *nsub_list, u32 addr) * tipc_nametbl_lock must be held. * Returns the publication item if successful, otherwise NULL. */ -static bool tipc_update_nametbl(struct distr_item *i, u32 node, u32 dtype) +static bool tipc_update_nametbl(struct net *net, struct distr_item *i, + u32 node, u32 dtype) { struct publication *publ = NULL; @@ -302,7 +307,7 @@ static bool tipc_update_nametbl(struct distr_item *i, u32 node, u32 dtype) TIPC_CLUSTER_SCOPE, node, ntohl(i->ref), ntohl(i->key)); if (publ) { - tipc_publ_subscribe(publ, node); + tipc_publ_subscribe(net, publ, node); return true; } } else if (dtype == WITHDRAWAL) { @@ -310,7 +315,7 @@ static bool tipc_update_nametbl(struct distr_item *i, u32 node, u32 dtype) node, ntohl(i->ref), ntohl(i->key)); if (publ) { - tipc_publ_unsubscribe(publ, node); + tipc_publ_unsubscribe(net, publ, node); kfree_rcu(publ, rcu); return true; } @@ -343,7 +348,7 @@ static void tipc_named_add_backlog(struct distr_item *i, u32 type, u32 node) * tipc_named_process_backlog - try to process any pending name table updates * from the network. */ -void tipc_named_process_backlog(void) +void tipc_named_process_backlog(struct net *net) { struct distr_queue_item *e, *tmp; char addr[16]; @@ -351,7 +356,7 @@ void tipc_named_process_backlog(void) list_for_each_entry_safe(e, tmp, &tipc_dist_queue, next) { if (time_after(e->expires, now)) { - if (!tipc_update_nametbl(&e->i, e->node, e->dtype)) + if (!tipc_update_nametbl(net, &e->i, e->node, e->dtype)) continue; } else { tipc_addr_string_fill(addr, e->node); @@ -369,7 +374,7 @@ void tipc_named_process_backlog(void) /** * tipc_named_rcv - process name table update message sent by another node */ -void tipc_named_rcv(struct sk_buff *buf) +void tipc_named_rcv(struct net *net, struct sk_buff *buf) { struct tipc_msg *msg = buf_msg(buf); struct distr_item *item = (struct distr_item *)msg_data(msg); @@ -378,11 +383,11 @@ void tipc_named_rcv(struct sk_buff *buf) spin_lock_bh(&tipc_nametbl_lock); while (count--) { - if (!tipc_update_nametbl(item, node, msg_type(msg))) + if (!tipc_update_nametbl(net, item, node, msg_type(msg))) tipc_named_add_backlog(item, msg_type(msg), node); item++; } - tipc_named_process_backlog(); + tipc_named_process_backlog(net); spin_unlock_bh(&tipc_nametbl_lock); kfree_skb(buf); } diff --git a/net/tipc/name_distr.h b/net/tipc/name_distr.h index cef55ce..8039d84 100644 --- a/net/tipc/name_distr.h +++ b/net/tipc/name_distr.h @@ -69,11 +69,11 @@ struct distr_item { struct sk_buff *tipc_named_publish(struct publication *publ); struct sk_buff *tipc_named_withdraw(struct publication *publ); -void named_cluster_distribute(struct sk_buff *buf); -void tipc_named_node_up(u32 dnode); -void tipc_named_rcv(struct sk_buff *buf); +void named_cluster_distribute(struct net *net, struct sk_buff *buf); +void tipc_named_node_up(struct net *net, u32 dnode); +void tipc_named_rcv(struct net *net, struct sk_buff *buf); void tipc_named_reinit(void); -void tipc_named_process_backlog(void); -void tipc_publ_notify(struct list_head *nsub_list, u32 addr); +void tipc_named_process_backlog(struct net *net); +void tipc_publ_notify(struct net *net, struct list_head *nsub_list, u32 addr); #endif diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index c8df022..cf17790 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -650,8 +650,9 @@ exit: /* * tipc_nametbl_publish - add name publication to network name tables */ -struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper, - u32 scope, u32 port_ref, u32 key) +struct publication *tipc_nametbl_publish(struct net *net, u32 type, u32 lower, + u32 upper, u32 scope, u32 port_ref, + u32 key) { struct publication *publ; struct sk_buff *buf = NULL; @@ -670,19 +671,20 @@ struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper, tipc_nametbl->local_publ_count++; buf = tipc_named_publish(publ); /* Any pending external events? */ - tipc_named_process_backlog(); + tipc_named_process_backlog(net); } spin_unlock_bh(&tipc_nametbl_lock); if (buf) - named_cluster_distribute(buf); + named_cluster_distribute(net, buf); return publ; } /** * tipc_nametbl_withdraw - withdraw name publication from network name tables */ -int tipc_nametbl_withdraw(u32 type, u32 lower, u32 ref, u32 key) +int tipc_nametbl_withdraw(struct net *net, u32 type, u32 lower, u32 ref, + u32 key) { struct publication *publ; struct sk_buff *skb = NULL; @@ -693,7 +695,7 @@ int tipc_nametbl_withdraw(u32 type, u32 lower, u32 ref, u32 key) tipc_nametbl->local_publ_count--; skb = tipc_named_withdraw(publ); /* Any pending external events? */ - tipc_named_process_backlog(); + tipc_named_process_backlog(net); list_del_init(&publ->pport_list); kfree_rcu(publ, rcu); } else { @@ -704,7 +706,7 @@ int tipc_nametbl_withdraw(u32 type, u32 lower, u32 ref, u32 key) spin_unlock_bh(&tipc_nametbl_lock); if (skb) { - named_cluster_distribute(skb); + named_cluster_distribute(net, skb); return 1; } return 0; diff --git a/net/tipc/name_table.h b/net/tipc/name_table.h index 5f0dee9..efccaca 100644 --- a/net/tipc/name_table.h +++ b/net/tipc/name_table.h @@ -104,9 +104,11 @@ struct sk_buff *tipc_nametbl_get(const void *req_tlv_area, int req_tlv_space); u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *node); int tipc_nametbl_mc_translate(u32 type, u32 lower, u32 upper, u32 limit, struct tipc_port_list *dports); -struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper, - u32 scope, u32 port_ref, u32 key); -int tipc_nametbl_withdraw(u32 type, u32 lower, u32 ref, u32 key); +struct publication *tipc_nametbl_publish(struct net *net, u32 type, u32 lower, + u32 upper, u32 scope, u32 port_ref, + u32 key); +int tipc_nametbl_withdraw(struct net *net, u32 type, u32 lower, u32 ref, + u32 key); struct publication *tipc_nametbl_insert_publ(u32 type, u32 lower, u32 upper, u32 scope, u32 node, u32 ref, u32 key); diff --git a/net/tipc/net.c b/net/tipc/net.c index 5ce9d62..de18aac 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -121,7 +121,7 @@ int tipc_net_start(struct net *net, u32 addr) if (res) return res; - tipc_nametbl_publish(TIPC_CFG_SRV, tipc_own_addr, tipc_own_addr, + tipc_nametbl_publish(net, TIPC_CFG_SRV, tipc_own_addr, tipc_own_addr, TIPC_ZONE_SCOPE, 0, tipc_own_addr); pr_info("Started in network mode\n"); @@ -131,16 +131,17 @@ int tipc_net_start(struct net *net, u32 addr) return 0; } -void tipc_net_stop(void) +void tipc_net_stop(struct net *net) { if (!tipc_own_addr) return; - tipc_nametbl_withdraw(TIPC_CFG_SRV, tipc_own_addr, 0, tipc_own_addr); + tipc_nametbl_withdraw(net, TIPC_CFG_SRV, tipc_own_addr, 0, + tipc_own_addr); rtnl_lock(); - tipc_bearer_stop(); + tipc_bearer_stop(net); tipc_bclink_stop(); - tipc_node_stop(); + tipc_node_stop(net); rtnl_unlock(); pr_info("Left network mode\n"); diff --git a/net/tipc/net.h b/net/tipc/net.h index 2c4812f..77a7a11 100644 --- a/net/tipc/net.h +++ b/net/tipc/net.h @@ -41,7 +41,7 @@ int tipc_net_start(struct net *net, u32 addr); -void tipc_net_stop(void); +void tipc_net_stop(struct net *net); int tipc_nl_net_dump(struct sk_buff *skb, struct netlink_callback *cb); int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info); diff --git a/net/tipc/node.c b/net/tipc/node.c index 8d353ec..a0ca1ac 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -40,17 +40,9 @@ #include "name_distr.h" #include "socket.h" -#define NODE_HTABLE_SIZE 512 - static void node_lost_contact(struct tipc_node *n_ptr); static void node_established_contact(struct tipc_node *n_ptr); -static struct hlist_head node_htable[NODE_HTABLE_SIZE]; -LIST_HEAD(tipc_node_list); -static u32 tipc_num_nodes; -static u32 tipc_num_links; -static DEFINE_SPINLOCK(node_list_lock); - struct tipc_sock_conn { u32 port; u32 peer_port; @@ -78,15 +70,17 @@ static unsigned int tipc_hashfn(u32 addr) /* * tipc_node_find - locate specified node object, if it exists */ -struct tipc_node *tipc_node_find(u32 addr) +struct tipc_node *tipc_node_find(struct net *net, u32 addr) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_node *node; if (unlikely(!in_own_cluster_exact(addr))) return NULL; rcu_read_lock(); - hlist_for_each_entry_rcu(node, &node_htable[tipc_hashfn(addr)], hash) { + hlist_for_each_entry_rcu(node, &tn->node_htable[tipc_hashfn(addr)], + hash) { if (node->addr == addr) { rcu_read_unlock(); return node; @@ -96,20 +90,22 @@ struct tipc_node *tipc_node_find(u32 addr) return NULL; } -struct tipc_node *tipc_node_create(u32 addr) +struct tipc_node *tipc_node_create(struct net *net, u32 addr) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_node *n_ptr, *temp_node; - spin_lock_bh(&node_list_lock); + spin_lock_bh(&tn->node_list_lock); n_ptr = kzalloc(sizeof(*n_ptr), GFP_ATOMIC); if (!n_ptr) { - spin_unlock_bh(&node_list_lock); + spin_unlock_bh(&tn->node_list_lock); pr_warn("Node creation failed, no memory\n"); return NULL; } n_ptr->addr = addr; + n_ptr->net = net; spin_lock_init(&n_ptr->lock); INIT_HLIST_NODE(&n_ptr->hash); INIT_LIST_HEAD(&n_ptr->list); @@ -118,9 +114,9 @@ struct tipc_node *tipc_node_create(u32 addr) skb_queue_head_init(&n_ptr->waiting_sks); __skb_queue_head_init(&n_ptr->bclink.deferred_queue); - hlist_add_head_rcu(&n_ptr->hash, &node_htable[tipc_hashfn(addr)]); + hlist_add_head_rcu(&n_ptr->hash, &tn->node_htable[tipc_hashfn(addr)]); - list_for_each_entry_rcu(temp_node, &tipc_node_list, list) { + list_for_each_entry_rcu(temp_node, &tn->node_list, list) { if (n_ptr->addr < temp_node->addr) break; } @@ -128,32 +124,33 @@ struct tipc_node *tipc_node_create(u32 addr) n_ptr->action_flags = TIPC_WAIT_PEER_LINKS_DOWN; n_ptr->signature = INVALID_NODE_SIG; - tipc_num_nodes++; + tn->num_nodes++; - spin_unlock_bh(&node_list_lock); + spin_unlock_bh(&tn->node_list_lock); return n_ptr; } -static void tipc_node_delete(struct tipc_node *n_ptr) +static void tipc_node_delete(struct tipc_net *tn, struct tipc_node *n_ptr) { list_del_rcu(&n_ptr->list); hlist_del_rcu(&n_ptr->hash); kfree_rcu(n_ptr, rcu); - tipc_num_nodes--; + tn->num_nodes--; } -void tipc_node_stop(void) +void tipc_node_stop(struct net *net) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_node *node, *t_node; - spin_lock_bh(&node_list_lock); - list_for_each_entry_safe(node, t_node, &tipc_node_list, list) - tipc_node_delete(node); - spin_unlock_bh(&node_list_lock); + spin_lock_bh(&tn->node_list_lock); + list_for_each_entry_safe(node, t_node, &tn->node_list, list) + tipc_node_delete(tn, node); + spin_unlock_bh(&tn->node_list_lock); } -int tipc_node_add_conn(u32 dnode, u32 port, u32 peer_port) +int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port) { struct tipc_node *node; struct tipc_sock_conn *conn; @@ -161,7 +158,7 @@ int tipc_node_add_conn(u32 dnode, u32 port, u32 peer_port) if (in_own_node(dnode)) return 0; - node = tipc_node_find(dnode); + node = tipc_node_find(net, dnode); if (!node) { pr_warn("Connecting sock to node 0x%x failed\n", dnode); return -EHOSTUNREACH; @@ -179,7 +176,7 @@ int tipc_node_add_conn(u32 dnode, u32 port, u32 peer_port) return 0; } -void tipc_node_remove_conn(u32 dnode, u32 port) +void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port) { struct tipc_node *node; struct tipc_sock_conn *conn, *safe; @@ -187,7 +184,7 @@ void tipc_node_remove_conn(u32 dnode, u32 port) if (in_own_node(dnode)) return; - node = tipc_node_find(dnode); + node = tipc_node_find(net, dnode); if (!node) return; @@ -201,7 +198,7 @@ void tipc_node_remove_conn(u32 dnode, u32 port) tipc_node_unlock(node); } -void tipc_node_abort_sock_conns(struct list_head *conns) +void tipc_node_abort_sock_conns(struct net *net, struct list_head *conns) { struct tipc_sock_conn *conn, *safe; struct sk_buff *buf; @@ -212,7 +209,7 @@ void tipc_node_abort_sock_conns(struct list_head *conns) conn->peer_node, conn->port, conn->peer_port, TIPC_ERR_NO_NODE); if (likely(buf)) - tipc_sk_rcv(buf); + tipc_sk_rcv(net, buf); list_del(&conn->list); kfree(conn); } @@ -342,24 +339,27 @@ int tipc_node_is_up(struct tipc_node *n_ptr) void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr) { + struct tipc_net *tn = net_generic(n_ptr->net, tipc_net_id); + n_ptr->links[l_ptr->bearer_id] = l_ptr; - spin_lock_bh(&node_list_lock); - tipc_num_links++; - spin_unlock_bh(&node_list_lock); + spin_lock_bh(&tn->node_list_lock); + tn->num_links++; + spin_unlock_bh(&tn->node_list_lock); n_ptr->link_cnt++; } void tipc_node_detach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr) { + struct tipc_net *tn = net_generic(n_ptr->net, tipc_net_id); int i; for (i = 0; i < MAX_BEARERS; i++) { if (l_ptr != n_ptr->links[i]) continue; n_ptr->links[i] = NULL; - spin_lock_bh(&node_list_lock); - tipc_num_links--; - spin_unlock_bh(&node_list_lock); + spin_lock_bh(&tn->node_list_lock); + tn->num_links--; + spin_unlock_bh(&tn->node_list_lock); n_ptr->link_cnt--; } } @@ -414,8 +414,10 @@ static void node_lost_contact(struct tipc_node *n_ptr) TIPC_NOTIFY_NODE_DOWN; } -struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space) +struct sk_buff *tipc_node_get_nodes(struct net *net, const void *req_tlv_area, + int req_tlv_space) { + struct tipc_net *tn = net_generic(net, tipc_net_id); u32 domain; struct sk_buff *buf; struct tipc_node *n_ptr; @@ -430,20 +432,20 @@ struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space) return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE " (network address)"); - spin_lock_bh(&node_list_lock); - if (!tipc_num_nodes) { - spin_unlock_bh(&node_list_lock); + spin_lock_bh(&tn->node_list_lock); + if (!tn->num_nodes) { + spin_unlock_bh(&tn->node_list_lock); return tipc_cfg_reply_none(); } /* For now, get space for all other nodes */ - payload_size = TLV_SPACE(sizeof(node_info)) * tipc_num_nodes; + payload_size = TLV_SPACE(sizeof(node_info)) * tn->num_nodes; if (payload_size > 32768u) { - spin_unlock_bh(&node_list_lock); + spin_unlock_bh(&tn->node_list_lock); return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED " (too many nodes)"); } - spin_unlock_bh(&node_list_lock); + spin_unlock_bh(&tn->node_list_lock); buf = tipc_cfg_reply_alloc(payload_size); if (!buf) @@ -451,7 +453,7 @@ struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space) /* Add TLVs for all nodes in scope */ rcu_read_lock(); - list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) { + list_for_each_entry_rcu(n_ptr, &tn->node_list, list) { if (!tipc_in_scope(domain, n_ptr->addr)) continue; node_info.addr = htonl(n_ptr->addr); @@ -463,8 +465,10 @@ struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space) return buf; } -struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space) +struct sk_buff *tipc_node_get_links(struct net *net, const void *req_tlv_area, + int req_tlv_space) { + struct tipc_net *tn = net_generic(net, tipc_net_id); u32 domain; struct sk_buff *buf; struct tipc_node *n_ptr; @@ -482,15 +486,15 @@ struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space) if (!tipc_own_addr) return tipc_cfg_reply_none(); - spin_lock_bh(&node_list_lock); + spin_lock_bh(&tn->node_list_lock); /* Get space for all unicast links + broadcast link */ - payload_size = TLV_SPACE((sizeof(link_info)) * (tipc_num_links + 1)); + payload_size = TLV_SPACE((sizeof(link_info)) * (tn->num_links + 1)); if (payload_size > 32768u) { - spin_unlock_bh(&node_list_lock); + spin_unlock_bh(&tn->node_list_lock); return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED " (too many links)"); } - spin_unlock_bh(&node_list_lock); + spin_unlock_bh(&tn->node_list_lock); buf = tipc_cfg_reply_alloc(payload_size); if (!buf) @@ -504,7 +508,7 @@ struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space) /* Add TLVs for any other links in scope */ rcu_read_lock(); - list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) { + list_for_each_entry_rcu(n_ptr, &tn->node_list, list) { u32 i; if (!tipc_in_scope(domain, n_ptr->addr)) @@ -534,10 +538,11 @@ struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space) * * Returns 0 on success */ -int tipc_node_get_linkname(u32 bearer_id, u32 addr, char *linkname, size_t len) +int tipc_node_get_linkname(struct net *net, u32 bearer_id, u32 addr, + char *linkname, size_t len) { struct tipc_link *link; - struct tipc_node *node = tipc_node_find(addr); + struct tipc_node *node = tipc_node_find(net, addr); if ((bearer_id >= MAX_BEARERS) || !node) return -EINVAL; @@ -554,6 +559,7 @@ int tipc_node_get_linkname(u32 bearer_id, u32 addr, char *linkname, size_t len) void tipc_node_unlock(struct tipc_node *node) { + struct net *net = node->net; LIST_HEAD(nsub_list); LIST_HEAD(conn_sks); struct sk_buff_head waiting_sks; @@ -585,26 +591,26 @@ void tipc_node_unlock(struct tipc_node *node) spin_unlock_bh(&node->lock); while (!skb_queue_empty(&waiting_sks)) - tipc_sk_rcv(__skb_dequeue(&waiting_sks)); + tipc_sk_rcv(net, __skb_dequeue(&waiting_sks)); if (!list_empty(&conn_sks)) - tipc_node_abort_sock_conns(&conn_sks); + tipc_node_abort_sock_conns(net, &conn_sks); if (!list_empty(&nsub_list)) - tipc_publ_notify(&nsub_list, addr); + tipc_publ_notify(net, &nsub_list, addr); if (flags & TIPC_WAKEUP_BCAST_USERS) - tipc_bclink_wakeup_users(); + tipc_bclink_wakeup_users(net); if (flags & TIPC_NOTIFY_NODE_UP) - tipc_named_node_up(addr); + tipc_named_node_up(net, addr); if (flags & TIPC_NOTIFY_LINK_UP) - tipc_nametbl_publish(TIPC_LINK_STATE, addr, addr, + tipc_nametbl_publish(net, TIPC_LINK_STATE, addr, addr, TIPC_NODE_SCOPE, link_id, addr); if (flags & TIPC_NOTIFY_LINK_DOWN) - tipc_nametbl_withdraw(TIPC_LINK_STATE, addr, + tipc_nametbl_withdraw(net, TIPC_LINK_STATE, addr, link_id, addr); } @@ -645,6 +651,8 @@ msg_full: int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb) { int err; + struct net *net = sock_net(skb->sk); + struct tipc_net *tn = net_generic(net, tipc_net_id); int done = cb->args[0]; int last_addr = cb->args[1]; struct tipc_node *node; @@ -659,7 +667,7 @@ int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb) rcu_read_lock(); - if (last_addr && !tipc_node_find(last_addr)) { + if (last_addr && !tipc_node_find(net, last_addr)) { rcu_read_unlock(); /* We never set seq or call nl_dump_check_consistent() this * means that setting prev_seq here will cause the consistence @@ -671,7 +679,7 @@ int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb) return -EPIPE; } - list_for_each_entry_rcu(node, &tipc_node_list, list) { + list_for_each_entry_rcu(node, &tn->node_list, list) { if (last_addr) { if (node->addr == last_addr) last_addr = 0; diff --git a/net/tipc/node.h b/net/tipc/node.h index cbe0e95..43ef88e 100644 --- a/net/tipc/node.h +++ b/net/tipc/node.h @@ -42,10 +42,10 @@ #include "bearer.h" #include "msg.h" -/* - * Out-of-range value for node signature - */ -#define INVALID_NODE_SIG 0x10000 +/* Out-of-range value for node signature */ +#define INVALID_NODE_SIG 0x10000 + +#define NODE_HTABLE_SIZE 512 /* Flags used to take different actions according to flag type * TIPC_WAIT_PEER_LINKS_DOWN: wait to see that peer's links are down @@ -90,6 +90,7 @@ struct tipc_node_bclink { * struct tipc_node - TIPC node structure * @addr: network address of node * @lock: spinlock governing access to structure + * @net: the applicable net namespace * @hash: links to adjacent nodes in unsorted hash chain * @active_links: pointers to active links to node * @links: pointers to all links to node @@ -106,6 +107,7 @@ struct tipc_node_bclink { struct tipc_node { u32 addr; spinlock_t lock; + struct net *net; struct hlist_node hash; struct tipc_link *active_links[2]; u32 act_mtus[2]; @@ -123,23 +125,24 @@ struct tipc_node { struct rcu_head rcu; }; -extern struct list_head tipc_node_list; - -struct tipc_node *tipc_node_find(u32 addr); -struct tipc_node *tipc_node_create(u32 addr); -void tipc_node_stop(void); +struct tipc_node *tipc_node_find(struct net *net, u32 addr); +struct tipc_node *tipc_node_create(struct net *net, u32 addr); +void tipc_node_stop(struct net *net); void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr); void tipc_node_detach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr); void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr); void tipc_node_link_up(struct tipc_node *n_ptr, struct tipc_link *l_ptr); int tipc_node_active_links(struct tipc_node *n_ptr); int tipc_node_is_up(struct tipc_node *n_ptr); -struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space); -struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space); -int tipc_node_get_linkname(u32 bearer_id, u32 node, char *linkname, size_t len); +struct sk_buff *tipc_node_get_links(struct net *net, const void *req_tlv_area, + int req_tlv_space); +struct sk_buff *tipc_node_get_nodes(struct net *net, const void *req_tlv_area, + int req_tlv_space); +int tipc_node_get_linkname(struct net *net, u32 bearer_id, u32 node, + char *linkname, size_t len); void tipc_node_unlock(struct tipc_node *node); -int tipc_node_add_conn(u32 dnode, u32 port, u32 peer_port); -void tipc_node_remove_conn(u32 dnode, u32 port); +int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port); +void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port); int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb); @@ -154,12 +157,12 @@ static inline bool tipc_node_blocked(struct tipc_node *node) TIPC_NOTIFY_NODE_DOWN | TIPC_WAIT_OWN_LINKS_DOWN)); } -static inline uint tipc_node_get_mtu(u32 addr, u32 selector) +static inline uint tipc_node_get_mtu(struct net *net, u32 addr, u32 selector) { struct tipc_node *node; u32 mtu; - node = tipc_node_find(addr); + node = tipc_node_find(net, addr); if (likely(node)) mtu = node->act_mtus[selector & 1]; diff --git a/net/tipc/socket.c b/net/tipc/socket.c index c58f66b..6883145 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -257,7 +257,7 @@ static void tsk_rej_rx_queue(struct sock *sk) while ((skb = __skb_dequeue(&sk->sk_receive_queue))) { if (tipc_msg_reverse(skb, &dnode, TIPC_ERR_NO_PORT)) - tipc_link_xmit_skb(skb, dnode, 0); + tipc_link_xmit_skb(sock_net(sk), skb, dnode, 0); } } @@ -473,6 +473,7 @@ static void tipc_sk_callback(struct rcu_head *head) static int tipc_release(struct socket *sock) { struct sock *sk = sock->sk; + struct net *net = sock_net(sk); struct tipc_sock *tsk; struct sk_buff *skb; u32 dnode, probing_state; @@ -503,10 +504,10 @@ static int tipc_release(struct socket *sock) (sock->state == SS_CONNECTED)) { sock->state = SS_DISCONNECTING; tsk->connected = 0; - tipc_node_remove_conn(dnode, tsk->portid); + tipc_node_remove_conn(net, dnode, tsk->portid); } if (tipc_msg_reverse(skb, &dnode, TIPC_ERR_NO_PORT)) - tipc_link_xmit_skb(skb, dnode, 0); + tipc_link_xmit_skb(net, skb, dnode, 0); } } @@ -521,8 +522,8 @@ static int tipc_release(struct socket *sock) tsk_peer_port(tsk), tsk->portid, TIPC_ERR_NO_PORT); if (skb) - tipc_link_xmit_skb(skb, dnode, tsk->portid); - tipc_node_remove_conn(dnode, tsk->portid); + tipc_link_xmit_skb(net, skb, dnode, tsk->portid); + tipc_node_remove_conn(net, dnode, tsk->portid); } /* Discard any remaining (connection-based) messages in receive queue */ @@ -725,6 +726,7 @@ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq, struct msghdr *msg, size_t dsz, long timeo) { struct sock *sk = sock->sk; + struct net *net = sock_net(sk); struct tipc_msg *mhdr = &tipc_sk(sk)->phdr; struct sk_buff_head head; uint mtu; @@ -747,7 +749,7 @@ new_mtu: return rc; do { - rc = tipc_bclink_xmit(&head); + rc = tipc_bclink_xmit(net, &head); if (likely(rc >= 0)) { rc = dsz; break; @@ -766,7 +768,7 @@ new_mtu: /* tipc_sk_mcast_rcv - Deliver multicast message to all destination sockets */ -void tipc_sk_mcast_rcv(struct sk_buff *buf) +void tipc_sk_mcast_rcv(struct net *net, struct sk_buff *buf) { struct tipc_msg *msg = buf_msg(buf); struct tipc_port_list dports = {0, NULL, }; @@ -798,7 +800,7 @@ void tipc_sk_mcast_rcv(struct sk_buff *buf) continue; } msg_set_destport(msg, item->ports[i]); - tipc_sk_rcv(b); + tipc_sk_rcv(net, b); } } tipc_port_list_free(&dports); @@ -886,6 +888,7 @@ static int tipc_sendmsg(struct kiocb *iocb, struct socket *sock, DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name); struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); + struct net *net = sock_net(sk); struct tipc_msg *mhdr = &tsk->phdr; u32 dnode, dport; struct sk_buff_head head; @@ -960,7 +963,7 @@ static int tipc_sendmsg(struct kiocb *iocb, struct socket *sock, } new_mtu: - mtu = tipc_node_get_mtu(dnode, tsk->portid); + mtu = tipc_node_get_mtu(net, dnode, tsk->portid); __skb_queue_head_init(&head); rc = tipc_msg_build(mhdr, m, 0, dsz, mtu, &head); if (rc < 0) @@ -969,7 +972,7 @@ new_mtu: do { skb = skb_peek(&head); TIPC_SKB_CB(skb)->wakeup_pending = tsk->link_cong; - rc = tipc_link_xmit(&head, dnode, tsk->portid); + rc = tipc_link_xmit(net, &head, dnode, tsk->portid); if (likely(rc >= 0)) { if (sock->state != SS_READY) sock->state = SS_CONNECTING; @@ -1038,6 +1041,7 @@ static int tipc_send_stream(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t dsz) { struct sock *sk = sock->sk; + struct net *net = sock_net(sk); struct tipc_sock *tsk = tipc_sk(sk); struct tipc_msg *mhdr = &tsk->phdr; struct sk_buff_head head; @@ -1081,7 +1085,7 @@ next: goto exit; do { if (likely(!tsk_conn_cong(tsk))) { - rc = tipc_link_xmit(&head, dnode, portid); + rc = tipc_link_xmit(net, &head, dnode, portid); if (likely(!rc)) { tsk->sent_unacked++; sent += send; @@ -1090,7 +1094,8 @@ next: goto next; } if (rc == -EMSGSIZE) { - tsk->max_pkt = tipc_node_get_mtu(dnode, portid); + tsk->max_pkt = tipc_node_get_mtu(net, dnode, + portid); goto next; } if (rc != -ELINKCONG) @@ -1132,6 +1137,7 @@ static int tipc_send_packet(struct kiocb *iocb, struct socket *sock, static void tipc_sk_finish_conn(struct tipc_sock *tsk, u32 peer_port, u32 peer_node) { + struct net *net = sock_net(&tsk->sk); struct tipc_msg *msg = &tsk->phdr; msg_set_destnode(msg, peer_node); @@ -1145,8 +1151,8 @@ static void tipc_sk_finish_conn(struct tipc_sock *tsk, u32 peer_port, tsk->connected = 1; if (!mod_timer(&tsk->timer, jiffies + tsk->probing_intv)) sock_hold(&tsk->sk); - tipc_node_add_conn(peer_node, tsk->portid, peer_port); - tsk->max_pkt = tipc_node_get_mtu(peer_node, tsk->portid); + tipc_node_add_conn(net, peer_node, tsk->portid, peer_port); + tsk->max_pkt = tipc_node_get_mtu(net, peer_node, tsk->portid); } /** @@ -1245,6 +1251,7 @@ static int tipc_sk_anc_data_recv(struct msghdr *m, struct tipc_msg *msg, static void tipc_sk_send_ack(struct tipc_sock *tsk, uint ack) { + struct net *net = sock_net(&tsk->sk); struct sk_buff *skb = NULL; struct tipc_msg *msg; u32 peer_port = tsk_peer_port(tsk); @@ -1258,7 +1265,7 @@ static void tipc_sk_send_ack(struct tipc_sock *tsk, uint ack) return; msg = buf_msg(skb); msg_set_msgcnt(msg, ack); - tipc_link_xmit_skb(skb, dnode, msg_link_selector(msg)); + tipc_link_xmit_skb(net, skb, dnode, msg_link_selector(msg)); } static int tipc_wait_for_rcvmsg(struct socket *sock, long *timeop) @@ -1551,6 +1558,7 @@ static void tipc_data_ready(struct sock *sk) static int filter_connect(struct tipc_sock *tsk, struct sk_buff **buf) { struct sock *sk = &tsk->sk; + struct net *net = sock_net(sk); struct socket *sock = sk->sk_socket; struct tipc_msg *msg = buf_msg(*buf); int retval = -TIPC_ERR_NO_PORT; @@ -1566,7 +1574,7 @@ static int filter_connect(struct tipc_sock *tsk, struct sk_buff **buf) sock->state = SS_DISCONNECTING; tsk->connected = 0; /* let timer expire on it's own */ - tipc_node_remove_conn(tsk_peer_node(tsk), + tipc_node_remove_conn(net, tsk_peer_node(tsk), tsk->portid); } retval = TIPC_OK; @@ -1737,7 +1745,7 @@ static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb) if ((rc < 0) && !tipc_msg_reverse(skb, &onode, -rc)) return 0; - tipc_link_xmit_skb(skb, onode, 0); + tipc_link_xmit_skb(sock_net(sk), skb, onode, 0); return 0; } @@ -1748,7 +1756,7 @@ static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb) * Consumes buffer * Returns 0 if success, or errno: -EHOSTUNREACH */ -int tipc_sk_rcv(struct sk_buff *skb) +int tipc_sk_rcv(struct net *net, struct sk_buff *skb) { struct tipc_sock *tsk; struct sock *sk; @@ -1785,7 +1793,7 @@ exit: if ((rc < 0) && !tipc_msg_reverse(skb, &dnode, -rc)) return -EHOSTUNREACH; - tipc_link_xmit_skb(skb, dnode, 0); + tipc_link_xmit_skb(net, skb, dnode, 0); return (rc < 0) ? -EHOSTUNREACH : 0; } @@ -2042,6 +2050,7 @@ exit: static int tipc_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; + struct net *net = sock_net(sk); struct tipc_sock *tsk = tipc_sk(sk); struct sk_buff *skb; u32 dnode; @@ -2065,8 +2074,9 @@ restart: goto restart; } if (tipc_msg_reverse(skb, &dnode, TIPC_CONN_SHUTDOWN)) - tipc_link_xmit_skb(skb, dnode, tsk->portid); - tipc_node_remove_conn(dnode, tsk->portid); + tipc_link_xmit_skb(net, skb, dnode, + tsk->portid); + tipc_node_remove_conn(net, dnode, tsk->portid); } else { dnode = tsk_peer_node(tsk); skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, @@ -2074,11 +2084,11 @@ restart: 0, dnode, tipc_own_addr, tsk_peer_port(tsk), tsk->portid, TIPC_CONN_SHUTDOWN); - tipc_link_xmit_skb(skb, dnode, tsk->portid); + tipc_link_xmit_skb(net, skb, dnode, tsk->portid); } tsk->connected = 0; sock->state = SS_DISCONNECTING; - tipc_node_remove_conn(dnode, tsk->portid); + tipc_node_remove_conn(net, dnode, tsk->portid); /* fall through */ case SS_DISCONNECTING: @@ -2130,7 +2140,7 @@ static void tipc_sk_timeout(unsigned long data) } bh_unlock_sock(sk); if (skb) - tipc_link_xmit_skb(skb, peer_node, tsk->portid); + tipc_link_xmit_skb(sock_net(sk), skb, peer_node, tsk->portid); exit: sock_put(sk); } @@ -2138,6 +2148,7 @@ exit: static int tipc_sk_publish(struct tipc_sock *tsk, uint scope, struct tipc_name_seq const *seq) { + struct net *net = sock_net(&tsk->sk); struct publication *publ; u32 key; @@ -2147,7 +2158,7 @@ static int tipc_sk_publish(struct tipc_sock *tsk, uint scope, if (key == tsk->portid) return -EADDRINUSE; - publ = tipc_nametbl_publish(seq->type, seq->lower, seq->upper, + publ = tipc_nametbl_publish(net, seq->type, seq->lower, seq->upper, scope, tsk->portid, key); if (unlikely(!publ)) return -EINVAL; @@ -2161,6 +2172,7 @@ static int tipc_sk_publish(struct tipc_sock *tsk, uint scope, static int tipc_sk_withdraw(struct tipc_sock *tsk, uint scope, struct tipc_name_seq const *seq) { + struct net *net = sock_net(&tsk->sk); struct publication *publ; struct publication *safe; int rc = -EINVAL; @@ -2175,12 +2187,12 @@ static int tipc_sk_withdraw(struct tipc_sock *tsk, uint scope, continue; if (publ->upper != seq->upper) break; - tipc_nametbl_withdraw(publ->type, publ->lower, + tipc_nametbl_withdraw(net, publ->type, publ->lower, publ->ref, publ->key); rc = 0; break; } - tipc_nametbl_withdraw(publ->type, publ->lower, + tipc_nametbl_withdraw(net, publ->type, publ->lower, publ->ref, publ->key); rc = 0; } @@ -2492,8 +2504,9 @@ static int tipc_getsockopt(struct socket *sock, int lvl, int opt, return put_user(sizeof(value), ol); } -static int tipc_ioctl(struct socket *sk, unsigned int cmd, unsigned long arg) +static int tipc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { + struct sock *sk = sock->sk; struct tipc_sioc_ln_req lnr; void __user *argp = (void __user *)arg; @@ -2501,7 +2514,8 @@ static int tipc_ioctl(struct socket *sk, unsigned int cmd, unsigned long arg) case SIOCGETLINKNAME: if (copy_from_user(&lnr, argp, sizeof(lnr))) return -EFAULT; - if (!tipc_node_get_linkname(lnr.bearer_id & 0xffff, lnr.peer, + if (!tipc_node_get_linkname(sock_net(sk), + lnr.bearer_id & 0xffff, lnr.peer, lnr.linkname, TIPC_MAX_LINK_NAME)) { if (copy_to_user(argp, &lnr, sizeof(lnr))) return -EFAULT; diff --git a/net/tipc/socket.h b/net/tipc/socket.h index 46bc370..eb15c31 100644 --- a/net/tipc/socket.h +++ b/net/tipc/socket.h @@ -49,9 +49,9 @@ int tipc_sock_create_local(int type, struct socket **res); void tipc_sock_release_local(struct socket *sock); int tipc_sock_accept_local(struct socket *sock, struct socket **newsock, int flags); -int tipc_sk_rcv(struct sk_buff *buf); +int tipc_sk_rcv(struct net *net, struct sk_buff *buf); struct sk_buff *tipc_sk_socks_show(void); -void tipc_sk_mcast_rcv(struct sk_buff *buf); +void tipc_sk_mcast_rcv(struct net *net, struct sk_buff *buf); void tipc_sk_reinit(void); int tipc_sk_rht_init(void); void tipc_sk_rht_destroy(void); -- cgit v0.10.2 From 7f9f95d9d9bcdf253c4149a157b096958013eceb Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Fri, 9 Jan 2015 15:27:06 +0800 Subject: tipc: make bearer list support net namespace Bearer list defined as a global variable is used to store bearer instances. When tipc supports net namespace, bearers created in one namespace must be isolated with others allocated in other namespaces, which requires us that the bearer list(bearer_list) must be moved to tipc_net structure. As a result, a net namespace pointer has to be passed to functions which access the bearer list. Signed-off-by: Ying Xue Tested-by: Tero Aho Reviewed-by: Jon Maloy Signed-off-by: David S. Miller diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index 816c0e4..e7c5383 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -369,7 +369,7 @@ void tipc_bclink_update_link_state(struct net *net, struct tipc_node *n_ptr, msg_set_bcgap_to(msg, to); tipc_bclink_lock(); - tipc_bearer_send(MAX_BEARERS, buf, NULL); + tipc_bearer_send(net, MAX_BEARERS, buf, NULL); bcl->stats.sent_nacks++; tipc_bclink_unlock(); kfree_skb(buf); @@ -425,7 +425,7 @@ int tipc_bclink_xmit(struct net *net, struct sk_buff_head *list) if (likely(bclink)) { tipc_bclink_lock(); if (likely(bclink->bcast_nodes.count)) { - rc = __tipc_link_xmit(bcl, list); + rc = __tipc_link_xmit(net, bcl, list); if (likely(!rc)) { u32 len = skb_queue_len(&bcl->outqueue); @@ -682,13 +682,14 @@ static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1, if (bp_index == 0) { /* Use original buffer for first bearer */ - tipc_bearer_send(b->identity, buf, &b->bcast_addr); + tipc_bearer_send(net, b->identity, buf, &b->bcast_addr); } else { /* Avoid concurrent buffer access */ tbuf = pskb_copy_for_clone(buf, GFP_ATOMIC); if (!tbuf) break; - tipc_bearer_send(b->identity, tbuf, &b->bcast_addr); + tipc_bearer_send(net, b->identity, tbuf, + &b->bcast_addr); kfree_skb(tbuf); /* Bearer keeps a clone */ } if (bcbearer->remains_new.count == 0) @@ -703,8 +704,10 @@ static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1, /** * tipc_bcbearer_sort - create sets of bearer pairs used by broadcast bearer */ -void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action) +void tipc_bcbearer_sort(struct net *net, struct tipc_node_map *nm_ptr, + u32 node, bool action) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_bcbearer_pair *bp_temp = bcbearer->bpairs_temp; struct tipc_bcbearer_pair *bp_curr; struct tipc_bearer *b; @@ -723,7 +726,7 @@ void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action) rcu_read_lock(); for (b_index = 0; b_index < MAX_BEARERS; b_index++) { - b = rcu_dereference_rtnl(bearer_list[b_index]); + b = rcu_dereference_rtnl(tn->bearer_list[b_index]); if (!b || !b->nodes.count) continue; @@ -939,8 +942,10 @@ int tipc_bclink_set_queue_limits(u32 limit) return 0; } -int tipc_bclink_init(void) +int tipc_bclink_init(struct net *net) { + struct tipc_net *tn = net_generic(net, tipc_net_id); + bcbearer = kzalloc(sizeof(*bcbearer), GFP_ATOMIC); if (!bcbearer) return -ENOMEM; @@ -967,19 +972,21 @@ int tipc_bclink_init(void) bcl->max_pkt = MAX_PKT_DEFAULT_MCAST; tipc_link_set_queue_limits(bcl, BCLINK_WIN_DEFAULT); bcl->bearer_id = MAX_BEARERS; - rcu_assign_pointer(bearer_list[MAX_BEARERS], &bcbearer->bearer); + rcu_assign_pointer(tn->bearer_list[MAX_BEARERS], &bcbearer->bearer); bcl->state = WORKING_WORKING; strlcpy(bcl->name, tipc_bclink_name, TIPC_MAX_LINK_NAME); return 0; } -void tipc_bclink_stop(void) +void tipc_bclink_stop(struct net *net) { + struct tipc_net *tn = net_generic(net, tipc_net_id); + tipc_bclink_lock(); tipc_link_purge_queues(bcl); tipc_bclink_unlock(); - RCU_INIT_POINTER(bearer_list[BCBEARER], NULL); + RCU_INIT_POINTER(tn->bearer_list[BCBEARER], NULL); synchronize_net(); kfree(bcbearer); kfree(bclink); diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h index fd0d17a..692efb6 100644 --- a/net/tipc/bcast.h +++ b/net/tipc/bcast.h @@ -84,8 +84,8 @@ static inline int tipc_nmap_equal(struct tipc_node_map *nm_a, void tipc_port_list_add(struct tipc_port_list *pl_ptr, u32 port); void tipc_port_list_free(struct tipc_port_list *pl_ptr); -int tipc_bclink_init(void); -void tipc_bclink_stop(void); +int tipc_bclink_init(struct net *net); +void tipc_bclink_stop(struct net *net); void tipc_bclink_set_flags(unsigned int flags); void tipc_bclink_add_node(u32 addr); void tipc_bclink_remove_node(u32 addr); @@ -99,7 +99,8 @@ void tipc_bclink_update_link_state(struct net *net, struct tipc_node *n_ptr, int tipc_bclink_stats(char *stats_buf, const u32 buf_size); int tipc_bclink_reset_stats(void); int tipc_bclink_set_queue_limits(u32 limit); -void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action); +void tipc_bcbearer_sort(struct net *net, struct tipc_node_map *nm_ptr, + u32 node, bool action); uint tipc_bclink_get_mtu(void); int tipc_bclink_xmit(struct net *net, struct sk_buff_head *list); void tipc_bclink_wakeup_users(struct net *net); diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index a735c08..2fe9dcb 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -34,6 +34,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include #include "core.h" #include "config.h" #include "bearer.h" @@ -67,8 +68,6 @@ static const struct nla_policy tipc_nl_media_policy[TIPC_NLA_MEDIA_MAX + 1] = { [TIPC_NLA_MEDIA_PROP] = { .type = NLA_NESTED } }; -struct tipc_bearer __rcu *bearer_list[MAX_BEARERS + 1]; - static void bearer_disable(struct net *net, struct tipc_bearer *b_ptr, bool shutting_down); @@ -191,13 +190,14 @@ static int bearer_name_validate(const char *name, /** * tipc_bearer_find - locates bearer object with matching bearer name */ -struct tipc_bearer *tipc_bearer_find(const char *name) +struct tipc_bearer *tipc_bearer_find(struct net *net, const char *name) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_bearer *b_ptr; u32 i; for (i = 0; i < MAX_BEARERS; i++) { - b_ptr = rtnl_dereference(bearer_list[i]); + b_ptr = rtnl_dereference(tn->bearer_list[i]); if (b_ptr && (!strcmp(b_ptr->name, name))) return b_ptr; } @@ -207,8 +207,9 @@ struct tipc_bearer *tipc_bearer_find(const char *name) /** * tipc_bearer_get_names - record names of bearers in buffer */ -struct sk_buff *tipc_bearer_get_names(void) +struct sk_buff *tipc_bearer_get_names(struct net *net) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct sk_buff *buf; struct tipc_bearer *b; int i, j; @@ -219,7 +220,7 @@ struct sk_buff *tipc_bearer_get_names(void) for (i = 0; media_info_array[i] != NULL; i++) { for (j = 0; j < MAX_BEARERS; j++) { - b = rtnl_dereference(bearer_list[j]); + b = rtnl_dereference(tn->bearer_list[j]); if (!b) continue; if (b->media == media_info_array[i]) { @@ -232,27 +233,29 @@ struct sk_buff *tipc_bearer_get_names(void) return buf; } -void tipc_bearer_add_dest(u32 bearer_id, u32 dest) +void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_bearer *b_ptr; rcu_read_lock(); - b_ptr = rcu_dereference_rtnl(bearer_list[bearer_id]); + b_ptr = rcu_dereference_rtnl(tn->bearer_list[bearer_id]); if (b_ptr) { - tipc_bcbearer_sort(&b_ptr->nodes, dest, true); + tipc_bcbearer_sort(net, &b_ptr->nodes, dest, true); tipc_disc_add_dest(b_ptr->link_req); } rcu_read_unlock(); } -void tipc_bearer_remove_dest(u32 bearer_id, u32 dest) +void tipc_bearer_remove_dest(struct net *net, u32 bearer_id, u32 dest) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_bearer *b_ptr; rcu_read_lock(); - b_ptr = rcu_dereference_rtnl(bearer_list[bearer_id]); + b_ptr = rcu_dereference_rtnl(tn->bearer_list[bearer_id]); if (b_ptr) { - tipc_bcbearer_sort(&b_ptr->nodes, dest, false); + tipc_bcbearer_sort(net, &b_ptr->nodes, dest, false); tipc_disc_remove_dest(b_ptr->link_req); } rcu_read_unlock(); @@ -264,6 +267,7 @@ void tipc_bearer_remove_dest(u32 bearer_id, u32 dest) int tipc_enable_bearer(struct net *net, const char *name, u32 disc_domain, u32 priority) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_bearer *b_ptr; struct tipc_media *m_ptr; struct tipc_bearer_names b_names; @@ -315,7 +319,7 @@ restart: bearer_id = MAX_BEARERS; with_this_prio = 1; for (i = MAX_BEARERS; i-- != 0; ) { - b_ptr = rtnl_dereference(bearer_list[i]); + b_ptr = rtnl_dereference(tn->bearer_list[i]); if (!b_ptr) { bearer_id = i; continue; @@ -349,7 +353,7 @@ restart: strcpy(b_ptr->name, name); b_ptr->media = m_ptr; - res = m_ptr->enable_media(b_ptr); + res = m_ptr->enable_media(net, b_ptr); if (res) { pr_warn("Bearer <%s> rejected, enable failure (%d)\n", name, -res); @@ -371,7 +375,7 @@ restart: return -EINVAL; } - rcu_assign_pointer(bearer_list[bearer_id], b_ptr); + rcu_assign_pointer(tn->bearer_list[bearer_id], b_ptr); pr_info("Enabled bearer <%s>, discovery domain %s, priority %u\n", name, @@ -398,6 +402,7 @@ static int tipc_reset_bearer(struct net *net, struct tipc_bearer *b_ptr) static void bearer_disable(struct net *net, struct tipc_bearer *b_ptr, bool shutting_down) { + struct tipc_net *tn = net_generic(net, tipc_net_id); u32 i; pr_info("Disabling bearer <%s>\n", b_ptr->name); @@ -408,8 +413,8 @@ static void bearer_disable(struct net *net, struct tipc_bearer *b_ptr, tipc_disc_delete(b_ptr->link_req); for (i = 0; i < MAX_BEARERS; i++) { - if (b_ptr == rtnl_dereference(bearer_list[i])) { - RCU_INIT_POINTER(bearer_list[i], NULL); + if (b_ptr == rtnl_dereference(tn->bearer_list[i])) { + RCU_INIT_POINTER(tn->bearer_list[i], NULL); break; } } @@ -421,7 +426,7 @@ int tipc_disable_bearer(struct net *net, const char *name) struct tipc_bearer *b_ptr; int res; - b_ptr = tipc_bearer_find(name); + b_ptr = tipc_bearer_find(net, name); if (b_ptr == NULL) { pr_warn("Attempt to disable unknown bearer <%s>\n", name); res = -EINVAL; @@ -432,13 +437,13 @@ int tipc_disable_bearer(struct net *net, const char *name) return res; } -int tipc_enable_l2_media(struct tipc_bearer *b) +int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b) { struct net_device *dev; char *driver_name = strchr((const char *)b->name, ':') + 1; /* Find device with specified name */ - dev = dev_get_by_name(&init_net, driver_name); + dev = dev_get_by_name(net, driver_name); if (!dev) return -ENODEV; @@ -514,13 +519,14 @@ int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b, * The media send routine must not alter the buffer being passed in * as it may be needed for later retransmission! */ -void tipc_bearer_send(u32 bearer_id, struct sk_buff *buf, +void tipc_bearer_send(struct net *net, u32 bearer_id, struct sk_buff *buf, struct tipc_media_addr *dest) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_bearer *b_ptr; rcu_read_lock(); - b_ptr = rcu_dereference_rtnl(bearer_list[bearer_id]); + b_ptr = rcu_dereference_rtnl(tn->bearer_list[bearer_id]); if (likely(b_ptr)) b_ptr->media->send_msg(buf, b_ptr, dest); rcu_read_unlock(); @@ -630,14 +636,15 @@ void tipc_bearer_cleanup(void) void tipc_bearer_stop(struct net *net) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_bearer *b_ptr; u32 i; for (i = 0; i < MAX_BEARERS; i++) { - b_ptr = rtnl_dereference(bearer_list[i]); + b_ptr = rtnl_dereference(tn->bearer_list[i]); if (b_ptr) { bearer_disable(net, b_ptr, true); - bearer_list[i] = NULL; + tn->bearer_list[i] = NULL; } } } @@ -694,6 +701,8 @@ int tipc_nl_bearer_dump(struct sk_buff *skb, struct netlink_callback *cb) int i = cb->args[0]; struct tipc_bearer *bearer; struct tipc_nl_msg msg; + struct net *net = sock_net(skb->sk); + struct tipc_net *tn = net_generic(net, tipc_net_id); if (i == MAX_BEARERS) return 0; @@ -704,7 +713,7 @@ int tipc_nl_bearer_dump(struct sk_buff *skb, struct netlink_callback *cb) rtnl_lock(); for (i = 0; i < MAX_BEARERS; i++) { - bearer = rtnl_dereference(bearer_list[i]); + bearer = rtnl_dereference(tn->bearer_list[i]); if (!bearer) continue; @@ -726,6 +735,7 @@ int tipc_nl_bearer_get(struct sk_buff *skb, struct genl_info *info) struct tipc_bearer *bearer; struct tipc_nl_msg msg; struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1]; + struct net *net = genl_info_net(info); if (!info->attrs[TIPC_NLA_BEARER]) return -EINVAL; @@ -749,7 +759,7 @@ int tipc_nl_bearer_get(struct sk_buff *skb, struct genl_info *info) msg.seq = info->snd_seq; rtnl_lock(); - bearer = tipc_bearer_find(name); + bearer = tipc_bearer_find(net, name); if (!bearer) { err = -EINVAL; goto err_out; @@ -791,7 +801,7 @@ int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info) name = nla_data(attrs[TIPC_NLA_BEARER_NAME]); rtnl_lock(); - bearer = tipc_bearer_find(name); + bearer = tipc_bearer_find(net, name); if (!bearer) { rtnl_unlock(); return -EINVAL; @@ -861,6 +871,7 @@ int tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info) char *name; struct tipc_bearer *b; struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1]; + struct net *net = genl_info_net(info); if (!info->attrs[TIPC_NLA_BEARER]) return -EINVAL; @@ -876,7 +887,7 @@ int tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info) name = nla_data(attrs[TIPC_NLA_BEARER_NAME]); rtnl_lock(); - b = tipc_bearer_find(name); + b = tipc_bearer_find(net, name); if (!b) { rtnl_unlock(); return -EINVAL; diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h index 91a8eee..01722d6 100644 --- a/net/tipc/bearer.h +++ b/net/tipc/bearer.h @@ -92,7 +92,7 @@ struct tipc_media { int (*send_msg)(struct sk_buff *buf, struct tipc_bearer *b_ptr, struct tipc_media_addr *dest); - int (*enable_media)(struct tipc_bearer *b_ptr); + int (*enable_media)(struct net *net, struct tipc_bearer *b_ptr); void (*disable_media)(struct tipc_bearer *b_ptr); int (*addr2str)(struct tipc_media_addr *addr, char *strbuf, @@ -159,8 +159,6 @@ struct tipc_bearer_names { struct tipc_link; -extern struct tipc_bearer __rcu *bearer_list[]; - /* * TIPC routines available to supported media types */ @@ -193,20 +191,20 @@ int tipc_media_set_priority(const char *name, u32 new_value); int tipc_media_set_window(const char *name, u32 new_value); void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a); struct sk_buff *tipc_media_get_names(void); -int tipc_enable_l2_media(struct tipc_bearer *b); +int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b); void tipc_disable_l2_media(struct tipc_bearer *b); int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b, struct tipc_media_addr *dest); -struct sk_buff *tipc_bearer_get_names(void); -void tipc_bearer_add_dest(u32 bearer_id, u32 dest); -void tipc_bearer_remove_dest(u32 bearer_id, u32 dest); -struct tipc_bearer *tipc_bearer_find(const char *name); +struct sk_buff *tipc_bearer_get_names(struct net *net); +void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest); +void tipc_bearer_remove_dest(struct net *net, u32 bearer_id, u32 dest); +struct tipc_bearer *tipc_bearer_find(struct net *net, const char *name); struct tipc_media *tipc_media_find(const char *name); int tipc_bearer_setup(void); void tipc_bearer_cleanup(void); void tipc_bearer_stop(struct net *net); -void tipc_bearer_send(u32 bearer_id, struct sk_buff *buf, +void tipc_bearer_send(struct net *net, u32 bearer_id, struct sk_buff *buf, struct tipc_media_addr *dest); #endif /* _TIPC_BEARER_H */ diff --git a/net/tipc/config.c b/net/tipc/config.c index cf2d417..ac73291 100644 --- a/net/tipc/config.c +++ b/net/tipc/config.c @@ -251,7 +251,7 @@ struct sk_buff *tipc_cfg_do_cmd(struct net *net, u32 orig_node, u16 cmd, rep_tlv_buf = tipc_nametbl_get(req_tlv_area, req_tlv_space); break; case TIPC_CMD_GET_BEARER_NAMES: - rep_tlv_buf = tipc_bearer_get_names(); + rep_tlv_buf = tipc_bearer_get_names(net); break; case TIPC_CMD_GET_MEDIA_NAMES: rep_tlv_buf = tipc_media_get_names(); diff --git a/net/tipc/core.h b/net/tipc/core.h index 4fb1133..75a332b 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -60,6 +60,7 @@ #include #include "node.h" +#include "bearer.h" #define TIPC_MOD_VER "2.0.0" @@ -87,6 +88,9 @@ struct tipc_net { struct list_head node_list; u32 num_nodes; u32 num_links; + + /* Bearer list */ + struct tipc_bearer __rcu *bearer_list[MAX_BEARERS + 1]; }; #ifdef CONFIG_SYSCTL diff --git a/net/tipc/discover.c b/net/tipc/discover.c index f0fd8b4..f93dd3d 100644 --- a/net/tipc/discover.c +++ b/net/tipc/discover.c @@ -51,6 +51,7 @@ /** * struct tipc_link_req - information about an ongoing link setup request * @bearer_id: identity of bearer issuing requests + * @net: network namespace instance * @dest: destination address for request messages * @domain: network domain to which links can be established * @num_nodes: number of nodes currently discovered (i.e. with an active link) @@ -62,6 +63,7 @@ struct tipc_link_req { u32 bearer_id; struct tipc_media_addr dest; + struct net *net; u32 domain; int num_nodes; spinlock_t lock; @@ -254,7 +256,7 @@ void tipc_disc_rcv(struct net *net, struct sk_buff *buf, rbuf = tipc_buf_acquire(INT_H_SIZE); if (rbuf) { tipc_disc_init_msg(net, rbuf, DSC_RESP_MSG, bearer); - tipc_bearer_send(bearer->identity, rbuf, &maddr); + tipc_bearer_send(net, bearer->identity, rbuf, &maddr); kfree_skb(rbuf); } } @@ -328,7 +330,7 @@ static void disc_timeout(unsigned long data) * hold at fast polling rate if don't have any associated nodes, * otherwise hold at slow polling rate */ - tipc_bearer_send(req->bearer_id, req->buf, &req->dest); + tipc_bearer_send(req->net, req->bearer_id, req->buf, &req->dest); req->timer_intv *= 2; @@ -370,6 +372,7 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b_ptr, tipc_disc_init_msg(net, req->buf, DSC_REQ_MSG, b_ptr); memcpy(&req->dest, dest, sizeof(*dest)); + req->net = net; req->bearer_id = b_ptr->identity; req->domain = b_ptr->domain; req->num_nodes = 0; @@ -378,7 +381,7 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b_ptr, setup_timer(&req->timer, disc_timeout, (unsigned long)req); mod_timer(&req->timer, jiffies + req->timer_intv); b_ptr->link_req = req; - tipc_bearer_send(req->bearer_id, req->buf, &req->dest); + tipc_bearer_send(net, req->bearer_id, req->buf, &req->dest); return 0; } @@ -405,11 +408,12 @@ void tipc_disc_reset(struct net *net, struct tipc_bearer *b_ptr) spin_lock_bh(&req->lock); tipc_disc_init_msg(net, req->buf, DSC_REQ_MSG, b_ptr); + req->net = net; req->bearer_id = b_ptr->identity; req->domain = b_ptr->domain; req->num_nodes = 0; req->timer_intv = TIPC_LINK_REQ_INIT; mod_timer(&req->timer, jiffies + req->timer_intv); - tipc_bearer_send(req->bearer_id, req->buf, &req->dest); + tipc_bearer_send(net, req->bearer_id, req->buf, &req->dest); spin_unlock_bh(&req->lock); } diff --git a/net/tipc/link.c b/net/tipc/link.c index f650565..be21e6a 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -129,11 +129,13 @@ static unsigned int align(unsigned int i) static void link_init_max_pkt(struct tipc_link *l_ptr) { + struct tipc_node *node = l_ptr->owner; + struct tipc_net *tn = net_generic(node->net, tipc_net_id); struct tipc_bearer *b_ptr; u32 max_pkt; rcu_read_lock(); - b_ptr = rcu_dereference_rtnl(bearer_list[l_ptr->bearer_id]); + b_ptr = rcu_dereference_rtnl(tn->bearer_list[l_ptr->bearer_id]); if (!b_ptr) { rcu_read_unlock(); return; @@ -431,7 +433,7 @@ void tipc_link_reset(struct tipc_link *l_ptr) return; tipc_node_link_down(l_ptr->owner, l_ptr); - tipc_bearer_remove_dest(l_ptr->bearer_id, l_ptr->addr); + tipc_bearer_remove_dest(owner->net, l_ptr->bearer_id, l_ptr->addr); if (was_active_link && tipc_node_active_links(l_ptr->owner)) { l_ptr->reset_checkpoint = checkpoint; @@ -471,11 +473,14 @@ void tipc_link_reset_list(struct net *net, unsigned int bearer_id) rcu_read_unlock(); } -static void link_activate(struct tipc_link *l_ptr) +static void link_activate(struct tipc_link *link) { - l_ptr->next_in_no = l_ptr->stats.recv_info = 1; - tipc_node_link_up(l_ptr->owner, l_ptr); - tipc_bearer_add_dest(l_ptr->bearer_id, l_ptr->addr); + struct tipc_node *node = link->owner; + + link->next_in_no = 1; + link->stats.recv_info = 1; + tipc_node_link_up(node, link); + tipc_bearer_add_dest(node->net, link->bearer_id, link->addr); } /** @@ -707,7 +712,8 @@ drop: * Only the socket functions tipc_send_stream() and tipc_send_packet() need * to act on the return value, since they may need to do more send attempts. */ -int __tipc_link_xmit(struct tipc_link *link, struct sk_buff_head *list) +int __tipc_link_xmit(struct net *net, struct tipc_link *link, + struct sk_buff_head *list) { struct tipc_msg *msg = buf_msg(skb_peek(list)); uint psz = msg_size(msg); @@ -740,7 +746,8 @@ int __tipc_link_xmit(struct tipc_link *link, struct sk_buff_head *list) if (skb_queue_len(outqueue) < sndlim) { __skb_queue_tail(outqueue, skb); - tipc_bearer_send(link->bearer_id, skb, addr); + tipc_bearer_send(net, link->bearer_id, + skb, addr); link->next_out = NULL; link->unacked_window = 0; } else if (tipc_msg_bundle(outqueue, skb, mtu)) { @@ -774,7 +781,7 @@ static int __tipc_link_xmit_skb(struct tipc_link *link, struct sk_buff *skb) struct sk_buff_head head; skb2list(skb, &head); - return __tipc_link_xmit(link, &head); + return __tipc_link_xmit(link->owner->net, link, &head); } int tipc_link_xmit_skb(struct net *net, struct sk_buff *skb, u32 dnode, @@ -808,7 +815,7 @@ int tipc_link_xmit(struct net *net, struct sk_buff_head *list, u32 dnode, tipc_node_lock(node); link = node->active_links[selector & 1]; if (link) - rc = __tipc_link_xmit(link, list); + rc = __tipc_link_xmit(net, link, list); tipc_node_unlock(node); } @@ -900,7 +907,8 @@ void tipc_link_push_packets(struct tipc_link *l_ptr) msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in); if (msg_user(msg) == MSG_BUNDLER) TIPC_SKB_CB(skb)->bundling = false; - tipc_bearer_send(l_ptr->bearer_id, skb, + tipc_bearer_send(l_ptr->owner->net, + l_ptr->bearer_id, skb, &l_ptr->media_addr); l_ptr->next_out = tipc_skb_queue_next(outqueue, skb); } else { @@ -997,7 +1005,8 @@ void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *skb, msg = buf_msg(skb); msg_set_ack(msg, mod(l_ptr->next_in_no - 1)); msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in); - tipc_bearer_send(l_ptr->bearer_id, skb, &l_ptr->media_addr); + tipc_bearer_send(l_ptr->owner->net, l_ptr->bearer_id, skb, + &l_ptr->media_addr); retransmits--; l_ptr->stats.retransmitted++; } @@ -1459,7 +1468,8 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg, skb_copy_to_linear_data(buf, msg, sizeof(l_ptr->proto_msg)); buf->priority = TC_PRIO_CONTROL; - tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr); + tipc_bearer_send(l_ptr->owner->net, l_ptr->bearer_id, buf, + &l_ptr->media_addr); l_ptr->unacked_window = 0; kfree_skb(buf); } @@ -2037,7 +2047,7 @@ static int link_cmd_set_value(struct net *net, const char *name, u32 new_value, return res; } - b_ptr = tipc_bearer_find(name); + b_ptr = tipc_bearer_find(net, name); if (b_ptr) { switch (cmd) { case TIPC_CMD_SET_LINK_TOL: @@ -2295,10 +2305,11 @@ struct sk_buff *tipc_link_cmd_show_stats(struct net *net, static void link_print(struct tipc_link *l_ptr, const char *str) { + struct tipc_net *tn = net_generic(l_ptr->owner->net, tipc_net_id); struct tipc_bearer *b_ptr; rcu_read_lock(); - b_ptr = rcu_dereference_rtnl(bearer_list[l_ptr->bearer_id]); + b_ptr = rcu_dereference_rtnl(tn->bearer_list[l_ptr->bearer_id]); if (b_ptr) pr_info("%s Link %x<%s>:", str, l_ptr->addr, b_ptr->name); rcu_read_unlock(); diff --git a/net/tipc/link.h b/net/tipc/link.h index 380e27e..9df7fa4 100644 --- a/net/tipc/link.h +++ b/net/tipc/link.h @@ -223,7 +223,8 @@ int tipc_link_xmit_skb(struct net *net, struct sk_buff *skb, u32 dest, u32 selector); int tipc_link_xmit(struct net *net, struct sk_buff_head *list, u32 dest, u32 selector); -int __tipc_link_xmit(struct tipc_link *link, struct sk_buff_head *list); +int __tipc_link_xmit(struct net *net, struct tipc_link *link, + struct sk_buff_head *list); void tipc_link_bundle_rcv(struct net *net, struct sk_buff *buf); void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int prob, u32 gap, u32 tolerance, u32 priority, u32 acked_mtu); diff --git a/net/tipc/net.c b/net/tipc/net.c index de18aac..38633e5 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -117,7 +117,7 @@ int tipc_net_start(struct net *net, u32 addr) tipc_own_addr = addr; tipc_named_reinit(); tipc_sk_reinit(); - res = tipc_bclink_init(); + res = tipc_bclink_init(net); if (res) return res; @@ -140,7 +140,7 @@ void tipc_net_stop(struct net *net) tipc_own_addr); rtnl_lock(); tipc_bearer_stop(net); - tipc_bclink_stop(); + tipc_bclink_stop(net); tipc_node_stop(net); rtnl_unlock(); -- cgit v0.10.2 From 1da465683a93142488a54a9038155f23d6349441 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Fri, 9 Jan 2015 15:27:07 +0800 Subject: tipc: make tipc broadcast link support net namespace TIPC broadcast link is statically established and its relevant states are maintained with the global variables: "bcbearer", "bclink" and "bcl". Allowing different namespace to own different broadcast link instances, these variables must be moved to tipc_net structure and broadcast link instances would be allocated and initialized when namespace is created. Signed-off-by: Ying Xue Tested-by: Tero Aho Reviewed-by: Jon Maloy Signed-off-by: David S. Miller diff --git a/net/tipc/addr.c b/net/tipc/addr.c index 357b74b..9e6eeb4 100644 --- a/net/tipc/addr.c +++ b/net/tipc/addr.c @@ -34,7 +34,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "core.h" +#include #include "addr.h" /** diff --git a/net/tipc/addr.h b/net/tipc/addr.h index 3e1f18e..4e364c4 100644 --- a/net/tipc/addr.h +++ b/net/tipc/addr.h @@ -37,6 +37,9 @@ #ifndef _TIPC_ADDR_H #define _TIPC_ADDR_H +#include +#include + #define TIPC_ZONE_MASK 0xff000000u #define TIPC_CLUSTER_MASK 0xfffff000u diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index e7c5383..bc58097 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -35,77 +35,14 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "core.h" -#include "link.h" #include "socket.h" #include "msg.h" #include "bcast.h" #include "name_distr.h" +#include "core.h" #define MAX_PKT_DEFAULT_MCAST 1500 /* bcast link max packet size (fixed) */ #define BCLINK_WIN_DEFAULT 20 /* bcast link window size (default) */ -#define BCBEARER MAX_BEARERS - -/** - * struct tipc_bcbearer_pair - a pair of bearers used by broadcast link - * @primary: pointer to primary bearer - * @secondary: pointer to secondary bearer - * - * Bearers must have same priority and same set of reachable destinations - * to be paired. - */ - -struct tipc_bcbearer_pair { - struct tipc_bearer *primary; - struct tipc_bearer *secondary; -}; - -/** - * struct tipc_bcbearer - bearer used by broadcast link - * @bearer: (non-standard) broadcast bearer structure - * @media: (non-standard) broadcast media structure - * @bpairs: array of bearer pairs - * @bpairs_temp: temporary array of bearer pairs used by tipc_bcbearer_sort() - * @remains: temporary node map used by tipc_bcbearer_send() - * @remains_new: temporary node map used tipc_bcbearer_send() - * - * Note: The fields labelled "temporary" are incorporated into the bearer - * to avoid consuming potentially limited stack space through the use of - * large local variables within multicast routines. Concurrent access is - * prevented through use of the spinlock "bclink_lock". - */ -struct tipc_bcbearer { - struct tipc_bearer bearer; - struct tipc_media media; - struct tipc_bcbearer_pair bpairs[MAX_BEARERS]; - struct tipc_bcbearer_pair bpairs_temp[TIPC_MAX_LINK_PRI + 1]; - struct tipc_node_map remains; - struct tipc_node_map remains_new; -}; - -/** - * struct tipc_bclink - link used for broadcast messages - * @lock: spinlock governing access to structure - * @link: (non-standard) broadcast link structure - * @node: (non-standard) node structure representing b'cast link's peer node - * @flags: represent bclink states - * @bcast_nodes: map of broadcast-capable nodes - * @retransmit_to: node that most recently requested a retransmit - * - * Handles sequence numbering, fragmentation, bundling, etc. - */ -struct tipc_bclink { - spinlock_t lock; - struct tipc_link link; - struct tipc_node node; - unsigned int flags; - struct tipc_node_map bcast_nodes; - struct tipc_node *retransmit_to; -}; - -static struct tipc_bcbearer *bcbearer; -static struct tipc_bclink *bclink; -static struct tipc_link *bcl; const char tipc_bclink_name[] = "broadcast-link"; @@ -115,25 +52,28 @@ static void tipc_nmap_diff(struct tipc_node_map *nm_a, static void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node); static void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node); -static void tipc_bclink_lock(void) +static void tipc_bclink_lock(struct net *net) { - spin_lock_bh(&bclink->lock); + struct tipc_net *tn = net_generic(net, tipc_net_id); + + spin_lock_bh(&tn->bclink->lock); } -static void tipc_bclink_unlock(void) +static void tipc_bclink_unlock(struct net *net) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_node *node = NULL; - if (likely(!bclink->flags)) { - spin_unlock_bh(&bclink->lock); + if (likely(!tn->bclink->flags)) { + spin_unlock_bh(&tn->bclink->lock); return; } - if (bclink->flags & TIPC_BCLINK_RESET) { - bclink->flags &= ~TIPC_BCLINK_RESET; - node = tipc_bclink_retransmit_to(); + if (tn->bclink->flags & TIPC_BCLINK_RESET) { + tn->bclink->flags &= ~TIPC_BCLINK_RESET; + node = tipc_bclink_retransmit_to(net); } - spin_unlock_bh(&bclink->lock); + spin_unlock_bh(&tn->bclink->lock); if (node) tipc_link_reset_all(node); @@ -144,9 +84,11 @@ uint tipc_bclink_get_mtu(void) return MAX_PKT_DEFAULT_MCAST; } -void tipc_bclink_set_flags(unsigned int flags) +void tipc_bclink_set_flags(struct net *net, unsigned int flags) { - bclink->flags |= flags; + struct tipc_net *tn = net_generic(net, tipc_net_id); + + tn->bclink->flags |= flags; } static u32 bcbuf_acks(struct sk_buff *buf) @@ -164,31 +106,40 @@ static void bcbuf_decr_acks(struct sk_buff *buf) bcbuf_set_acks(buf, bcbuf_acks(buf) - 1); } -void tipc_bclink_add_node(u32 addr) +void tipc_bclink_add_node(struct net *net, u32 addr) { - tipc_bclink_lock(); - tipc_nmap_add(&bclink->bcast_nodes, addr); - tipc_bclink_unlock(); + struct tipc_net *tn = net_generic(net, tipc_net_id); + + tipc_bclink_lock(net); + tipc_nmap_add(&tn->bclink->bcast_nodes, addr); + tipc_bclink_unlock(net); } -void tipc_bclink_remove_node(u32 addr) +void tipc_bclink_remove_node(struct net *net, u32 addr) { - tipc_bclink_lock(); - tipc_nmap_remove(&bclink->bcast_nodes, addr); - tipc_bclink_unlock(); + struct tipc_net *tn = net_generic(net, tipc_net_id); + + tipc_bclink_lock(net); + tipc_nmap_remove(&tn->bclink->bcast_nodes, addr); + tipc_bclink_unlock(net); } -static void bclink_set_last_sent(void) +static void bclink_set_last_sent(struct net *net) { + struct tipc_net *tn = net_generic(net, tipc_net_id); + struct tipc_link *bcl = tn->bcl; + if (bcl->next_out) bcl->fsm_msg_cnt = mod(buf_seqno(bcl->next_out) - 1); else bcl->fsm_msg_cnt = mod(bcl->next_out_no - 1); } -u32 tipc_bclink_get_last_sent(void) +u32 tipc_bclink_get_last_sent(struct net *net) { - return bcl->fsm_msg_cnt; + struct tipc_net *tn = net_generic(net, tipc_net_id); + + return tn->bcl->fsm_msg_cnt; } static void bclink_update_last_sent(struct tipc_node *node, u32 seqno) @@ -203,9 +154,11 @@ static void bclink_update_last_sent(struct tipc_node *node, u32 seqno) * * Called with bclink_lock locked */ -struct tipc_node *tipc_bclink_retransmit_to(void) +struct tipc_node *tipc_bclink_retransmit_to(struct net *net) { - return bclink->retransmit_to; + struct tipc_net *tn = net_generic(net, tipc_net_id); + + return tn->bclink->retransmit_to; } /** @@ -215,9 +168,10 @@ struct tipc_node *tipc_bclink_retransmit_to(void) * * Called with bclink_lock locked */ -static void bclink_retransmit_pkt(u32 after, u32 to) +static void bclink_retransmit_pkt(struct tipc_net *tn, u32 after, u32 to) { struct sk_buff *skb; + struct tipc_link *bcl = tn->bcl; skb_queue_walk(&bcl->outqueue, skb) { if (more(buf_seqno(skb), after)) { @@ -234,9 +188,10 @@ static void bclink_retransmit_pkt(u32 after, u32 to) */ void tipc_bclink_wakeup_users(struct net *net) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct sk_buff *skb; - while ((skb = skb_dequeue(&bclink->link.waiting_sks))) + while ((skb = skb_dequeue(&tn->bclink->link.waiting_sks))) tipc_sk_rcv(net, skb); } @@ -252,10 +207,12 @@ void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked) struct sk_buff *skb, *tmp; struct sk_buff *next; unsigned int released = 0; + struct net *net = n_ptr->net; + struct tipc_net *tn = net_generic(net, tipc_net_id); - tipc_bclink_lock(); + tipc_bclink_lock(net); /* Bail out if tx queue is empty (no clean up is required) */ - skb = skb_peek(&bcl->outqueue); + skb = skb_peek(&tn->bcl->outqueue); if (!skb) goto exit; @@ -266,43 +223,43 @@ void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked) * acknowledge sent messages only (if other nodes still exist) * or both sent and unsent messages (otherwise) */ - if (bclink->bcast_nodes.count) - acked = bcl->fsm_msg_cnt; + if (tn->bclink->bcast_nodes.count) + acked = tn->bcl->fsm_msg_cnt; else - acked = bcl->next_out_no; + acked = tn->bcl->next_out_no; } else { /* * Bail out if specified sequence number does not correspond * to a message that has been sent and not yet acknowledged */ if (less(acked, buf_seqno(skb)) || - less(bcl->fsm_msg_cnt, acked) || + less(tn->bcl->fsm_msg_cnt, acked) || less_eq(acked, n_ptr->bclink.acked)) goto exit; } /* Skip over packets that node has previously acknowledged */ - skb_queue_walk(&bcl->outqueue, skb) { + skb_queue_walk(&tn->bcl->outqueue, skb) { if (more(buf_seqno(skb), n_ptr->bclink.acked)) break; } /* Update packets that node is now acknowledging */ - skb_queue_walk_from_safe(&bcl->outqueue, skb, tmp) { + skb_queue_walk_from_safe(&tn->bcl->outqueue, skb, tmp) { if (more(buf_seqno(skb), acked)) break; - next = tipc_skb_queue_next(&bcl->outqueue, skb); - if (skb != bcl->next_out) { + next = tipc_skb_queue_next(&tn->bcl->outqueue, skb); + if (skb != tn->bcl->next_out) { bcbuf_decr_acks(skb); } else { bcbuf_set_acks(skb, 0); - bcl->next_out = next; - bclink_set_last_sent(); + tn->bcl->next_out = next; + bclink_set_last_sent(net); } if (bcbuf_acks(skb) == 0) { - __skb_unlink(skb, &bcl->outqueue); + __skb_unlink(skb, &tn->bcl->outqueue); kfree_skb(skb); released = 1; } @@ -310,15 +267,15 @@ void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked) n_ptr->bclink.acked = acked; /* Try resolving broadcast link congestion, if necessary */ - if (unlikely(bcl->next_out)) { - tipc_link_push_packets(bcl); - bclink_set_last_sent(); + if (unlikely(tn->bcl->next_out)) { + tipc_link_push_packets(tn->bcl); + bclink_set_last_sent(net); } - if (unlikely(released && !skb_queue_empty(&bcl->waiting_sks))) + if (unlikely(released && !skb_queue_empty(&tn->bcl->waiting_sks))) n_ptr->action_flags |= TIPC_WAKEUP_BCAST_USERS; exit: - tipc_bclink_unlock(); + tipc_bclink_unlock(net); } /** @@ -368,10 +325,10 @@ void tipc_bclink_update_link_state(struct net *net, struct tipc_node *n_ptr, msg_set_bcgap_after(msg, n_ptr->bclink.last_in); msg_set_bcgap_to(msg, to); - tipc_bclink_lock(); + tipc_bclink_lock(net); tipc_bearer_send(net, MAX_BEARERS, buf, NULL); - bcl->stats.sent_nacks++; - tipc_bclink_unlock(); + tn->bcl->stats.sent_nacks++; + tipc_bclink_unlock(net); kfree_skb(buf); n_ptr->bclink.oos_state++; @@ -410,6 +367,9 @@ static void bclink_peek_nack(struct net *net, struct tipc_msg *msg) */ int tipc_bclink_xmit(struct net *net, struct sk_buff_head *list) { + struct tipc_net *tn = net_generic(net, tipc_net_id); + struct tipc_link *bcl = tn->bcl; + struct tipc_bclink *bclink = tn->bclink; int rc = 0; int bc = 0; struct sk_buff *skb; @@ -423,19 +383,19 @@ int tipc_bclink_xmit(struct net *net, struct sk_buff_head *list) /* Broadcast to all other nodes */ if (likely(bclink)) { - tipc_bclink_lock(); + tipc_bclink_lock(net); if (likely(bclink->bcast_nodes.count)) { rc = __tipc_link_xmit(net, bcl, list); if (likely(!rc)) { u32 len = skb_queue_len(&bcl->outqueue); - bclink_set_last_sent(); + bclink_set_last_sent(net); bcl->stats.queue_sz_counts++; bcl->stats.accu_queue_sz += len; } bc = 1; } - tipc_bclink_unlock(); + tipc_bclink_unlock(net); } if (unlikely(!bc)) @@ -457,10 +417,12 @@ int tipc_bclink_xmit(struct net *net, struct sk_buff_head *list) */ static void bclink_accept_pkt(struct tipc_node *node, u32 seqno) { + struct tipc_net *tn = net_generic(node->net, tipc_net_id); + bclink_update_last_sent(node, seqno); node->bclink.last_in = seqno; node->bclink.oos_state = 0; - bcl->stats.recv_info++; + tn->bcl->stats.recv_info++; /* * Unicast an ACK periodically, ensuring that @@ -469,7 +431,7 @@ static void bclink_accept_pkt(struct tipc_node *node, u32 seqno) if (((seqno - tipc_own_addr) % TIPC_MIN_LINK_WIN) == 0) { tipc_link_proto_xmit(node->active_links[node->addr & 1], STATE_MSG, 0, 0, 0, 0, 0); - bcl->stats.sent_acks++; + tn->bcl->stats.sent_acks++; } } @@ -481,6 +443,7 @@ static void bclink_accept_pkt(struct tipc_node *node, u32 seqno) void tipc_bclink_rcv(struct net *net, struct sk_buff *buf) { struct tipc_net *tn = net_generic(net, tipc_net_id); + struct tipc_link *bcl = tn->bcl; struct tipc_msg *msg = buf_msg(buf); struct tipc_node *node; u32 next_in; @@ -506,12 +469,12 @@ void tipc_bclink_rcv(struct net *net, struct sk_buff *buf) if (msg_destnode(msg) == tipc_own_addr) { tipc_bclink_acknowledge(node, msg_bcast_ack(msg)); tipc_node_unlock(node); - tipc_bclink_lock(); + tipc_bclink_lock(net); bcl->stats.recv_nacks++; - bclink->retransmit_to = node; - bclink_retransmit_pkt(msg_bcgap_after(msg), + tn->bclink->retransmit_to = node; + bclink_retransmit_pkt(tn, msg_bcgap_after(msg), msg_bcgap_to(msg)); - tipc_bclink_unlock(); + tipc_bclink_unlock(net); } else { tipc_node_unlock(node); bclink_peek_nack(net, msg); @@ -527,47 +490,47 @@ void tipc_bclink_rcv(struct net *net, struct sk_buff *buf) receive: /* Deliver message to destination */ if (likely(msg_isdata(msg))) { - tipc_bclink_lock(); + tipc_bclink_lock(net); bclink_accept_pkt(node, seqno); - tipc_bclink_unlock(); + tipc_bclink_unlock(net); tipc_node_unlock(node); if (likely(msg_mcast(msg))) tipc_sk_mcast_rcv(net, buf); else kfree_skb(buf); } else if (msg_user(msg) == MSG_BUNDLER) { - tipc_bclink_lock(); + tipc_bclink_lock(net); bclink_accept_pkt(node, seqno); bcl->stats.recv_bundles++; bcl->stats.recv_bundled += msg_msgcnt(msg); - tipc_bclink_unlock(); + tipc_bclink_unlock(net); tipc_node_unlock(node); tipc_link_bundle_rcv(net, buf); } else if (msg_user(msg) == MSG_FRAGMENTER) { tipc_buf_append(&node->bclink.reasm_buf, &buf); if (unlikely(!buf && !node->bclink.reasm_buf)) goto unlock; - tipc_bclink_lock(); + tipc_bclink_lock(net); bclink_accept_pkt(node, seqno); bcl->stats.recv_fragments++; if (buf) { bcl->stats.recv_fragmented++; msg = buf_msg(buf); - tipc_bclink_unlock(); + tipc_bclink_unlock(net); goto receive; } - tipc_bclink_unlock(); + tipc_bclink_unlock(net); tipc_node_unlock(node); } else if (msg_user(msg) == NAME_DISTRIBUTOR) { - tipc_bclink_lock(); + tipc_bclink_lock(net); bclink_accept_pkt(node, seqno); - tipc_bclink_unlock(); + tipc_bclink_unlock(net); tipc_node_unlock(node); tipc_named_rcv(net, buf); } else { - tipc_bclink_lock(); + tipc_bclink_lock(net); bclink_accept_pkt(node, seqno); - tipc_bclink_unlock(); + tipc_bclink_unlock(net); tipc_node_unlock(node); kfree_skb(buf); } @@ -605,14 +568,14 @@ receive: buf = NULL; } - tipc_bclink_lock(); + tipc_bclink_lock(net); if (deferred) bcl->stats.deferred_recv++; else bcl->stats.duplicates++; - tipc_bclink_unlock(); + tipc_bclink_unlock(net); unlock: tipc_node_unlock(node); @@ -623,7 +586,7 @@ exit: u32 tipc_bclink_acks_missing(struct tipc_node *n_ptr) { return (n_ptr->bclink.recv_permitted && - (tipc_bclink_get_last_sent() != n_ptr->bclink.acked)); + (tipc_bclink_get_last_sent(n_ptr->net) != n_ptr->bclink.acked)); } @@ -636,13 +599,15 @@ u32 tipc_bclink_acks_missing(struct tipc_node *n_ptr) * Returns 0 (packet sent successfully) under all circumstances, * since the broadcast link's pseudo-bearer never blocks */ -static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1, +static int tipc_bcbearer_send(struct net *net, struct sk_buff *buf, + struct tipc_bearer *unused1, struct tipc_media_addr *unused2) { int bp_index; struct tipc_msg *msg = buf_msg(buf); - struct net *net = sock_net(buf->sk); struct tipc_net *tn = net_generic(net, tipc_net_id); + struct tipc_bcbearer *bcbearer = tn->bcbearer; + struct tipc_bclink *bclink = tn->bclink; /* Prepare broadcast link message for reliable transmission, * if first time trying to send it; @@ -653,7 +618,7 @@ static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1, bcbuf_set_acks(buf, bclink->bcast_nodes.count); msg_set_non_seq(msg, 1); msg_set_mc_netid(msg, tn->net_id); - bcl->stats.sent_info++; + tn->bcl->stats.sent_info++; if (WARN_ON(!bclink->bcast_nodes.count)) { dump_stack(); @@ -708,13 +673,14 @@ void tipc_bcbearer_sort(struct net *net, struct tipc_node_map *nm_ptr, u32 node, bool action) { struct tipc_net *tn = net_generic(net, tipc_net_id); + struct tipc_bcbearer *bcbearer = tn->bcbearer; struct tipc_bcbearer_pair *bp_temp = bcbearer->bpairs_temp; struct tipc_bcbearer_pair *bp_curr; struct tipc_bearer *b; int b_index; int pri; - tipc_bclink_lock(); + tipc_bclink_lock(net); if (action) tipc_nmap_add(nm_ptr, node); @@ -761,7 +727,7 @@ void tipc_bcbearer_sort(struct net *net, struct tipc_node_map *nm_ptr, bp_curr++; } - tipc_bclink_unlock(); + tipc_bclink_unlock(net); } static int __tipc_nl_add_bc_link_stat(struct sk_buff *skb, @@ -815,17 +781,19 @@ msg_full: return -EMSGSIZE; } -int tipc_nl_add_bc_link(struct tipc_nl_msg *msg) +int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg) { int err; void *hdr; struct nlattr *attrs; struct nlattr *prop; + struct tipc_net *tn = net_generic(net, tipc_net_id); + struct tipc_link *bcl = tn->bcl; if (!bcl) return 0; - tipc_bclink_lock(); + tipc_bclink_lock(net); hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family, NLM_F_MULTI, TIPC_NL_LINK_GET); @@ -860,7 +828,7 @@ int tipc_nl_add_bc_link(struct tipc_nl_msg *msg) if (err) goto attr_msg_full; - tipc_bclink_unlock(); + tipc_bclink_unlock(net); nla_nest_end(msg->skb, attrs); genlmsg_end(msg->skb, hdr); @@ -871,21 +839,23 @@ prop_msg_full: attr_msg_full: nla_nest_cancel(msg->skb, attrs); msg_full: - tipc_bclink_unlock(); + tipc_bclink_unlock(net); genlmsg_cancel(msg->skb, hdr); return -EMSGSIZE; } -int tipc_bclink_stats(char *buf, const u32 buf_size) +int tipc_bclink_stats(struct net *net, char *buf, const u32 buf_size) { int ret; struct tipc_stats *s; + struct tipc_net *tn = net_generic(net, tipc_net_id); + struct tipc_link *bcl = tn->bcl; if (!bcl) return 0; - tipc_bclink_lock(); + tipc_bclink_lock(net); s = &bcl->stats; @@ -914,37 +884,46 @@ int tipc_bclink_stats(char *buf, const u32 buf_size) s->queue_sz_counts ? (s->accu_queue_sz / s->queue_sz_counts) : 0); - tipc_bclink_unlock(); + tipc_bclink_unlock(net); return ret; } -int tipc_bclink_reset_stats(void) +int tipc_bclink_reset_stats(struct net *net) { + struct tipc_net *tn = net_generic(net, tipc_net_id); + struct tipc_link *bcl = tn->bcl; + if (!bcl) return -ENOPROTOOPT; - tipc_bclink_lock(); + tipc_bclink_lock(net); memset(&bcl->stats, 0, sizeof(bcl->stats)); - tipc_bclink_unlock(); + tipc_bclink_unlock(net); return 0; } -int tipc_bclink_set_queue_limits(u32 limit) +int tipc_bclink_set_queue_limits(struct net *net, u32 limit) { + struct tipc_net *tn = net_generic(net, tipc_net_id); + struct tipc_link *bcl = tn->bcl; + if (!bcl) return -ENOPROTOOPT; if ((limit < TIPC_MIN_LINK_WIN) || (limit > TIPC_MAX_LINK_WIN)) return -EINVAL; - tipc_bclink_lock(); + tipc_bclink_lock(net); tipc_link_set_queue_limits(bcl, limit); - tipc_bclink_unlock(); + tipc_bclink_unlock(net); return 0; } int tipc_bclink_init(struct net *net) { struct tipc_net *tn = net_generic(net, tipc_net_id); + struct tipc_bcbearer *bcbearer; + struct tipc_bclink *bclink; + struct tipc_link *bcl; bcbearer = kzalloc(sizeof(*bcbearer), GFP_ATOMIC); if (!bcbearer) @@ -969,12 +948,16 @@ int tipc_bclink_init(struct net *net) spin_lock_init(&bclink->node.lock); __skb_queue_head_init(&bclink->node.waiting_sks); bcl->owner = &bclink->node; + bcl->owner->net = net; bcl->max_pkt = MAX_PKT_DEFAULT_MCAST; tipc_link_set_queue_limits(bcl, BCLINK_WIN_DEFAULT); bcl->bearer_id = MAX_BEARERS; rcu_assign_pointer(tn->bearer_list[MAX_BEARERS], &bcbearer->bearer); bcl->state = WORKING_WORKING; strlcpy(bcl->name, tipc_bclink_name, TIPC_MAX_LINK_NAME); + tn->bcbearer = bcbearer; + tn->bclink = bclink; + tn->bcl = bcl; return 0; } @@ -982,14 +965,14 @@ void tipc_bclink_stop(struct net *net) { struct tipc_net *tn = net_generic(net, tipc_net_id); - tipc_bclink_lock(); - tipc_link_purge_queues(bcl); - tipc_bclink_unlock(); + tipc_bclink_lock(net); + tipc_link_purge_queues(tn->bcl); + tipc_bclink_unlock(net); RCU_INIT_POINTER(tn->bearer_list[BCBEARER], NULL); synchronize_net(); - kfree(bcbearer); - kfree(bclink); + kfree(tn->bcbearer); + kfree(tn->bclink); } /** diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h index 692efb6..a4583a1 100644 --- a/net/tipc/bcast.h +++ b/net/tipc/bcast.h @@ -37,23 +37,13 @@ #ifndef _TIPC_BCAST_H #define _TIPC_BCAST_H -#include "netlink.h" +#include +#include "link.h" +#include "node.h" -#define MAX_NODES 4096 -#define WSIZE 32 -#define TIPC_BCLINK_RESET 1 - -/** - * struct tipc_node_map - set of node identifiers - * @count: # of nodes in set - * @map: bitmap of node identifiers that are in the set - */ -struct tipc_node_map { - u32 count; - u32 map[MAX_NODES / WSIZE]; -}; - -#define PLSIZE 32 +#define TIPC_BCLINK_RESET 1 +#define PLSIZE 32 +#define BCBEARER MAX_BEARERS /** * struct tipc_port_list - set of node local destination ports @@ -67,9 +57,64 @@ struct tipc_port_list { u32 ports[PLSIZE]; }; +/** + * struct tipc_bcbearer_pair - a pair of bearers used by broadcast link + * @primary: pointer to primary bearer + * @secondary: pointer to secondary bearer + * + * Bearers must have same priority and same set of reachable destinations + * to be paired. + */ -struct tipc_node; +struct tipc_bcbearer_pair { + struct tipc_bearer *primary; + struct tipc_bearer *secondary; +}; +/** + * struct tipc_bcbearer - bearer used by broadcast link + * @bearer: (non-standard) broadcast bearer structure + * @media: (non-standard) broadcast media structure + * @bpairs: array of bearer pairs + * @bpairs_temp: temporary array of bearer pairs used by tipc_bcbearer_sort() + * @remains: temporary node map used by tipc_bcbearer_send() + * @remains_new: temporary node map used tipc_bcbearer_send() + * + * Note: The fields labelled "temporary" are incorporated into the bearer + * to avoid consuming potentially limited stack space through the use of + * large local variables within multicast routines. Concurrent access is + * prevented through use of the spinlock "bclink_lock". + */ +struct tipc_bcbearer { + struct tipc_bearer bearer; + struct tipc_media media; + struct tipc_bcbearer_pair bpairs[MAX_BEARERS]; + struct tipc_bcbearer_pair bpairs_temp[TIPC_MAX_LINK_PRI + 1]; + struct tipc_node_map remains; + struct tipc_node_map remains_new; +}; + +/** + * struct tipc_bclink - link used for broadcast messages + * @lock: spinlock governing access to structure + * @link: (non-standard) broadcast link structure + * @node: (non-standard) node structure representing b'cast link's peer node + * @flags: represent bclink states + * @bcast_nodes: map of broadcast-capable nodes + * @retransmit_to: node that most recently requested a retransmit + * + * Handles sequence numbering, fragmentation, bundling, etc. + */ +struct tipc_bclink { + spinlock_t lock; + struct tipc_link link; + struct tipc_node node; + unsigned int flags; + struct tipc_node_map bcast_nodes; + struct tipc_node *retransmit_to; +}; + +struct tipc_node; extern const char tipc_bclink_name[]; /** @@ -86,24 +131,24 @@ void tipc_port_list_free(struct tipc_port_list *pl_ptr); int tipc_bclink_init(struct net *net); void tipc_bclink_stop(struct net *net); -void tipc_bclink_set_flags(unsigned int flags); -void tipc_bclink_add_node(u32 addr); -void tipc_bclink_remove_node(u32 addr); -struct tipc_node *tipc_bclink_retransmit_to(void); +void tipc_bclink_set_flags(struct net *tn, unsigned int flags); +void tipc_bclink_add_node(struct net *net, u32 addr); +void tipc_bclink_remove_node(struct net *net, u32 addr); +struct tipc_node *tipc_bclink_retransmit_to(struct net *tn); void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked); void tipc_bclink_rcv(struct net *net, struct sk_buff *buf); -u32 tipc_bclink_get_last_sent(void); +u32 tipc_bclink_get_last_sent(struct net *net); u32 tipc_bclink_acks_missing(struct tipc_node *n_ptr); void tipc_bclink_update_link_state(struct net *net, struct tipc_node *n_ptr, u32 last_sent); -int tipc_bclink_stats(char *stats_buf, const u32 buf_size); -int tipc_bclink_reset_stats(void); -int tipc_bclink_set_queue_limits(u32 limit); +int tipc_bclink_stats(struct net *net, char *stats_buf, const u32 buf_size); +int tipc_bclink_reset_stats(struct net *net); +int tipc_bclink_set_queue_limits(struct net *net, u32 limit); void tipc_bcbearer_sort(struct net *net, struct tipc_node_map *nm_ptr, u32 node, bool action); uint tipc_bclink_get_mtu(void); int tipc_bclink_xmit(struct net *net, struct sk_buff_head *list); void tipc_bclink_wakeup_users(struct net *net); -int tipc_nl_add_bc_link(struct tipc_nl_msg *msg); +int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg); #endif diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 2fe9dcb..9a0d6ed 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -40,6 +40,7 @@ #include "bearer.h" #include "link.h" #include "discover.h" +#include "bcast.h" #define MAX_ADDR_STR 60 @@ -482,8 +483,8 @@ void tipc_disable_l2_media(struct tipc_bearer *b) * @b_ptr: the bearer through which the packet is to be sent * @dest: peer destination address */ -int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b, - struct tipc_media_addr *dest) +int tipc_l2_send_msg(struct net *net, struct sk_buff *buf, + struct tipc_bearer *b, struct tipc_media_addr *dest) { struct sk_buff *clone; struct net_device *dev; @@ -528,7 +529,7 @@ void tipc_bearer_send(struct net *net, u32 bearer_id, struct sk_buff *buf, rcu_read_lock(); b_ptr = rcu_dereference_rtnl(tn->bearer_list[bearer_id]); if (likely(b_ptr)) - b_ptr->media->send_msg(buf, b_ptr, dest); + b_ptr->media->send_msg(net, buf, b_ptr, dest); rcu_read_unlock(); } diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h index 01722d6..c035e3e 100644 --- a/net/tipc/bearer.h +++ b/net/tipc/bearer.h @@ -37,12 +37,13 @@ #ifndef _TIPC_BEARER_H #define _TIPC_BEARER_H -#include "bcast.h" #include "netlink.h" #include #define MAX_BEARERS 2 #define MAX_MEDIA 2 +#define MAX_NODES 4096 +#define WSIZE 32 /* Identifiers associated with TIPC message header media address info * - address info field is 32 bytes long @@ -59,6 +60,16 @@ #define TIPC_MEDIA_TYPE_IB 2 /** + * struct tipc_node_map - set of node identifiers + * @count: # of nodes in set + * @map: bitmap of node identifiers that are in the set + */ +struct tipc_node_map { + u32 count; + u32 map[MAX_NODES / WSIZE]; +}; + +/** * struct tipc_media_addr - destination address used by TIPC bearers * @value: address info (format defined by media) * @media_id: TIPC media type identifier @@ -89,7 +100,7 @@ struct tipc_bearer; * @name: media name */ struct tipc_media { - int (*send_msg)(struct sk_buff *buf, + int (*send_msg)(struct net *net, struct sk_buff *buf, struct tipc_bearer *b_ptr, struct tipc_media_addr *dest); int (*enable_media)(struct net *net, struct tipc_bearer *b_ptr); @@ -157,8 +168,6 @@ struct tipc_bearer_names { char if_name[TIPC_MAX_IF_NAME]; }; -struct tipc_link; - /* * TIPC routines available to supported media types */ @@ -193,8 +202,8 @@ void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a); struct sk_buff *tipc_media_get_names(void); int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b); void tipc_disable_l2_media(struct tipc_bearer *b); -int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b, - struct tipc_media_addr *dest); +int tipc_l2_send_msg(struct net *net, struct sk_buff *buf, + struct tipc_bearer *b, struct tipc_media_addr *dest); struct sk_buff *tipc_bearer_get_names(struct net *net); void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest); diff --git a/net/tipc/core.h b/net/tipc/core.h index 75a332b..3f6f9e0 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -61,6 +61,11 @@ #include "node.h" #include "bearer.h" +#include "bcast.h" +#include "netlink.h" +#include "link.h" +#include "node.h" +#include "msg.h" #define TIPC_MOD_VER "2.0.0" @@ -91,6 +96,11 @@ struct tipc_net { /* Bearer list */ struct tipc_bearer __rcu *bearer_list[MAX_BEARERS + 1]; + + /* Broadcast link */ + struct tipc_bcbearer *bcbearer; + struct tipc_bclink *bclink; + struct tipc_link *bcl; }; #ifdef CONFIG_SYSCTL diff --git a/net/tipc/link.c b/net/tipc/link.c index be21e6a..a84d5c6 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -941,6 +941,7 @@ static void link_retransmit_failure(struct tipc_link *l_ptr, struct sk_buff *buf) { struct tipc_msg *msg = buf_msg(buf); + struct net *net = l_ptr->owner->net; pr_warn("Retransmission failure on link <%s>\n", l_ptr->name); @@ -958,7 +959,7 @@ static void link_retransmit_failure(struct tipc_link *l_ptr, pr_cont("Outstanding acks: %lu\n", (unsigned long) TIPC_SKB_CB(buf)->handle); - n_ptr = tipc_bclink_retransmit_to(); + n_ptr = tipc_bclink_retransmit_to(net); tipc_node_lock(n_ptr); tipc_addr_string_fill(addr_string, n_ptr->addr); @@ -973,7 +974,7 @@ static void link_retransmit_failure(struct tipc_link *l_ptr, tipc_node_unlock(n_ptr); - tipc_bclink_set_flags(TIPC_BCLINK_RESET); + tipc_bclink_set_flags(net, TIPC_BCLINK_RESET); l_ptr->stale_count = 0; } } @@ -1404,7 +1405,7 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg, msg_set_type(msg, msg_typ); msg_set_net_plane(msg, l_ptr->net_plane); msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in); - msg_set_last_bcast(msg, tipc_bclink_get_last_sent()); + msg_set_last_bcast(msg, tipc_bclink_get_last_sent(l_ptr->owner->net)); if (msg_typ == STATE_MSG) { u32 next_sent = mod(l_ptr->next_out_no); @@ -2105,7 +2106,7 @@ struct sk_buff *tipc_link_cmd_config(struct net *net, const void *req_tlv_area, if (!strcmp(args->name, tipc_bclink_name)) { if ((cmd == TIPC_CMD_SET_LINK_WINDOW) && - (tipc_bclink_set_queue_limits(new_value) == 0)) + (tipc_bclink_set_queue_limits(net, new_value) == 0)) return tipc_cfg_reply_none(); return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED " (cannot change setting on broadcast link)"); @@ -2143,7 +2144,7 @@ struct sk_buff *tipc_link_cmd_reset_stats(struct net *net, link_name = (char *)TLV_DATA(req_tlv_area); if (!strcmp(link_name, tipc_bclink_name)) { - if (tipc_bclink_reset_stats()) + if (tipc_bclink_reset_stats(net)) return tipc_cfg_reply_error_string("link not found"); return tipc_cfg_reply_none(); } @@ -2191,7 +2192,7 @@ static int tipc_link_stats(struct net *net, const char *name, char *buf, int ret; if (!strcmp(name, tipc_bclink_name)) - return tipc_bclink_stats(buf, buf_size); + return tipc_bclink_stats(net, buf, buf_size); node = tipc_link_find_owner(net, name, &bearer_id); if (!node) @@ -2640,7 +2641,7 @@ int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb) prev_node = node->addr; } } else { - err = tipc_nl_add_bc_link(&msg); + err = tipc_nl_add_bc_link(net, &msg); if (err) goto out; @@ -2739,7 +2740,7 @@ int tipc_nl_link_reset_stats(struct sk_buff *skb, struct genl_info *info) link_name = nla_data(attrs[TIPC_NLA_LINK_NAME]); if (strcmp(link_name, tipc_bclink_name) == 0) { - err = tipc_bclink_reset_stats(); + err = tipc_bclink_reset_stats(net); if (err) return err; return 0; diff --git a/net/tipc/msg.h b/net/tipc/msg.h index 0065a2e..75f3242 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -37,7 +37,7 @@ #ifndef _TIPC_MSG_H #define _TIPC_MSG_H -#include "bearer.h" +#include /* * Constants and routines used to read and write TIPC payload message headers diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index cf17790..beed5fd 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -39,6 +39,7 @@ #include "name_table.h" #include "name_distr.h" #include "subscr.h" +#include "bcast.h" #define TIPC_NAMETBL_SIZE 1024 /* must be a power of 2 */ diff --git a/net/tipc/net.c b/net/tipc/net.c index 38633e5..7548ba8 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -41,6 +41,7 @@ #include "socket.h" #include "node.h" #include "config.h" +#include "bcast.h" static const struct nla_policy tipc_nl_net_policy[TIPC_NLA_NET_MAX + 1] = { [TIPC_NLA_NET_UNSPEC] = { .type = NLA_UNSPEC }, diff --git a/net/tipc/node.c b/net/tipc/node.c index a0ca1ac..3db5012 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -368,8 +368,8 @@ static void node_established_contact(struct tipc_node *n_ptr) { n_ptr->action_flags |= TIPC_NOTIFY_NODE_UP; n_ptr->bclink.oos_state = 0; - n_ptr->bclink.acked = tipc_bclink_get_last_sent(); - tipc_bclink_add_node(n_ptr->addr); + n_ptr->bclink.acked = tipc_bclink_get_last_sent(n_ptr->net); + tipc_bclink_add_node(n_ptr->net, n_ptr->addr); } static void node_lost_contact(struct tipc_node *n_ptr) @@ -389,7 +389,7 @@ static void node_lost_contact(struct tipc_node *n_ptr) n_ptr->bclink.reasm_buf = NULL; } - tipc_bclink_remove_node(n_ptr->addr); + tipc_bclink_remove_node(n_ptr->net, n_ptr->addr); tipc_bclink_acknowledge(n_ptr, INVALID_LINK_SEQ); n_ptr->bclink.recv_permitted = false; -- cgit v0.10.2 From e05b31f4bf8994d49322e9afb004ad479a129db0 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Fri, 9 Jan 2015 15:27:08 +0800 Subject: tipc: make tipc socket support net namespace Now tipc socket table is statically allocated as a global variable. Through it, we can look up one socket instance with port ID, insert a new socket instance to the table, and delete a socket from the table. But when tipc supports net namespace, each namespace must own its specific socket table. So the global variable of socket table must be redefined in tipc_net structure. As a concequence, a new socket table will be allocated when a new namespace is created, and a socket table will be deallocated when namespace is destroyed. Signed-off-by: Ying Xue Tested-by: Tero Aho Reviewed-by: Jon Maloy Signed-off-by: David S. Miller diff --git a/net/tipc/config.c b/net/tipc/config.c index ac73291..20b1c58 100644 --- a/net/tipc/config.c +++ b/net/tipc/config.c @@ -257,7 +257,7 @@ struct sk_buff *tipc_cfg_do_cmd(struct net *net, u32 orig_node, u16 cmd, rep_tlv_buf = tipc_media_get_names(); break; case TIPC_CMD_SHOW_PORTS: - rep_tlv_buf = tipc_sk_socks_show(); + rep_tlv_buf = tipc_sk_socks_show(net); break; case TIPC_CMD_SHOW_STATS: rep_tlv_buf = tipc_show_stats(); diff --git a/net/tipc/core.c b/net/tipc/core.c index 7b84439..23ff3ca 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -55,17 +55,20 @@ int sysctl_tipc_rmem[3] __read_mostly; /* min/default/max */ static int __net_init tipc_init_net(struct net *net) { struct tipc_net *tn = net_generic(net, tipc_net_id); + int err; tn->net_id = 4711; INIT_LIST_HEAD(&tn->node_list); spin_lock_init(&tn->node_list_lock); - return 0; + err = tipc_sk_rht_init(net); + return err; } static void __net_exit tipc_exit_net(struct net *net) { tipc_net_stop(net); + tipc_sk_rht_destroy(net); } static struct pernet_operations tipc_net_ops = { @@ -95,10 +98,6 @@ static int __init tipc_init(void) if (err) goto out_pernet; - err = tipc_sk_rht_init(); - if (err) - goto out_reftbl; - err = tipc_nametbl_init(); if (err) goto out_nametbl; @@ -136,8 +135,6 @@ out_socket: out_netlink: tipc_nametbl_stop(); out_nametbl: - tipc_sk_rht_destroy(); -out_reftbl: unregister_pernet_subsys(&tipc_net_ops); out_pernet: pr_err("Unable to start in single node mode\n"); @@ -153,7 +150,6 @@ static void __exit tipc_exit(void) tipc_nametbl_stop(); tipc_socket_stop(); tipc_unregister_sysctl(); - tipc_sk_rht_destroy(); pr_info("Deactivated\n"); } diff --git a/net/tipc/core.h b/net/tipc/core.h index 3f6f9e0..1a7f816 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -58,6 +58,7 @@ #include #include #include +#include #include "node.h" #include "bearer.h" @@ -101,6 +102,9 @@ struct tipc_net { struct tipc_bcbearer *bcbearer; struct tipc_bclink *bclink; struct tipc_link *bcl; + + /* Socket hash table */ + struct rhashtable sk_rht; }; #ifdef CONFIG_SYSCTL diff --git a/net/tipc/net.c b/net/tipc/net.c index 7548ba8..44ccf47 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -117,7 +117,7 @@ int tipc_net_start(struct net *net, u32 addr) tipc_own_addr = addr; tipc_named_reinit(); - tipc_sk_reinit(); + tipc_sk_reinit(net); res = tipc_bclink_init(net); if (res) return res; diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 6883145..accb02c 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -115,7 +115,7 @@ static int tipc_sk_publish(struct tipc_sock *tsk, uint scope, struct tipc_name_seq const *seq); static int tipc_sk_withdraw(struct tipc_sock *tsk, uint scope, struct tipc_name_seq const *seq); -static struct tipc_sock *tipc_sk_lookup(u32 portid); +static struct tipc_sock *tipc_sk_lookup(struct net *net, u32 portid); static int tipc_sk_insert(struct tipc_sock *tsk); static void tipc_sk_remove(struct tipc_sock *tsk); @@ -179,9 +179,6 @@ static const struct nla_policy tipc_nl_sock_policy[TIPC_NLA_SOCK_MAX + 1] = { * - port reference */ -/* Protects tipc socket hash table mutations */ -static struct rhashtable tipc_sk_rht; - static u32 tsk_peer_node(struct tipc_sock *tsk) { return msg_destnode(&tsk->phdr); @@ -1766,7 +1763,7 @@ int tipc_sk_rcv(struct net *net, struct sk_buff *skb) u32 dnode; /* Validate destination and message */ - tsk = tipc_sk_lookup(dport); + tsk = tipc_sk_lookup(net, dport); if (unlikely(!tsk)) { rc = tipc_msg_eval(skb, &dnode); goto exit; @@ -2245,8 +2242,9 @@ static int tipc_sk_show(struct tipc_sock *tsk, char *buf, return ret; } -struct sk_buff *tipc_sk_socks_show(void) +struct sk_buff *tipc_sk_socks_show(struct net *net) { + struct tipc_net *tn = net_generic(net, tipc_net_id); const struct bucket_table *tbl; struct rhash_head *pos; struct sk_buff *buf; @@ -2265,7 +2263,7 @@ struct sk_buff *tipc_sk_socks_show(void) pb_len = ULTRA_STRING_MAX_LEN; rcu_read_lock(); - tbl = rht_dereference_rcu((&tipc_sk_rht)->tbl, &tipc_sk_rht); + tbl = rht_dereference_rcu((&tn->sk_rht)->tbl, &tn->sk_rht); for (i = 0; i < tbl->size; i++) { rht_for_each_entry_rcu(tsk, pos, tbl, i, node) { spin_lock_bh(&tsk->sk.sk_lock.slock); @@ -2286,8 +2284,9 @@ struct sk_buff *tipc_sk_socks_show(void) /* tipc_sk_reinit: set non-zero address in all existing sockets * when we go from standalone to network mode. */ -void tipc_sk_reinit(void) +void tipc_sk_reinit(struct net *net) { + struct tipc_net *tn = net_generic(net, tipc_net_id); const struct bucket_table *tbl; struct rhash_head *pos; struct tipc_sock *tsk; @@ -2295,7 +2294,7 @@ void tipc_sk_reinit(void) int i; rcu_read_lock(); - tbl = rht_dereference_rcu((&tipc_sk_rht)->tbl, &tipc_sk_rht); + tbl = rht_dereference_rcu((&tn->sk_rht)->tbl, &tn->sk_rht); for (i = 0; i < tbl->size; i++) { rht_for_each_entry_rcu(tsk, pos, tbl, i, node) { spin_lock_bh(&tsk->sk.sk_lock.slock); @@ -2308,12 +2307,13 @@ void tipc_sk_reinit(void) rcu_read_unlock(); } -static struct tipc_sock *tipc_sk_lookup(u32 portid) +static struct tipc_sock *tipc_sk_lookup(struct net *net, u32 portid) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_sock *tsk; rcu_read_lock(); - tsk = rhashtable_lookup(&tipc_sk_rht, &portid); + tsk = rhashtable_lookup(&tn->sk_rht, &portid); if (tsk) sock_hold(&tsk->sk); rcu_read_unlock(); @@ -2323,6 +2323,9 @@ static struct tipc_sock *tipc_sk_lookup(u32 portid) static int tipc_sk_insert(struct tipc_sock *tsk) { + struct sock *sk = &tsk->sk; + struct net *net = sock_net(sk); + struct tipc_net *tn = net_generic(net, tipc_net_id); u32 remaining = (TIPC_MAX_PORT - TIPC_MIN_PORT) + 1; u32 portid = prandom_u32() % remaining + TIPC_MIN_PORT; @@ -2332,7 +2335,7 @@ static int tipc_sk_insert(struct tipc_sock *tsk) portid = TIPC_MIN_PORT; tsk->portid = portid; sock_hold(&tsk->sk); - if (rhashtable_lookup_insert(&tipc_sk_rht, &tsk->node)) + if (rhashtable_lookup_insert(&tn->sk_rht, &tsk->node)) return 0; sock_put(&tsk->sk); } @@ -2343,15 +2346,17 @@ static int tipc_sk_insert(struct tipc_sock *tsk) static void tipc_sk_remove(struct tipc_sock *tsk) { struct sock *sk = &tsk->sk; + struct tipc_net *tn = net_generic(sock_net(sk), tipc_net_id); - if (rhashtable_remove(&tipc_sk_rht, &tsk->node)) { + if (rhashtable_remove(&tn->sk_rht, &tsk->node)) { WARN_ON(atomic_read(&sk->sk_refcnt) == 1); __sock_put(sk); } } -int tipc_sk_rht_init(void) +int tipc_sk_rht_init(struct net *net) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct rhashtable_params rht_params = { .nelem_hint = 192, .head_offset = offsetof(struct tipc_sock, node), @@ -2364,15 +2369,17 @@ int tipc_sk_rht_init(void) .shrink_decision = rht_shrink_below_30, }; - return rhashtable_init(&tipc_sk_rht, &rht_params); + return rhashtable_init(&tn->sk_rht, &rht_params); } -void tipc_sk_rht_destroy(void) +void tipc_sk_rht_destroy(struct net *net) { + struct tipc_net *tn = net_generic(net, tipc_net_id); + /* Wait for socket readers to complete */ synchronize_net(); - rhashtable_destroy(&tipc_sk_rht); + rhashtable_destroy(&tn->sk_rht); } /** @@ -2730,10 +2737,12 @@ int tipc_nl_sk_dump(struct sk_buff *skb, struct netlink_callback *cb) struct rhash_head *pos; u32 prev_portid = cb->args[0]; u32 portid = prev_portid; + struct net *net = sock_net(skb->sk); + struct tipc_net *tn = net_generic(net, tipc_net_id); int i; rcu_read_lock(); - tbl = rht_dereference_rcu((&tipc_sk_rht)->tbl, &tipc_sk_rht); + tbl = rht_dereference_rcu((&tn->sk_rht)->tbl, &tn->sk_rht); for (i = 0; i < tbl->size; i++) { rht_for_each_entry_rcu(tsk, pos, tbl, i, node) { spin_lock_bh(&tsk->sk.sk_lock.slock); @@ -2839,6 +2848,7 @@ int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb) u32 tsk_portid = cb->args[0]; u32 last_publ = cb->args[1]; u32 done = cb->args[2]; + struct net *net = sock_net(skb->sk); struct tipc_sock *tsk; if (!tsk_portid) { @@ -2864,7 +2874,7 @@ int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb) if (done) return 0; - tsk = tipc_sk_lookup(tsk_portid); + tsk = tipc_sk_lookup(net, tsk_portid); if (!tsk) return -EINVAL; diff --git a/net/tipc/socket.h b/net/tipc/socket.h index eb15c31..c15c4e1 100644 --- a/net/tipc/socket.h +++ b/net/tipc/socket.h @@ -50,11 +50,11 @@ void tipc_sock_release_local(struct socket *sock); int tipc_sock_accept_local(struct socket *sock, struct socket **newsock, int flags); int tipc_sk_rcv(struct net *net, struct sk_buff *buf); -struct sk_buff *tipc_sk_socks_show(void); +struct sk_buff *tipc_sk_socks_show(struct net *net); void tipc_sk_mcast_rcv(struct net *net, struct sk_buff *buf); -void tipc_sk_reinit(void); -int tipc_sk_rht_init(void); -void tipc_sk_rht_destroy(void); +void tipc_sk_reinit(struct net *net); +int tipc_sk_rht_init(struct net *net); +void tipc_sk_rht_destroy(struct net *net); int tipc_nl_sk_dump(struct sk_buff *skb, struct netlink_callback *cb); int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb); -- cgit v0.10.2 From 4ac1c8d0ee9faf3a4be185cc4db1381fa0d81280 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Fri, 9 Jan 2015 15:27:09 +0800 Subject: tipc: name tipc name table support net namespace TIPC name table is used to store the mapping relationship between TIPC service name and socket port ID. When tipc supports namespace, it allows users to publish service names only owned by a certain namespace. Therefore, every namespace must have its private name table to prevent service names published to one namespace from being contaminated by other service names in another namespace. Therefore, The name table global variable (ie, nametbl) and its lock must be moved to tipc_net structure, and a parameter of namespace must be added for necessary functions so that they can obtain name table variable defined in tipc_net structure. Signed-off-by: Ying Xue Tested-by: Tero Aho Reviewed-by: Jon Maloy Signed-off-by: David S. Miller diff --git a/net/tipc/config.c b/net/tipc/config.c index 20b1c58..974723a 100644 --- a/net/tipc/config.c +++ b/net/tipc/config.c @@ -248,7 +248,8 @@ struct sk_buff *tipc_cfg_do_cmd(struct net *net, u32 orig_node, u16 cmd, req_tlv_space); break; case TIPC_CMD_SHOW_NAME_TABLE: - rep_tlv_buf = tipc_nametbl_get(req_tlv_area, req_tlv_space); + rep_tlv_buf = tipc_nametbl_get(net, req_tlv_area, + req_tlv_space); break; case TIPC_CMD_GET_BEARER_NAMES: rep_tlv_buf = tipc_bearer_get_names(net); diff --git a/net/tipc/core.c b/net/tipc/core.c index 23ff3ca..63cde81 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -62,12 +62,24 @@ static int __net_init tipc_init_net(struct net *net) spin_lock_init(&tn->node_list_lock); err = tipc_sk_rht_init(net); + if (err) + goto out_sk_rht; + + err = tipc_nametbl_init(net); + if (err) + goto out_nametbl; + return 0; + +out_nametbl: + tipc_sk_rht_destroy(net); +out_sk_rht: return err; } static void __net_exit tipc_exit_net(struct net *net) { tipc_net_stop(net); + tipc_nametbl_stop(net); tipc_sk_rht_destroy(net); } @@ -98,10 +110,6 @@ static int __init tipc_init(void) if (err) goto out_pernet; - err = tipc_nametbl_init(); - if (err) - goto out_nametbl; - err = tipc_netlink_start(); if (err) goto out_netlink; @@ -133,8 +141,6 @@ out_sysctl: out_socket: tipc_netlink_stop(); out_netlink: - tipc_nametbl_stop(); -out_nametbl: unregister_pernet_subsys(&tipc_net_ops); out_pernet: pr_err("Unable to start in single node mode\n"); @@ -147,7 +153,6 @@ static void __exit tipc_exit(void) tipc_bearer_cleanup(); tipc_netlink_stop(); tipc_subscr_stop(); - tipc_nametbl_stop(); tipc_socket_stop(); tipc_unregister_sysctl(); diff --git a/net/tipc/core.h b/net/tipc/core.h index 1a7f816..8939929 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -105,6 +105,10 @@ struct tipc_net { /* Socket hash table */ struct rhashtable sk_rht; + + /* Name table */ + spinlock_t nametbl_lock; + struct name_table *nametbl; }; #ifdef CONFIG_SYSCTL diff --git a/net/tipc/msg.c b/net/tipc/msg.c index a38f6a6..642fb13 100644 --- a/net/tipc/msg.c +++ b/net/tipc/msg.c @@ -426,7 +426,7 @@ exit: * Returns 0 (TIPC_OK) if message ok and we can try again, -TIPC error * code if message to be rejected */ -int tipc_msg_eval(struct sk_buff *buf, u32 *dnode) +int tipc_msg_eval(struct net *net, struct sk_buff *buf, u32 *dnode) { struct tipc_msg *msg = buf_msg(buf); u32 dport; @@ -441,7 +441,7 @@ int tipc_msg_eval(struct sk_buff *buf, u32 *dnode) return -TIPC_ERR_NO_NAME; *dnode = addr_domain(msg_lookup_scope(msg)); - dport = tipc_nametbl_translate(msg_nametype(msg), + dport = tipc_nametbl_translate(net, msg_nametype(msg), msg_nameinst(msg), dnode); if (!dport) diff --git a/net/tipc/msg.h b/net/tipc/msg.h index 75f3242..69f37e6 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -749,7 +749,7 @@ static inline u32 msg_tot_origport(struct tipc_msg *m) struct sk_buff *tipc_buf_acquire(u32 size); bool tipc_msg_reverse(struct sk_buff *buf, u32 *dnode, int err); -int tipc_msg_eval(struct sk_buff *buf, u32 *dnode); +int tipc_msg_eval(struct net *net, struct sk_buff *buf, u32 *dnode); void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, u32 hsize, u32 destnode); struct sk_buff *tipc_msg_create(uint user, uint type, uint hdr_sz, diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c index d40df58..ba42132 100644 --- a/net/tipc/name_distr.c +++ b/net/tipc/name_distr.c @@ -109,13 +109,14 @@ void named_cluster_distribute(struct net *net, struct sk_buff *skb) /** * tipc_named_publish - tell other nodes about a new publication by this node */ -struct sk_buff *tipc_named_publish(struct publication *publ) +struct sk_buff *tipc_named_publish(struct net *net, struct publication *publ) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct sk_buff *buf; struct distr_item *item; list_add_tail_rcu(&publ->local_list, - &tipc_nametbl->publ_list[publ->scope]); + &tn->nametbl->publ_list[publ->scope]); if (publ->scope == TIPC_NODE_SCOPE) return NULL; @@ -206,15 +207,16 @@ static void named_distribute(struct net *net, struct sk_buff_head *list, */ void tipc_named_node_up(struct net *net, u32 dnode) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct sk_buff_head head; __skb_queue_head_init(&head); rcu_read_lock(); named_distribute(net, &head, dnode, - &tipc_nametbl->publ_list[TIPC_CLUSTER_SCOPE]); + &tn->nametbl->publ_list[TIPC_CLUSTER_SCOPE]); named_distribute(net, &head, dnode, - &tipc_nametbl->publ_list[TIPC_ZONE_SCOPE]); + &tn->nametbl->publ_list[TIPC_ZONE_SCOPE]); rcu_read_unlock(); tipc_link_xmit(net, &head, dnode, dnode); @@ -262,14 +264,15 @@ static void tipc_publ_unsubscribe(struct net *net, struct publication *publ, */ static void tipc_publ_purge(struct net *net, struct publication *publ, u32 addr) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct publication *p; - spin_lock_bh(&tipc_nametbl_lock); - p = tipc_nametbl_remove_publ(publ->type, publ->lower, + spin_lock_bh(&tn->nametbl_lock); + p = tipc_nametbl_remove_publ(net, publ->type, publ->lower, publ->node, publ->ref, publ->key); if (p) tipc_publ_unsubscribe(net, p, addr); - spin_unlock_bh(&tipc_nametbl_lock); + spin_unlock_bh(&tn->nametbl_lock); if (p != publ) { pr_err("Unable to remove publication from failed node\n" @@ -302,7 +305,8 @@ static bool tipc_update_nametbl(struct net *net, struct distr_item *i, struct publication *publ = NULL; if (dtype == PUBLICATION) { - publ = tipc_nametbl_insert_publ(ntohl(i->type), ntohl(i->lower), + publ = tipc_nametbl_insert_publ(net, ntohl(i->type), + ntohl(i->lower), ntohl(i->upper), TIPC_CLUSTER_SCOPE, node, ntohl(i->ref), ntohl(i->key)); @@ -311,7 +315,8 @@ static bool tipc_update_nametbl(struct net *net, struct distr_item *i, return true; } } else if (dtype == WITHDRAWAL) { - publ = tipc_nametbl_remove_publ(ntohl(i->type), ntohl(i->lower), + publ = tipc_nametbl_remove_publ(net, ntohl(i->type), + ntohl(i->lower), node, ntohl(i->ref), ntohl(i->key)); if (publ) { @@ -376,19 +381,20 @@ void tipc_named_process_backlog(struct net *net) */ void tipc_named_rcv(struct net *net, struct sk_buff *buf) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_msg *msg = buf_msg(buf); struct distr_item *item = (struct distr_item *)msg_data(msg); u32 count = msg_data_sz(msg) / ITEM_SIZE; u32 node = msg_orignode(msg); - spin_lock_bh(&tipc_nametbl_lock); + spin_lock_bh(&tn->nametbl_lock); while (count--) { if (!tipc_update_nametbl(net, item, node, msg_type(msg))) tipc_named_add_backlog(item, msg_type(msg), node); item++; } tipc_named_process_backlog(net); - spin_unlock_bh(&tipc_nametbl_lock); + spin_unlock_bh(&tn->nametbl_lock); kfree_skb(buf); } @@ -399,17 +405,18 @@ void tipc_named_rcv(struct net *net, struct sk_buff *buf) * All name table entries published by this node are updated to reflect * the node's new network address. */ -void tipc_named_reinit(void) +void tipc_named_reinit(struct net *net) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct publication *publ; int scope; - spin_lock_bh(&tipc_nametbl_lock); + spin_lock_bh(&tn->nametbl_lock); for (scope = TIPC_ZONE_SCOPE; scope <= TIPC_NODE_SCOPE; scope++) - list_for_each_entry_rcu(publ, &tipc_nametbl->publ_list[scope], + list_for_each_entry_rcu(publ, &tn->nametbl->publ_list[scope], local_list) publ->node = tipc_own_addr; - spin_unlock_bh(&tipc_nametbl_lock); + spin_unlock_bh(&tn->nametbl_lock); } diff --git a/net/tipc/name_distr.h b/net/tipc/name_distr.h index 8039d84..1ed2d7e 100644 --- a/net/tipc/name_distr.h +++ b/net/tipc/name_distr.h @@ -67,12 +67,12 @@ struct distr_item { __be32 key; }; -struct sk_buff *tipc_named_publish(struct publication *publ); +struct sk_buff *tipc_named_publish(struct net *net, struct publication *publ); struct sk_buff *tipc_named_withdraw(struct publication *publ); void named_cluster_distribute(struct net *net, struct sk_buff *buf); void tipc_named_node_up(struct net *net, u32 dnode); void tipc_named_rcv(struct net *net, struct sk_buff *buf); -void tipc_named_reinit(void); +void tipc_named_reinit(struct net *net); void tipc_named_process_backlog(struct net *net); void tipc_publ_notify(struct net *net, struct list_head *nsub_list, u32 addr); diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index beed5fd..57e39c1 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -34,6 +34,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include #include "core.h" #include "config.h" #include "name_table.h" @@ -106,9 +107,6 @@ struct name_seq { struct rcu_head rcu; }; -struct name_table *tipc_nametbl; -DEFINE_SPINLOCK(tipc_nametbl_lock); - static int hash(int x) { return x & (TIPC_NAMETBL_SIZE - 1); @@ -448,12 +446,13 @@ static void tipc_nameseq_subscribe(struct name_seq *nseq, } } -static struct name_seq *nametbl_find_seq(u32 type) +static struct name_seq *nametbl_find_seq(struct net *net, u32 type) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct hlist_head *seq_head; struct name_seq *ns; - seq_head = &tipc_nametbl->seq_hlist[hash(type)]; + seq_head = &tn->nametbl->seq_hlist[hash(type)]; hlist_for_each_entry_rcu(ns, seq_head, ns_list) { if (ns->type == type) return ns; @@ -462,11 +461,13 @@ static struct name_seq *nametbl_find_seq(u32 type) return NULL; }; -struct publication *tipc_nametbl_insert_publ(u32 type, u32 lower, u32 upper, - u32 scope, u32 node, u32 port, u32 key) +struct publication *tipc_nametbl_insert_publ(struct net *net, u32 type, + u32 lower, u32 upper, u32 scope, + u32 node, u32 port, u32 key) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct publication *publ; - struct name_seq *seq = nametbl_find_seq(type); + struct name_seq *seq = nametbl_find_seq(net, type); int index = hash(type); if ((scope < TIPC_ZONE_SCOPE) || (scope > TIPC_NODE_SCOPE) || @@ -477,8 +478,7 @@ struct publication *tipc_nametbl_insert_publ(u32 type, u32 lower, u32 upper, } if (!seq) - seq = tipc_nameseq_create(type, - &tipc_nametbl->seq_hlist[index]); + seq = tipc_nameseq_create(type, &tn->nametbl->seq_hlist[index]); if (!seq) return NULL; @@ -489,11 +489,12 @@ struct publication *tipc_nametbl_insert_publ(u32 type, u32 lower, u32 upper, return publ; } -struct publication *tipc_nametbl_remove_publ(u32 type, u32 lower, - u32 node, u32 ref, u32 key) +struct publication *tipc_nametbl_remove_publ(struct net *net, u32 type, + u32 lower, u32 node, u32 ref, + u32 key) { struct publication *publ; - struct name_seq *seq = nametbl_find_seq(type); + struct name_seq *seq = nametbl_find_seq(net, type); if (!seq) return NULL; @@ -524,7 +525,8 @@ struct publication *tipc_nametbl_remove_publ(u32 type, u32 lower, * - if name translation is attempted and fails, sets 'destnode' to 0 * and returns 0 */ -u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *destnode) +u32 tipc_nametbl_translate(struct net *net, u32 type, u32 instance, + u32 *destnode) { struct sub_seq *sseq; struct name_info *info; @@ -537,7 +539,7 @@ u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *destnode) return 0; rcu_read_lock(); - seq = nametbl_find_seq(type); + seq = nametbl_find_seq(net, type); if (unlikely(!seq)) goto not_found; spin_lock_bh(&seq->lock); @@ -610,8 +612,8 @@ not_found: * * Returns non-zero if any off-node ports overlap */ -int tipc_nametbl_mc_translate(u32 type, u32 lower, u32 upper, u32 limit, - struct tipc_port_list *dports) +int tipc_nametbl_mc_translate(struct net *net, u32 type, u32 lower, u32 upper, + u32 limit, struct tipc_port_list *dports) { struct name_seq *seq; struct sub_seq *sseq; @@ -620,7 +622,7 @@ int tipc_nametbl_mc_translate(u32 type, u32 lower, u32 upper, u32 limit, int res = 0; rcu_read_lock(); - seq = nametbl_find_seq(type); + seq = nametbl_find_seq(net, type); if (!seq) goto exit; @@ -657,24 +659,25 @@ struct publication *tipc_nametbl_publish(struct net *net, u32 type, u32 lower, { struct publication *publ; struct sk_buff *buf = NULL; + struct tipc_net *tn = net_generic(net, tipc_net_id); - spin_lock_bh(&tipc_nametbl_lock); - if (tipc_nametbl->local_publ_count >= TIPC_MAX_PUBLICATIONS) { + spin_lock_bh(&tn->nametbl_lock); + if (tn->nametbl->local_publ_count >= TIPC_MAX_PUBLICATIONS) { pr_warn("Publication failed, local publication limit reached (%u)\n", TIPC_MAX_PUBLICATIONS); - spin_unlock_bh(&tipc_nametbl_lock); + spin_unlock_bh(&tn->nametbl_lock); return NULL; } - publ = tipc_nametbl_insert_publ(type, lower, upper, scope, - tipc_own_addr, port_ref, key); + publ = tipc_nametbl_insert_publ(net, type, lower, upper, scope, + tipc_own_addr, port_ref, key); if (likely(publ)) { - tipc_nametbl->local_publ_count++; - buf = tipc_named_publish(publ); + tn->nametbl->local_publ_count++; + buf = tipc_named_publish(net, publ); /* Any pending external events? */ tipc_named_process_backlog(net); } - spin_unlock_bh(&tipc_nametbl_lock); + spin_unlock_bh(&tn->nametbl_lock); if (buf) named_cluster_distribute(net, buf); @@ -689,11 +692,13 @@ int tipc_nametbl_withdraw(struct net *net, u32 type, u32 lower, u32 ref, { struct publication *publ; struct sk_buff *skb = NULL; + struct tipc_net *tn = net_generic(net, tipc_net_id); - spin_lock_bh(&tipc_nametbl_lock); - publ = tipc_nametbl_remove_publ(type, lower, tipc_own_addr, ref, key); + spin_lock_bh(&tn->nametbl_lock); + publ = tipc_nametbl_remove_publ(net, type, lower, tipc_own_addr, + ref, key); if (likely(publ)) { - tipc_nametbl->local_publ_count--; + tn->nametbl->local_publ_count--; skb = tipc_named_withdraw(publ); /* Any pending external events? */ tipc_named_process_backlog(net); @@ -704,7 +709,7 @@ int tipc_nametbl_withdraw(struct net *net, u32 type, u32 lower, u32 ref, "(type=%u, lower=%u, ref=%u, key=%u)\n", type, lower, ref, key); } - spin_unlock_bh(&tipc_nametbl_lock); + spin_unlock_bh(&tn->nametbl_lock); if (skb) { named_cluster_distribute(net, skb); @@ -718,15 +723,15 @@ int tipc_nametbl_withdraw(struct net *net, u32 type, u32 lower, u32 ref, */ void tipc_nametbl_subscribe(struct tipc_subscription *s) { + struct tipc_net *tn = net_generic(s->net, tipc_net_id); u32 type = s->seq.type; int index = hash(type); struct name_seq *seq; - spin_lock_bh(&tipc_nametbl_lock); - seq = nametbl_find_seq(type); + spin_lock_bh(&tn->nametbl_lock); + seq = nametbl_find_seq(s->net, type); if (!seq) - seq = tipc_nameseq_create(type, - &tipc_nametbl->seq_hlist[index]); + seq = tipc_nameseq_create(type, &tn->nametbl->seq_hlist[index]); if (seq) { spin_lock_bh(&seq->lock); tipc_nameseq_subscribe(seq, s); @@ -735,7 +740,7 @@ void tipc_nametbl_subscribe(struct tipc_subscription *s) pr_warn("Failed to create subscription for {%u,%u,%u}\n", s->seq.type, s->seq.lower, s->seq.upper); } - spin_unlock_bh(&tipc_nametbl_lock); + spin_unlock_bh(&tn->nametbl_lock); } /** @@ -743,10 +748,11 @@ void tipc_nametbl_subscribe(struct tipc_subscription *s) */ void tipc_nametbl_unsubscribe(struct tipc_subscription *s) { + struct tipc_net *tn = net_generic(s->net, tipc_net_id); struct name_seq *seq; - spin_lock_bh(&tipc_nametbl_lock); - seq = nametbl_find_seq(s->seq.type); + spin_lock_bh(&tn->nametbl_lock); + seq = nametbl_find_seq(s->net, s->seq.type); if (seq != NULL) { spin_lock_bh(&seq->lock); list_del_init(&s->nameseq_list); @@ -759,7 +765,7 @@ void tipc_nametbl_unsubscribe(struct tipc_subscription *s) spin_unlock_bh(&seq->lock); } } - spin_unlock_bh(&tipc_nametbl_lock); + spin_unlock_bh(&tn->nametbl_lock); } /** @@ -861,9 +867,10 @@ static int nametbl_header(char *buf, int len, u32 depth) /** * nametbl_list - print specified name table contents into the given buffer */ -static int nametbl_list(char *buf, int len, u32 depth_info, +static int nametbl_list(struct net *net, char *buf, int len, u32 depth_info, u32 type, u32 lowbound, u32 upbound) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct hlist_head *seq_head; struct name_seq *seq; int all_types; @@ -883,7 +890,7 @@ static int nametbl_list(char *buf, int len, u32 depth_info, lowbound = 0; upbound = ~0; for (i = 0; i < TIPC_NAMETBL_SIZE; i++) { - seq_head = &tipc_nametbl->seq_hlist[i]; + seq_head = &tn->nametbl->seq_hlist[i]; hlist_for_each_entry_rcu(seq, seq_head, ns_list) { ret += nameseq_list(seq, buf + ret, len - ret, depth, seq->type, @@ -899,7 +906,7 @@ static int nametbl_list(char *buf, int len, u32 depth_info, } ret += nametbl_header(buf + ret, len - ret, depth); i = hash(type); - seq_head = &tipc_nametbl->seq_hlist[i]; + seq_head = &tn->nametbl->seq_hlist[i]; hlist_for_each_entry_rcu(seq, seq_head, ns_list) { if (seq->type == type) { ret += nameseq_list(seq, buf + ret, len - ret, @@ -912,7 +919,8 @@ static int nametbl_list(char *buf, int len, u32 depth_info, return ret; } -struct sk_buff *tipc_nametbl_get(const void *req_tlv_area, int req_tlv_space) +struct sk_buff *tipc_nametbl_get(struct net *net, const void *req_tlv_area, + int req_tlv_space) { struct sk_buff *buf; struct tipc_name_table_query *argv; @@ -933,7 +941,7 @@ struct sk_buff *tipc_nametbl_get(const void *req_tlv_area, int req_tlv_space) pb_len = ULTRA_STRING_MAX_LEN; argv = (struct tipc_name_table_query *)TLV_DATA(req_tlv_area); rcu_read_lock(); - str_len = nametbl_list(pb, pb_len, ntohl(argv->depth), + str_len = nametbl_list(net, pb, pb_len, ntohl(argv->depth), ntohl(argv->type), ntohl(argv->lowbound), ntohl(argv->upbound)); rcu_read_unlock(); @@ -944,8 +952,10 @@ struct sk_buff *tipc_nametbl_get(const void *req_tlv_area, int req_tlv_space) return buf; } -int tipc_nametbl_init(void) +int tipc_nametbl_init(struct net *net) { + struct tipc_net *tn = net_generic(net, tipc_net_id); + struct name_table *tipc_nametbl; int i; tipc_nametbl = kzalloc(sizeof(*tipc_nametbl), GFP_ATOMIC); @@ -958,6 +968,8 @@ int tipc_nametbl_init(void) INIT_LIST_HEAD(&tipc_nametbl->publ_list[TIPC_ZONE_SCOPE]); INIT_LIST_HEAD(&tipc_nametbl->publ_list[TIPC_CLUSTER_SCOPE]); INIT_LIST_HEAD(&tipc_nametbl->publ_list[TIPC_NODE_SCOPE]); + tn->nametbl = tipc_nametbl; + spin_lock_init(&tn->nametbl_lock); return 0; } @@ -966,7 +978,7 @@ int tipc_nametbl_init(void) * * tipc_nametbl_lock must be held when calling this function */ -static void tipc_purge_publications(struct name_seq *seq) +static void tipc_purge_publications(struct net *net, struct name_seq *seq) { struct publication *publ, *safe; struct sub_seq *sseq; @@ -976,8 +988,8 @@ static void tipc_purge_publications(struct name_seq *seq) sseq = seq->sseqs; info = sseq->info; list_for_each_entry_safe(publ, safe, &info->zone_list, zone_list) { - tipc_nametbl_remove_publ(publ->type, publ->lower, publ->node, - publ->ref, publ->key); + tipc_nametbl_remove_publ(net, publ->type, publ->lower, + publ->node, publ->ref, publ->key); kfree_rcu(publ, rcu); } hlist_del_init_rcu(&seq->ns_list); @@ -987,25 +999,27 @@ static void tipc_purge_publications(struct name_seq *seq) kfree_rcu(seq, rcu); } -void tipc_nametbl_stop(void) +void tipc_nametbl_stop(struct net *net) { u32 i; struct name_seq *seq; struct hlist_head *seq_head; + struct tipc_net *tn = net_generic(net, tipc_net_id); + struct name_table *tipc_nametbl = tn->nametbl; /* Verify name table is empty and purge any lingering * publications, then release the name table */ - spin_lock_bh(&tipc_nametbl_lock); + spin_lock_bh(&tn->nametbl_lock); for (i = 0; i < TIPC_NAMETBL_SIZE; i++) { if (hlist_empty(&tipc_nametbl->seq_hlist[i])) continue; seq_head = &tipc_nametbl->seq_hlist[i]; hlist_for_each_entry_rcu(seq, seq_head, ns_list) { - tipc_purge_publications(seq); + tipc_purge_publications(net, seq); } } - spin_unlock_bh(&tipc_nametbl_lock); + spin_unlock_bh(&tn->nametbl_lock); synchronize_net(); kfree(tipc_nametbl); @@ -1109,9 +1123,10 @@ static int __tipc_nl_subseq_list(struct tipc_nl_msg *msg, struct name_seq *seq, return 0; } -static int __tipc_nl_seq_list(struct tipc_nl_msg *msg, u32 *last_type, - u32 *last_lower, u32 *last_publ) +static int tipc_nl_seq_list(struct net *net, struct tipc_nl_msg *msg, + u32 *last_type, u32 *last_lower, u32 *last_publ) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct hlist_head *seq_head; struct name_seq *seq = NULL; int err; @@ -1123,10 +1138,10 @@ static int __tipc_nl_seq_list(struct tipc_nl_msg *msg, u32 *last_type, i = 0; for (; i < TIPC_NAMETBL_SIZE; i++) { - seq_head = &tipc_nametbl->seq_hlist[i]; + seq_head = &tn->nametbl->seq_hlist[i]; if (*last_type) { - seq = nametbl_find_seq(*last_type); + seq = nametbl_find_seq(net, *last_type); if (!seq) return -EPIPE; } else { @@ -1160,6 +1175,7 @@ int tipc_nl_name_table_dump(struct sk_buff *skb, struct netlink_callback *cb) u32 last_type = cb->args[0]; u32 last_lower = cb->args[1]; u32 last_publ = cb->args[2]; + struct net *net = sock_net(skb->sk); struct tipc_nl_msg msg; if (done) @@ -1170,7 +1186,7 @@ int tipc_nl_name_table_dump(struct sk_buff *skb, struct netlink_callback *cb) msg.seq = cb->nlh->nlmsg_seq; rcu_read_lock(); - err = __tipc_nl_seq_list(&msg, &last_type, &last_lower, &last_publ); + err = tipc_nl_seq_list(net, &msg, &last_type, &last_lower, &last_publ); if (!err) { done = 1; } else if (err != -EMSGSIZE) { diff --git a/net/tipc/name_table.h b/net/tipc/name_table.h index efccaca..f67b3d8 100644 --- a/net/tipc/name_table.h +++ b/net/tipc/name_table.h @@ -95,28 +95,27 @@ struct name_table { u32 local_publ_count; }; -extern spinlock_t tipc_nametbl_lock; -extern struct name_table *tipc_nametbl; - int tipc_nl_name_table_dump(struct sk_buff *skb, struct netlink_callback *cb); -struct sk_buff *tipc_nametbl_get(const void *req_tlv_area, int req_tlv_space); -u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *node); -int tipc_nametbl_mc_translate(u32 type, u32 lower, u32 upper, u32 limit, - struct tipc_port_list *dports); +struct sk_buff *tipc_nametbl_get(struct net *net, const void *req_tlv_area, + int req_tlv_space); +u32 tipc_nametbl_translate(struct net *net, u32 type, u32 instance, u32 *node); +int tipc_nametbl_mc_translate(struct net *net, u32 type, u32 lower, u32 upper, + u32 limit, struct tipc_port_list *dports); struct publication *tipc_nametbl_publish(struct net *net, u32 type, u32 lower, u32 upper, u32 scope, u32 port_ref, u32 key); int tipc_nametbl_withdraw(struct net *net, u32 type, u32 lower, u32 ref, u32 key); -struct publication *tipc_nametbl_insert_publ(u32 type, u32 lower, u32 upper, - u32 scope, u32 node, u32 ref, +struct publication *tipc_nametbl_insert_publ(struct net *net, u32 type, + u32 lower, u32 upper, u32 scope, + u32 node, u32 ref, u32 key); +struct publication *tipc_nametbl_remove_publ(struct net *net, u32 type, + u32 lower, u32 node, u32 ref, u32 key); -struct publication *tipc_nametbl_remove_publ(u32 type, u32 lower, u32 node, - u32 ref, u32 key); void tipc_nametbl_subscribe(struct tipc_subscription *s); void tipc_nametbl_unsubscribe(struct tipc_subscription *s); -int tipc_nametbl_init(void); -void tipc_nametbl_stop(void); +int tipc_nametbl_init(struct net *net); +void tipc_nametbl_stop(struct net *net); #endif diff --git a/net/tipc/net.c b/net/tipc/net.c index 44ccf47..04445d21 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -116,7 +116,7 @@ int tipc_net_start(struct net *net, u32 addr) int res; tipc_own_addr = addr; - tipc_named_reinit(); + tipc_named_reinit(net); tipc_sk_reinit(net); res = tipc_bclink_init(net); if (res) diff --git a/net/tipc/server.c b/net/tipc/server.c index 869eb09..b5bdaf7 100644 --- a/net/tipc/server.c +++ b/net/tipc/server.c @@ -256,7 +256,8 @@ static int tipc_receive_from_sock(struct tipc_conn *con) goto out_close; } - s->tipc_conn_recvmsg(con->conid, &addr, con->usr_data, buf, ret); + s->tipc_conn_recvmsg(sock_net(con->sock->sk), con->conid, &addr, + con->usr_data, buf, ret); kmem_cache_free(s->rcvbuf_cache, buf); diff --git a/net/tipc/server.h b/net/tipc/server.h index 87bc05c..9c979a0 100644 --- a/net/tipc/server.h +++ b/net/tipc/server.h @@ -38,6 +38,7 @@ #include #include +#include #define TIPC_SERVER_NAME_LEN 32 @@ -66,10 +67,11 @@ struct tipc_server { struct workqueue_struct *rcv_wq; struct workqueue_struct *send_wq; int max_rcvbuf_size; - void *(*tipc_conn_new) (int conid); - void (*tipc_conn_shutdown) (int conid, void *usr_data); - void (*tipc_conn_recvmsg) (int conid, struct sockaddr_tipc *addr, - void *usr_data, void *buf, size_t len); + void *(*tipc_conn_new)(int conid); + void (*tipc_conn_shutdown)(int conid, void *usr_data); + void (*tipc_conn_recvmsg)(struct net *net, int conid, + struct sockaddr_tipc *addr, void *usr_data, + void *buf, size_t len); struct sockaddr_tipc *saddr; const char name[TIPC_SERVER_NAME_LEN]; int imp; diff --git a/net/tipc/socket.c b/net/tipc/socket.c index accb02c..4670e1e 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -778,11 +778,8 @@ void tipc_sk_mcast_rcv(struct net *net, struct sk_buff *buf) scope = TIPC_NODE_SCOPE; /* Create destination port list: */ - tipc_nametbl_mc_translate(msg_nametype(msg), - msg_namelower(msg), - msg_nameupper(msg), - scope, - &dports); + tipc_nametbl_mc_translate(net, msg_nametype(msg), msg_namelower(msg), + msg_nameupper(msg), scope, &dports); last = dports.count; if (!last) { kfree_skb(buf); @@ -943,7 +940,7 @@ static int tipc_sendmsg(struct kiocb *iocb, struct socket *sock, msg_set_nametype(mhdr, type); msg_set_nameinst(mhdr, inst); msg_set_lookup_scope(mhdr, tipc_addr_scope(domain)); - dport = tipc_nametbl_translate(type, inst, &dnode); + dport = tipc_nametbl_translate(net, type, inst, &dnode); msg_set_destnode(mhdr, dnode); msg_set_destport(mhdr, dport); if (unlikely(!dport && !dnode)) { @@ -1765,7 +1762,7 @@ int tipc_sk_rcv(struct net *net, struct sk_buff *skb) /* Validate destination and message */ tsk = tipc_sk_lookup(net, dport); if (unlikely(!tsk)) { - rc = tipc_msg_eval(skb, &dnode); + rc = tipc_msg_eval(net, skb, &dnode); goto exit; } sk = &tsk->sk; diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c index e6cb959..b71dbc0 100644 --- a/net/tipc/subscr.c +++ b/net/tipc/subscr.c @@ -50,8 +50,9 @@ struct tipc_subscriber { struct list_head subscription_list; }; -static void subscr_conn_msg_event(int conid, struct sockaddr_tipc *addr, - void *usr_data, void *buf, size_t len); +static void subscr_conn_msg_event(struct net *net, int conid, + struct sockaddr_tipc *addr, void *usr_data, + void *buf, size_t len); static void *subscr_named_msg_event(int conid); static void subscr_conn_shutdown_event(int conid, void *usr_data); @@ -260,7 +261,7 @@ static void subscr_cancel(struct tipc_subscr *s, * * Called with subscriber lock held. */ -static int subscr_subscribe(struct tipc_subscr *s, +static int subscr_subscribe(struct net *net, struct tipc_subscr *s, struct tipc_subscriber *subscriber, struct tipc_subscription **sub_p) { struct tipc_subscription *sub; @@ -291,6 +292,7 @@ static int subscr_subscribe(struct tipc_subscr *s, } /* Initialize subscription object */ + sub->net = net; sub->seq.type = htohl(s->seq.type, swap); sub->seq.lower = htohl(s->seq.lower, swap); sub->seq.upper = htohl(s->seq.upper, swap); @@ -323,14 +325,16 @@ static void subscr_conn_shutdown_event(int conid, void *usr_data) } /* Handle one request to create a new subscription for the subscriber */ -static void subscr_conn_msg_event(int conid, struct sockaddr_tipc *addr, - void *usr_data, void *buf, size_t len) +static void subscr_conn_msg_event(struct net *net, int conid, + struct sockaddr_tipc *addr, void *usr_data, + void *buf, size_t len) { struct tipc_subscriber *subscriber = usr_data; struct tipc_subscription *sub = NULL; spin_lock_bh(&subscriber->lock); - if (subscr_subscribe((struct tipc_subscr *)buf, subscriber, &sub) < 0) { + if (subscr_subscribe(net, (struct tipc_subscr *)buf, subscriber, + &sub) < 0) { spin_unlock_bh(&subscriber->lock); subscr_terminate(subscriber); return; diff --git a/net/tipc/subscr.h b/net/tipc/subscr.h index 0d39589..670f570 100644 --- a/net/tipc/subscr.h +++ b/net/tipc/subscr.h @@ -49,6 +49,7 @@ struct tipc_subscriber; * struct tipc_subscription - TIPC network topology subscription object * @subscriber: pointer to its subscriber * @seq: name sequence associated with subscription + * @net: point to network namespace * @timeout: duration of subscription (in ms) * @filter: event filtering to be done for subscription * @timer: timer governing subscription duration (optional) @@ -61,6 +62,7 @@ struct tipc_subscriber; struct tipc_subscription { struct tipc_subscriber *subscriber; struct tipc_name_seq seq; + struct net *net; unsigned long timeout; u32 filter; struct timer_list timer; -- cgit v0.10.2 From 347475395434abb2b61bf59c2952470f37072567 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Fri, 9 Jan 2015 15:27:10 +0800 Subject: tipc: make tipc node address support net namespace If net namespace is supported in tipc, each namespace will be treated as a separate tipc node. Therefore, every namespace must own its private tipc node address. This means the "tipc_own_addr" global variable of node address must be moved to tipc_net structure to satisfy the requirement. It's turned out that users also can assign node address for every namespace. Signed-off-by: Ying Xue Tested-by: Tero Aho Reviewed-by: Jon Maloy Signed-off-by: David S. Miller diff --git a/net/tipc/addr.c b/net/tipc/addr.c index 9e6eeb4..48fd3b5 100644 --- a/net/tipc/addr.c +++ b/net/tipc/addr.c @@ -36,6 +36,49 @@ #include #include "addr.h" +#include "core.h" + +/** + * in_own_cluster - test for cluster inclusion; <0.0.0> always matches + */ +int in_own_cluster(struct net *net, u32 addr) +{ + return in_own_cluster_exact(net, addr) || !addr; +} + +int in_own_cluster_exact(struct net *net, u32 addr) +{ + struct tipc_net *tn = net_generic(net, tipc_net_id); + + return !((addr ^ tn->own_addr) >> 12); +} + +/** + * in_own_node - test for node inclusion; <0.0.0> always matches + */ +int in_own_node(struct net *net, u32 addr) +{ + struct tipc_net *tn = net_generic(net, tipc_net_id); + + return (addr == tn->own_addr) || !addr; +} + +/** + * addr_domain - convert 2-bit scope value to equivalent message lookup domain + * + * Needed when address of a named message must be looked up a second time + * after a network hop. + */ +u32 addr_domain(struct net *net, u32 sc) +{ + struct tipc_net *tn = net_generic(net, tipc_net_id); + + if (likely(sc == TIPC_NODE_SCOPE)) + return tn->own_addr; + if (sc == TIPC_CLUSTER_SCOPE) + return tipc_cluster_mask(tn->own_addr); + return tipc_zone_mask(tn->own_addr); +} /** * tipc_addr_domain_valid - validates a network domain address diff --git a/net/tipc/addr.h b/net/tipc/addr.h index 4e364c4..c700c2d 100644 --- a/net/tipc/addr.h +++ b/net/tipc/addr.h @@ -39,12 +39,12 @@ #include #include +#include +#include #define TIPC_ZONE_MASK 0xff000000u #define TIPC_CLUSTER_MASK 0xfffff000u -extern u32 tipc_own_addr __read_mostly; - static inline u32 tipc_zone_mask(u32 addr) { return addr & TIPC_ZONE_MASK; @@ -55,42 +55,10 @@ static inline u32 tipc_cluster_mask(u32 addr) return addr & TIPC_CLUSTER_MASK; } -static inline int in_own_cluster_exact(u32 addr) -{ - return !((addr ^ tipc_own_addr) >> 12); -} - -/** - * in_own_node - test for node inclusion; <0.0.0> always matches - */ -static inline int in_own_node(u32 addr) -{ - return (addr == tipc_own_addr) || !addr; -} - -/** - * in_own_cluster - test for cluster inclusion; <0.0.0> always matches - */ -static inline int in_own_cluster(u32 addr) -{ - return in_own_cluster_exact(addr) || !addr; -} - -/** - * addr_domain - convert 2-bit scope value to equivalent message lookup domain - * - * Needed when address of a named message must be looked up a second time - * after a network hop. - */ -static inline u32 addr_domain(u32 sc) -{ - if (likely(sc == TIPC_NODE_SCOPE)) - return tipc_own_addr; - if (sc == TIPC_CLUSTER_SCOPE) - return tipc_cluster_mask(tipc_own_addr); - return tipc_zone_mask(tipc_own_addr); -} - +int in_own_cluster(struct net *net, u32 addr); +int in_own_cluster_exact(struct net *net, u32 addr); +int in_own_node(struct net *net, u32 addr); +u32 addr_domain(struct net *net, u32 sc); int tipc_addr_domain_valid(u32); int tipc_addr_node_valid(u32 addr); int tipc_in_scope(u32 domain, u32 addr); diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index bc58097..53f8bf0 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -317,7 +317,7 @@ void tipc_bclink_update_link_state(struct net *net, struct tipc_node *n_ptr, struct sk_buff *skb = skb_peek(&n_ptr->bclink.deferred_queue); u32 to = skb ? buf_seqno(skb) - 1 : n_ptr->bclink.last_sent; - tipc_msg_init(msg, BCAST_PROTOCOL, STATE_MSG, + tipc_msg_init(net, msg, BCAST_PROTOCOL, STATE_MSG, INT_H_SIZE, n_ptr->addr); msg_set_non_seq(msg, 1); msg_set_mc_netid(msg, tn->net_id); @@ -428,7 +428,7 @@ static void bclink_accept_pkt(struct tipc_node *node, u32 seqno) * Unicast an ACK periodically, ensuring that * all nodes in the cluster don't ACK at the same time */ - if (((seqno - tipc_own_addr) % TIPC_MIN_LINK_WIN) == 0) { + if (((seqno - tn->own_addr) % TIPC_MIN_LINK_WIN) == 0) { tipc_link_proto_xmit(node->active_links[node->addr & 1], STATE_MSG, 0, 0, 0, 0, 0); tn->bcl->stats.sent_acks++; @@ -466,7 +466,7 @@ void tipc_bclink_rcv(struct net *net, struct sk_buff *buf) if (unlikely(msg_user(msg) == BCAST_PROTOCOL)) { if (msg_type(msg) != STATE_MSG) goto unlock; - if (msg_destnode(msg) == tipc_own_addr) { + if (msg_destnode(msg) == tn->own_addr) { tipc_bclink_acknowledge(node, msg_bcast_ack(msg)); tipc_node_unlock(node); tipc_bclink_lock(net); diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 9a0d6ed..33dc348 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -278,7 +278,7 @@ int tipc_enable_bearer(struct net *net, const char *name, u32 disc_domain, u32 i; int res = -EINVAL; - if (!tipc_own_addr) { + if (!tn->own_addr) { pr_warn("Bearer <%s> rejected, not supported in standalone mode\n", name); return -ENOPROTOOPT; @@ -288,11 +288,11 @@ int tipc_enable_bearer(struct net *net, const char *name, u32 disc_domain, return -EINVAL; } if (tipc_addr_domain_valid(disc_domain) && - (disc_domain != tipc_own_addr)) { - if (tipc_in_scope(disc_domain, tipc_own_addr)) { - disc_domain = tipc_own_addr & TIPC_CLUSTER_MASK; + (disc_domain != tn->own_addr)) { + if (tipc_in_scope(disc_domain, tn->own_addr)) { + disc_domain = tn->own_addr & TIPC_CLUSTER_MASK; res = 0; /* accept any node in own cluster */ - } else if (in_own_cluster_exact(disc_domain)) + } else if (in_own_cluster_exact(net, disc_domain)) res = 0; /* accept specified node in own cluster */ } if (res) { @@ -817,6 +817,7 @@ int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info) int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info) { struct net *net = genl_info_net(info); + struct tipc_net *tn = net_generic(net, tipc_net_id); int err; char *bearer; struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1]; @@ -824,7 +825,7 @@ int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info) u32 prio; prio = TIPC_MEDIA_LINK_PRI; - domain = tipc_own_addr & TIPC_CLUSTER_MASK; + domain = tn->own_addr & TIPC_CLUSTER_MASK; if (!info->attrs[TIPC_NLA_BEARER]) return -EINVAL; diff --git a/net/tipc/config.c b/net/tipc/config.c index 974723a..6873360 100644 --- a/net/tipc/config.c +++ b/net/tipc/config.c @@ -163,18 +163,19 @@ static struct sk_buff *cfg_disable_bearer(struct net *net) static struct sk_buff *cfg_set_own_addr(struct net *net) { + struct tipc_net *tn = net_generic(net, tipc_net_id); u32 addr; if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_NET_ADDR)) return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); addr = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); - if (addr == tipc_own_addr) + if (addr == tn->own_addr) return tipc_cfg_reply_none(); if (!tipc_addr_node_valid(addr)) return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE " (node address)"); - if (tipc_own_addr) + if (tn->own_addr) return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED " (cannot change node address once assigned)"); if (!tipc_net_start(net, addr)) @@ -196,7 +197,7 @@ static struct sk_buff *cfg_set_netid(struct net *net) if (value < 1 || value > 9999) return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE " (network id must be 1-9999)"); - if (tipc_own_addr) + if (tn->own_addr) return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED " (cannot change network id once TIPC has joined a network)"); tn->net_id = value; @@ -218,7 +219,7 @@ struct sk_buff *tipc_cfg_do_cmd(struct net *net, u32 orig_node, u16 cmd, rep_headroom = reply_headroom; /* Check command authorization */ - if (likely(in_own_node(orig_node))) { + if (likely(in_own_node(net, orig_node))) { /* command is permitted */ } else { rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED diff --git a/net/tipc/core.c b/net/tipc/core.c index 63cde81..7c09670 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -48,7 +48,6 @@ int tipc_random __read_mostly; /* configurable TIPC parameters */ -u32 tipc_own_addr __read_mostly; int tipc_net_id __read_mostly; int sysctl_tipc_rmem[3] __read_mostly; /* min/default/max */ @@ -58,6 +57,7 @@ static int __net_init tipc_init_net(struct net *net) int err; tn->net_id = 4711; + tn->own_addr = 0; INIT_LIST_HEAD(&tn->node_list); spin_lock_init(&tn->node_list_lock); @@ -96,8 +96,6 @@ static int __init tipc_init(void) pr_info("Activated (version " TIPC_MOD_VER ")\n"); - tipc_own_addr = 0; - sysctl_tipc_rmem[0] = TIPC_CONN_OVERLOAD_LIMIT >> 4 << TIPC_LOW_IMPORTANCE; sysctl_tipc_rmem[1] = TIPC_CONN_OVERLOAD_LIMIT >> 4 << diff --git a/net/tipc/core.h b/net/tipc/core.h index 8939929..afabf39 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -72,10 +72,6 @@ int tipc_snprintf(char *buf, int len, const char *fmt, ...); -/* - * Global configuration variables - */ -extern u32 tipc_own_addr __read_mostly; extern int tipc_net_id __read_mostly; extern int sysctl_tipc_rmem[3] __read_mostly; extern int sysctl_tipc_named_timeout __read_mostly; @@ -86,6 +82,7 @@ extern int sysctl_tipc_named_timeout __read_mostly; extern int tipc_random __read_mostly; struct tipc_net { + u32 own_addr; int net_id; /* Node table and node list */ diff --git a/net/tipc/discover.c b/net/tipc/discover.c index f93dd3d..786411d 100644 --- a/net/tipc/discover.c +++ b/net/tipc/discover.c @@ -86,7 +86,7 @@ static void tipc_disc_init_msg(struct net *net, struct sk_buff *buf, u32 type, u32 dest_domain = b_ptr->domain; msg = buf_msg(buf); - tipc_msg_init(msg, LINK_CONFIG, type, INT_H_SIZE, dest_domain); + tipc_msg_init(net, msg, LINK_CONFIG, type, INT_H_SIZE, dest_domain); msg_set_non_seq(msg, 1); msg_set_node_sig(msg, tipc_random); msg_set_dest_domain(msg, dest_domain); @@ -153,12 +153,12 @@ void tipc_disc_rcv(struct net *net, struct sk_buff *buf, if (!tipc_addr_node_valid(onode)) return; - if (in_own_node(onode)) { + if (in_own_node(net, onode)) { if (memcmp(&maddr, &bearer->addr, sizeof(maddr))) - disc_dupl_alert(bearer, tipc_own_addr, &maddr); + disc_dupl_alert(bearer, tn->own_addr, &maddr); return; } - if (!tipc_in_scope(ddom, tipc_own_addr)) + if (!tipc_in_scope(ddom, tn->own_addr)) return; if (!tipc_in_scope(bearer->domain, onode)) return; diff --git a/net/tipc/link.c b/net/tipc/link.c index a84d5c6..9972567 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -241,6 +241,7 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr, struct tipc_bearer *b_ptr, const struct tipc_media_addr *media_addr) { + struct tipc_net *tn = net_generic(n_ptr->net, tipc_net_id); struct tipc_link *l_ptr; struct tipc_msg *msg; char *if_name; @@ -270,8 +271,8 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr, l_ptr->addr = peer; if_name = strchr(b_ptr->name, ':') + 1; sprintf(l_ptr->name, "%u.%u.%u:%s-%u.%u.%u:unknown", - tipc_zone(tipc_own_addr), tipc_cluster(tipc_own_addr), - tipc_node(tipc_own_addr), + tipc_zone(tn->own_addr), tipc_cluster(tn->own_addr), + tipc_node(tn->own_addr), if_name, tipc_zone(peer), tipc_cluster(peer), tipc_node(peer)); /* note: peer i/f name is updated by reset/activate message */ @@ -285,7 +286,8 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr, l_ptr->pmsg = (struct tipc_msg *)&l_ptr->proto_msg; msg = l_ptr->pmsg; - tipc_msg_init(msg, LINK_PROTOCOL, RESET_MSG, INT_H_SIZE, l_ptr->addr); + tipc_msg_init(n_ptr->net, msg, LINK_PROTOCOL, RESET_MSG, INT_H_SIZE, + l_ptr->addr); msg_set_size(msg, sizeof(l_ptr->proto_msg)); msg_set_session(msg, (tipc_random & 0xffff)); msg_set_bearer_id(msg, b_ptr->identity); @@ -358,10 +360,12 @@ void tipc_link_delete_list(struct net *net, unsigned int bearer_id, static bool link_schedule_user(struct tipc_link *link, u32 oport, uint chain_sz, uint imp) { + struct net *net = link->owner->net; + struct tipc_net *tn = net_generic(net, tipc_net_id); struct sk_buff *buf; - buf = tipc_msg_create(SOCK_WAKEUP, 0, INT_H_SIZE, 0, tipc_own_addr, - tipc_own_addr, oport, 0, 0); + buf = tipc_msg_create(net, SOCK_WAKEUP, 0, INT_H_SIZE, 0, tn->own_addr, + tn->own_addr, oport, 0, 0); if (!buf) return false; TIPC_SKB_CB(buf)->chain_sz = chain_sz; @@ -753,7 +757,7 @@ int __tipc_link_xmit(struct net *net, struct tipc_link *link, } else if (tipc_msg_bundle(outqueue, skb, mtu)) { link->stats.sent_bundled++; continue; - } else if (tipc_msg_make_bundle(outqueue, skb, mtu, + } else if (tipc_msg_make_bundle(net, outqueue, skb, mtu, link->addr)) { link->stats.sent_bundled++; link->stats.sent_bundles++; @@ -822,7 +826,7 @@ int tipc_link_xmit(struct net *net, struct sk_buff_head *list, u32 dnode, if (link) return rc; - if (likely(in_own_node(dnode))) { + if (likely(in_own_node(net, dnode))) { /* As a node local message chain never contains more than one * buffer, we just need to dequeue one SKB buffer from the * head list. @@ -852,7 +856,8 @@ static void tipc_link_sync_xmit(struct tipc_link *link) return; msg = buf_msg(skb); - tipc_msg_init(msg, BCAST_PROTOCOL, STATE_MSG, INT_H_SIZE, link->addr); + tipc_msg_init(link->owner->net, msg, BCAST_PROTOCOL, STATE_MSG, + INT_H_SIZE, link->addr); msg_set_last_bcast(msg, link->owner->bclink.acked); __tipc_link_xmit_skb(link, skb); } @@ -1092,6 +1097,7 @@ static int link_recv_buf_validate(struct sk_buff *buf) */ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct sk_buff_head head; struct tipc_node *n_ptr; struct tipc_link *l_ptr; @@ -1125,7 +1131,7 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr) /* Discard unicast link messages destined for another node */ if (unlikely(!msg_short(msg) && - (msg_destnode(msg) != tipc_own_addr))) + (msg_destnode(msg) != tn->own_addr))) goto discard; /* Locate neighboring node that sent message */ @@ -1483,6 +1489,7 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg, static void tipc_link_proto_rcv(struct net *net, struct tipc_link *l_ptr, struct sk_buff *buf) { + struct tipc_net *tn = net_generic(net, tipc_net_id); u32 rec_gap = 0; u32 max_pkt_info; u32 max_pkt_ack; @@ -1494,7 +1501,7 @@ static void tipc_link_proto_rcv(struct net *net, struct tipc_link *l_ptr, goto exit; if (l_ptr->net_plane != msg_net_plane(msg)) - if (tipc_own_addr > msg_prevnode(msg)) + if (tn->own_addr > msg_prevnode(msg)) l_ptr->net_plane = msg_net_plane(msg); switch (msg_type(msg)) { @@ -1662,8 +1669,8 @@ void tipc_link_failover_send_queue(struct tipc_link *l_ptr) if (!tunnel) return; - tipc_msg_init(&tunnel_hdr, CHANGEOVER_PROTOCOL, - ORIGINAL_MSG, INT_H_SIZE, l_ptr->addr); + tipc_msg_init(l_ptr->owner->net, &tunnel_hdr, CHANGEOVER_PROTOCOL, + ORIGINAL_MSG, INT_H_SIZE, l_ptr->addr); msg_set_bearer_id(&tunnel_hdr, l_ptr->peer_bearer_id); msg_set_msgcnt(&tunnel_hdr, msgcount); @@ -1720,8 +1727,8 @@ void tipc_link_dup_queue_xmit(struct tipc_link *l_ptr, struct sk_buff *skb; struct tipc_msg tunnel_hdr; - tipc_msg_init(&tunnel_hdr, CHANGEOVER_PROTOCOL, - DUPLICATE_MSG, INT_H_SIZE, l_ptr->addr); + tipc_msg_init(l_ptr->owner->net, &tunnel_hdr, CHANGEOVER_PROTOCOL, + DUPLICATE_MSG, INT_H_SIZE, l_ptr->addr); msg_set_msgcnt(&tunnel_hdr, skb_queue_len(&l_ptr->outqueue)); msg_set_bearer_id(&tunnel_hdr, l_ptr->peer_bearer_id); skb_queue_walk(&l_ptr->outqueue, skb) { @@ -2506,12 +2513,14 @@ msg_full: } /* Caller should hold appropriate locks to protect the link */ -static int __tipc_nl_add_link(struct tipc_nl_msg *msg, struct tipc_link *link) +static int __tipc_nl_add_link(struct net *net, struct tipc_nl_msg *msg, + struct tipc_link *link) { int err; void *hdr; struct nlattr *attrs; struct nlattr *prop; + struct tipc_net *tn = net_generic(net, tipc_net_id); hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family, NLM_F_MULTI, TIPC_NL_LINK_GET); @@ -2525,7 +2534,7 @@ static int __tipc_nl_add_link(struct tipc_nl_msg *msg, struct tipc_link *link) if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, link->name)) goto attr_msg_full; if (nla_put_u32(msg->skb, TIPC_NLA_LINK_DEST, - tipc_cluster_mask(tipc_own_addr))) + tipc_cluster_mask(tn->own_addr))) goto attr_msg_full; if (nla_put_u32(msg->skb, TIPC_NLA_LINK_MTU, link->max_pkt)) goto attr_msg_full; @@ -2575,9 +2584,8 @@ msg_full: } /* Caller should hold node lock */ -static int __tipc_nl_add_node_links(struct tipc_nl_msg *msg, - struct tipc_node *node, - u32 *prev_link) +static int __tipc_nl_add_node_links(struct net *net, struct tipc_nl_msg *msg, + struct tipc_node *node, u32 *prev_link) { u32 i; int err; @@ -2588,7 +2596,7 @@ static int __tipc_nl_add_node_links(struct tipc_nl_msg *msg, if (!node->links[i]) continue; - err = __tipc_nl_add_link(msg, node->links[i]); + err = __tipc_nl_add_link(net, msg, node->links[i]); if (err) return err; } @@ -2633,7 +2641,8 @@ int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb) list_for_each_entry_continue_rcu(node, &tn->node_list, list) { tipc_node_lock(node); - err = __tipc_nl_add_node_links(&msg, node, &prev_link); + err = __tipc_nl_add_node_links(net, &msg, node, + &prev_link); tipc_node_unlock(node); if (err) goto out; @@ -2647,7 +2656,8 @@ int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb) list_for_each_entry_rcu(node, &tn->node_list, list) { tipc_node_lock(node); - err = __tipc_nl_add_node_links(&msg, node, &prev_link); + err = __tipc_nl_add_node_links(net, &msg, node, + &prev_link); tipc_node_unlock(node); if (err) goto out; @@ -2700,7 +2710,7 @@ int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info) goto err_out; } - err = __tipc_nl_add_link(&msg, link); + err = __tipc_nl_add_link(net, &msg, link); if (err) goto err_out; diff --git a/net/tipc/msg.c b/net/tipc/msg.c index 642fb13..18aba9e 100644 --- a/net/tipc/msg.c +++ b/net/tipc/msg.c @@ -70,25 +70,27 @@ struct sk_buff *tipc_buf_acquire(u32 size) return skb; } -void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, u32 hsize, - u32 destnode) +void tipc_msg_init(struct net *net, struct tipc_msg *m, u32 user, u32 type, + u32 hsize, u32 destnode) { + struct tipc_net *tn = net_generic(net, tipc_net_id); + memset(m, 0, hsize); msg_set_version(m); msg_set_user(m, user); msg_set_hdr_sz(m, hsize); msg_set_size(m, hsize); - msg_set_prevnode(m, tipc_own_addr); + msg_set_prevnode(m, tn->own_addr); msg_set_type(m, type); if (hsize > SHORT_H_SIZE) { - msg_set_orignode(m, tipc_own_addr); + msg_set_orignode(m, tn->own_addr); msg_set_destnode(m, destnode); } } -struct sk_buff *tipc_msg_create(uint user, uint type, uint hdr_sz, - uint data_sz, u32 dnode, u32 onode, - u32 dport, u32 oport, int errcode) +struct sk_buff *tipc_msg_create(struct net *net, uint user, uint type, + uint hdr_sz, uint data_sz, u32 dnode, + u32 onode, u32 dport, u32 oport, int errcode) { struct tipc_msg *msg; struct sk_buff *buf; @@ -98,7 +100,7 @@ struct sk_buff *tipc_msg_create(uint user, uint type, uint hdr_sz, return NULL; msg = buf_msg(buf); - tipc_msg_init(msg, user, type, hdr_sz, dnode); + tipc_msg_init(net, msg, user, type, hdr_sz, dnode); msg_set_size(msg, hdr_sz + data_sz); msg_set_prevnode(msg, onode); msg_set_origport(msg, oport); @@ -194,8 +196,8 @@ err: * * Returns message data size or errno: -ENOMEM, -EFAULT */ -int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset, - int dsz, int pktmax, struct sk_buff_head *list) +int tipc_msg_build(struct net *net, struct tipc_msg *mhdr, struct msghdr *m, + int offset, int dsz, int pktmax, struct sk_buff_head *list) { int mhsz = msg_hdr_sz(mhdr); int msz = mhsz + dsz; @@ -227,8 +229,8 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset, } /* Prepare reusable fragment header */ - tipc_msg_init(&pkthdr, MSG_FRAGMENTER, FIRST_FRAGMENT, - INT_H_SIZE, msg_destnode(mhdr)); + tipc_msg_init(net, &pkthdr, MSG_FRAGMENTER, FIRST_FRAGMENT, INT_H_SIZE, + msg_destnode(mhdr)); msg_set_size(&pkthdr, pktmax); msg_set_fragm_no(&pkthdr, pktno); @@ -339,8 +341,8 @@ bool tipc_msg_bundle(struct sk_buff_head *list, struct sk_buff *skb, u32 mtu) * Replaces buffer if successful * Returns true if success, otherwise false */ -bool tipc_msg_make_bundle(struct sk_buff_head *list, struct sk_buff *skb, - u32 mtu, u32 dnode) +bool tipc_msg_make_bundle(struct net *net, struct sk_buff_head *list, + struct sk_buff *skb, u32 mtu, u32 dnode) { struct sk_buff *bskb; struct tipc_msg *bmsg; @@ -363,7 +365,7 @@ bool tipc_msg_make_bundle(struct sk_buff_head *list, struct sk_buff *skb, skb_trim(bskb, INT_H_SIZE); bmsg = buf_msg(bskb); - tipc_msg_init(bmsg, MSG_BUNDLER, 0, INT_H_SIZE, dnode); + tipc_msg_init(net, bmsg, MSG_BUNDLER, 0, INT_H_SIZE, dnode); msg_set_seqno(bmsg, msg_seqno(msg)); msg_set_ack(bmsg, msg_ack(msg)); msg_set_bcast_ack(bmsg, msg_bcast_ack(msg)); @@ -380,8 +382,10 @@ bool tipc_msg_make_bundle(struct sk_buff_head *list, struct sk_buff *skb, * Consumes buffer if failure * Returns true if success, otherwise false */ -bool tipc_msg_reverse(struct sk_buff *buf, u32 *dnode, int err) +bool tipc_msg_reverse(struct net *net, struct sk_buff *buf, u32 *dnode, + int err) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_msg *msg = buf_msg(buf); uint imp = msg_importance(msg); struct tipc_msg ohdr; @@ -401,7 +405,7 @@ bool tipc_msg_reverse(struct sk_buff *buf, u32 *dnode, int err) msg_set_errcode(msg, err); msg_set_origport(msg, msg_destport(&ohdr)); msg_set_destport(msg, msg_origport(&ohdr)); - msg_set_prevnode(msg, tipc_own_addr); + msg_set_prevnode(msg, tn->own_addr); if (!msg_short(msg)) { msg_set_orignode(msg, msg_destnode(&ohdr)); msg_set_destnode(msg, msg_orignode(&ohdr)); @@ -440,7 +444,7 @@ int tipc_msg_eval(struct net *net, struct sk_buff *buf, u32 *dnode) if (msg_reroute_cnt(msg) > 0) return -TIPC_ERR_NO_NAME; - *dnode = addr_domain(msg_lookup_scope(msg)); + *dnode = addr_domain(net, msg_lookup_scope(msg)); dport = tipc_nametbl_translate(net, msg_nametype(msg), msg_nameinst(msg), dnode); diff --git a/net/tipc/msg.h b/net/tipc/msg.h index 69f37e6..526ef34 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -748,19 +748,20 @@ static inline u32 msg_tot_origport(struct tipc_msg *m) } struct sk_buff *tipc_buf_acquire(u32 size); -bool tipc_msg_reverse(struct sk_buff *buf, u32 *dnode, int err); +bool tipc_msg_reverse(struct net *net, struct sk_buff *buf, u32 *dnode, + int err); int tipc_msg_eval(struct net *net, struct sk_buff *buf, u32 *dnode); -void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, u32 hsize, - u32 destnode); -struct sk_buff *tipc_msg_create(uint user, uint type, uint hdr_sz, - uint data_sz, u32 dnode, u32 onode, - u32 dport, u32 oport, int errcode); +void tipc_msg_init(struct net *net, struct tipc_msg *m, u32 user, u32 type, + u32 hsize, u32 destnode); +struct sk_buff *tipc_msg_create(struct net *net, uint user, uint type, + uint hdr_sz, uint data_sz, u32 dnode, + u32 onode, u32 dport, u32 oport, int errcode); int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf); bool tipc_msg_bundle(struct sk_buff_head *list, struct sk_buff *skb, u32 mtu); -bool tipc_msg_make_bundle(struct sk_buff_head *list, struct sk_buff *skb, - u32 mtu, u32 dnode); -int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset, - int dsz, int mtu, struct sk_buff_head *list); +bool tipc_msg_make_bundle(struct net *net, struct sk_buff_head *list, + struct sk_buff *skb, u32 mtu, u32 dnode); +int tipc_msg_build(struct net *net, struct tipc_msg *mhdr, struct msghdr *m, + int offset, int dsz, int mtu, struct sk_buff_head *list); struct sk_buff *tipc_msg_reassemble(struct sk_buff_head *list); #endif diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c index ba42132..7f31cd4 100644 --- a/net/tipc/name_distr.c +++ b/net/tipc/name_distr.c @@ -68,14 +68,16 @@ static void publ_to_item(struct distr_item *i, struct publication *p) /** * named_prepare_buf - allocate & initialize a publication message */ -static struct sk_buff *named_prepare_buf(u32 type, u32 size, u32 dest) +static struct sk_buff *named_prepare_buf(struct net *net, u32 type, u32 size, + u32 dest) { struct sk_buff *buf = tipc_buf_acquire(INT_H_SIZE + size); struct tipc_msg *msg; if (buf != NULL) { msg = buf_msg(buf); - tipc_msg_init(msg, NAME_DISTRIBUTOR, type, INT_H_SIZE, dest); + tipc_msg_init(net, msg, NAME_DISTRIBUTOR, type, INT_H_SIZE, + dest); msg_set_size(msg, INT_H_SIZE + size); } return buf; @@ -91,7 +93,7 @@ void named_cluster_distribute(struct net *net, struct sk_buff *skb) rcu_read_lock(); list_for_each_entry_rcu(node, &tn->node_list, list) { dnode = node->addr; - if (in_own_node(dnode)) + if (in_own_node(net, dnode)) continue; if (!tipc_node_active_links(node)) continue; @@ -121,7 +123,7 @@ struct sk_buff *tipc_named_publish(struct net *net, struct publication *publ) if (publ->scope == TIPC_NODE_SCOPE) return NULL; - buf = named_prepare_buf(PUBLICATION, ITEM_SIZE, 0); + buf = named_prepare_buf(net, PUBLICATION, ITEM_SIZE, 0); if (!buf) { pr_warn("Publication distribution failure\n"); return NULL; @@ -135,7 +137,7 @@ struct sk_buff *tipc_named_publish(struct net *net, struct publication *publ) /** * tipc_named_withdraw - tell other nodes about a withdrawn publication by this node */ -struct sk_buff *tipc_named_withdraw(struct publication *publ) +struct sk_buff *tipc_named_withdraw(struct net *net, struct publication *publ) { struct sk_buff *buf; struct distr_item *item; @@ -145,7 +147,7 @@ struct sk_buff *tipc_named_withdraw(struct publication *publ) if (publ->scope == TIPC_NODE_SCOPE) return NULL; - buf = named_prepare_buf(WITHDRAWAL, ITEM_SIZE, 0); + buf = named_prepare_buf(net, WITHDRAWAL, ITEM_SIZE, 0); if (!buf) { pr_warn("Withdrawal distribution failure\n"); return NULL; @@ -175,7 +177,8 @@ static void named_distribute(struct net *net, struct sk_buff_head *list, list_for_each_entry(publ, pls, local_list) { /* Prepare next buffer: */ if (!skb) { - skb = named_prepare_buf(PUBLICATION, msg_rem, dnode); + skb = named_prepare_buf(net, PUBLICATION, msg_rem, + dnode); if (!skb) { pr_warn("Bulk publication failure\n"); return; @@ -227,7 +230,7 @@ static void tipc_publ_subscribe(struct net *net, struct publication *publ, { struct tipc_node *node; - if (in_own_node(addr)) + if (in_own_node(net, addr)) return; node = tipc_node_find(net, addr); @@ -416,7 +419,7 @@ void tipc_named_reinit(struct net *net) for (scope = TIPC_ZONE_SCOPE; scope <= TIPC_NODE_SCOPE; scope++) list_for_each_entry_rcu(publ, &tn->nametbl->publ_list[scope], local_list) - publ->node = tipc_own_addr; + publ->node = tn->own_addr; spin_unlock_bh(&tn->nametbl_lock); } diff --git a/net/tipc/name_distr.h b/net/tipc/name_distr.h index 1ed2d7e..5ec10b5 100644 --- a/net/tipc/name_distr.h +++ b/net/tipc/name_distr.h @@ -68,7 +68,7 @@ struct distr_item { }; struct sk_buff *tipc_named_publish(struct net *net, struct publication *publ); -struct sk_buff *tipc_named_withdraw(struct publication *publ); +struct sk_buff *tipc_named_withdraw(struct net *net, struct publication *publ); void named_cluster_distribute(struct net *net, struct sk_buff *buf); void tipc_named_node_up(struct net *net, u32 dnode); void tipc_named_rcv(struct net *net, struct sk_buff *buf); diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index 57e39c1..ce09b86 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -227,9 +227,11 @@ static u32 nameseq_locate_subseq(struct name_seq *nseq, u32 instance) /** * tipc_nameseq_insert_publ */ -static struct publication *tipc_nameseq_insert_publ(struct name_seq *nseq, - u32 type, u32 lower, u32 upper, - u32 scope, u32 node, u32 port, u32 key) +static struct publication *tipc_nameseq_insert_publ(struct net *net, + struct name_seq *nseq, + u32 type, u32 lower, + u32 upper, u32 scope, + u32 node, u32 port, u32 key) { struct tipc_subscription *s; struct tipc_subscription *st; @@ -314,12 +316,12 @@ static struct publication *tipc_nameseq_insert_publ(struct name_seq *nseq, list_add(&publ->zone_list, &info->zone_list); info->zone_list_size++; - if (in_own_cluster(node)) { + if (in_own_cluster(net, node)) { list_add(&publ->cluster_list, &info->cluster_list); info->cluster_list_size++; } - if (in_own_node(node)) { + if (in_own_node(net, node)) { list_add(&publ->node_list, &info->node_list); info->node_list_size++; } @@ -348,8 +350,10 @@ static struct publication *tipc_nameseq_insert_publ(struct name_seq *nseq, * A failed withdraw request simply returns a failure indication and lets the * caller issue any error or warning messages associated with such a problem. */ -static struct publication *tipc_nameseq_remove_publ(struct name_seq *nseq, u32 inst, - u32 node, u32 ref, u32 key) +static struct publication *tipc_nameseq_remove_publ(struct net *net, + struct name_seq *nseq, + u32 inst, u32 node, + u32 ref, u32 key) { struct publication *publ; struct sub_seq *sseq = nameseq_find_subseq(nseq, inst); @@ -377,13 +381,13 @@ found: info->zone_list_size--; /* Remove publication from cluster scope list, if present */ - if (in_own_cluster(node)) { + if (in_own_cluster(net, node)) { list_del(&publ->cluster_list); info->cluster_list_size--; } /* Remove publication from node scope list, if present */ - if (in_own_node(node)) { + if (in_own_node(net, node)) { list_del(&publ->node_list); info->node_list_size--; } @@ -483,7 +487,7 @@ struct publication *tipc_nametbl_insert_publ(struct net *net, u32 type, return NULL; spin_lock_bh(&seq->lock); - publ = tipc_nameseq_insert_publ(seq, type, lower, upper, + publ = tipc_nameseq_insert_publ(net, seq, type, lower, upper, scope, node, port, key); spin_unlock_bh(&seq->lock); return publ; @@ -500,7 +504,7 @@ struct publication *tipc_nametbl_remove_publ(struct net *net, u32 type, return NULL; spin_lock_bh(&seq->lock); - publ = tipc_nameseq_remove_publ(seq, lower, node, ref, key); + publ = tipc_nameseq_remove_publ(net, seq, lower, node, ref, key); if (!seq->first_free && list_empty(&seq->subscriptions)) { hlist_del_init_rcu(&seq->ns_list); kfree(seq->sseqs); @@ -528,6 +532,7 @@ struct publication *tipc_nametbl_remove_publ(struct net *net, u32 type, u32 tipc_nametbl_translate(struct net *net, u32 type, u32 instance, u32 *destnode) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct sub_seq *sseq; struct name_info *info; struct publication *publ; @@ -535,7 +540,7 @@ u32 tipc_nametbl_translate(struct net *net, u32 type, u32 instance, u32 ref = 0; u32 node = 0; - if (!tipc_in_scope(*destnode, tipc_own_addr)) + if (!tipc_in_scope(*destnode, tn->own_addr)) return 0; rcu_read_lock(); @@ -572,13 +577,13 @@ u32 tipc_nametbl_translate(struct net *net, u32 type, u32 instance, } /* Round-Robin Algorithm */ - else if (*destnode == tipc_own_addr) { + else if (*destnode == tn->own_addr) { if (list_empty(&info->node_list)) goto no_match; publ = list_first_entry(&info->node_list, struct publication, node_list); list_move_tail(&publ->node_list, &info->node_list); - } else if (in_own_cluster_exact(*destnode)) { + } else if (in_own_cluster_exact(net, *destnode)) { if (list_empty(&info->cluster_list)) goto no_match; publ = list_first_entry(&info->cluster_list, struct publication, @@ -670,7 +675,7 @@ struct publication *tipc_nametbl_publish(struct net *net, u32 type, u32 lower, } publ = tipc_nametbl_insert_publ(net, type, lower, upper, scope, - tipc_own_addr, port_ref, key); + tn->own_addr, port_ref, key); if (likely(publ)) { tn->nametbl->local_publ_count++; buf = tipc_named_publish(net, publ); @@ -695,11 +700,11 @@ int tipc_nametbl_withdraw(struct net *net, u32 type, u32 lower, u32 ref, struct tipc_net *tn = net_generic(net, tipc_net_id); spin_lock_bh(&tn->nametbl_lock); - publ = tipc_nametbl_remove_publ(net, type, lower, tipc_own_addr, + publ = tipc_nametbl_remove_publ(net, type, lower, tn->own_addr, ref, key); if (likely(publ)) { tn->nametbl->local_publ_count--; - skb = tipc_named_withdraw(publ); + skb = tipc_named_withdraw(net, publ); /* Any pending external events? */ tipc_named_process_backlog(net); list_del_init(&publ->pport_list); diff --git a/net/tipc/net.c b/net/tipc/net.c index 04445d21..263267e 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -115,30 +115,32 @@ int tipc_net_start(struct net *net, u32 addr) char addr_string[16]; int res; - tipc_own_addr = addr; + tn->own_addr = addr; tipc_named_reinit(net); tipc_sk_reinit(net); res = tipc_bclink_init(net); if (res) return res; - tipc_nametbl_publish(net, TIPC_CFG_SRV, tipc_own_addr, tipc_own_addr, - TIPC_ZONE_SCOPE, 0, tipc_own_addr); + tipc_nametbl_publish(net, TIPC_CFG_SRV, tn->own_addr, tn->own_addr, + TIPC_ZONE_SCOPE, 0, tn->own_addr); pr_info("Started in network mode\n"); pr_info("Own node address %s, network identity %u\n", - tipc_addr_string_fill(addr_string, tipc_own_addr), + tipc_addr_string_fill(addr_string, tn->own_addr), tn->net_id); return 0; } void tipc_net_stop(struct net *net) { - if (!tipc_own_addr) + struct tipc_net *tn = net_generic(net, tipc_net_id); + + if (!tn->own_addr) return; - tipc_nametbl_withdraw(net, TIPC_CFG_SRV, tipc_own_addr, 0, - tipc_own_addr); + tipc_nametbl_withdraw(net, TIPC_CFG_SRV, tn->own_addr, 0, + tn->own_addr); rtnl_lock(); tipc_bearer_stop(net); tipc_bclink_stop(net); @@ -224,7 +226,7 @@ int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info) u32 val; /* Can't change net id once TIPC has joined a network */ - if (tipc_own_addr) + if (tn->own_addr) return -EPERM; val = nla_get_u32(attrs[TIPC_NLA_NET_ID]); @@ -238,7 +240,7 @@ int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info) u32 addr; /* Can't change net addr once TIPC has joined a network */ - if (tipc_own_addr) + if (tn->own_addr) return -EPERM; addr = nla_get_u32(attrs[TIPC_NLA_NET_ADDR]); diff --git a/net/tipc/node.c b/net/tipc/node.c index 3db5012..b1eb092 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -75,7 +75,7 @@ struct tipc_node *tipc_node_find(struct net *net, u32 addr) struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_node *node; - if (unlikely(!in_own_cluster_exact(addr))) + if (unlikely(!in_own_cluster_exact(net, addr))) return NULL; rcu_read_lock(); @@ -155,7 +155,7 @@ int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port) struct tipc_node *node; struct tipc_sock_conn *conn; - if (in_own_node(dnode)) + if (in_own_node(net, dnode)) return 0; node = tipc_node_find(net, dnode); @@ -181,7 +181,7 @@ void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port) struct tipc_node *node; struct tipc_sock_conn *conn, *safe; - if (in_own_node(dnode)) + if (in_own_node(net, dnode)) return; node = tipc_node_find(net, dnode); @@ -200,14 +200,16 @@ void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port) void tipc_node_abort_sock_conns(struct net *net, struct list_head *conns) { + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_sock_conn *conn, *safe; struct sk_buff *buf; list_for_each_entry_safe(conn, safe, conns, list) { - buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, - SHORT_H_SIZE, 0, tipc_own_addr, - conn->peer_node, conn->port, - conn->peer_port, TIPC_ERR_NO_NODE); + buf = tipc_msg_create(net, TIPC_CRITICAL_IMPORTANCE, + TIPC_CONN_MSG, SHORT_H_SIZE, 0, + tn->own_addr, conn->peer_node, + conn->port, conn->peer_port, + TIPC_ERR_NO_NODE); if (likely(buf)) tipc_sk_rcv(net, buf); list_del(&conn->list); @@ -287,6 +289,7 @@ static void node_select_active_links(struct tipc_node *n_ptr) */ void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr) { + struct tipc_net *tn = net_generic(n_ptr->net, tipc_net_id); struct tipc_link **active; n_ptr->working_links--; @@ -321,7 +324,7 @@ void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr) } /* Loopback link went down? No fragmentation needed from now on. */ - if (n_ptr->addr == tipc_own_addr) { + if (n_ptr->addr == tn->own_addr) { n_ptr->act_mtus[0] = MAX_MSG_SIZE; n_ptr->act_mtus[1] = MAX_MSG_SIZE; } @@ -483,7 +486,7 @@ struct sk_buff *tipc_node_get_links(struct net *net, const void *req_tlv_area, return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE " (network address)"); - if (!tipc_own_addr) + if (!tn->own_addr) return tipc_cfg_reply_none(); spin_lock_bh(&tn->node_list_lock); @@ -501,7 +504,7 @@ struct sk_buff *tipc_node_get_links(struct net *net, const void *req_tlv_area, return NULL; /* Add TLV for broadcast link */ - link_info.dest = htonl(tipc_cluster_mask(tipc_own_addr)); + link_info.dest = htonl(tipc_cluster_mask(tn->own_addr)); link_info.up = htonl(1); strlcpy(link_info.str, tipc_bclink_name, TIPC_MAX_LINK_NAME); tipc_cfg_append_tlv(buf, TIPC_TLV_LINK_INFO, &link_info, sizeof(link_info)); diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 4670e1e..9b8470e 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -251,10 +251,11 @@ static void tsk_rej_rx_queue(struct sock *sk) { struct sk_buff *skb; u32 dnode; + struct net *net = sock_net(sk); while ((skb = __skb_dequeue(&sk->sk_receive_queue))) { - if (tipc_msg_reverse(skb, &dnode, TIPC_ERR_NO_PORT)) - tipc_link_xmit_skb(sock_net(sk), skb, dnode, 0); + if (tipc_msg_reverse(net, skb, &dnode, TIPC_ERR_NO_PORT)) + tipc_link_xmit_skb(net, skb, dnode, 0); } } @@ -265,6 +266,7 @@ static void tsk_rej_rx_queue(struct sock *sk) */ static bool tsk_peer_msg(struct tipc_sock *tsk, struct tipc_msg *msg) { + struct tipc_net *tn = net_generic(sock_net(&tsk->sk), tipc_net_id); u32 peer_port = tsk_peer_port(tsk); u32 orig_node; u32 peer_node; @@ -281,10 +283,10 @@ static bool tsk_peer_msg(struct tipc_sock *tsk, struct tipc_msg *msg) if (likely(orig_node == peer_node)) return true; - if (!orig_node && (peer_node == tipc_own_addr)) + if (!orig_node && (peer_node == tn->own_addr)) return true; - if (!peer_node && (orig_node == tipc_own_addr)) + if (!peer_node && (orig_node == tn->own_addr)) return true; return false; @@ -346,7 +348,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock, tsk->max_pkt = MAX_PKT_DEFAULT; INIT_LIST_HEAD(&tsk->publications); msg = &tsk->phdr; - tipc_msg_init(msg, TIPC_LOW_IMPORTANCE, TIPC_NAMED_MSG, + tipc_msg_init(net, msg, TIPC_LOW_IMPORTANCE, TIPC_NAMED_MSG, NAMED_H_SIZE, 0); /* Finish initializing socket data structures */ @@ -471,6 +473,7 @@ static int tipc_release(struct socket *sock) { struct sock *sk = sock->sk; struct net *net = sock_net(sk); + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_sock *tsk; struct sk_buff *skb; u32 dnode, probing_state; @@ -503,7 +506,8 @@ static int tipc_release(struct socket *sock) tsk->connected = 0; tipc_node_remove_conn(net, dnode, tsk->portid); } - if (tipc_msg_reverse(skb, &dnode, TIPC_ERR_NO_PORT)) + if (tipc_msg_reverse(net, skb, &dnode, + TIPC_ERR_NO_PORT)) tipc_link_xmit_skb(net, skb, dnode, 0); } } @@ -514,9 +518,9 @@ static int tipc_release(struct socket *sock) sock_put(sk); tipc_sk_remove(tsk); if (tsk->connected) { - skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, - SHORT_H_SIZE, 0, dnode, tipc_own_addr, - tsk_peer_port(tsk), + skb = tipc_msg_create(net, TIPC_CRITICAL_IMPORTANCE, + TIPC_CONN_MSG, SHORT_H_SIZE, 0, dnode, + tn->own_addr, tsk_peer_port(tsk), tsk->portid, TIPC_ERR_NO_PORT); if (skb) tipc_link_xmit_skb(net, skb, dnode, tsk->portid); @@ -614,6 +618,7 @@ static int tipc_getname(struct socket *sock, struct sockaddr *uaddr, { struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr; struct tipc_sock *tsk = tipc_sk(sock->sk); + struct tipc_net *tn = net_generic(sock_net(sock->sk), tipc_net_id); memset(addr, 0, sizeof(*addr)); if (peer) { @@ -624,7 +629,7 @@ static int tipc_getname(struct socket *sock, struct sockaddr *uaddr, addr->addr.id.node = tsk_peer_node(tsk); } else { addr->addr.id.ref = tsk->portid; - addr->addr.id.node = tipc_own_addr; + addr->addr.id.node = tn->own_addr; } *uaddr_len = sizeof(*addr); @@ -741,7 +746,7 @@ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq, new_mtu: mtu = tipc_bclink_get_mtu(); __skb_queue_head_init(&head); - rc = tipc_msg_build(mhdr, msg, 0, dsz, mtu, &head); + rc = tipc_msg_build(net, mhdr, msg, 0, dsz, mtu, &head); if (unlikely(rc < 0)) return rc; @@ -774,7 +779,7 @@ void tipc_sk_mcast_rcv(struct net *net, struct sk_buff *buf) uint i, last, dst = 0; u32 scope = TIPC_CLUSTER_SCOPE; - if (in_own_node(msg_orignode(msg))) + if (in_own_node(net, msg_orignode(msg))) scope = TIPC_NODE_SCOPE; /* Create destination port list: */ @@ -826,7 +831,7 @@ static int tipc_sk_proto_rcv(struct tipc_sock *tsk, u32 *dnode, if (conn_cong) tsk->sk.sk_write_space(&tsk->sk); } else if (msg_type(msg) == CONN_PROBE) { - if (!tipc_msg_reverse(buf, dnode, TIPC_OK)) + if (!tipc_msg_reverse(sock_net(&tsk->sk), buf, dnode, TIPC_OK)) return TIPC_OK; msg_set_type(msg, CONN_PROBE_REPLY); return TIPC_FWD_MSG; @@ -959,7 +964,7 @@ static int tipc_sendmsg(struct kiocb *iocb, struct socket *sock, new_mtu: mtu = tipc_node_get_mtu(net, dnode, tsk->portid); __skb_queue_head_init(&head); - rc = tipc_msg_build(mhdr, m, 0, dsz, mtu, &head); + rc = tipc_msg_build(net, mhdr, m, 0, dsz, mtu, &head); if (rc < 0) goto exit; @@ -1074,7 +1079,7 @@ next: mtu = tsk->max_pkt; send = min_t(uint, dsz - sent, TIPC_MAX_USER_MSG_SIZE); __skb_queue_head_init(&head); - rc = tipc_msg_build(mhdr, m, sent, send, mtu, &head); + rc = tipc_msg_build(net, mhdr, m, sent, send, mtu, &head); if (unlikely(rc < 0)) goto exit; do { @@ -1246,6 +1251,7 @@ static int tipc_sk_anc_data_recv(struct msghdr *m, struct tipc_msg *msg, static void tipc_sk_send_ack(struct tipc_sock *tsk, uint ack) { struct net *net = sock_net(&tsk->sk); + struct tipc_net *tn = net_generic(net, tipc_net_id); struct sk_buff *skb = NULL; struct tipc_msg *msg; u32 peer_port = tsk_peer_port(tsk); @@ -1253,8 +1259,9 @@ static void tipc_sk_send_ack(struct tipc_sock *tsk, uint ack) if (!tsk->connected) return; - skb = tipc_msg_create(CONN_MANAGER, CONN_ACK, INT_H_SIZE, 0, dnode, - tipc_own_addr, peer_port, tsk->portid, TIPC_OK); + skb = tipc_msg_create(net, CONN_MANAGER, CONN_ACK, INT_H_SIZE, 0, + dnode, tn->own_addr, peer_port, tsk->portid, + TIPC_OK); if (!skb) return; msg = buf_msg(skb); @@ -1726,6 +1733,7 @@ static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb) int rc; u32 onode; struct tipc_sock *tsk = tipc_sk(sk); + struct net *net = sock_net(sk); uint truesize = skb->truesize; rc = filter_rcv(sk, skb); @@ -1736,10 +1744,10 @@ static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb) return 0; } - if ((rc < 0) && !tipc_msg_reverse(skb, &onode, -rc)) + if ((rc < 0) && !tipc_msg_reverse(net, skb, &onode, -rc)) return 0; - tipc_link_xmit_skb(sock_net(sk), skb, onode, 0); + tipc_link_xmit_skb(net, skb, onode, 0); return 0; } @@ -1784,7 +1792,7 @@ int tipc_sk_rcv(struct net *net, struct sk_buff *skb) if (likely(!rc)) return 0; exit: - if ((rc < 0) && !tipc_msg_reverse(skb, &dnode, -rc)) + if ((rc < 0) && !tipc_msg_reverse(net, skb, &dnode, -rc)) return -EHOSTUNREACH; tipc_link_xmit_skb(net, skb, dnode, 0); @@ -2045,6 +2053,7 @@ static int tipc_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; struct net *net = sock_net(sk); + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_sock *tsk = tipc_sk(sk); struct sk_buff *skb; u32 dnode; @@ -2067,15 +2076,16 @@ restart: kfree_skb(skb); goto restart; } - if (tipc_msg_reverse(skb, &dnode, TIPC_CONN_SHUTDOWN)) + if (tipc_msg_reverse(net, skb, &dnode, + TIPC_CONN_SHUTDOWN)) tipc_link_xmit_skb(net, skb, dnode, tsk->portid); tipc_node_remove_conn(net, dnode, tsk->portid); } else { dnode = tsk_peer_node(tsk); - skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, + skb = tipc_msg_create(net, TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, SHORT_H_SIZE, - 0, dnode, tipc_own_addr, + 0, dnode, tn->own_addr, tsk_peer_port(tsk), tsk->portid, TIPC_CONN_SHUTDOWN); tipc_link_xmit_skb(net, skb, dnode, tsk->portid); @@ -2107,6 +2117,8 @@ static void tipc_sk_timeout(unsigned long data) { struct tipc_sock *tsk = (struct tipc_sock *)data; struct sock *sk = &tsk->sk; + struct net *net = sock_net(sk); + struct tipc_net *tn = net_generic(net, tipc_net_id); struct sk_buff *skb = NULL; u32 peer_port, peer_node; @@ -2120,13 +2132,13 @@ static void tipc_sk_timeout(unsigned long data) if (tsk->probing_state == TIPC_CONN_PROBING) { /* Previous probe not answered -> self abort */ - skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, - SHORT_H_SIZE, 0, tipc_own_addr, - peer_node, tsk->portid, peer_port, - TIPC_ERR_NO_PORT); + skb = tipc_msg_create(net, TIPC_CRITICAL_IMPORTANCE, + TIPC_CONN_MSG, SHORT_H_SIZE, 0, + tn->own_addr, peer_node, tsk->portid, + peer_port, TIPC_ERR_NO_PORT); } else { - skb = tipc_msg_create(CONN_MANAGER, CONN_PROBE, INT_H_SIZE, - 0, peer_node, tipc_own_addr, + skb = tipc_msg_create(net, CONN_MANAGER, CONN_PROBE, INT_H_SIZE, + 0, peer_node, tn->own_addr, peer_port, tsk->portid, TIPC_OK); tsk->probing_state = TIPC_CONN_PROBING; if (!mod_timer(&tsk->timer, jiffies + tsk->probing_intv)) @@ -2198,14 +2210,16 @@ static int tipc_sk_withdraw(struct tipc_sock *tsk, uint scope, static int tipc_sk_show(struct tipc_sock *tsk, char *buf, int len, int full_id) { + struct net *net = sock_net(&tsk->sk); + struct tipc_net *tn = net_generic(net, tipc_net_id); struct publication *publ; int ret; if (full_id) ret = tipc_snprintf(buf, len, "<%u.%u.%u:%u>:", - tipc_zone(tipc_own_addr), - tipc_cluster(tipc_own_addr), - tipc_node(tipc_own_addr), tsk->portid); + tipc_zone(tn->own_addr), + tipc_cluster(tn->own_addr), + tipc_node(tn->own_addr), tsk->portid); else ret = tipc_snprintf(buf, len, "%-10u:", tsk->portid); @@ -2296,8 +2310,8 @@ void tipc_sk_reinit(struct net *net) rht_for_each_entry_rcu(tsk, pos, tbl, i, node) { spin_lock_bh(&tsk->sk.sk_lock.slock); msg = &tsk->phdr; - msg_set_prevnode(msg, tipc_own_addr); - msg_set_orignode(msg, tipc_own_addr); + msg_set_prevnode(msg, tn->own_addr); + msg_set_orignode(msg, tn->own_addr); spin_unlock_bh(&tsk->sk.sk_lock.slock); } } @@ -2691,6 +2705,8 @@ static int __tipc_nl_add_sk(struct sk_buff *skb, struct netlink_callback *cb, int err; void *hdr; struct nlattr *attrs; + struct net *net = sock_net(skb->sk); + struct tipc_net *tn = net_generic(net, tipc_net_id); hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, &tipc_genl_v2_family, NLM_F_MULTI, TIPC_NL_SOCK_GET); @@ -2702,7 +2718,7 @@ static int __tipc_nl_add_sk(struct sk_buff *skb, struct netlink_callback *cb, goto genlmsg_cancel; if (nla_put_u32(skb, TIPC_NLA_SOCK_REF, tsk->portid)) goto attr_msg_cancel; - if (nla_put_u32(skb, TIPC_NLA_SOCK_ADDR, tipc_own_addr)) + if (nla_put_u32(skb, TIPC_NLA_SOCK_ADDR, tn->own_addr)) goto attr_msg_cancel; if (tsk->connected) { -- cgit v0.10.2 From a62fbccecd62bacb4416fc427239f5b43b25d05e Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Fri, 9 Jan 2015 15:27:11 +0800 Subject: tipc: make subscriber server support net namespace TIPC establishes one subscriber server which allows users to subscribe their interesting name service status. After tipc supports namespace, one dedicated tipc stack instance is created for each namespace, and each instance can be deemed as one independent TIPC node. As a result, subscriber server must be built for each namespace. Signed-off-by: Ying Xue Tested-by: Tero Aho Reviewed-by: Jon Maloy Signed-off-by: David S. Miller diff --git a/net/tipc/core.c b/net/tipc/core.c index 7c09670..4a8b795 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -68,8 +68,14 @@ static int __net_init tipc_init_net(struct net *net) err = tipc_nametbl_init(net); if (err) goto out_nametbl; + + err = tipc_subscr_start(net); + if (err) + goto out_subscr; return 0; +out_subscr: + tipc_nametbl_stop(net); out_nametbl: tipc_sk_rht_destroy(net); out_sk_rht: @@ -78,6 +84,7 @@ out_sk_rht: static void __net_exit tipc_exit_net(struct net *net) { + tipc_subscr_stop(net); tipc_net_stop(net); tipc_nametbl_stop(net); tipc_sk_rht_destroy(net); @@ -104,10 +111,6 @@ static int __init tipc_init(void) get_random_bytes(&tipc_random, sizeof(tipc_random)); - err = register_pernet_subsys(&tipc_net_ops); - if (err) - goto out_pernet; - err = tipc_netlink_start(); if (err) goto out_netlink; @@ -120,9 +123,9 @@ static int __init tipc_init(void) if (err) goto out_sysctl; - err = tipc_subscr_start(); + err = register_pernet_subsys(&tipc_net_ops); if (err) - goto out_subscr; + goto out_pernet; err = tipc_bearer_setup(); if (err) @@ -131,28 +134,25 @@ static int __init tipc_init(void) pr_info("Started in single node mode\n"); return 0; out_bearer: - tipc_subscr_stop(); -out_subscr: + unregister_pernet_subsys(&tipc_net_ops); +out_pernet: tipc_unregister_sysctl(); out_sysctl: tipc_socket_stop(); out_socket: tipc_netlink_stop(); out_netlink: - unregister_pernet_subsys(&tipc_net_ops); -out_pernet: pr_err("Unable to start in single node mode\n"); return err; } static void __exit tipc_exit(void) { - unregister_pernet_subsys(&tipc_net_ops); tipc_bearer_cleanup(); tipc_netlink_stop(); - tipc_subscr_stop(); tipc_socket_stop(); tipc_unregister_sysctl(); + unregister_pernet_subsys(&tipc_net_ops); pr_info("Deactivated\n"); } diff --git a/net/tipc/core.h b/net/tipc/core.h index afabf39..639f562 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -106,6 +106,10 @@ struct tipc_net { /* Name table */ spinlock_t nametbl_lock; struct name_table *nametbl; + + /* Topology subscription server */ + struct tipc_server *topsrv; + atomic_t subscription_count; }; #ifdef CONFIG_SYSCTL diff --git a/net/tipc/server.c b/net/tipc/server.c index b5bdaf7..eadd4ed 100644 --- a/net/tipc/server.c +++ b/net/tipc/server.c @@ -309,7 +309,7 @@ static struct socket *tipc_create_listen_sock(struct tipc_conn *con) struct socket *sock = NULL; int ret; - ret = tipc_sock_create_local(s->type, &sock); + ret = tipc_sock_create_local(s->net, s->type, &sock); if (ret < 0) return NULL; ret = kernel_setsockopt(sock, SOL_TIPC, TIPC_IMPORTANCE, diff --git a/net/tipc/server.h b/net/tipc/server.h index 9c979a0..9015fae 100644 --- a/net/tipc/server.h +++ b/net/tipc/server.h @@ -47,6 +47,7 @@ * @conn_idr: identifier set of connection * @idr_lock: protect the connection identifier set * @idr_in_use: amount of allocated identifier entry + * @net: network namspace instance * @rcvbuf_cache: memory cache of server receive buffer * @rcv_wq: receive workqueue * @send_wq: send workqueue @@ -63,6 +64,7 @@ struct tipc_server { struct idr conn_idr; spinlock_t idr_lock; int idr_in_use; + struct net *net; struct kmem_cache *rcvbuf_cache; struct workqueue_struct *rcv_wq; struct workqueue_struct *send_wq; @@ -73,7 +75,7 @@ struct tipc_server { struct sockaddr_tipc *addr, void *usr_data, void *buf, size_t len); struct sockaddr_tipc *saddr; - const char name[TIPC_SERVER_NAME_LEN]; + char name[TIPC_SERVER_NAME_LEN]; int imp; int type; }; diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 9b8470e..2cec496 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -388,7 +388,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock, * * Returns 0 on success, errno otherwise */ -int tipc_sock_create_local(int type, struct socket **res) +int tipc_sock_create_local(struct net *net, int type, struct socket **res) { int rc; @@ -397,7 +397,7 @@ int tipc_sock_create_local(int type, struct socket **res) pr_err("Failed to create kernel socket\n"); return rc; } - tipc_sk_create(&init_net, *res, 0, 1); + tipc_sk_create(net, *res, 0, 1); return 0; } diff --git a/net/tipc/socket.h b/net/tipc/socket.h index c15c4e1..f56c3fd 100644 --- a/net/tipc/socket.h +++ b/net/tipc/socket.h @@ -45,7 +45,7 @@ int tipc_socket_init(void); void tipc_socket_stop(void); -int tipc_sock_create_local(int type, struct socket **res); +int tipc_sock_create_local(struct net *net, int type, struct socket **res); void tipc_sock_release_local(struct socket *sock); int tipc_sock_accept_local(struct socket *sock, struct socket **newsock, int flags); diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c index b71dbc0..72c339e 100644 --- a/net/tipc/subscr.c +++ b/net/tipc/subscr.c @@ -50,34 +50,6 @@ struct tipc_subscriber { struct list_head subscription_list; }; -static void subscr_conn_msg_event(struct net *net, int conid, - struct sockaddr_tipc *addr, void *usr_data, - void *buf, size_t len); -static void *subscr_named_msg_event(int conid); -static void subscr_conn_shutdown_event(int conid, void *usr_data); - -static atomic_t subscription_count = ATOMIC_INIT(0); - -static struct sockaddr_tipc topsrv_addr __read_mostly = { - .family = AF_TIPC, - .addrtype = TIPC_ADDR_NAMESEQ, - .addr.nameseq.type = TIPC_TOP_SRV, - .addr.nameseq.lower = TIPC_TOP_SRV, - .addr.nameseq.upper = TIPC_TOP_SRV, - .scope = TIPC_NODE_SCOPE -}; - -static struct tipc_server topsrv __read_mostly = { - .saddr = &topsrv_addr, - .imp = TIPC_CRITICAL_IMPORTANCE, - .type = SOCK_SEQPACKET, - .max_rcvbuf_size = sizeof(struct tipc_subscr), - .name = "topology_server", - .tipc_conn_recvmsg = subscr_conn_msg_event, - .tipc_conn_new = subscr_named_msg_event, - .tipc_conn_shutdown = subscr_conn_shutdown_event, -}; - /** * htohl - convert value to endianness used by destination * @in: value to convert @@ -94,6 +66,7 @@ static void subscr_send_event(struct tipc_subscription *sub, u32 found_lower, u32 found_upper, u32 event, u32 port_ref, u32 node) { + struct tipc_net *tn = net_generic(sub->net, tipc_net_id); struct tipc_subscriber *subscriber = sub->subscriber; struct kvec msg_sect; @@ -104,8 +77,8 @@ static void subscr_send_event(struct tipc_subscription *sub, u32 found_lower, sub->evt.found_upper = htohl(found_upper, sub->swap); sub->evt.port.ref = htohl(port_ref, sub->swap); sub->evt.port.node = htohl(node, sub->swap); - tipc_conn_sendmsg(&topsrv, subscriber->conid, NULL, msg_sect.iov_base, - msg_sect.iov_len); + tipc_conn_sendmsg(tn->topsrv, subscriber->conid, NULL, + msg_sect.iov_base, msg_sect.iov_len); } /** @@ -146,6 +119,7 @@ static void subscr_timeout(unsigned long data) { struct tipc_subscription *sub = (struct tipc_subscription *)data; struct tipc_subscriber *subscriber = sub->subscriber; + struct tipc_net *tn = net_generic(sub->net, tipc_net_id); /* The spin lock per subscriber is used to protect its members */ spin_lock_bh(&subscriber->lock); @@ -170,7 +144,7 @@ static void subscr_timeout(unsigned long data) /* Now destroy subscription */ kfree(sub); - atomic_dec(&subscription_count); + atomic_dec(&tn->subscription_count); } /** @@ -180,10 +154,12 @@ static void subscr_timeout(unsigned long data) */ static void subscr_del(struct tipc_subscription *sub) { + struct tipc_net *tn = net_generic(sub->net, tipc_net_id); + tipc_nametbl_unsubscribe(sub); list_del(&sub->subscription_list); kfree(sub); - atomic_dec(&subscription_count); + atomic_dec(&tn->subscription_count); } /** @@ -191,9 +167,12 @@ static void subscr_del(struct tipc_subscription *sub) * * Note: Must call it in process context since it might sleep. */ -static void subscr_terminate(struct tipc_subscriber *subscriber) +static void subscr_terminate(struct tipc_subscription *sub) { - tipc_conn_terminate(&topsrv, subscriber->conid); + struct tipc_subscriber *subscriber = sub->subscriber; + struct tipc_net *tn = net_generic(sub->net, tipc_net_id); + + tipc_conn_terminate(tn->topsrv, subscriber->conid); } static void subscr_release(struct tipc_subscriber *subscriber) @@ -263,7 +242,9 @@ static void subscr_cancel(struct tipc_subscr *s, */ static int subscr_subscribe(struct net *net, struct tipc_subscr *s, struct tipc_subscriber *subscriber, - struct tipc_subscription **sub_p) { + struct tipc_subscription **sub_p) +{ + struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_subscription *sub; int swap; @@ -278,7 +259,7 @@ static int subscr_subscribe(struct net *net, struct tipc_subscr *s, } /* Refuse subscription if global limit exceeded */ - if (atomic_read(&subscription_count) >= TIPC_MAX_SUBSCRIPTIONS) { + if (atomic_read(&tn->subscription_count) >= TIPC_MAX_SUBSCRIPTIONS) { pr_warn("Subscription rejected, limit reached (%u)\n", TIPC_MAX_SUBSCRIPTIONS); return -EINVAL; @@ -309,7 +290,7 @@ static int subscr_subscribe(struct net *net, struct tipc_subscr *s, sub->subscriber = subscriber; sub->swap = swap; memcpy(&sub->evt.s, s, sizeof(struct tipc_subscr)); - atomic_inc(&subscription_count); + atomic_inc(&tn->subscription_count); if (sub->timeout != TIPC_WAIT_FOREVER) { setup_timer(&sub->timer, subscr_timeout, (unsigned long)sub); mod_timer(&sub->timer, jiffies + sub->timeout); @@ -336,7 +317,7 @@ static void subscr_conn_msg_event(struct net *net, int conid, if (subscr_subscribe(net, (struct tipc_subscr *)buf, subscriber, &sub) < 0) { spin_unlock_bh(&subscriber->lock); - subscr_terminate(subscriber); + subscr_terminate(sub); return; } if (sub) @@ -344,7 +325,6 @@ static void subscr_conn_msg_event(struct net *net, int conid, spin_unlock_bh(&subscriber->lock); } - /* Handle one request to establish a new subscriber */ static void *subscr_named_msg_event(int conid) { @@ -363,12 +343,50 @@ static void *subscr_named_msg_event(int conid) return (void *)subscriber; } -int tipc_subscr_start(void) +int tipc_subscr_start(struct net *net) { - return tipc_server_start(&topsrv); + struct tipc_net *tn = net_generic(net, tipc_net_id); + const char name[] = "topology_server"; + struct tipc_server *topsrv; + struct sockaddr_tipc *saddr; + + saddr = kzalloc(sizeof(*saddr), GFP_ATOMIC); + if (!saddr) + return -ENOMEM; + saddr->family = AF_TIPC; + saddr->addrtype = TIPC_ADDR_NAMESEQ; + saddr->addr.nameseq.type = TIPC_TOP_SRV; + saddr->addr.nameseq.lower = TIPC_TOP_SRV; + saddr->addr.nameseq.upper = TIPC_TOP_SRV; + saddr->scope = TIPC_NODE_SCOPE; + + topsrv = kzalloc(sizeof(*topsrv), GFP_ATOMIC); + if (!topsrv) { + kfree(saddr); + return -ENOMEM; + } + topsrv->net = net; + topsrv->saddr = saddr; + topsrv->imp = TIPC_CRITICAL_IMPORTANCE; + topsrv->type = SOCK_SEQPACKET; + topsrv->max_rcvbuf_size = sizeof(struct tipc_subscr); + topsrv->tipc_conn_recvmsg = subscr_conn_msg_event; + topsrv->tipc_conn_new = subscr_named_msg_event; + topsrv->tipc_conn_shutdown = subscr_conn_shutdown_event; + + strncpy(topsrv->name, name, strlen(name) + 1); + tn->topsrv = topsrv; + atomic_set(&tn->subscription_count, 0); + + return tipc_server_start(topsrv); } -void tipc_subscr_stop(void) +void tipc_subscr_stop(struct net *net) { - tipc_server_stop(&topsrv); + struct tipc_net *tn = net_generic(net, tipc_net_id); + struct tipc_server *topsrv = tn->topsrv; + + tipc_server_stop(topsrv); + kfree(topsrv->saddr); + kfree(topsrv); } diff --git a/net/tipc/subscr.h b/net/tipc/subscr.h index 670f570..33488bd 100644 --- a/net/tipc/subscr.h +++ b/net/tipc/subscr.h @@ -74,13 +74,10 @@ struct tipc_subscription { int tipc_subscr_overlap(struct tipc_subscription *sub, u32 found_lower, u32 found_upper); - void tipc_subscr_report_overlap(struct tipc_subscription *sub, u32 found_lower, u32 found_upper, u32 event, u32 port_ref, u32 node, int must); - -int tipc_subscr_start(void); - -void tipc_subscr_stop(void); +int tipc_subscr_start(struct net *net); +void tipc_subscr_stop(struct net *net); #endif -- cgit v0.10.2 From bafa29e34185fb70496bfd604dc9e4071049f023 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Fri, 9 Jan 2015 15:27:12 +0800 Subject: tipc: make tipc random value aware of net namespace After namespace is supported, each namespace should own its private random value. So the global variable representing the random value must be moved to tipc_net structure. Signed-off-by: Ying Xue Tested-by: Tero Aho Reviewed-by: Jon Maloy Signed-off-by: David S. Miller diff --git a/net/tipc/core.c b/net/tipc/core.c index 4a8b795..674bd26 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -44,9 +44,6 @@ #include -/* global variables used by multiple sub-systems within TIPC */ -int tipc_random __read_mostly; - /* configurable TIPC parameters */ int tipc_net_id __read_mostly; int sysctl_tipc_rmem[3] __read_mostly; /* min/default/max */ @@ -58,6 +55,7 @@ static int __net_init tipc_init_net(struct net *net) tn->net_id = 4711; tn->own_addr = 0; + get_random_bytes(&tn->random, sizeof(int)); INIT_LIST_HEAD(&tn->node_list); spin_lock_init(&tn->node_list_lock); @@ -109,8 +107,6 @@ static int __init tipc_init(void) TIPC_CRITICAL_IMPORTANCE; sysctl_tipc_rmem[2] = TIPC_CONN_OVERLOAD_LIMIT; - get_random_bytes(&tipc_random, sizeof(tipc_random)); - err = tipc_netlink_start(); if (err) goto out_netlink; diff --git a/net/tipc/core.h b/net/tipc/core.h index 639f562..817b2e9 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -76,14 +76,10 @@ extern int tipc_net_id __read_mostly; extern int sysctl_tipc_rmem[3] __read_mostly; extern int sysctl_tipc_named_timeout __read_mostly; -/* - * Other global variables - */ -extern int tipc_random __read_mostly; - struct tipc_net { u32 own_addr; int net_id; + int random; /* Node table and node list */ spinlock_t node_list_lock; diff --git a/net/tipc/discover.c b/net/tipc/discover.c index 786411d..5b40cb8 100644 --- a/net/tipc/discover.c +++ b/net/tipc/discover.c @@ -88,7 +88,7 @@ static void tipc_disc_init_msg(struct net *net, struct sk_buff *buf, u32 type, msg = buf_msg(buf); tipc_msg_init(net, msg, LINK_CONFIG, type, INT_H_SIZE, dest_domain); msg_set_non_seq(msg, 1); - msg_set_node_sig(msg, tipc_random); + msg_set_node_sig(msg, tn->random); msg_set_dest_domain(msg, dest_domain); msg_set_bc_netid(msg, tn->net_id); b_ptr->media->addr2msg(msg_media_addr(msg), &b_ptr->addr); diff --git a/net/tipc/link.c b/net/tipc/link.c index 9972567..193bc15 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -289,7 +289,7 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr, tipc_msg_init(n_ptr->net, msg, LINK_PROTOCOL, RESET_MSG, INT_H_SIZE, l_ptr->addr); msg_set_size(msg, sizeof(l_ptr->proto_msg)); - msg_set_session(msg, (tipc_random & 0xffff)); + msg_set_session(msg, (tn->random & 0xffff)); msg_set_bearer_id(msg, b_ptr->identity); strcpy((char *)msg_data(msg), if_name); -- cgit v0.10.2 From d49e204161af6e47d8473423aa6856388c02f254 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Fri, 9 Jan 2015 15:27:13 +0800 Subject: tipc: make netlink support net namespace Currently tipc module only allows users sitting on "init_net" namespace to configure it through netlink interface. But now almost each tipc component is able to be aware of net namespace, so it's time to open the permission for users residing in other namespaces, allowing them to configure their own tipc stack instance through netlink interface. Signed-off-by: Ying Xue Tested-by: Tero Aho Reviewed-by: Jon Maloy Signed-off-by: David S. Miller diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 282b596..fe0f513 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -54,7 +54,8 @@ static int handle_cmd(struct sk_buff *skb, struct genl_info *info) int hdr_space = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN); u16 cmd; - if ((req_userhdr->cmd & 0xC000) && (!netlink_capable(skb, CAP_NET_ADMIN))) + if ((req_userhdr->cmd & 0xC000) && + (!netlink_net_capable(skb, CAP_NET_ADMIN))) cmd = TIPC_CMD_NOT_NET_ADMIN; else cmd = req_userhdr->cmd; @@ -70,7 +71,7 @@ static int handle_cmd(struct sk_buff *skb, struct genl_info *info) rep_nlh = nlmsg_hdr(rep_buf); memcpy(rep_nlh, req_nlh, hdr_space); rep_nlh->nlmsg_len = rep_buf->len; - genlmsg_unicast(&init_net, rep_buf, NETLINK_CB(skb).portid); + genlmsg_unicast(net, rep_buf, NETLINK_CB(skb).portid); } return 0; @@ -95,6 +96,7 @@ static struct genl_family tipc_genl_family = { .version = TIPC_GENL_VERSION, .hdrsize = TIPC_GENL_HDRLEN, .maxattr = 0, + .netnsok = true, }; /* Legacy ASCII API */ @@ -114,6 +116,7 @@ struct genl_family tipc_genl_v2_family = { .version = TIPC_GENL_V2_VERSION, .hdrsize = 0, .maxattr = TIPC_NLA_MAX, + .netnsok = true, }; static const struct genl_ops tipc_genl_v2_ops[] = { -- cgit v0.10.2 From 61864d844c296933d40c02683252bbea5193b101 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 12 Jan 2015 22:29:57 +0100 Subject: ALSA: move line6 usb driver into sound/usb Promote line6 driver from staging to sound/usb/line6 directory, and maintain through sound subsystem tree. This commit just moves the code and adapts Makefile / Kconfig. The further renames and misc cleanups will follow. Signed-off-by: Takashi Iwai diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 815de37..9049dd9 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -46,8 +46,6 @@ source "drivers/staging/rtl8723au/Kconfig" source "drivers/staging/rts5208/Kconfig" -source "drivers/staging/line6/Kconfig" - source "drivers/staging/octeon/Kconfig" source "drivers/staging/octeon-usb/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 33c640b..fe26ff1 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_R8712U) += rtl8712/ obj-$(CONFIG_R8188EU) += rtl8188eu/ obj-$(CONFIG_R8723AU) += rtl8723au/ obj-$(CONFIG_RTS5208) += rts5208/ -obj-$(CONFIG_LINE6_USB) += line6/ obj-$(CONFIG_NETLOGIC_XLR_NET) += netlogic/ obj-$(CONFIG_OCTEON_ETHERNET) += octeon/ obj-$(CONFIG_OCTEON_USB) += octeon-usb/ diff --git a/drivers/staging/line6/Kconfig b/drivers/staging/line6/Kconfig deleted file mode 100644 index 4f1219b..0000000 --- a/drivers/staging/line6/Kconfig +++ /dev/null @@ -1,38 +0,0 @@ -menuconfig LINE6_USB - tristate "Line6 USB support" - depends on USB && SND - select SND_RAWMIDI - select SND_PCM - help - This is a driver for the guitar amp, cab, and effects modeller - PODxt Pro by Line6 (and similar devices), supporting the - following features: - * Reading/writing individual parameters - * Reading/writing complete channel, effects setup, and amp - setup data - * Channel switching - * Virtual MIDI interface - * Tuner access - * Playback/capture/mixer device for any ALSA-compatible PCM - audio application - * Signal routing (record clean/processed guitar signal, - re-amping) - - Preliminary support for the Variax Workbench and TonePort - devices is included. - -if LINE6_USB - -config LINE6_USB_IMPULSE_RESPONSE - bool "measure impulse response" - default n - help - Say Y here to add code to measure the impulse response of a Line6 - device. This is more accurate than user-space methods since it - bypasses any PCM data buffering (e.g., by ALSA or jack). This is - useful for assessing the performance of new devices, but is not - required for normal operation. - - If unsure, say N. - -endif # LINE6_USB diff --git a/drivers/staging/line6/Makefile b/drivers/staging/line6/Makefile deleted file mode 100644 index ae5c374..0000000 --- a/drivers/staging/line6/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -obj-$(CONFIG_LINE6_USB) += line6usb.o - -line6usb-y := \ - audio.o \ - capture.o \ - driver.o \ - midi.o \ - midibuf.o \ - pcm.o \ - playback.o \ - pod.o \ - toneport.o \ - variax.o \ - podhd.o diff --git a/drivers/staging/line6/audio.c b/drivers/staging/line6/audio.c deleted file mode 100644 index 171d80c..0000000 --- a/drivers/staging/line6/audio.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#include -#include -#include - -#include "driver.h" -#include "audio.h" - -/* - Initialize the Line6 USB audio system. -*/ -int line6_init_audio(struct usb_line6 *line6) -{ - struct snd_card *card; - int err; - - err = snd_card_new(line6->ifcdev, - SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, - THIS_MODULE, 0, &card); - if (err < 0) - return err; - - line6->card = card; - - strcpy(card->id, line6->properties->id); - strcpy(card->driver, DRIVER_NAME); - strcpy(card->shortname, line6->properties->name); - /* longname is 80 chars - see asound.h */ - sprintf(card->longname, "Line6 %s at USB %s", line6->properties->name, - dev_name(line6->ifcdev)); - return 0; -} - -/* - Register the Line6 USB audio system. -*/ -int line6_register_audio(struct usb_line6 *line6) -{ - int err; - - err = snd_card_register(line6->card); - if (err < 0) - return err; - - return 0; -} - -/* - Cleanup the Line6 USB audio system. -*/ -void line6_cleanup_audio(struct usb_line6 *line6) -{ - struct snd_card *card = line6->card; - - if (card == NULL) - return; - - snd_card_disconnect(card); - snd_card_free(card); - line6->card = NULL; -} diff --git a/drivers/staging/line6/audio.h b/drivers/staging/line6/audio.h deleted file mode 100644 index 5f8a09a..0000000 --- a/drivers/staging/line6/audio.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef AUDIO_H -#define AUDIO_H - -#include "driver.h" - -extern void line6_cleanup_audio(struct usb_line6 *); -extern int line6_init_audio(struct usb_line6 *); -extern int line6_register_audio(struct usb_line6 *); - -#endif diff --git a/drivers/staging/line6/capture.c b/drivers/staging/line6/capture.c deleted file mode 100644 index f24c7c5..0000000 --- a/drivers/staging/line6/capture.c +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#include -#include -#include -#include - -#include "audio.h" -#include "capture.h" -#include "driver.h" -#include "pcm.h" -#include "pod.h" - -/* - Find a free URB and submit it. -*/ -static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm) -{ - int index; - unsigned long flags; - int i, urb_size; - int ret; - struct urb *urb_in; - - spin_lock_irqsave(&line6pcm->lock_audio_in, flags); - index = - find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS); - - if (index < 0 || index >= LINE6_ISO_BUFFERS) { - spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); - dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); - return -EINVAL; - } - - urb_in = line6pcm->urb_audio_in[index]; - urb_size = 0; - - for (i = 0; i < LINE6_ISO_PACKETS; ++i) { - struct usb_iso_packet_descriptor *fin = - &urb_in->iso_frame_desc[i]; - fin->offset = urb_size; - fin->length = line6pcm->max_packet_size; - urb_size += line6pcm->max_packet_size; - } - - urb_in->transfer_buffer = - line6pcm->buffer_in + - index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; - urb_in->transfer_buffer_length = urb_size; - urb_in->context = line6pcm; - - ret = usb_submit_urb(urb_in, GFP_ATOMIC); - - if (ret == 0) - set_bit(index, &line6pcm->active_urb_in); - else - dev_err(line6pcm->line6->ifcdev, - "URB in #%d submission failed (%d)\n", index, ret); - - spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); - return 0; -} - -/* - Submit all currently available capture URBs. -*/ -int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm) -{ - int ret, i; - - for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { - ret = submit_audio_in_urb(line6pcm); - if (ret < 0) - return ret; - } - - return 0; -} - -/* - Unlink all currently active capture URBs. -*/ -void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm) -{ - unsigned int i; - - for (i = LINE6_ISO_BUFFERS; i--;) { - if (test_bit(i, &line6pcm->active_urb_in)) { - if (!test_and_set_bit(i, &line6pcm->unlink_urb_in)) { - struct urb *u = line6pcm->urb_audio_in[i]; - - usb_unlink_urb(u); - } - } - } -} - -/* - Wait until unlinking of all currently active capture URBs has been - finished. -*/ -void line6_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) -{ - int timeout = HZ; - unsigned int i; - int alive; - - do { - alive = 0; - for (i = LINE6_ISO_BUFFERS; i--;) { - if (test_bit(i, &line6pcm->active_urb_in)) - alive++; - } - if (!alive) - break; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } while (--timeout > 0); - if (alive) - snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); -} - -/* - Unlink all currently active capture URBs, and wait for finishing. -*/ -void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) -{ - line6_unlink_audio_in_urbs(line6pcm); - line6_wait_clear_audio_in_urbs(line6pcm); -} - -/* - Copy data into ALSA capture buffer. -*/ -void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize) -{ - struct snd_pcm_substream *substream = - get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE); - struct snd_pcm_runtime *runtime = substream->runtime; - const int bytes_per_frame = line6pcm->properties->bytes_per_frame; - int frames = fsize / bytes_per_frame; - - if (runtime == NULL) - return; - - if (line6pcm->pos_in_done + frames > runtime->buffer_size) { - /* - The transferred area goes over buffer boundary, - copy two separate chunks. - */ - int len; - - len = runtime->buffer_size - line6pcm->pos_in_done; - - if (len > 0) { - memcpy(runtime->dma_area + - line6pcm->pos_in_done * bytes_per_frame, fbuf, - len * bytes_per_frame); - memcpy(runtime->dma_area, fbuf + len * bytes_per_frame, - (frames - len) * bytes_per_frame); - } else { - /* this is somewhat paranoid */ - dev_err(line6pcm->line6->ifcdev, - "driver bug: len = %d\n", len); - } - } else { - /* copy single chunk */ - memcpy(runtime->dma_area + - line6pcm->pos_in_done * bytes_per_frame, fbuf, fsize); - } - - line6pcm->pos_in_done += frames; - if (line6pcm->pos_in_done >= runtime->buffer_size) - line6pcm->pos_in_done -= runtime->buffer_size; -} - -void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length) -{ - struct snd_pcm_substream *substream = - get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE); - - line6pcm->bytes_in += length; - if (line6pcm->bytes_in >= line6pcm->period_in) { - line6pcm->bytes_in %= line6pcm->period_in; - snd_pcm_period_elapsed(substream); - } -} - -void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm) -{ - kfree(line6pcm->buffer_in); - line6pcm->buffer_in = NULL; -} - -/* - * Callback for completed capture URB. - */ -static void audio_in_callback(struct urb *urb) -{ - int i, index, length = 0, shutdown = 0; - unsigned long flags; - - struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context; - - line6pcm->last_frame_in = urb->start_frame; - - /* find index of URB */ - for (index = 0; index < LINE6_ISO_BUFFERS; ++index) - if (urb == line6pcm->urb_audio_in[index]) - break; - - spin_lock_irqsave(&line6pcm->lock_audio_in, flags); - - for (i = 0; i < LINE6_ISO_PACKETS; ++i) { - char *fbuf; - int fsize; - struct usb_iso_packet_descriptor *fin = &urb->iso_frame_desc[i]; - - if (fin->status == -EXDEV) { - shutdown = 1; - break; - } - - fbuf = urb->transfer_buffer + fin->offset; - fsize = fin->actual_length; - - if (fsize > line6pcm->max_packet_size) { - dev_err(line6pcm->line6->ifcdev, - "driver and/or device bug: packet too large (%d > %d)\n", - fsize, line6pcm->max_packet_size); - } - - length += fsize; - - /* the following assumes LINE6_ISO_PACKETS == 1: */ - line6pcm->prev_fbuf = fbuf; - line6pcm->prev_fsize = fsize; - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE)) -#endif - if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, - &line6pcm->flags) && (fsize > 0)) - line6_capture_copy(line6pcm, fbuf, fsize); - } - - clear_bit(index, &line6pcm->active_urb_in); - - if (test_and_clear_bit(index, &line6pcm->unlink_urb_in)) - shutdown = 1; - - spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); - - if (!shutdown) { - submit_audio_in_urb(line6pcm); - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE)) -#endif - if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, - &line6pcm->flags)) - line6_capture_check_period(line6pcm, length); - } -} - -/* open capture callback */ -static int snd_line6_capture_open(struct snd_pcm_substream *substream) -{ - int err; - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - err = snd_pcm_hw_constraint_ratdens(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - (&line6pcm-> - properties->snd_line6_rates)); - if (err < 0) - return err; - - runtime->hw = line6pcm->properties->snd_line6_capture_hw; - return 0; -} - -/* close capture callback */ -static int snd_line6_capture_close(struct snd_pcm_substream *substream) -{ - return 0; -} - -/* hw_params capture callback */ -static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - int ret; - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - /* -- Florian Demski [FD] */ - /* don't ask me why, but this fixes the bug on my machine */ - if (line6pcm == NULL) { - if (substream->pcm == NULL) - return -ENOMEM; - if (substream->pcm->private_data == NULL) - return -ENOMEM; - substream->private_data = substream->pcm->private_data; - line6pcm = snd_pcm_substream_chip(substream); - } - /* -- [FD] end */ - - ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); - - if (ret < 0) - return ret; - - ret = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (ret < 0) { - line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); - return ret; - } - - line6pcm->period_in = params_period_bytes(hw_params); - return 0; -} - -/* hw_free capture callback */ -static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); - return snd_pcm_lib_free_pages(substream); -} - -/* trigger callback */ -int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd) -{ - int err; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: -#ifdef CONFIG_PM - case SNDRV_PCM_TRIGGER_RESUME: -#endif - err = line6_pcm_acquire(line6pcm, - LINE6_BIT_PCM_ALSA_CAPTURE_STREAM); - - if (err < 0) - return err; - - break; - - case SNDRV_PCM_TRIGGER_STOP: -#ifdef CONFIG_PM - case SNDRV_PCM_TRIGGER_SUSPEND: -#endif - err = line6_pcm_release(line6pcm, - LINE6_BIT_PCM_ALSA_CAPTURE_STREAM); - - if (err < 0) - return err; - - break; - - default: - return -EINVAL; - } - - return 0; -} - -/* capture pointer callback */ -static snd_pcm_uframes_t -snd_line6_capture_pointer(struct snd_pcm_substream *substream) -{ - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - return line6pcm->pos_in_done; -} - -/* capture operators */ -struct snd_pcm_ops snd_line6_capture_ops = { - .open = snd_line6_capture_open, - .close = snd_line6_capture_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_line6_capture_hw_params, - .hw_free = snd_line6_capture_hw_free, - .prepare = snd_line6_prepare, - .trigger = snd_line6_trigger, - .pointer = snd_line6_capture_pointer, -}; - -int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm) -{ - struct usb_line6 *line6 = line6pcm->line6; - int i; - - /* create audio URBs and fill in constant values: */ - for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { - struct urb *urb; - - /* URB for audio in: */ - urb = line6pcm->urb_audio_in[i] = - usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); - - if (urb == NULL) { - dev_err(line6->ifcdev, "Out of memory\n"); - return -ENOMEM; - } - - urb->dev = line6->usbdev; - urb->pipe = - usb_rcvisocpipe(line6->usbdev, - line6->properties->ep_audio_r & - USB_ENDPOINT_NUMBER_MASK); - urb->transfer_flags = URB_ISO_ASAP; - urb->start_frame = -1; - urb->number_of_packets = LINE6_ISO_PACKETS; - urb->interval = LINE6_ISO_INTERVAL; - urb->error_count = 0; - urb->complete = audio_in_callback; - } - - return 0; -} diff --git a/drivers/staging/line6/capture.h b/drivers/staging/line6/capture.h deleted file mode 100644 index 4157bcb..0000000 --- a/drivers/staging/line6/capture.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef CAPTURE_H -#define CAPTURE_H - -#include - -#include "driver.h" -#include "pcm.h" - -extern struct snd_pcm_ops snd_line6_capture_ops; - -extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, - int fsize); -extern void line6_capture_check_period(struct snd_line6_pcm *line6pcm, - int length); -extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm); -extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm - *line6pcm); -extern void line6_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm); -extern int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd); - -#endif diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c deleted file mode 100644 index fc852f6..0000000 --- a/drivers/staging/line6/driver.c +++ /dev/null @@ -1,1114 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#include -#include -#include -#include - -#include "audio.h" -#include "capture.h" -#include "driver.h" -#include "midi.h" -#include "playback.h" -#include "pod.h" -#include "podhd.h" -#include "revision.h" -#include "toneport.h" -#include "usbdefs.h" -#include "variax.h" - -#define DRIVER_AUTHOR "Markus Grabner " -#define DRIVER_DESC "Line6 USB Driver" -#define DRIVER_VERSION "0.9.1beta" DRIVER_REVISION - -#define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod) -#define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n) - -/* table of devices that work with this driver */ -static const struct usb_device_id line6_id_table[] = { - { LINE6_DEVICE(0x4250), .driver_info = LINE6_BASSPODXT }, - { LINE6_DEVICE(0x4642), .driver_info = LINE6_BASSPODXTLIVE }, - { LINE6_DEVICE(0x4252), .driver_info = LINE6_BASSPODXTPRO }, - { LINE6_DEVICE(0x4750), .driver_info = LINE6_GUITARPORT }, - { LINE6_IF_NUM(0x5051, 1), .driver_info = LINE6_POCKETPOD }, - { LINE6_DEVICE(0x5057), .driver_info = LINE6_PODHD300 }, - { LINE6_DEVICE(0x5058), .driver_info = LINE6_PODHD400 }, - { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 }, - { LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 }, - { LINE6_DEVICE(0x4153), .driver_info = LINE6_PODSTUDIO_GX }, - { LINE6_DEVICE(0x4150), .driver_info = LINE6_PODSTUDIO_UX1 }, - { LINE6_IF_NUM(0x4151, 0), .driver_info = LINE6_PODSTUDIO_UX2 }, - { LINE6_DEVICE(0x5044), .driver_info = LINE6_PODXT }, - { LINE6_IF_NUM(0x4650, 0), .driver_info = LINE6_PODXTLIVE_POD }, - { LINE6_IF_NUM(0x4650, 1), .driver_info = LINE6_PODXTLIVE_VARIAX }, - { LINE6_DEVICE(0x5050), .driver_info = LINE6_PODXTPRO }, - { LINE6_DEVICE(0x4147), .driver_info = LINE6_TONEPORT_GX }, - { LINE6_DEVICE(0x4141), .driver_info = LINE6_TONEPORT_UX1 }, - { LINE6_IF_NUM(0x4142, 0), .driver_info = LINE6_TONEPORT_UX2 }, - { LINE6_DEVICE(0x534d), .driver_info = LINE6_VARIAX }, - {} -}; - -MODULE_DEVICE_TABLE(usb, line6_id_table); - -static const struct line6_properties line6_properties_table[] = { - [LINE6_BASSPODXT] = { - .id = "BassPODxt", - .name = "BassPODxt", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM - | LINE6_CAP_HWMON, - .altsetting = 5, - .ep_ctrl_r = 0x84, - .ep_ctrl_w = 0x03, - .ep_audio_r = 0x82, - .ep_audio_w = 0x01, - }, - [LINE6_BASSPODXTLIVE] = { - .id = "BassPODxtLive", - .name = "BassPODxt Live", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM - | LINE6_CAP_HWMON, - .altsetting = 1, - .ep_ctrl_r = 0x84, - .ep_ctrl_w = 0x03, - .ep_audio_r = 0x82, - .ep_audio_w = 0x01, - }, - [LINE6_BASSPODXTPRO] = { - .id = "BassPODxtPro", - .name = "BassPODxt Pro", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM - | LINE6_CAP_HWMON, - .altsetting = 5, - .ep_ctrl_r = 0x84, - .ep_ctrl_w = 0x03, - .ep_audio_r = 0x82, - .ep_audio_w = 0x01, - }, - [LINE6_GUITARPORT] = { - .id = "GuitarPort", - .name = "GuitarPort", - .capabilities = LINE6_CAP_PCM, - .altsetting = 2, /* 1..4 seem to be ok */ - /* no control channel */ - .ep_audio_r = 0x82, - .ep_audio_w = 0x01, - }, - [LINE6_POCKETPOD] = { - .id = "PocketPOD", - .name = "Pocket POD", - .capabilities = LINE6_CAP_CONTROL, - .altsetting = 0, - .ep_ctrl_r = 0x82, - .ep_ctrl_w = 0x02, - /* no audio channel */ - }, - [LINE6_PODHD300] = { - .id = "PODHD300", - .name = "POD HD300", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM - | LINE6_CAP_HWMON, - .altsetting = 5, - .ep_ctrl_r = 0x84, - .ep_ctrl_w = 0x03, - .ep_audio_r = 0x82, - .ep_audio_w = 0x01, - }, - [LINE6_PODHD400] = { - .id = "PODHD400", - .name = "POD HD400", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM - | LINE6_CAP_HWMON, - .altsetting = 5, - .ep_ctrl_r = 0x84, - .ep_ctrl_w = 0x03, - .ep_audio_r = 0x82, - .ep_audio_w = 0x01, - }, - [LINE6_PODHD500_0] = { - .id = "PODHD500", - .name = "POD HD500", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM - | LINE6_CAP_HWMON, - .altsetting = 1, - .ep_ctrl_r = 0x81, - .ep_ctrl_w = 0x01, - .ep_audio_r = 0x86, - .ep_audio_w = 0x02, - }, - [LINE6_PODHD500_1] = { - .id = "PODHD500", - .name = "POD HD500", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM - | LINE6_CAP_HWMON, - .altsetting = 1, - .ep_ctrl_r = 0x81, - .ep_ctrl_w = 0x01, - .ep_audio_r = 0x86, - .ep_audio_w = 0x02, - }, - [LINE6_PODSTUDIO_GX] = { - .id = "PODStudioGX", - .name = "POD Studio GX", - .capabilities = LINE6_CAP_PCM, - .altsetting = 2, /* 1..4 seem to be ok */ - /* no control channel */ - .ep_audio_r = 0x82, - .ep_audio_w = 0x01, - }, - [LINE6_PODSTUDIO_UX1] = { - .id = "PODStudioUX1", - .name = "POD Studio UX1", - .capabilities = LINE6_CAP_PCM, - .altsetting = 2, /* 1..4 seem to be ok */ - /* no control channel */ - .ep_audio_r = 0x82, - .ep_audio_w = 0x01, - }, - [LINE6_PODSTUDIO_UX2] = { - .id = "PODStudioUX2", - .name = "POD Studio UX2", - .capabilities = LINE6_CAP_PCM, - .altsetting = 2, /* defaults to 44.1kHz, 16-bit */ - /* no control channel */ - .ep_audio_r = 0x82, - .ep_audio_w = 0x01, - }, - [LINE6_PODXT] = { - .id = "PODxt", - .name = "PODxt", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM - | LINE6_CAP_HWMON, - .altsetting = 5, - .ep_ctrl_r = 0x84, - .ep_ctrl_w = 0x03, - .ep_audio_r = 0x82, - .ep_audio_w = 0x01, - }, - [LINE6_PODXTLIVE_POD] = { - .id = "PODxtLive", - .name = "PODxt Live", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM - | LINE6_CAP_HWMON, - .altsetting = 1, - .ep_ctrl_r = 0x84, - .ep_ctrl_w = 0x03, - .ep_audio_r = 0x82, - .ep_audio_w = 0x01, - }, - [LINE6_PODXTLIVE_VARIAX] = { - .id = "PODxtLive", - .name = "PODxt Live", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM - | LINE6_CAP_HWMON, - .altsetting = 1, - .ep_ctrl_r = 0x86, - .ep_ctrl_w = 0x05, - .ep_audio_r = 0x82, - .ep_audio_w = 0x01, - }, - [LINE6_PODXTPRO] = { - .id = "PODxtPro", - .name = "PODxt Pro", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM - | LINE6_CAP_HWMON, - .altsetting = 5, - .ep_ctrl_r = 0x84, - .ep_ctrl_w = 0x03, - .ep_audio_r = 0x82, - .ep_audio_w = 0x01, - }, - [LINE6_TONEPORT_GX] = { - .id = "TonePortGX", - .name = "TonePort GX", - .capabilities = LINE6_CAP_PCM, - .altsetting = 2, /* 1..4 seem to be ok */ - /* no control channel */ - .ep_audio_r = 0x82, - .ep_audio_w = 0x01, - }, - [LINE6_TONEPORT_UX1] = { - .id = "TonePortUX1", - .name = "TonePort UX1", - .capabilities = LINE6_CAP_PCM, - .altsetting = 2, /* 1..4 seem to be ok */ - /* no control channel */ - .ep_audio_r = 0x82, - .ep_audio_w = 0x01, - }, - [LINE6_TONEPORT_UX2] = { - .id = "TonePortUX2", - .name = "TonePort UX2", - .capabilities = LINE6_CAP_PCM, - .altsetting = 2, /* defaults to 44.1kHz, 16-bit */ - /* no control channel */ - .ep_audio_r = 0x82, - .ep_audio_w = 0x01, - }, - [LINE6_VARIAX] = { - .id = "Variax", - .name = "Variax Workbench", - .capabilities = LINE6_CAP_CONTROL, - .altsetting = 1, - .ep_ctrl_r = 0x82, - .ep_ctrl_w = 0x01, - /* no audio channel */ - } -}; - -/* - This is Line6's MIDI manufacturer ID. -*/ -const unsigned char line6_midi_id[] = { - 0x00, 0x01, 0x0c -}; - -/* - Code to request version of POD, Variax interface - (and maybe other devices). -*/ -static const char line6_request_version[] = { - 0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7 -}; - -/** - Class for asynchronous messages. -*/ -struct message { - struct usb_line6 *line6; - const char *buffer; - int size; - int done; -}; - -/* - Forward declarations. -*/ -static void line6_data_received(struct urb *urb); -static int line6_send_raw_message_async_part(struct message *msg, - struct urb *urb); - -/* - Start to listen on endpoint. -*/ -static int line6_start_listen(struct usb_line6 *line6) -{ - int err; - - usb_fill_int_urb(line6->urb_listen, line6->usbdev, - usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r), - line6->buffer_listen, LINE6_BUFSIZE_LISTEN, - line6_data_received, line6, line6->interval); - line6->urb_listen->actual_length = 0; - err = usb_submit_urb(line6->urb_listen, GFP_ATOMIC); - return err; -} - -/* - Stop listening on endpoint. -*/ -static void line6_stop_listen(struct usb_line6 *line6) -{ - usb_kill_urb(line6->urb_listen); -} - -/* - Send raw message in pieces of wMaxPacketSize bytes. -*/ -int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, - int size) -{ - int i, done = 0; - - for (i = 0; i < size; i += line6->max_packet_size) { - int partial; - const char *frag_buf = buffer + i; - int frag_size = min(line6->max_packet_size, size - i); - int retval; - - retval = usb_interrupt_msg(line6->usbdev, - usb_sndintpipe(line6->usbdev, - line6->properties->ep_ctrl_w), - (char *)frag_buf, frag_size, - &partial, LINE6_TIMEOUT * HZ); - - if (retval) { - dev_err(line6->ifcdev, - "usb_interrupt_msg failed (%d)\n", retval); - break; - } - - done += frag_size; - } - - return done; -} - -/* - Notification of completion of asynchronous request transmission. -*/ -static void line6_async_request_sent(struct urb *urb) -{ - struct message *msg = (struct message *)urb->context; - - if (msg->done >= msg->size) { - usb_free_urb(urb); - kfree(msg); - } else - line6_send_raw_message_async_part(msg, urb); -} - -/* - Asynchronously send part of a raw message. -*/ -static int line6_send_raw_message_async_part(struct message *msg, - struct urb *urb) -{ - int retval; - struct usb_line6 *line6 = msg->line6; - int done = msg->done; - int bytes = min(msg->size - done, line6->max_packet_size); - - usb_fill_int_urb(urb, line6->usbdev, - usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w), - (char *)msg->buffer + done, bytes, - line6_async_request_sent, msg, line6->interval); - - msg->done += bytes; - retval = usb_submit_urb(urb, GFP_ATOMIC); - - if (retval < 0) { - dev_err(line6->ifcdev, "%s: usb_submit_urb failed (%d)\n", - __func__, retval); - usb_free_urb(urb); - kfree(msg); - return retval; - } - - return 0; -} - -/* - Setup and start timer. -*/ -void line6_start_timer(struct timer_list *timer, unsigned int msecs, - void (*function)(unsigned long), unsigned long data) -{ - setup_timer(timer, function, data); - timer->expires = jiffies + msecs * HZ / 1000; - add_timer(timer); -} - -/* - Asynchronously send raw message. -*/ -int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer, - int size) -{ - struct message *msg; - struct urb *urb; - - /* create message: */ - msg = kmalloc(sizeof(struct message), GFP_ATOMIC); - if (msg == NULL) - return -ENOMEM; - - /* create URB: */ - urb = usb_alloc_urb(0, GFP_ATOMIC); - - if (urb == NULL) { - kfree(msg); - dev_err(line6->ifcdev, "Out of memory\n"); - return -ENOMEM; - } - - /* set message data: */ - msg->line6 = line6; - msg->buffer = buffer; - msg->size = size; - msg->done = 0; - - /* start sending: */ - return line6_send_raw_message_async_part(msg, urb); -} - -/* - Send asynchronous device version request. -*/ -int line6_version_request_async(struct usb_line6 *line6) -{ - char *buffer; - int retval; - - buffer = kmemdup(line6_request_version, - sizeof(line6_request_version), GFP_ATOMIC); - if (buffer == NULL) { - dev_err(line6->ifcdev, "Out of memory"); - return -ENOMEM; - } - - retval = line6_send_raw_message_async(line6, buffer, - sizeof(line6_request_version)); - kfree(buffer); - return retval; -} - -/* - Send sysex message in pieces of wMaxPacketSize bytes. -*/ -int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer, - int size) -{ - return line6_send_raw_message(line6, buffer, - size + SYSEX_EXTRA_SIZE) - - SYSEX_EXTRA_SIZE; -} - -/* - Allocate buffer for sysex message and prepare header. - @param code sysex message code - @param size number of bytes between code and sysex end -*/ -char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2, - int size) -{ - char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_ATOMIC); - - if (!buffer) - return NULL; - - buffer[0] = LINE6_SYSEX_BEGIN; - memcpy(buffer + 1, line6_midi_id, sizeof(line6_midi_id)); - buffer[sizeof(line6_midi_id) + 1] = code1; - buffer[sizeof(line6_midi_id) + 2] = code2; - buffer[sizeof(line6_midi_id) + 3 + size] = LINE6_SYSEX_END; - return buffer; -} - -/* - Notification of data received from the Line6 device. -*/ -static void line6_data_received(struct urb *urb) -{ - struct usb_line6 *line6 = (struct usb_line6 *)urb->context; - struct midi_buffer *mb = &line6->line6midi->midibuf_in; - int done; - - if (urb->status == -ESHUTDOWN) - return; - - done = - line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length); - - if (done < urb->actual_length) { - line6_midibuf_ignore(mb, done); - dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n", - done, urb->actual_length); - } - - for (;;) { - done = - line6_midibuf_read(mb, line6->buffer_message, - LINE6_MESSAGE_MAXLEN); - - if (done == 0) - break; - - line6->message_length = done; - line6_midi_receive(line6, line6->buffer_message, done); - - if (line6->process_message) - line6->process_message(line6); - } - - line6_start_listen(line6); -} - -/* - Send channel number (i.e., switch to a different sound). -*/ -int line6_send_program(struct usb_line6 *line6, u8 value) -{ - int retval; - unsigned char *buffer; - int partial; - - buffer = kmalloc(2, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - buffer[0] = LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST; - buffer[1] = value; - - retval = usb_interrupt_msg(line6->usbdev, - usb_sndintpipe(line6->usbdev, - line6->properties->ep_ctrl_w), - buffer, 2, &partial, LINE6_TIMEOUT * HZ); - - if (retval) - dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", - retval); - - kfree(buffer); - return retval; -} - -/* - Transmit Line6 control parameter. -*/ -int line6_transmit_parameter(struct usb_line6 *line6, int param, u8 value) -{ - int retval; - unsigned char *buffer; - int partial; - - buffer = kmalloc(3, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - buffer[0] = LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST; - buffer[1] = param; - buffer[2] = value; - - retval = usb_interrupt_msg(line6->usbdev, - usb_sndintpipe(line6->usbdev, - line6->properties->ep_ctrl_w), - buffer, 3, &partial, LINE6_TIMEOUT * HZ); - - if (retval) - dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", - retval); - - kfree(buffer); - return retval; -} - -/* - Read data from device. -*/ -int line6_read_data(struct usb_line6 *line6, int address, void *data, - size_t datalen) -{ - struct usb_device *usbdev = line6->usbdev; - int ret; - unsigned char len; - - /* query the serial number: */ - ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - (datalen << 8) | 0x21, address, - NULL, 0, LINE6_TIMEOUT * HZ); - - if (ret < 0) { - dev_err(line6->ifcdev, "read request failed (error %d)\n", ret); - return ret; - } - - /* Wait for data length. We'll get 0xff until length arrives. */ - do { - ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | - USB_DIR_IN, - 0x0012, 0x0000, &len, 1, - LINE6_TIMEOUT * HZ); - if (ret < 0) { - dev_err(line6->ifcdev, - "receive length failed (error %d)\n", ret); - return ret; - } - } while (len == 0xff); - - if (len != datalen) { - /* should be equal or something went wrong */ - dev_err(line6->ifcdev, - "length mismatch (expected %d, got %d)\n", - (int)datalen, (int)len); - return -EINVAL; - } - - /* receive the result: */ - ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x0013, 0x0000, data, datalen, - LINE6_TIMEOUT * HZ); - - if (ret < 0) { - dev_err(line6->ifcdev, "read failed (error %d)\n", ret); - return ret; - } - - return 0; -} - -/* - Write data to device. -*/ -int line6_write_data(struct usb_line6 *line6, int address, void *data, - size_t datalen) -{ - struct usb_device *usbdev = line6->usbdev; - int ret; - unsigned char status; - - ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - 0x0022, address, data, datalen, - LINE6_TIMEOUT * HZ); - - if (ret < 0) { - dev_err(line6->ifcdev, - "write request failed (error %d)\n", ret); - return ret; - } - - do { - ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), - 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | - USB_DIR_IN, - 0x0012, 0x0000, - &status, 1, LINE6_TIMEOUT * HZ); - - if (ret < 0) { - dev_err(line6->ifcdev, - "receiving status failed (error %d)\n", ret); - return ret; - } - } while (status == 0xff); - - if (status != 0) { - dev_err(line6->ifcdev, "write failed (error %d)\n", ret); - return -EINVAL; - } - - return 0; -} - -/* - Read Line6 device serial number. - (POD, TonePort, GuitarPort) -*/ -int line6_read_serial_number(struct usb_line6 *line6, int *serial_number) -{ - return line6_read_data(line6, 0x80d0, serial_number, - sizeof(*serial_number)); -} - -/* - No operation (i.e., unsupported). -*/ -ssize_t line6_nop_read(struct device *dev, struct device_attribute *attr, - char *buf) -{ - return 0; -} - -/* - Generic destructor. -*/ -static void line6_destruct(struct usb_interface *interface) -{ - struct usb_line6 *line6; - - if (interface == NULL) - return; - line6 = usb_get_intfdata(interface); - if (line6 == NULL) - return; - - /* free buffer memory first: */ - kfree(line6->buffer_message); - kfree(line6->buffer_listen); - - /* then free URBs: */ - usb_free_urb(line6->urb_listen); - - /* make sure the device isn't destructed twice: */ - usb_set_intfdata(interface, NULL); - - /* free interface data: */ - kfree(line6); -} - -/* - Probe USB device. -*/ -static int line6_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - enum line6_device_type devtype; - struct usb_device *usbdev; - struct usb_line6 *line6; - const struct line6_properties *properties; - int interface_number; - int size = 0; - int ret; - - if (interface == NULL) - return -ENODEV; - usbdev = interface_to_usbdev(interface); - if (usbdev == NULL) - return -ENODEV; - - /* we don't handle multiple configurations */ - if (usbdev->descriptor.bNumConfigurations != 1) { - ret = -ENODEV; - goto err_put; - } - - devtype = id->driver_info; - - /* initialize device info: */ - properties = &line6_properties_table[devtype]; - dev_info(&interface->dev, "Line6 %s found\n", properties->name); - - /* query interface number */ - interface_number = interface->cur_altsetting->desc.bInterfaceNumber; - - ret = usb_set_interface(usbdev, interface_number, - properties->altsetting); - if (ret < 0) { - dev_err(&interface->dev, "set_interface failed\n"); - goto err_put; - } - - /* initialize device data based on device: */ - switch (devtype) { - case LINE6_BASSPODXT: - case LINE6_BASSPODXTLIVE: - case LINE6_BASSPODXTPRO: - case LINE6_PODXT: - case LINE6_PODXTPRO: - size = sizeof(struct usb_line6_pod); - break; - - case LINE6_PODHD300: - case LINE6_PODHD400: - size = sizeof(struct usb_line6_podhd); - break; - - case LINE6_PODHD500_0: - case LINE6_PODHD500_1: - size = sizeof(struct usb_line6_podhd); - break; - - case LINE6_POCKETPOD: - size = sizeof(struct usb_line6_pod); - break; - - case LINE6_PODSTUDIO_GX: - case LINE6_PODSTUDIO_UX1: - case LINE6_PODSTUDIO_UX2: - case LINE6_TONEPORT_GX: - case LINE6_TONEPORT_UX1: - case LINE6_TONEPORT_UX2: - case LINE6_GUITARPORT: - size = sizeof(struct usb_line6_toneport); - break; - - case LINE6_PODXTLIVE_POD: - size = sizeof(struct usb_line6_pod); - break; - - case LINE6_PODXTLIVE_VARIAX: - size = sizeof(struct usb_line6_variax); - break; - - case LINE6_VARIAX: - size = sizeof(struct usb_line6_variax); - break; - - default: - MISSING_CASE; - ret = -ENODEV; - goto err_put; - } - - if (size == 0) { - dev_err(&interface->dev, - "driver bug: interface data size not set\n"); - ret = -ENODEV; - goto err_put; - } - - line6 = kzalloc(size, GFP_KERNEL); - if (line6 == NULL) { - ret = -ENODEV; - goto err_put; - } - - /* store basic data: */ - line6->properties = properties; - line6->usbdev = usbdev; - line6->ifcdev = &interface->dev; - line6->type = devtype; - - /* get data from endpoint descriptor (see usb_maxpacket): */ - { - struct usb_host_endpoint *ep; - unsigned pipe = usb_rcvintpipe(usbdev, properties->ep_ctrl_r); - unsigned epnum = usb_pipeendpoint(pipe); - ep = usbdev->ep_in[epnum]; - - if (ep != NULL) { - line6->interval = ep->desc.bInterval; - line6->max_packet_size = - le16_to_cpu(ep->desc.wMaxPacketSize); - } else { - line6->interval = LINE6_FALLBACK_INTERVAL; - line6->max_packet_size = LINE6_FALLBACK_MAXPACKETSIZE; - dev_err(line6->ifcdev, - "endpoint not available, using fallback values"); - } - } - - usb_set_intfdata(interface, line6); - - if (properties->capabilities & LINE6_CAP_CONTROL) { - /* initialize USB buffers: */ - line6->buffer_listen = - kmalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL); - if (line6->buffer_listen == NULL) { - ret = -ENOMEM; - goto err_destruct; - } - - line6->buffer_message = - kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL); - if (line6->buffer_message == NULL) { - ret = -ENOMEM; - goto err_destruct; - } - - line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL); - - if (line6->urb_listen == NULL) { - dev_err(&interface->dev, "Out of memory\n"); - line6_destruct(interface); - ret = -ENOMEM; - goto err_destruct; - } - - ret = line6_start_listen(line6); - if (ret < 0) { - dev_err(&interface->dev, "%s: usb_submit_urb failed\n", - __func__); - goto err_destruct; - } - } - - /* initialize device data based on device: */ - switch (devtype) { - case LINE6_BASSPODXT: - case LINE6_BASSPODXTLIVE: - case LINE6_BASSPODXTPRO: - case LINE6_POCKETPOD: - case LINE6_PODXT: - case LINE6_PODXTPRO: - ret = line6_pod_init(interface, line6); - break; - - case LINE6_PODHD300: - case LINE6_PODHD400: - case LINE6_PODHD500_0: - case LINE6_PODHD500_1: - ret = line6_podhd_init(interface, line6); - break; - - case LINE6_PODXTLIVE_POD: - ret = line6_pod_init(interface, line6); - break; - - case LINE6_PODXTLIVE_VARIAX: - ret = line6_variax_init(interface, line6); - break; - - case LINE6_VARIAX: - ret = line6_variax_init(interface, line6); - break; - - case LINE6_PODSTUDIO_GX: - case LINE6_PODSTUDIO_UX1: - case LINE6_PODSTUDIO_UX2: - case LINE6_TONEPORT_GX: - case LINE6_TONEPORT_UX1: - case LINE6_TONEPORT_UX2: - case LINE6_GUITARPORT: - ret = line6_toneport_init(interface, line6); - break; - - default: - MISSING_CASE; - ret = -ENODEV; - } - - if (ret < 0) - goto err_destruct; - - ret = sysfs_create_link(&interface->dev.kobj, &usbdev->dev.kobj, - "usb_device"); - if (ret < 0) - goto err_destruct; - - /* creation of additional special files should go here */ - - dev_info(&interface->dev, "Line6 %s now attached\n", - line6->properties->name); - - /* increment reference counters: */ - usb_get_intf(interface); - usb_get_dev(usbdev); - - return 0; - -err_destruct: - line6_destruct(interface); -err_put: - return ret; -} - -/* - Line6 device disconnected. -*/ -static void line6_disconnect(struct usb_interface *interface) -{ - struct usb_line6 *line6; - struct usb_device *usbdev; - int interface_number; - - if (interface == NULL) - return; - usbdev = interface_to_usbdev(interface); - if (usbdev == NULL) - return; - - /* removal of additional special files should go here */ - - sysfs_remove_link(&interface->dev.kobj, "usb_device"); - - interface_number = interface->cur_altsetting->desc.bInterfaceNumber; - line6 = usb_get_intfdata(interface); - - if (line6 != NULL) { - if (line6->urb_listen != NULL) - line6_stop_listen(line6); - - if (usbdev != line6->usbdev) - dev_err(line6->ifcdev, - "driver bug: inconsistent usb device\n"); - - line6->disconnect(interface); - - dev_info(&interface->dev, "Line6 %s now disconnected\n", - line6->properties->name); - } - - line6_destruct(interface); - - /* decrement reference counters: */ - usb_put_intf(interface); - usb_put_dev(usbdev); -} - -#ifdef CONFIG_PM - -/* - Suspend Line6 device. -*/ -static int line6_suspend(struct usb_interface *interface, pm_message_t message) -{ - struct usb_line6 *line6 = usb_get_intfdata(interface); - struct snd_line6_pcm *line6pcm = line6->line6pcm; - - snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot); - - if (line6->properties->capabilities & LINE6_CAP_CONTROL) - line6_stop_listen(line6); - - if (line6pcm != NULL) { - snd_pcm_suspend_all(line6pcm->pcm); - line6_pcm_disconnect(line6pcm); - line6pcm->flags = 0; - } - - return 0; -} - -/* - Resume Line6 device. -*/ -static int line6_resume(struct usb_interface *interface) -{ - struct usb_line6 *line6 = usb_get_intfdata(interface); - - if (line6->properties->capabilities & LINE6_CAP_CONTROL) - line6_start_listen(line6); - - snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0); - return 0; -} - -/* - Resume Line6 device after reset. -*/ -static int line6_reset_resume(struct usb_interface *interface) -{ - struct usb_line6 *line6 = usb_get_intfdata(interface); - - switch (line6->type) { - case LINE6_PODSTUDIO_GX: - case LINE6_PODSTUDIO_UX1: - case LINE6_PODSTUDIO_UX2: - case LINE6_TONEPORT_GX: - case LINE6_TONEPORT_UX1: - case LINE6_TONEPORT_UX2: - case LINE6_GUITARPORT: - line6_toneport_reset_resume((struct usb_line6_toneport *)line6); - - default: - break; - } - - return line6_resume(interface); -} - -#endif /* CONFIG_PM */ - -static struct usb_driver line6_driver = { - .name = DRIVER_NAME, - .probe = line6_probe, - .disconnect = line6_disconnect, -#ifdef CONFIG_PM - .suspend = line6_suspend, - .resume = line6_resume, - .reset_resume = line6_reset_resume, -#endif - .id_table = line6_id_table, -}; - -module_usb_driver(line6_driver); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/staging/line6/driver.h b/drivers/staging/line6/driver.h deleted file mode 100644 index ad203f1..0000000 --- a/drivers/staging/line6/driver.h +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef DRIVER_H -#define DRIVER_H - -#include -#include -#include - -#include "midi.h" - -#define DRIVER_NAME "line6usb" - -enum line6_device_type { - LINE6_BASSPODXT, - LINE6_BASSPODXTLIVE, - LINE6_BASSPODXTPRO, - LINE6_GUITARPORT, - LINE6_POCKETPOD, - LINE6_PODHD300, - LINE6_PODHD400, - LINE6_PODHD500_0, - LINE6_PODHD500_1, - LINE6_PODSTUDIO_GX, - LINE6_PODSTUDIO_UX1, - LINE6_PODSTUDIO_UX2, - LINE6_PODXT, - LINE6_PODXTLIVE_POD, - LINE6_PODXTLIVE_VARIAX, - LINE6_PODXTPRO, - LINE6_TONEPORT_GX, - LINE6_TONEPORT_UX1, - LINE6_TONEPORT_UX2, - LINE6_VARIAX -}; - -#define LINE6_TIMEOUT 1 -#define LINE6_BUFSIZE_LISTEN 32 -#define LINE6_MESSAGE_MAXLEN 256 - -/* - Line6 MIDI control commands -*/ -#define LINE6_PARAM_CHANGE 0xb0 -#define LINE6_PROGRAM_CHANGE 0xc0 -#define LINE6_SYSEX_BEGIN 0xf0 -#define LINE6_SYSEX_END 0xf7 -#define LINE6_RESET 0xff - -/* - MIDI channel for messages initiated by the host - (and eventually echoed back by the device) -*/ -#define LINE6_CHANNEL_HOST 0x00 - -/* - MIDI channel for messages initiated by the device -*/ -#define LINE6_CHANNEL_DEVICE 0x02 - -#define LINE6_CHANNEL_UNKNOWN 5 /* don't know yet what this is good for */ - -#define LINE6_CHANNEL_MASK 0x0f - -#define MISSING_CASE \ - pr_err("line6usb driver bug: missing case in %s:%d\n", \ - __FILE__, __LINE__) - -#define CHECK_RETURN(x) \ -do { \ - err = x; \ - if (err < 0) \ - return err; \ -} while (0) - -#define CHECK_STARTUP_PROGRESS(x, n) \ -do { \ - if ((x) >= (n)) \ - return; \ - x = (n); \ -} while (0) - -extern const unsigned char line6_midi_id[3]; - -static const int SYSEX_DATA_OFS = sizeof(line6_midi_id) + 3; -static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4; - -/** - Common properties of Line6 devices. -*/ -struct line6_properties { - /** - Card id string (maximum 16 characters). - This can be used to address the device in ALSA programs as - "default:CARD=" - */ - const char *id; - - /** - Card short name (maximum 32 characters). - */ - const char *name; - - /** - Bit vector defining this device's capabilities in the - line6usb driver. - */ - int capabilities; - - int altsetting; - - unsigned ep_ctrl_r; - unsigned ep_ctrl_w; - unsigned ep_audio_r; - unsigned ep_audio_w; -}; - -/** - Common data shared by all Line6 devices. - Corresponds to a pair of USB endpoints. -*/ -struct usb_line6 { - /** - USB device. - */ - struct usb_device *usbdev; - - /** - Device type. - */ - enum line6_device_type type; - - /** - Properties. - */ - const struct line6_properties *properties; - - /** - Interval (ms). - */ - int interval; - - /** - Maximum size of USB packet. - */ - int max_packet_size; - - /** - Device representing the USB interface. - */ - struct device *ifcdev; - - /** - Line6 sound card data structure. - Each device has at least MIDI or PCM. - */ - struct snd_card *card; - - /** - Line6 PCM device data structure. - */ - struct snd_line6_pcm *line6pcm; - - /** - Line6 MIDI device data structure. - */ - struct snd_line6_midi *line6midi; - - /** - URB for listening to PODxt Pro control endpoint. - */ - struct urb *urb_listen; - - /** - Buffer for listening to PODxt Pro control endpoint. - */ - unsigned char *buffer_listen; - - /** - Buffer for message to be processed. - */ - unsigned char *buffer_message; - - /** - Length of message to be processed. - */ - int message_length; - - void (*process_message)(struct usb_line6 *); - void (*disconnect)(struct usb_interface *); -}; - -extern char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, - int code2, int size); -extern ssize_t line6_nop_read(struct device *dev, - struct device_attribute *attr, char *buf); -extern int line6_read_data(struct usb_line6 *line6, int address, void *data, - size_t datalen); -extern int line6_read_serial_number(struct usb_line6 *line6, - int *serial_number); -extern int line6_send_program(struct usb_line6 *line6, u8 value); -extern int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, - int size); -extern int line6_send_raw_message_async(struct usb_line6 *line6, - const char *buffer, int size); -extern int line6_send_sysex_message(struct usb_line6 *line6, - const char *buffer, int size); -extern ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count); -extern void line6_start_timer(struct timer_list *timer, unsigned int msecs, - void (*function)(unsigned long), - unsigned long data); -extern int line6_transmit_parameter(struct usb_line6 *line6, int param, - u8 value); -extern int line6_version_request_async(struct usb_line6 *line6); -extern int line6_write_data(struct usb_line6 *line6, int address, void *data, - size_t datalen); - -#endif diff --git a/drivers/staging/line6/midi.c b/drivers/staging/line6/midi.c deleted file mode 100644 index c9d725a..0000000 --- a/drivers/staging/line6/midi.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#include -#include -#include -#include - -#include "audio.h" -#include "driver.h" -#include "midi.h" -#include "pod.h" -#include "usbdefs.h" - -#define line6_rawmidi_substream_midi(substream) \ - ((struct snd_line6_midi *)((substream)->rmidi->private_data)) - -static int send_midi_async(struct usb_line6 *line6, unsigned char *data, - int length); - -/* - Pass data received via USB to MIDI. -*/ -void line6_midi_receive(struct usb_line6 *line6, unsigned char *data, - int length) -{ - if (line6->line6midi->substream_receive) - snd_rawmidi_receive(line6->line6midi->substream_receive, - data, length); -} - -/* - Read data from MIDI buffer and transmit them via USB. -*/ -static void line6_midi_transmit(struct snd_rawmidi_substream *substream) -{ - struct usb_line6 *line6 = - line6_rawmidi_substream_midi(substream)->line6; - struct snd_line6_midi *line6midi = line6->line6midi; - struct midi_buffer *mb = &line6midi->midibuf_out; - unsigned long flags; - unsigned char chunk[LINE6_FALLBACK_MAXPACKETSIZE]; - int req, done; - - spin_lock_irqsave(&line6->line6midi->midi_transmit_lock, flags); - - for (;;) { - req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size); - done = snd_rawmidi_transmit_peek(substream, chunk, req); - - if (done == 0) - break; - - line6_midibuf_write(mb, chunk, done); - snd_rawmidi_transmit_ack(substream, done); - } - - for (;;) { - done = line6_midibuf_read(mb, chunk, - LINE6_FALLBACK_MAXPACKETSIZE); - - if (done == 0) - break; - - send_midi_async(line6, chunk, done); - } - - spin_unlock_irqrestore(&line6->line6midi->midi_transmit_lock, flags); -} - -/* - Notification of completion of MIDI transmission. -*/ -static void midi_sent(struct urb *urb) -{ - unsigned long flags; - int status; - int num; - struct usb_line6 *line6 = (struct usb_line6 *)urb->context; - - status = urb->status; - kfree(urb->transfer_buffer); - usb_free_urb(urb); - - if (status == -ESHUTDOWN) - return; - - spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags); - num = --line6->line6midi->num_active_send_urbs; - - if (num == 0) { - line6_midi_transmit(line6->line6midi->substream_transmit); - num = line6->line6midi->num_active_send_urbs; - } - - if (num == 0) - wake_up(&line6->line6midi->send_wait); - - spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags); -} - -/* - Send an asynchronous MIDI message. - Assumes that line6->line6midi->send_urb_lock is held - (i.e., this function is serialized). -*/ -static int send_midi_async(struct usb_line6 *line6, unsigned char *data, - int length) -{ - struct urb *urb; - int retval; - unsigned char *transfer_buffer; - - urb = usb_alloc_urb(0, GFP_ATOMIC); - - if (urb == NULL) { - dev_err(line6->ifcdev, "Out of memory\n"); - return -ENOMEM; - } - - transfer_buffer = kmemdup(data, length, GFP_ATOMIC); - - if (transfer_buffer == NULL) { - usb_free_urb(urb); - dev_err(line6->ifcdev, "Out of memory\n"); - return -ENOMEM; - } - - usb_fill_int_urb(urb, line6->usbdev, - usb_sndbulkpipe(line6->usbdev, - line6->properties->ep_ctrl_w), - transfer_buffer, length, midi_sent, line6, - line6->interval); - urb->actual_length = 0; - retval = usb_submit_urb(urb, GFP_ATOMIC); - - if (retval < 0) { - dev_err(line6->ifcdev, "usb_submit_urb failed\n"); - usb_free_urb(urb); - return retval; - } - - ++line6->line6midi->num_active_send_urbs; - return 0; -} - -static int line6_midi_output_open(struct snd_rawmidi_substream *substream) -{ - return 0; -} - -static int line6_midi_output_close(struct snd_rawmidi_substream *substream) -{ - return 0; -} - -static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream, - int up) -{ - unsigned long flags; - struct usb_line6 *line6 = - line6_rawmidi_substream_midi(substream)->line6; - - line6->line6midi->substream_transmit = substream; - spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags); - - if (line6->line6midi->num_active_send_urbs == 0) - line6_midi_transmit(substream); - - spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags); -} - -static void line6_midi_output_drain(struct snd_rawmidi_substream *substream) -{ - struct usb_line6 *line6 = - line6_rawmidi_substream_midi(substream)->line6; - struct snd_line6_midi *midi = line6->line6midi; - - wait_event_interruptible(midi->send_wait, - midi->num_active_send_urbs == 0); -} - -static int line6_midi_input_open(struct snd_rawmidi_substream *substream) -{ - return 0; -} - -static int line6_midi_input_close(struct snd_rawmidi_substream *substream) -{ - return 0; -} - -static void line6_midi_input_trigger(struct snd_rawmidi_substream *substream, - int up) -{ - struct usb_line6 *line6 = - line6_rawmidi_substream_midi(substream)->line6; - - if (up) - line6->line6midi->substream_receive = substream; - else - line6->line6midi->substream_receive = NULL; -} - -static struct snd_rawmidi_ops line6_midi_output_ops = { - .open = line6_midi_output_open, - .close = line6_midi_output_close, - .trigger = line6_midi_output_trigger, - .drain = line6_midi_output_drain, -}; - -static struct snd_rawmidi_ops line6_midi_input_ops = { - .open = line6_midi_input_open, - .close = line6_midi_input_close, - .trigger = line6_midi_input_trigger, -}; - -/* - Cleanup the Line6 MIDI device. -*/ -static void line6_cleanup_midi(struct snd_rawmidi *rmidi) -{ -} - -/* Create a MIDI device */ -static int snd_line6_new_midi(struct snd_line6_midi *line6midi) -{ - struct snd_rawmidi *rmidi; - int err; - - err = snd_rawmidi_new(line6midi->line6->card, "Line6 MIDI", 0, 1, 1, - &rmidi); - if (err < 0) - return err; - - rmidi->private_data = line6midi; - rmidi->private_free = line6_cleanup_midi; - strcpy(rmidi->id, line6midi->line6->properties->id); - strcpy(rmidi->name, line6midi->line6->properties->name); - - rmidi->info_flags = - SNDRV_RAWMIDI_INFO_OUTPUT | - SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; - - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, - &line6_midi_output_ops); - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, - &line6_midi_input_ops); - return 0; -} - -/* MIDI device destructor */ -static int snd_line6_midi_free(struct snd_device *device) -{ - struct snd_line6_midi *line6midi = device->device_data; - - line6_midibuf_destroy(&line6midi->midibuf_in); - line6_midibuf_destroy(&line6midi->midibuf_out); - return 0; -} - -/* - Initialize the Line6 MIDI subsystem. -*/ -int line6_init_midi(struct usb_line6 *line6) -{ - static struct snd_device_ops midi_ops = { - .dev_free = snd_line6_midi_free, - }; - - int err; - struct snd_line6_midi *line6midi; - - if (!(line6->properties->capabilities & LINE6_CAP_CONTROL)) { - /* skip MIDI initialization and report success */ - return 0; - } - - line6midi = kzalloc(sizeof(struct snd_line6_midi), GFP_KERNEL); - - if (line6midi == NULL) - return -ENOMEM; - - err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0); - if (err < 0) { - kfree(line6midi); - return err; - } - - err = line6_midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1); - if (err < 0) { - kfree(line6midi->midibuf_in.buf); - kfree(line6midi); - return err; - } - - line6midi->line6 = line6; - line6->line6midi = line6midi; - - err = snd_device_new(line6->card, SNDRV_DEV_RAWMIDI, line6midi, - &midi_ops); - if (err < 0) - return err; - - err = snd_line6_new_midi(line6midi); - if (err < 0) - return err; - - init_waitqueue_head(&line6midi->send_wait); - spin_lock_init(&line6midi->send_urb_lock); - spin_lock_init(&line6midi->midi_transmit_lock); - return 0; -} diff --git a/drivers/staging/line6/midi.h b/drivers/staging/line6/midi.h deleted file mode 100644 index 78f903f..0000000 --- a/drivers/staging/line6/midi.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef MIDI_H -#define MIDI_H - -#include - -#include "midibuf.h" - -#define MIDI_BUFFER_SIZE 1024 - -struct snd_line6_midi { - /** - Pointer back to the Line6 driver data structure. - */ - struct usb_line6 *line6; - - /** - MIDI substream for receiving (or NULL if not active). - */ - struct snd_rawmidi_substream *substream_receive; - - /** - MIDI substream for transmitting (or NULL if not active). - */ - struct snd_rawmidi_substream *substream_transmit; - - /** - Number of currently active MIDI send URBs. - */ - int num_active_send_urbs; - - /** - Spin lock to protect updates of send_urb. - */ - spinlock_t send_urb_lock; - - /** - Spin lock to protect MIDI buffer handling. - */ - spinlock_t midi_transmit_lock; - - /** - Wait queue for MIDI transmission. - */ - wait_queue_head_t send_wait; - - /** - Buffer for incoming MIDI stream. - */ - struct midi_buffer midibuf_in; - - /** - Buffer for outgoing MIDI stream. - */ - struct midi_buffer midibuf_out; -}; - -extern int line6_init_midi(struct usb_line6 *line6); -extern void line6_midi_receive(struct usb_line6 *line6, unsigned char *data, - int length); - -#endif diff --git a/drivers/staging/line6/midibuf.c b/drivers/staging/line6/midibuf.c deleted file mode 100644 index 1ff8569..0000000 --- a/drivers/staging/line6/midibuf.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#include - -#include "midibuf.h" - -static int midibuf_message_length(unsigned char code) -{ - int message_length; - - if (code < 0x80) - message_length = -1; - else if (code < 0xf0) { - static const int length[] = { 3, 3, 3, 3, 2, 2, 3 }; - - message_length = length[(code >> 4) - 8]; - } else { - /* - Note that according to the MIDI specification 0xf2 is - the "Song Position Pointer", but this is used by Line6 - to send sysex messages to the host. - */ - static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1, - 1, 1, 1, -1, 1, 1 - }; - message_length = length[code & 0x0f]; - } - - return message_length; -} - -static int midibuf_is_empty(struct midi_buffer *this) -{ - return (this->pos_read == this->pos_write) && !this->full; -} - -static int midibuf_is_full(struct midi_buffer *this) -{ - return this->full; -} - -void line6_midibuf_reset(struct midi_buffer *this) -{ - this->pos_read = this->pos_write = this->full = 0; - this->command_prev = -1; -} - -int line6_midibuf_init(struct midi_buffer *this, int size, int split) -{ - this->buf = kmalloc(size, GFP_KERNEL); - - if (this->buf == NULL) - return -ENOMEM; - - this->size = size; - this->split = split; - line6_midibuf_reset(this); - return 0; -} - -void line6_midibuf_status(struct midi_buffer *this) -{ - pr_debug("midibuf size=%d split=%d pos_read=%d pos_write=%d full=%d command_prev=%02x\n", - this->size, this->split, this->pos_read, this->pos_write, - this->full, this->command_prev); -} - -int line6_midibuf_bytes_free(struct midi_buffer *this) -{ - return - midibuf_is_full(this) ? - 0 : - (this->pos_read - this->pos_write + this->size - 1) % this->size + - 1; -} - -int line6_midibuf_bytes_used(struct midi_buffer *this) -{ - return - midibuf_is_empty(this) ? - 0 : - (this->pos_write - this->pos_read + this->size - 1) % this->size + - 1; -} - -int line6_midibuf_write(struct midi_buffer *this, unsigned char *data, - int length) -{ - int bytes_free; - int length1, length2; - int skip_active_sense = 0; - - if (midibuf_is_full(this) || (length <= 0)) - return 0; - - /* skip trailing active sense */ - if (data[length - 1] == 0xfe) { - --length; - skip_active_sense = 1; - } - - bytes_free = line6_midibuf_bytes_free(this); - - if (length > bytes_free) - length = bytes_free; - - if (length > 0) { - length1 = this->size - this->pos_write; - - if (length < length1) { - /* no buffer wraparound */ - memcpy(this->buf + this->pos_write, data, length); - this->pos_write += length; - } else { - /* buffer wraparound */ - length2 = length - length1; - memcpy(this->buf + this->pos_write, data, length1); - memcpy(this->buf, data + length1, length2); - this->pos_write = length2; - } - - if (this->pos_write == this->pos_read) - this->full = 1; - } - - return length + skip_active_sense; -} - -int line6_midibuf_read(struct midi_buffer *this, unsigned char *data, - int length) -{ - int bytes_used; - int length1, length2; - int command; - int midi_length; - int repeat = 0; - int i; - - /* we need to be able to store at least a 3 byte MIDI message */ - if (length < 3) - return -EINVAL; - - if (midibuf_is_empty(this)) - return 0; - - bytes_used = line6_midibuf_bytes_used(this); - - if (length > bytes_used) - length = bytes_used; - - length1 = this->size - this->pos_read; - - /* check MIDI command length */ - command = this->buf[this->pos_read]; - - if (command & 0x80) { - midi_length = midibuf_message_length(command); - this->command_prev = command; - } else { - if (this->command_prev > 0) { - int midi_length_prev = - midibuf_message_length(this->command_prev); - - if (midi_length_prev > 0) { - midi_length = midi_length_prev - 1; - repeat = 1; - } else - midi_length = -1; - } else - midi_length = -1; - } - - if (midi_length < 0) { - /* search for end of message */ - if (length < length1) { - /* no buffer wraparound */ - for (i = 1; i < length; ++i) - if (this->buf[this->pos_read + i] & 0x80) - break; - - midi_length = i; - } else { - /* buffer wraparound */ - length2 = length - length1; - - for (i = 1; i < length1; ++i) - if (this->buf[this->pos_read + i] & 0x80) - break; - - if (i < length1) - midi_length = i; - else { - for (i = 0; i < length2; ++i) - if (this->buf[i] & 0x80) - break; - - midi_length = length1 + i; - } - } - - if (midi_length == length) - midi_length = -1; /* end of message not found */ - } - - if (midi_length < 0) { - if (!this->split) - return 0; /* command is not yet complete */ - } else { - if (length < midi_length) - return 0; /* command is not yet complete */ - - length = midi_length; - } - - if (length < length1) { - /* no buffer wraparound */ - memcpy(data + repeat, this->buf + this->pos_read, length); - this->pos_read += length; - } else { - /* buffer wraparound */ - length2 = length - length1; - memcpy(data + repeat, this->buf + this->pos_read, length1); - memcpy(data + repeat + length1, this->buf, length2); - this->pos_read = length2; - } - - if (repeat) - data[0] = this->command_prev; - - this->full = 0; - return length + repeat; -} - -int line6_midibuf_ignore(struct midi_buffer *this, int length) -{ - int bytes_used = line6_midibuf_bytes_used(this); - - if (length > bytes_used) - length = bytes_used; - - this->pos_read = (this->pos_read + length) % this->size; - this->full = 0; - return length; -} - -int line6_midibuf_skip_message(struct midi_buffer *this, unsigned short mask) -{ - int cmd = this->command_prev; - - if ((cmd >= 0x80) && (cmd < 0xf0)) - if ((mask & (1 << (cmd & 0x0f))) == 0) - return 1; - - return 0; -} - -void line6_midibuf_destroy(struct midi_buffer *this) -{ - kfree(this->buf); - this->buf = NULL; -} diff --git a/drivers/staging/line6/midibuf.h b/drivers/staging/line6/midibuf.h deleted file mode 100644 index 707482b..0000000 --- a/drivers/staging/line6/midibuf.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef MIDIBUF_H -#define MIDIBUF_H - -struct midi_buffer { - unsigned char *buf; - int size; - int split; - int pos_read, pos_write; - int full; - int command_prev; -}; - -extern int line6_midibuf_bytes_used(struct midi_buffer *mb); -extern int line6_midibuf_bytes_free(struct midi_buffer *mb); -extern void line6_midibuf_destroy(struct midi_buffer *mb); -extern int line6_midibuf_ignore(struct midi_buffer *mb, int length); -extern int line6_midibuf_init(struct midi_buffer *mb, int size, int split); -extern int line6_midibuf_read(struct midi_buffer *mb, unsigned char *data, - int length); -extern void line6_midibuf_reset(struct midi_buffer *mb); -extern int line6_midibuf_skip_message(struct midi_buffer *mb, - unsigned short mask); -extern void line6_midibuf_status(struct midi_buffer *mb); -extern int line6_midibuf_write(struct midi_buffer *mb, unsigned char *data, - int length); - -#endif diff --git a/drivers/staging/line6/pcm.c b/drivers/staging/line6/pcm.c deleted file mode 100644 index 6d4e5cd..0000000 --- a/drivers/staging/line6/pcm.c +++ /dev/null @@ -1,527 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#include -#include -#include -#include -#include - -#include "audio.h" -#include "capture.h" -#include "driver.h" -#include "playback.h" -#include "pod.h" - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - -static struct snd_line6_pcm *dev2pcm(struct device *dev) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6 *line6 = usb_get_intfdata(interface); - struct snd_line6_pcm *line6pcm = line6->line6pcm; - return line6pcm; -} - -/* - "read" request on "impulse_volume" special file. -*/ -static ssize_t impulse_volume_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_volume); -} - -/* - "write" request on "impulse_volume" special file. -*/ -static ssize_t impulse_volume_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct snd_line6_pcm *line6pcm = dev2pcm(dev); - int value; - int ret; - - ret = kstrtoint(buf, 10, &value); - if (ret < 0) - return ret; - - line6pcm->impulse_volume = value; - - if (value > 0) - line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_IMPULSE); - else - line6_pcm_release(line6pcm, LINE6_BITS_PCM_IMPULSE); - - return count; -} -static DEVICE_ATTR_RW(impulse_volume); - -/* - "read" request on "impulse_period" special file. -*/ -static ssize_t impulse_period_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_period); -} - -/* - "write" request on "impulse_period" special file. -*/ -static ssize_t impulse_period_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int value; - int ret; - - ret = kstrtoint(buf, 10, &value); - if (ret < 0) - return ret; - - dev2pcm(dev)->impulse_period = value; - return count; -} -static DEVICE_ATTR_RW(impulse_period); - -#endif - -static bool test_flags(unsigned long flags0, unsigned long flags1, - unsigned long mask) -{ - return ((flags0 & mask) == 0) && ((flags1 & mask) != 0); -} - -int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels) -{ - unsigned long flags_old, flags_new, flags_final; - int err; - - do { - flags_old = ACCESS_ONCE(line6pcm->flags); - flags_new = flags_old | channels; - } while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); - - flags_final = flags_old; - - line6pcm->prev_fbuf = NULL; - - if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_BUFFER)) { - /* Invoked multiple times in a row so allocate once only */ - if (!line6pcm->buffer_in) { - line6pcm->buffer_in = - kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * - line6pcm->max_packet_size, GFP_KERNEL); - if (!line6pcm->buffer_in) { - err = -ENOMEM; - goto pcm_acquire_error; - } - - flags_final |= channels & LINE6_BITS_CAPTURE_BUFFER; - } - } - - if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_STREAM)) { - /* - Waiting for completion of active URBs in the stop handler is - a bug, we therefore report an error if capturing is restarted - too soon. - */ - if (line6pcm->active_urb_in | line6pcm->unlink_urb_in) { - dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); - return -EBUSY; - } - - line6pcm->count_in = 0; - line6pcm->prev_fsize = 0; - err = line6_submit_audio_in_all_urbs(line6pcm); - - if (err < 0) - goto pcm_acquire_error; - - flags_final |= channels & LINE6_BITS_CAPTURE_STREAM; - } - - if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_BUFFER)) { - /* Invoked multiple times in a row so allocate once only */ - if (!line6pcm->buffer_out) { - line6pcm->buffer_out = - kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * - line6pcm->max_packet_size, GFP_KERNEL); - if (!line6pcm->buffer_out) { - err = -ENOMEM; - goto pcm_acquire_error; - } - - flags_final |= channels & LINE6_BITS_PLAYBACK_BUFFER; - } - } - - if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_STREAM)) { - /* - See comment above regarding PCM restart. - */ - if (line6pcm->active_urb_out | line6pcm->unlink_urb_out) { - dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); - return -EBUSY; - } - - line6pcm->count_out = 0; - err = line6_submit_audio_out_all_urbs(line6pcm); - - if (err < 0) - goto pcm_acquire_error; - - flags_final |= channels & LINE6_BITS_PLAYBACK_STREAM; - } - - return 0; - -pcm_acquire_error: - /* - If not all requested resources/streams could be obtained, release - those which were successfully obtained (if any). - */ - line6_pcm_release(line6pcm, flags_final & channels); - return err; -} - -int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels) -{ - unsigned long flags_old, flags_new; - - do { - flags_old = ACCESS_ONCE(line6pcm->flags); - flags_new = flags_old & ~channels; - } while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); - - if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_STREAM)) - line6_unlink_audio_in_urbs(line6pcm); - - if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_BUFFER)) { - line6_wait_clear_audio_in_urbs(line6pcm); - line6_free_capture_buffer(line6pcm); - } - - if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_STREAM)) - line6_unlink_audio_out_urbs(line6pcm); - - if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_BUFFER)) { - line6_wait_clear_audio_out_urbs(line6pcm); - line6_free_playback_buffer(line6pcm); - } - - return 0; -} - -/* trigger callback */ -int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - struct snd_pcm_substream *s; - int err; - unsigned long flags; - - spin_lock_irqsave(&line6pcm->lock_trigger, flags); - clear_bit(LINE6_INDEX_PREPARED, &line6pcm->flags); - - snd_pcm_group_for_each_entry(s, substream) { - switch (s->stream) { - case SNDRV_PCM_STREAM_PLAYBACK: - err = snd_line6_playback_trigger(line6pcm, cmd); - - if (err < 0) { - spin_unlock_irqrestore(&line6pcm->lock_trigger, - flags); - return err; - } - - break; - - case SNDRV_PCM_STREAM_CAPTURE: - err = snd_line6_capture_trigger(line6pcm, cmd); - - if (err < 0) { - spin_unlock_irqrestore(&line6pcm->lock_trigger, - flags); - return err; - } - - break; - - default: - dev_err(line6pcm->line6->ifcdev, - "Unknown stream direction %d\n", s->stream); - } - } - - spin_unlock_irqrestore(&line6pcm->lock_trigger, flags); - return 0; -} - -/* control info callback */ -static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 256; - return 0; -} - -/* control get callback */ -static int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int i; - struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - - for (i = 2; i--;) - ucontrol->value.integer.value[i] = line6pcm->volume_playback[i]; - - return 0; -} - -/* control put callback */ -static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int i, changed = 0; - struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - - for (i = 2; i--;) - if (line6pcm->volume_playback[i] != - ucontrol->value.integer.value[i]) { - line6pcm->volume_playback[i] = - ucontrol->value.integer.value[i]; - changed = 1; - } - - return changed; -} - -/* control definition */ -static struct snd_kcontrol_new line6_control_playback = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Playback Volume", - .index = 0, - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = snd_line6_control_playback_info, - .get = snd_line6_control_playback_get, - .put = snd_line6_control_playback_put -}; - -/* - Cleanup the PCM device. -*/ -static void line6_cleanup_pcm(struct snd_pcm *pcm) -{ - int i; - struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm); - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_volume); - device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_period); -#endif - - for (i = LINE6_ISO_BUFFERS; i--;) { - if (line6pcm->urb_audio_out[i]) { - usb_kill_urb(line6pcm->urb_audio_out[i]); - usb_free_urb(line6pcm->urb_audio_out[i]); - } - if (line6pcm->urb_audio_in[i]) { - usb_kill_urb(line6pcm->urb_audio_in[i]); - usb_free_urb(line6pcm->urb_audio_in[i]); - } - } -} - -/* create a PCM device */ -static int snd_line6_new_pcm(struct snd_line6_pcm *line6pcm) -{ - struct snd_pcm *pcm; - int err; - - err = snd_pcm_new(line6pcm->line6->card, - (char *)line6pcm->line6->properties->name, - 0, 1, 1, &pcm); - if (err < 0) - return err; - - pcm->private_data = line6pcm; - pcm->private_free = line6_cleanup_pcm; - line6pcm->pcm = pcm; - strcpy(pcm->name, line6pcm->line6->properties->name); - - /* set operators */ - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, - &snd_line6_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_line6_capture_ops); - - /* pre-allocation of buffers */ - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data - (GFP_KERNEL), 64 * 1024, - 128 * 1024); - - return 0; -} - -/* PCM device destructor */ -static int snd_line6_pcm_free(struct snd_device *device) -{ - return 0; -} - -/* - Stop substream if still running. -*/ -static void pcm_disconnect_substream(struct snd_pcm_substream *substream) -{ - if (substream->runtime && snd_pcm_running(substream)) { - snd_pcm_stream_lock_irq(substream); - snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); - snd_pcm_stream_unlock_irq(substream); - } -} - -/* - Stop PCM stream. -*/ -void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm) -{ - pcm_disconnect_substream(get_substream - (line6pcm, SNDRV_PCM_STREAM_CAPTURE)); - pcm_disconnect_substream(get_substream - (line6pcm, SNDRV_PCM_STREAM_PLAYBACK)); - line6_unlink_wait_clear_audio_out_urbs(line6pcm); - line6_unlink_wait_clear_audio_in_urbs(line6pcm); -} - -/* - Create and register the PCM device and mixer entries. - Create URBs for playback and capture. -*/ -int line6_init_pcm(struct usb_line6 *line6, - struct line6_pcm_properties *properties) -{ - static struct snd_device_ops pcm_ops = { - .dev_free = snd_line6_pcm_free, - }; - - int err; - unsigned ep_read = line6->properties->ep_audio_r; - unsigned ep_write = line6->properties->ep_audio_w; - struct snd_line6_pcm *line6pcm; - - if (!(line6->properties->capabilities & LINE6_CAP_PCM)) - return 0; /* skip PCM initialization and report success */ - - line6pcm = kzalloc(sizeof(*line6pcm), GFP_KERNEL); - - if (line6pcm == NULL) - return -ENOMEM; - - line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255; - line6pcm->volume_monitor = 255; - line6pcm->line6 = line6; - - /* Read and write buffers are sized identically, so choose minimum */ - line6pcm->max_packet_size = min( - usb_maxpacket(line6->usbdev, - usb_rcvisocpipe(line6->usbdev, ep_read), 0), - usb_maxpacket(line6->usbdev, - usb_sndisocpipe(line6->usbdev, ep_write), 1)); - - line6pcm->properties = properties; - line6->line6pcm = line6pcm; - - /* PCM device: */ - err = snd_device_new(line6->card, SNDRV_DEV_PCM, line6, &pcm_ops); - if (err < 0) - return err; - - err = snd_line6_new_pcm(line6pcm); - if (err < 0) - return err; - - spin_lock_init(&line6pcm->lock_audio_out); - spin_lock_init(&line6pcm->lock_audio_in); - spin_lock_init(&line6pcm->lock_trigger); - - err = line6_create_audio_out_urbs(line6pcm); - if (err < 0) - return err; - - err = line6_create_audio_in_urbs(line6pcm); - if (err < 0) - return err; - - /* mixer: */ - err = - snd_ctl_add(line6->card, - snd_ctl_new1(&line6_control_playback, line6pcm)); - if (err < 0) - return err; - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - /* impulse response test: */ - err = device_create_file(line6->ifcdev, &dev_attr_impulse_volume); - if (err < 0) - return err; - - err = device_create_file(line6->ifcdev, &dev_attr_impulse_period); - if (err < 0) - return err; - - line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD; -#endif - - return 0; -} - -/* prepare pcm callback */ -int snd_line6_prepare(struct snd_pcm_substream *substream) -{ - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - switch (substream->stream) { - case SNDRV_PCM_STREAM_PLAYBACK: - if ((line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM) == 0) - line6_unlink_wait_clear_audio_out_urbs(line6pcm); - - break; - - case SNDRV_PCM_STREAM_CAPTURE: - if ((line6pcm->flags & LINE6_BITS_CAPTURE_STREAM) == 0) - line6_unlink_wait_clear_audio_in_urbs(line6pcm); - - break; - - default: - MISSING_CASE; - } - - if (!test_and_set_bit(LINE6_INDEX_PREPARED, &line6pcm->flags)) { - line6pcm->count_out = 0; - line6pcm->pos_out = 0; - line6pcm->pos_out_done = 0; - line6pcm->bytes_out = 0; - line6pcm->count_in = 0; - line6pcm->pos_in_done = 0; - line6pcm->bytes_in = 0; - } - - return 0; -} diff --git a/drivers/staging/line6/pcm.h b/drivers/staging/line6/pcm.h deleted file mode 100644 index 7315e81..0000000 --- a/drivers/staging/line6/pcm.h +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -/* - PCM interface to POD series devices. -*/ - -#ifndef PCM_H -#define PCM_H - -#include - -#include "driver.h" -#include "usbdefs.h" - -/* number of URBs */ -#define LINE6_ISO_BUFFERS 2 - -/* - number of USB frames per URB - The Line6 Windows driver always transmits two frames per packet, but - the Linux driver performs significantly better (i.e., lower latency) - with only one frame per packet. -*/ -#define LINE6_ISO_PACKETS 1 - -/* in a "full speed" device (such as the PODxt Pro) this means 1ms */ -#define LINE6_ISO_INTERVAL 1 - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE -#define LINE6_IMPULSE_DEFAULT_PERIOD 100 -#endif - -/* - Get substream from Line6 PCM data structure -*/ -#define get_substream(line6pcm, stream) \ - (line6pcm->pcm->streams[stream].substream) - -/* - PCM mode bits. - - There are several features of the Line6 USB driver which require PCM - data to be exchanged with the device: - *) PCM playback and capture via ALSA - *) software monitoring (for devices without hardware monitoring) - *) optional impulse response measurement - However, from the device's point of view, there is just a single - capture and playback stream, which must be shared between these - subsystems. It is therefore necessary to maintain the state of the - subsystems with respect to PCM usage. We define several constants of - the form LINE6_BIT_PCM___ with the - following meanings: - *) is one of - -) ALSA: PCM playback and capture via ALSA - -) MONITOR: software monitoring - -) IMPULSE: optional impulse response measurement - *) is one of - -) PLAYBACK: audio output (from host to device) - -) CAPTURE: audio input (from device to host) - *) is one of - -) BUFFER: buffer required by PCM data stream - -) STREAM: actual PCM data stream - - The subsystems call line6_pcm_acquire() to acquire the (shared) - resources needed for a particular operation (e.g., allocate the buffer - for ALSA playback or start the capture stream for software monitoring). - When a resource is no longer needed, it is released by calling - line6_pcm_release(). Buffer allocation and stream startup are handled - separately to allow the ALSA kernel driver to perform them at - appropriate places (since the callback which starts a PCM stream is not - allowed to sleep). -*/ -enum { - /* individual bit indices: */ - LINE6_INDEX_PCM_ALSA_PLAYBACK_BUFFER, - LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, - LINE6_INDEX_PCM_ALSA_CAPTURE_BUFFER, - LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, - LINE6_INDEX_PCM_MONITOR_PLAYBACK_BUFFER, - LINE6_INDEX_PCM_MONITOR_PLAYBACK_STREAM, - LINE6_INDEX_PCM_MONITOR_CAPTURE_BUFFER, - LINE6_INDEX_PCM_MONITOR_CAPTURE_STREAM, -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - LINE6_INDEX_PCM_IMPULSE_PLAYBACK_BUFFER, - LINE6_INDEX_PCM_IMPULSE_PLAYBACK_STREAM, - LINE6_INDEX_PCM_IMPULSE_CAPTURE_BUFFER, - LINE6_INDEX_PCM_IMPULSE_CAPTURE_STREAM, -#endif - LINE6_INDEX_PAUSE_PLAYBACK, - LINE6_INDEX_PREPARED, - -#define LINE6_BIT(x) LINE6_BIT_ ## x = 1 << LINE6_INDEX_ ## x - - /* individual bit masks: */ - LINE6_BIT(PCM_ALSA_PLAYBACK_BUFFER), - LINE6_BIT(PCM_ALSA_PLAYBACK_STREAM), - LINE6_BIT(PCM_ALSA_CAPTURE_BUFFER), - LINE6_BIT(PCM_ALSA_CAPTURE_STREAM), - LINE6_BIT(PCM_MONITOR_PLAYBACK_BUFFER), - LINE6_BIT(PCM_MONITOR_PLAYBACK_STREAM), - LINE6_BIT(PCM_MONITOR_CAPTURE_BUFFER), - LINE6_BIT(PCM_MONITOR_CAPTURE_STREAM), -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - LINE6_BIT(PCM_IMPULSE_PLAYBACK_BUFFER), - LINE6_BIT(PCM_IMPULSE_PLAYBACK_STREAM), - LINE6_BIT(PCM_IMPULSE_CAPTURE_BUFFER), - LINE6_BIT(PCM_IMPULSE_CAPTURE_STREAM), -#endif - LINE6_BIT(PAUSE_PLAYBACK), - LINE6_BIT(PREPARED), - - /* combined bit masks (by operation): */ - LINE6_BITS_PCM_ALSA_BUFFER = - LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER | - LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER, - - LINE6_BITS_PCM_ALSA_STREAM = - LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM | - LINE6_BIT_PCM_ALSA_CAPTURE_STREAM, - - LINE6_BITS_PCM_MONITOR = - LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER | - LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM | - LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER | - LINE6_BIT_PCM_MONITOR_CAPTURE_STREAM, - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - LINE6_BITS_PCM_IMPULSE = - LINE6_BIT_PCM_IMPULSE_PLAYBACK_BUFFER | - LINE6_BIT_PCM_IMPULSE_PLAYBACK_STREAM | - LINE6_BIT_PCM_IMPULSE_CAPTURE_BUFFER | - LINE6_BIT_PCM_IMPULSE_CAPTURE_STREAM, -#endif - - /* combined bit masks (by direction): */ - LINE6_BITS_PLAYBACK_BUFFER = -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - LINE6_BIT_PCM_IMPULSE_PLAYBACK_BUFFER | -#endif - LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER | - LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER, - - LINE6_BITS_PLAYBACK_STREAM = -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - LINE6_BIT_PCM_IMPULSE_PLAYBACK_STREAM | -#endif - LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM | - LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM, - - LINE6_BITS_CAPTURE_BUFFER = -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - LINE6_BIT_PCM_IMPULSE_CAPTURE_BUFFER | -#endif - LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER | - LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER, - - LINE6_BITS_CAPTURE_STREAM = -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - LINE6_BIT_PCM_IMPULSE_CAPTURE_STREAM | -#endif - LINE6_BIT_PCM_ALSA_CAPTURE_STREAM | - LINE6_BIT_PCM_MONITOR_CAPTURE_STREAM, - - LINE6_BITS_STREAM = - LINE6_BITS_PLAYBACK_STREAM | - LINE6_BITS_CAPTURE_STREAM -}; - -struct line6_pcm_properties { - struct snd_pcm_hardware snd_line6_playback_hw, snd_line6_capture_hw; - struct snd_pcm_hw_constraint_ratdens snd_line6_rates; - int bytes_per_frame; -}; - -struct snd_line6_pcm { - /** - Pointer back to the Line6 driver data structure. - */ - struct usb_line6 *line6; - - /** - Properties. - */ - struct line6_pcm_properties *properties; - - /** - ALSA pcm stream - */ - struct snd_pcm *pcm; - - /** - URBs for audio playback. - */ - struct urb *urb_audio_out[LINE6_ISO_BUFFERS]; - - /** - URBs for audio capture. - */ - struct urb *urb_audio_in[LINE6_ISO_BUFFERS]; - - /** - Temporary buffer for playback. - Since the packet size is not known in advance, this buffer is - large enough to store maximum size packets. - */ - unsigned char *buffer_out; - - /** - Temporary buffer for capture. - Since the packet size is not known in advance, this buffer is - large enough to store maximum size packets. - */ - unsigned char *buffer_in; - - /** - Previously captured frame (for software monitoring). - */ - unsigned char *prev_fbuf; - - /** - Size of previously captured frame (for software monitoring). - */ - int prev_fsize; - - /** - Free frame position in the playback buffer. - */ - snd_pcm_uframes_t pos_out; - - /** - Count processed bytes for playback. - This is modulo period size (to determine when a period is - finished). - */ - unsigned bytes_out; - - /** - Counter to create desired playback sample rate. - */ - unsigned count_out; - - /** - Playback period size in bytes - */ - unsigned period_out; - - /** - Processed frame position in the playback buffer. - The contents of the output ring buffer have been consumed by - the USB subsystem (i.e., sent to the USB device) up to this - position. - */ - snd_pcm_uframes_t pos_out_done; - - /** - Count processed bytes for capture. - This is modulo period size (to determine when a period is - finished). - */ - unsigned bytes_in; - - /** - Counter to create desired capture sample rate. - */ - unsigned count_in; - - /** - Capture period size in bytes - */ - unsigned period_in; - - /** - Processed frame position in the capture buffer. - The contents of the output ring buffer have been consumed by - the USB subsystem (i.e., sent to the USB device) up to this - position. - */ - snd_pcm_uframes_t pos_in_done; - - /** - Bit mask of active playback URBs. - */ - unsigned long active_urb_out; - - /** - Maximum size of USB packet. - */ - int max_packet_size; - - /** - Bit mask of active capture URBs. - */ - unsigned long active_urb_in; - - /** - Bit mask of playback URBs currently being unlinked. - */ - unsigned long unlink_urb_out; - - /** - Bit mask of capture URBs currently being unlinked. - */ - unsigned long unlink_urb_in; - - /** - Spin lock to protect updates of the playback buffer positions (not - contents!) - */ - spinlock_t lock_audio_out; - - /** - Spin lock to protect updates of the capture buffer positions (not - contents!) - */ - spinlock_t lock_audio_in; - - /** - Spin lock to protect trigger. - */ - spinlock_t lock_trigger; - - /** - PCM playback volume (left and right). - */ - int volume_playback[2]; - - /** - PCM monitor volume. - */ - int volume_monitor; - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - /** - Volume of impulse response test signal (if zero, test is disabled). - */ - int impulse_volume; - - /** - Period of impulse response test signal. - */ - int impulse_period; - - /** - Counter for impulse response test signal. - */ - int impulse_count; -#endif - - /** - Several status bits (see LINE6_BIT_*). - */ - unsigned long flags; - - int last_frame_in, last_frame_out; -}; - -extern int line6_init_pcm(struct usb_line6 *line6, - struct line6_pcm_properties *properties); -extern int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd); -extern int snd_line6_prepare(struct snd_pcm_substream *substream); -extern void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm); -extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels); -extern int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels); - -#endif diff --git a/drivers/staging/line6/playback.c b/drivers/staging/line6/playback.c deleted file mode 100644 index da2e3b8..0000000 --- a/drivers/staging/line6/playback.c +++ /dev/null @@ -1,593 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#include -#include -#include -#include - -#include "audio.h" -#include "capture.h" -#include "driver.h" -#include "pcm.h" -#include "pod.h" -#include "playback.h" - -/* - Software stereo volume control. -*/ -static void change_volume(struct urb *urb_out, int volume[], - int bytes_per_frame) -{ - int chn = 0; - - if (volume[0] == 256 && volume[1] == 256) - return; /* maximum volume - no change */ - - if (bytes_per_frame == 4) { - short *p, *buf_end; - - p = (short *)urb_out->transfer_buffer; - buf_end = p + urb_out->transfer_buffer_length / sizeof(*p); - - for (; p < buf_end; ++p) { - *p = (*p * volume[chn & 1]) >> 8; - ++chn; - } - } else if (bytes_per_frame == 6) { - unsigned char *p, *buf_end; - - p = (unsigned char *)urb_out->transfer_buffer; - buf_end = p + urb_out->transfer_buffer_length; - - for (; p < buf_end; p += 3) { - int val; - - val = p[0] + (p[1] << 8) + ((signed char)p[2] << 16); - val = (val * volume[chn & 1]) >> 8; - p[0] = val; - p[1] = val >> 8; - p[2] = val >> 16; - ++chn; - } - } -} - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - -/* - Create signal for impulse response test. -*/ -static void create_impulse_test_signal(struct snd_line6_pcm *line6pcm, - struct urb *urb_out, int bytes_per_frame) -{ - int frames = urb_out->transfer_buffer_length / bytes_per_frame; - - if (bytes_per_frame == 4) { - int i; - short *pi = (short *)line6pcm->prev_fbuf; - short *po = (short *)urb_out->transfer_buffer; - - for (i = 0; i < frames; ++i) { - po[0] = pi[0]; - po[1] = 0; - pi += 2; - po += 2; - } - } else if (bytes_per_frame == 6) { - int i, j; - unsigned char *pi = line6pcm->prev_fbuf; - unsigned char *po = urb_out->transfer_buffer; - - for (i = 0; i < frames; ++i) { - for (j = 0; j < bytes_per_frame / 2; ++j) - po[j] = pi[j]; - - for (; j < bytes_per_frame; ++j) - po[j] = 0; - - pi += bytes_per_frame; - po += bytes_per_frame; - } - } - if (--line6pcm->impulse_count <= 0) { - ((unsigned char *)(urb_out->transfer_buffer))[bytes_per_frame - - 1] = - line6pcm->impulse_volume; - line6pcm->impulse_count = line6pcm->impulse_period; - } -} - -#endif - -/* - Add signal to buffer for software monitoring. -*/ -static void add_monitor_signal(struct urb *urb_out, unsigned char *signal, - int volume, int bytes_per_frame) -{ - if (volume == 0) - return; /* zero volume - no change */ - - if (bytes_per_frame == 4) { - short *pi, *po, *buf_end; - - pi = (short *)signal; - po = (short *)urb_out->transfer_buffer; - buf_end = po + urb_out->transfer_buffer_length / sizeof(*po); - - for (; po < buf_end; ++pi, ++po) - *po += (*pi * volume) >> 8; - } - - /* - We don't need to handle devices with 6 bytes per frame here - since they all support hardware monitoring. - */ -} - -/* - Find a free URB, prepare audio data, and submit URB. -*/ -static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) -{ - int index; - unsigned long flags; - int i, urb_size, urb_frames; - int ret; - const int bytes_per_frame = line6pcm->properties->bytes_per_frame; - const int frame_increment = - line6pcm->properties->snd_line6_rates.rats[0].num_min; - const int frame_factor = - line6pcm->properties->snd_line6_rates.rats[0].den * - (USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL); - struct urb *urb_out; - - spin_lock_irqsave(&line6pcm->lock_audio_out, flags); - index = - find_first_zero_bit(&line6pcm->active_urb_out, LINE6_ISO_BUFFERS); - - if (index < 0 || index >= LINE6_ISO_BUFFERS) { - spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); - dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); - return -EINVAL; - } - - urb_out = line6pcm->urb_audio_out[index]; - urb_size = 0; - - for (i = 0; i < LINE6_ISO_PACKETS; ++i) { - /* compute frame size for given sampling rate */ - int fsize = 0; - struct usb_iso_packet_descriptor *fout = - &urb_out->iso_frame_desc[i]; - - if (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM) - fsize = line6pcm->prev_fsize; - - if (fsize == 0) { - int n; - - line6pcm->count_out += frame_increment; - n = line6pcm->count_out / frame_factor; - line6pcm->count_out -= n * frame_factor; - fsize = n * bytes_per_frame; - } - - fout->offset = urb_size; - fout->length = fsize; - urb_size += fsize; - } - - if (urb_size == 0) { - /* can't determine URB size */ - spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); - dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n"); - return -EINVAL; - } - - urb_frames = urb_size / bytes_per_frame; - urb_out->transfer_buffer = - line6pcm->buffer_out + - index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; - urb_out->transfer_buffer_length = urb_size; - urb_out->context = line6pcm; - - if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags) && - !test_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags)) { - struct snd_pcm_runtime *runtime = - get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)->runtime; - - if (line6pcm->pos_out + urb_frames > runtime->buffer_size) { - /* - The transferred area goes over buffer boundary, - copy the data to the temp buffer. - */ - int len; - - len = runtime->buffer_size - line6pcm->pos_out; - - if (len > 0) { - memcpy(urb_out->transfer_buffer, - runtime->dma_area + - line6pcm->pos_out * bytes_per_frame, - len * bytes_per_frame); - memcpy(urb_out->transfer_buffer + - len * bytes_per_frame, runtime->dma_area, - (urb_frames - len) * bytes_per_frame); - } else - dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n", - len); - } else { - memcpy(urb_out->transfer_buffer, - runtime->dma_area + - line6pcm->pos_out * bytes_per_frame, - urb_out->transfer_buffer_length); - } - - line6pcm->pos_out += urb_frames; - if (line6pcm->pos_out >= runtime->buffer_size) - line6pcm->pos_out -= runtime->buffer_size; - } else { - memset(urb_out->transfer_buffer, 0, - urb_out->transfer_buffer_length); - } - - change_volume(urb_out, line6pcm->volume_playback, bytes_per_frame); - - if (line6pcm->prev_fbuf != NULL) { -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - if (line6pcm->flags & LINE6_BITS_PCM_IMPULSE) { - create_impulse_test_signal(line6pcm, urb_out, - bytes_per_frame); - if (line6pcm->flags & - LINE6_BIT_PCM_ALSA_CAPTURE_STREAM) { - line6_capture_copy(line6pcm, - urb_out->transfer_buffer, - urb_out-> - transfer_buffer_length); - line6_capture_check_period(line6pcm, - urb_out->transfer_buffer_length); - } - } else { -#endif - if (! - (line6pcm->line6-> - properties->capabilities & LINE6_CAP_HWMON) - && (line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM) - && (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM)) - add_monitor_signal(urb_out, line6pcm->prev_fbuf, - line6pcm->volume_monitor, - bytes_per_frame); -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - } -#endif - } - - ret = usb_submit_urb(urb_out, GFP_ATOMIC); - - if (ret == 0) - set_bit(index, &line6pcm->active_urb_out); - else - dev_err(line6pcm->line6->ifcdev, - "URB out #%d submission failed (%d)\n", index, ret); - - spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); - return 0; -} - -/* - Submit all currently available playback URBs. -*/ -int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm) -{ - int ret, i; - - for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { - ret = submit_audio_out_urb(line6pcm); - if (ret < 0) - return ret; - } - - return 0; -} - -/* - Unlink all currently active playback URBs. -*/ -void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm) -{ - unsigned int i; - - for (i = LINE6_ISO_BUFFERS; i--;) { - if (test_bit(i, &line6pcm->active_urb_out)) { - if (!test_and_set_bit(i, &line6pcm->unlink_urb_out)) { - struct urb *u = line6pcm->urb_audio_out[i]; - - usb_unlink_urb(u); - } - } - } -} - -/* - Wait until unlinking of all currently active playback URBs has been - finished. -*/ -void line6_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) -{ - int timeout = HZ; - unsigned int i; - int alive; - - do { - alive = 0; - for (i = LINE6_ISO_BUFFERS; i--;) { - if (test_bit(i, &line6pcm->active_urb_out)) - alive++; - } - if (!alive) - break; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } while (--timeout > 0); - if (alive) - snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); -} - -/* - Unlink all currently active playback URBs, and wait for finishing. -*/ -void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) -{ - line6_unlink_audio_out_urbs(line6pcm); - line6_wait_clear_audio_out_urbs(line6pcm); -} - -void line6_free_playback_buffer(struct snd_line6_pcm *line6pcm) -{ - kfree(line6pcm->buffer_out); - line6pcm->buffer_out = NULL; -} - -/* - Callback for completed playback URB. -*/ -static void audio_out_callback(struct urb *urb) -{ - int i, index, length = 0, shutdown = 0; - unsigned long flags; - struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context; - struct snd_pcm_substream *substream = - get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK); - -#if USE_CLEAR_BUFFER_WORKAROUND - memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); -#endif - - line6pcm->last_frame_out = urb->start_frame; - - /* find index of URB */ - for (index = LINE6_ISO_BUFFERS; index--;) - if (urb == line6pcm->urb_audio_out[index]) - break; - - if (index < 0) - return; /* URB has been unlinked asynchronously */ - - for (i = LINE6_ISO_PACKETS; i--;) - length += urb->iso_frame_desc[i].length; - - spin_lock_irqsave(&line6pcm->lock_audio_out, flags); - - if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags)) { - struct snd_pcm_runtime *runtime = substream->runtime; - - line6pcm->pos_out_done += - length / line6pcm->properties->bytes_per_frame; - - if (line6pcm->pos_out_done >= runtime->buffer_size) - line6pcm->pos_out_done -= runtime->buffer_size; - } - - clear_bit(index, &line6pcm->active_urb_out); - - for (i = LINE6_ISO_PACKETS; i--;) - if (urb->iso_frame_desc[i].status == -EXDEV) { - shutdown = 1; - break; - } - - if (test_and_clear_bit(index, &line6pcm->unlink_urb_out)) - shutdown = 1; - - spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); - - if (!shutdown) { - submit_audio_out_urb(line6pcm); - - if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, - &line6pcm->flags)) { - line6pcm->bytes_out += length; - if (line6pcm->bytes_out >= line6pcm->period_out) { - line6pcm->bytes_out %= line6pcm->period_out; - snd_pcm_period_elapsed(substream); - } - } - } -} - -/* open playback callback */ -static int snd_line6_playback_open(struct snd_pcm_substream *substream) -{ - int err; - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - err = snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - (&line6pcm-> - properties->snd_line6_rates)); - if (err < 0) - return err; - - runtime->hw = line6pcm->properties->snd_line6_playback_hw; - return 0; -} - -/* close playback callback */ -static int snd_line6_playback_close(struct snd_pcm_substream *substream) -{ - return 0; -} - -/* hw_params playback callback */ -static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - int ret; - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - /* -- Florian Demski [FD] */ - /* don't ask me why, but this fixes the bug on my machine */ - if (line6pcm == NULL) { - if (substream->pcm == NULL) - return -ENOMEM; - if (substream->pcm->private_data == NULL) - return -ENOMEM; - substream->private_data = substream->pcm->private_data; - line6pcm = snd_pcm_substream_chip(substream); - } - /* -- [FD] end */ - - ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); - - if (ret < 0) - return ret; - - ret = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (ret < 0) { - line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); - return ret; - } - - line6pcm->period_out = params_period_bytes(hw_params); - return 0; -} - -/* hw_free playback callback */ -static int snd_line6_playback_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); - return snd_pcm_lib_free_pages(substream); -} - -/* trigger playback callback */ -int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd) -{ - int err; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: -#ifdef CONFIG_PM - case SNDRV_PCM_TRIGGER_RESUME: -#endif - err = line6_pcm_acquire(line6pcm, - LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM); - - if (err < 0) - return err; - - break; - - case SNDRV_PCM_TRIGGER_STOP: -#ifdef CONFIG_PM - case SNDRV_PCM_TRIGGER_SUSPEND: -#endif - err = line6_pcm_release(line6pcm, - LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM); - - if (err < 0) - return err; - - break; - - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - set_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags); - break; - - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - clear_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags); - break; - - default: - return -EINVAL; - } - - return 0; -} - -/* playback pointer callback */ -static snd_pcm_uframes_t -snd_line6_playback_pointer(struct snd_pcm_substream *substream) -{ - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - return line6pcm->pos_out_done; -} - -/* playback operators */ -struct snd_pcm_ops snd_line6_playback_ops = { - .open = snd_line6_playback_open, - .close = snd_line6_playback_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_line6_playback_hw_params, - .hw_free = snd_line6_playback_hw_free, - .prepare = snd_line6_prepare, - .trigger = snd_line6_trigger, - .pointer = snd_line6_playback_pointer, -}; - -int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm) -{ - struct usb_line6 *line6 = line6pcm->line6; - int i; - - /* create audio URBs and fill in constant values: */ - for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { - struct urb *urb; - - /* URB for audio out: */ - urb = line6pcm->urb_audio_out[i] = - usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); - - if (urb == NULL) { - dev_err(line6->ifcdev, "Out of memory\n"); - return -ENOMEM; - } - - urb->dev = line6->usbdev; - urb->pipe = - usb_sndisocpipe(line6->usbdev, - line6->properties->ep_audio_w & - USB_ENDPOINT_NUMBER_MASK); - urb->transfer_flags = URB_ISO_ASAP; - urb->start_frame = -1; - urb->number_of_packets = LINE6_ISO_PACKETS; - urb->interval = LINE6_ISO_INTERVAL; - urb->error_count = 0; - urb->complete = audio_out_callback; - } - - return 0; -} diff --git a/drivers/staging/line6/playback.h b/drivers/staging/line6/playback.h deleted file mode 100644 index 743bd6f..0000000 --- a/drivers/staging/line6/playback.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef PLAYBACK_H -#define PLAYBACK_H - -#include - -#include "driver.h" - -/* - * When the TonePort is used with jack in full duplex mode and the outputs are - * not connected, the software monitor produces an ugly noise since everything - * written to the output buffer (i.e., the input signal) will be repeated in - * the next period (sounds like a delay effect). As a workaround, the output - * buffer is cleared after the data have been read, but there must be a better - * solution. Until one is found, this workaround can be used to fix the - * problem. - */ -#define USE_CLEAR_BUFFER_WORKAROUND 1 - -extern struct snd_pcm_ops snd_line6_playback_ops; - -extern int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_free_playback_buffer(struct snd_line6_pcm *line6pcm); -extern int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm - *line6pcm); -extern void line6_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm); -extern int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd); - -#endif diff --git a/drivers/staging/line6/pod.c b/drivers/staging/line6/pod.c deleted file mode 100644 index 85a4363..0000000 --- a/drivers/staging/line6/pod.c +++ /dev/null @@ -1,453 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#include -#include -#include - -#include "audio.h" -#include "capture.h" -#include "driver.h" -#include "playback.h" -#include "pod.h" - -#define POD_SYSEX_CODE 3 -#define POD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */ - -/* *INDENT-OFF* */ - -enum { - POD_SYSEX_SAVE = 0x24, - POD_SYSEX_SYSTEM = 0x56, - POD_SYSEX_SYSTEMREQ = 0x57, - /* POD_SYSEX_UPDATE = 0x6c, */ /* software update! */ - POD_SYSEX_STORE = 0x71, - POD_SYSEX_FINISH = 0x72, - POD_SYSEX_DUMPMEM = 0x73, - POD_SYSEX_DUMP = 0x74, - POD_SYSEX_DUMPREQ = 0x75 - - /* dumps entire internal memory of PODxt Pro */ - /* POD_SYSEX_DUMPMEM2 = 0x76 */ -}; - -enum { - POD_MONITOR_LEVEL = 0x04, - POD_SYSTEM_INVALID = 0x10000 -}; - -/* *INDENT-ON* */ - -enum { - POD_DUMP_MEMORY = 2 -}; - -enum { - POD_BUSY_READ, - POD_BUSY_WRITE, - POD_CHANNEL_DIRTY, - POD_SAVE_PRESSED, - POD_BUSY_MIDISEND -}; - -static struct snd_ratden pod_ratden = { - .num_min = 78125, - .num_max = 78125, - .num_step = 1, - .den = 2 -}; - -static struct line6_pcm_properties pod_pcm_properties = { - .snd_line6_playback_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | -#ifdef CONFIG_PM - SNDRV_PCM_INFO_RESUME | -#endif - SNDRV_PCM_INFO_SYNC_START), - .formats = SNDRV_PCM_FMTBIT_S24_3LE, - .rates = SNDRV_PCM_RATE_KNOT, - .rate_min = 39062, - .rate_max = 39063, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 60000, - .period_bytes_min = 64, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 1024}, - .snd_line6_capture_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | -#ifdef CONFIG_PM - SNDRV_PCM_INFO_RESUME | -#endif - SNDRV_PCM_INFO_SYNC_START), - .formats = SNDRV_PCM_FMTBIT_S24_3LE, - .rates = SNDRV_PCM_RATE_KNOT, - .rate_min = 39062, - .rate_max = 39063, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 60000, - .period_bytes_min = 64, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 1024}, - .snd_line6_rates = { - .nrats = 1, - .rats = &pod_ratden}, - .bytes_per_frame = POD_BYTES_PER_FRAME -}; - -static const char pod_version_header[] = { - 0xf2, 0x7e, 0x7f, 0x06, 0x02 -}; - -/* forward declarations: */ -static void pod_startup2(unsigned long data); -static void pod_startup3(struct usb_line6_pod *pod); - -static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, - int size) -{ - return line6_alloc_sysex_buffer(&pod->line6, POD_SYSEX_CODE, code, - size); -} - -/* - Process a completely received message. -*/ -static void line6_pod_process_message(struct usb_line6 *line6) -{ - struct usb_line6_pod *pod = (struct usb_line6_pod *) line6; - const unsigned char *buf = pod->line6.buffer_message; - - if (memcmp(buf, pod_version_header, sizeof(pod_version_header)) == 0) { - pod->firmware_version = buf[13] * 100 + buf[14] * 10 + buf[15]; - pod->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) | - (int) buf[10]; - pod_startup3(pod); - return; - } - - /* Only look for sysex messages from this device */ - if (buf[0] != (LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE) && - buf[0] != (LINE6_SYSEX_BEGIN | LINE6_CHANNEL_UNKNOWN)) { - return; - } - if (memcmp(buf + 1, line6_midi_id, sizeof(line6_midi_id)) != 0) - return; - - if (buf[5] == POD_SYSEX_SYSTEM && buf[6] == POD_MONITOR_LEVEL) { - short value = ((int)buf[7] << 12) | ((int)buf[8] << 8) | - ((int)buf[9] << 4) | (int)buf[10]; - pod->monitor_level = value; - } -} - -/* - Send system parameter (from integer). -*/ -static int pod_set_system_param_int(struct usb_line6_pod *pod, int value, - int code) -{ - char *sysex; - static const int size = 5; - - sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEM, size); - if (!sysex) - return -ENOMEM; - sysex[SYSEX_DATA_OFS] = code; - sysex[SYSEX_DATA_OFS + 1] = (value >> 12) & 0x0f; - sysex[SYSEX_DATA_OFS + 2] = (value >> 8) & 0x0f; - sysex[SYSEX_DATA_OFS + 3] = (value >> 4) & 0x0f; - sysex[SYSEX_DATA_OFS + 4] = (value) & 0x0f; - line6_send_sysex_message(&pod->line6, sysex, size); - kfree(sysex); - return 0; -} - -/* - "read" request on "serial_number" special file. -*/ -static ssize_t serial_number_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - - return sprintf(buf, "%d\n", pod->serial_number); -} - -/* - "read" request on "firmware_version" special file. -*/ -static ssize_t firmware_version_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - - return sprintf(buf, "%d.%02d\n", pod->firmware_version / 100, - pod->firmware_version % 100); -} - -/* - "read" request on "device_id" special file. -*/ -static ssize_t device_id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - - return sprintf(buf, "%d\n", pod->device_id); -} - -/* - POD startup procedure. - This is a sequence of functions with special requirements (e.g., must - not run immediately after initialization, must not run in interrupt - context). After the last one has finished, the device is ready to use. -*/ - -static void pod_startup1(struct usb_line6_pod *pod) -{ - CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_INIT); - - /* delay startup procedure: */ - line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2, - (unsigned long)pod); -} - -static void pod_startup2(unsigned long data) -{ - struct usb_line6_pod *pod = (struct usb_line6_pod *)data; - struct usb_line6 *line6 = &pod->line6; - - CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_VERSIONREQ); - - /* request firmware version: */ - line6_version_request_async(line6); -} - -static void pod_startup3(struct usb_line6_pod *pod) -{ - CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_WORKQUEUE); - - /* schedule work for global work queue: */ - schedule_work(&pod->startup_work); -} - -static void pod_startup4(struct work_struct *work) -{ - struct usb_line6_pod *pod = - container_of(work, struct usb_line6_pod, startup_work); - struct usb_line6 *line6 = &pod->line6; - - CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_SETUP); - - /* serial number: */ - line6_read_serial_number(&pod->line6, &pod->serial_number); - - /* ALSA audio interface: */ - line6_register_audio(line6); -} - -/* POD special files: */ -static DEVICE_ATTR_RO(device_id); -static DEVICE_ATTR_RO(firmware_version); -static DEVICE_ATTR_RO(serial_number); - -/* control info callback */ -static int snd_pod_control_monitor_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 65535; - return 0; -} - -/* control get callback */ -static int snd_pod_control_monitor_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; - - ucontrol->value.integer.value[0] = pod->monitor_level; - return 0; -} - -/* control put callback */ -static int snd_pod_control_monitor_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; - - if (ucontrol->value.integer.value[0] == pod->monitor_level) - return 0; - - pod->monitor_level = ucontrol->value.integer.value[0]; - pod_set_system_param_int(pod, ucontrol->value.integer.value[0], - POD_MONITOR_LEVEL); - return 1; -} - -/* control definition */ -static struct snd_kcontrol_new pod_control_monitor = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Monitor Playback Volume", - .index = 0, - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = snd_pod_control_monitor_info, - .get = snd_pod_control_monitor_get, - .put = snd_pod_control_monitor_put -}; - -/* - POD destructor. -*/ -static void pod_destruct(struct usb_interface *interface) -{ - struct usb_line6_pod *pod = usb_get_intfdata(interface); - - if (pod == NULL) - return; - line6_cleanup_audio(&pod->line6); - - del_timer(&pod->startup_timer); - cancel_work_sync(&pod->startup_work); -} - -/* - POD device disconnected. -*/ -static void line6_pod_disconnect(struct usb_interface *interface) -{ - struct usb_line6_pod *pod; - - if (interface == NULL) - return; - pod = usb_get_intfdata(interface); - - if (pod != NULL) { - struct snd_line6_pcm *line6pcm = pod->line6.line6pcm; - struct device *dev = &interface->dev; - - if (line6pcm != NULL) - line6_pcm_disconnect(line6pcm); - - if (dev != NULL) { - /* remove sysfs entries: */ - device_remove_file(dev, &dev_attr_device_id); - device_remove_file(dev, &dev_attr_firmware_version); - device_remove_file(dev, &dev_attr_serial_number); - } - } - - pod_destruct(interface); -} - -/* - Create sysfs entries. -*/ -static int pod_create_files2(struct device *dev) -{ - int err; - - CHECK_RETURN(device_create_file(dev, &dev_attr_device_id)); - CHECK_RETURN(device_create_file(dev, &dev_attr_firmware_version)); - CHECK_RETURN(device_create_file(dev, &dev_attr_serial_number)); - return 0; -} - -/* - Try to init POD device. -*/ -static int pod_try_init(struct usb_interface *interface, - struct usb_line6 *line6) -{ - int err; - struct usb_line6_pod *pod = (struct usb_line6_pod *) line6; - - line6->process_message = line6_pod_process_message; - line6->disconnect = line6_pod_disconnect; - - init_timer(&pod->startup_timer); - INIT_WORK(&pod->startup_work, pod_startup4); - - if ((interface == NULL) || (pod == NULL)) - return -ENODEV; - - /* create sysfs entries: */ - err = pod_create_files2(&interface->dev); - if (err < 0) - return err; - - /* initialize audio system: */ - err = line6_init_audio(line6); - if (err < 0) - return err; - - /* initialize MIDI subsystem: */ - err = line6_init_midi(line6); - if (err < 0) - return err; - - /* initialize PCM subsystem: */ - err = line6_init_pcm(line6, &pod_pcm_properties); - if (err < 0) - return err; - - /* register monitor control: */ - err = snd_ctl_add(line6->card, - snd_ctl_new1(&pod_control_monitor, line6->line6pcm)); - if (err < 0) - return err; - - /* - When the sound card is registered at this point, the PODxt Live - displays "Invalid Code Error 07", so we do it later in the event - handler. - */ - - if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) { - pod->monitor_level = POD_SYSTEM_INVALID; - - /* initiate startup procedure: */ - pod_startup1(pod); - } - - return 0; -} - -/* - Init POD device (and clean up in case of failure). -*/ -int line6_pod_init(struct usb_interface *interface, struct usb_line6 *line6) -{ - int err = pod_try_init(interface, line6); - - if (err < 0) - pod_destruct(interface); - - return err; -} diff --git a/drivers/staging/line6/pod.h b/drivers/staging/line6/pod.h deleted file mode 100644 index 87a8f0f..0000000 --- a/drivers/staging/line6/pod.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef POD_H -#define POD_H - -#include -#include -#include - -#include - -#include "driver.h" - -/* - Locate name in binary program dump -*/ -#define POD_NAME_OFFSET 0 -#define POD_NAME_LENGTH 16 - -/* - Other constants -*/ -#define POD_CONTROL_SIZE 0x80 -#define POD_BUFSIZE_DUMPREQ 7 -#define POD_STARTUP_DELAY 1000 - -/* - Stages of POD startup procedure -*/ -enum { - POD_STARTUP_INIT = 1, - POD_STARTUP_VERSIONREQ, - POD_STARTUP_WORKQUEUE, - POD_STARTUP_SETUP, - POD_STARTUP_LAST = POD_STARTUP_SETUP - 1 -}; - -struct usb_line6_pod { - /** - Generic Line6 USB data. - */ - struct usb_line6 line6; - - /** - Instrument monitor level. - */ - int monitor_level; - - /** - Timer for device initializaton. - */ - struct timer_list startup_timer; - - /** - Work handler for device initializaton. - */ - struct work_struct startup_work; - - /** - Current progress in startup procedure. - */ - int startup_progress; - - /** - Serial number of device. - */ - int serial_number; - - /** - Firmware version (x 100). - */ - int firmware_version; - - /** - Device ID. - */ - int device_id; -}; - -extern int line6_pod_init(struct usb_interface *interface, - struct usb_line6 *line6); - -#endif diff --git a/drivers/staging/line6/podhd.c b/drivers/staging/line6/podhd.c deleted file mode 100644 index 27c5402..0000000 --- a/drivers/staging/line6/podhd.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Line6 Pod HD - * - * Copyright (C) 2011 Stefan Hajnoczi - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#include -#include - -#include "audio.h" -#include "driver.h" -#include "pcm.h" -#include "podhd.h" - -#define PODHD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */ - -static struct snd_ratden podhd_ratden = { - .num_min = 48000, - .num_max = 48000, - .num_step = 1, - .den = 1, -}; - -static struct line6_pcm_properties podhd_pcm_properties = { - .snd_line6_playback_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | -#ifdef CONFIG_PM - SNDRV_PCM_INFO_RESUME | -#endif - SNDRV_PCM_INFO_SYNC_START), - .formats = SNDRV_PCM_FMTBIT_S24_3LE, - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 60000, - .period_bytes_min = 64, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 1024}, - .snd_line6_capture_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | -#ifdef CONFIG_PM - SNDRV_PCM_INFO_RESUME | -#endif - SNDRV_PCM_INFO_SYNC_START), - .formats = SNDRV_PCM_FMTBIT_S24_3LE, - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 60000, - .period_bytes_min = 64, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 1024}, - .snd_line6_rates = { - .nrats = 1, - .rats = &podhd_ratden}, - .bytes_per_frame = PODHD_BYTES_PER_FRAME -}; - -/* - POD HD destructor. -*/ -static void podhd_destruct(struct usb_interface *interface) -{ - struct usb_line6_podhd *podhd = usb_get_intfdata(interface); - - if (podhd == NULL) - return; - line6_cleanup_audio(&podhd->line6); -} - -/* - POD HD device disconnected. -*/ -static void line6_podhd_disconnect(struct usb_interface *interface) -{ - struct usb_line6_podhd *podhd; - - if (interface == NULL) - return; - podhd = usb_get_intfdata(interface); - - if (podhd != NULL) { - struct snd_line6_pcm *line6pcm = podhd->line6.line6pcm; - - if (line6pcm != NULL) - line6_pcm_disconnect(line6pcm); - } - - podhd_destruct(interface); -} - -/* - Try to init POD HD device. -*/ -static int podhd_try_init(struct usb_interface *interface, - struct usb_line6_podhd *podhd) -{ - int err; - struct usb_line6 *line6 = &podhd->line6; - - if ((interface == NULL) || (podhd == NULL)) - return -ENODEV; - - line6->disconnect = line6_podhd_disconnect; - - /* initialize audio system: */ - err = line6_init_audio(line6); - if (err < 0) - return err; - - /* initialize MIDI subsystem: */ - err = line6_init_midi(line6); - if (err < 0) - return err; - - /* initialize PCM subsystem: */ - err = line6_init_pcm(line6, &podhd_pcm_properties); - if (err < 0) - return err; - - /* register USB audio system: */ - err = line6_register_audio(line6); - return err; -} - -/* - Init POD HD device (and clean up in case of failure). -*/ -int line6_podhd_init(struct usb_interface *interface, struct usb_line6 *line6) -{ - struct usb_line6_podhd *podhd = (struct usb_line6_podhd *) line6; - int err = podhd_try_init(interface, podhd); - - if (err < 0) - podhd_destruct(interface); - - return err; -} diff --git a/drivers/staging/line6/podhd.h b/drivers/staging/line6/podhd.h deleted file mode 100644 index a14f711..0000000 --- a/drivers/staging/line6/podhd.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Line6 Pod HD - * - * Copyright (C) 2011 Stefan Hajnoczi - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef PODHD_H -#define PODHD_H - -#include - -#include "driver.h" - -struct usb_line6_podhd { - /** - Generic Line6 USB data. - */ - struct usb_line6 line6; -}; - -extern int line6_podhd_init(struct usb_interface *interface, - struct usb_line6 *line6); - -#endif /* PODHD_H */ diff --git a/drivers/staging/line6/revision.h b/drivers/staging/line6/revision.h deleted file mode 100644 index b4eee2b..0000000 --- a/drivers/staging/line6/revision.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef DRIVER_REVISION -/* current subversion revision */ -#define DRIVER_REVISION " (904)" -#endif diff --git a/drivers/staging/line6/toneport.c b/drivers/staging/line6/toneport.c deleted file mode 100644 index aae78d8..0000000 --- a/drivers/staging/line6/toneport.c +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * Emil Myhrman (emil.myhrman@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, version 2. - * - */ - -#include -#include - -#include "audio.h" -#include "capture.h" -#include "driver.h" -#include "playback.h" -#include "toneport.h" - -static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2); - -#define TONEPORT_PCM_DELAY 1 - -static struct snd_ratden toneport_ratden = { - .num_min = 44100, - .num_max = 44100, - .num_step = 1, - .den = 1 -}; - -static struct line6_pcm_properties toneport_pcm_properties = { - .snd_line6_playback_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | -#ifdef CONFIG_PM - SNDRV_PCM_INFO_RESUME | -#endif - SNDRV_PCM_INFO_SYNC_START), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_KNOT, - .rate_min = 44100, - .rate_max = 44100, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 60000, - .period_bytes_min = 64, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 1024}, - .snd_line6_capture_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | -#ifdef CONFIG_PM - SNDRV_PCM_INFO_RESUME | -#endif - SNDRV_PCM_INFO_SYNC_START), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_KNOT, - .rate_min = 44100, - .rate_max = 44100, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 60000, - .period_bytes_min = 64, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 1024}, - .snd_line6_rates = { - .nrats = 1, - .rats = &toneport_ratden}, - .bytes_per_frame = 4 -}; - -/* - For the led on Guitarport. - Brightness goes from 0x00 to 0x26. Set a value above this to have led - blink. - (void cmd_0x02(byte red, byte green) -*/ -static int led_red = 0x00; -static int led_green = 0x26; - -static const struct { - const char *name; - int code; -} toneport_source_info[] = { - {"Microphone", 0x0a01}, - {"Line", 0x0801}, - {"Instrument", 0x0b01}, - {"Inst & Mic", 0x0901} -}; - -static bool toneport_has_led(enum line6_device_type type) -{ - return - (type == LINE6_GUITARPORT) || - (type == LINE6_TONEPORT_GX); - /* add your device here if you are missing support for the LEDs */ -} - -static void toneport_update_led(struct device *dev) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_toneport *tp = usb_get_intfdata(interface); - struct usb_line6 *line6; - - if (!tp) - return; - - line6 = &tp->line6; - if (line6) - toneport_send_cmd(line6->usbdev, (led_red << 8) | 0x0002, - led_green); -} - -static ssize_t toneport_set_led_red(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int retval; - - retval = kstrtoint(buf, 10, &led_red); - if (retval) - return retval; - - toneport_update_led(dev); - return count; -} - -static ssize_t toneport_set_led_green(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int retval; - - retval = kstrtoint(buf, 10, &led_green); - if (retval) - return retval; - - toneport_update_led(dev); - return count; -} - -static DEVICE_ATTR(led_red, S_IWUSR | S_IRUGO, line6_nop_read, - toneport_set_led_red); -static DEVICE_ATTR(led_green, S_IWUSR | S_IRUGO, line6_nop_read, - toneport_set_led_green); - -static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2) -{ - int ret; - - ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - cmd1, cmd2, NULL, 0, LINE6_TIMEOUT * HZ); - - if (ret < 0) { - dev_err(&usbdev->dev, "send failed (error %d)\n", ret); - return ret; - } - - return 0; -} - -/* monitor info callback */ -static int snd_toneport_monitor_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 256; - return 0; -} - -/* monitor get callback */ -static int snd_toneport_monitor_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = line6pcm->volume_monitor; - return 0; -} - -/* monitor put callback */ -static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - - if (ucontrol->value.integer.value[0] == line6pcm->volume_monitor) - return 0; - - line6pcm->volume_monitor = ucontrol->value.integer.value[0]; - - if (line6pcm->volume_monitor > 0) - line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_MONITOR); - else - line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); - - return 1; -} - -/* source info callback */ -static int snd_toneport_source_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - const int size = ARRAY_SIZE(toneport_source_info); - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = size; - - if (uinfo->value.enumerated.item >= size) - uinfo->value.enumerated.item = size - 1; - - strcpy(uinfo->value.enumerated.name, - toneport_source_info[uinfo->value.enumerated.item].name); - - return 0; -} - -/* source get callback */ -static int snd_toneport_source_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - struct usb_line6_toneport *toneport = - (struct usb_line6_toneport *)line6pcm->line6; - ucontrol->value.enumerated.item[0] = toneport->source; - return 0; -} - -/* source put callback */ -static int snd_toneport_source_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - struct usb_line6_toneport *toneport = - (struct usb_line6_toneport *)line6pcm->line6; - unsigned int source; - - source = ucontrol->value.enumerated.item[0]; - if (source >= ARRAY_SIZE(toneport_source_info)) - return -EINVAL; - if (source == toneport->source) - return 0; - - toneport->source = source; - toneport_send_cmd(toneport->line6.usbdev, - toneport_source_info[source].code, 0x0000); - return 1; -} - -static void toneport_start_pcm(unsigned long arg) -{ - struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg; - struct usb_line6 *line6 = &toneport->line6; - - line6_pcm_acquire(line6->line6pcm, LINE6_BITS_PCM_MONITOR); -} - -/* control definition */ -static struct snd_kcontrol_new toneport_control_monitor = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Monitor Playback Volume", - .index = 0, - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = snd_toneport_monitor_info, - .get = snd_toneport_monitor_get, - .put = snd_toneport_monitor_put -}; - -/* source selector definition */ -static struct snd_kcontrol_new toneport_control_source = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Capture Source", - .index = 0, - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = snd_toneport_source_info, - .get = snd_toneport_source_get, - .put = snd_toneport_source_put -}; - -/* - Toneport destructor. -*/ -static void toneport_destruct(struct usb_interface *interface) -{ - struct usb_line6_toneport *toneport = usb_get_intfdata(interface); - - if (toneport == NULL) - return; - line6_cleanup_audio(&toneport->line6); -} - -/* - Setup Toneport device. -*/ -static void toneport_setup(struct usb_line6_toneport *toneport) -{ - int ticks; - struct usb_line6 *line6 = &toneport->line6; - struct usb_device *usbdev = line6->usbdev; - - /* sync time on device with host: */ - ticks = (int)get_seconds(); - line6_write_data(line6, 0x80c6, &ticks, 4); - - /* enable device: */ - toneport_send_cmd(usbdev, 0x0301, 0x0000); - - /* initialize source select: */ - switch (line6->type) { - case LINE6_TONEPORT_UX1: - case LINE6_TONEPORT_UX2: - case LINE6_PODSTUDIO_UX1: - case LINE6_PODSTUDIO_UX2: - toneport_send_cmd(usbdev, - toneport_source_info[toneport->source].code, - 0x0000); - default: - break; - } - - if (toneport_has_led(line6->type)) - toneport_update_led(&usbdev->dev); -} - -/* - Toneport device disconnected. -*/ -static void line6_toneport_disconnect(struct usb_interface *interface) -{ - struct usb_line6_toneport *toneport; - u16 idProduct; - - if (interface == NULL) - return; - - toneport = usb_get_intfdata(interface); - del_timer_sync(&toneport->timer); - idProduct = le16_to_cpu(toneport->line6.usbdev->descriptor.idProduct); - - if (toneport_has_led(idProduct)) { - device_remove_file(&interface->dev, &dev_attr_led_red); - device_remove_file(&interface->dev, &dev_attr_led_green); - } - - if (toneport != NULL) { - struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm; - - if (line6pcm != NULL) { - line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); - line6_pcm_disconnect(line6pcm); - } - } - - toneport_destruct(interface); -} - - -/* - Try to init Toneport device. -*/ -static int toneport_try_init(struct usb_interface *interface, - struct usb_line6 *line6) -{ - int err; - struct usb_line6_toneport *toneport = (struct usb_line6_toneport *) line6; - - if ((interface == NULL) || (toneport == NULL)) - return -ENODEV; - - line6->disconnect = line6_toneport_disconnect; - - /* initialize audio system: */ - err = line6_init_audio(line6); - if (err < 0) - return err; - - /* initialize PCM subsystem: */ - err = line6_init_pcm(line6, &toneport_pcm_properties); - if (err < 0) - return err; - - /* register monitor control: */ - err = snd_ctl_add(line6->card, - snd_ctl_new1(&toneport_control_monitor, - line6->line6pcm)); - if (err < 0) - return err; - - /* register source select control: */ - switch (line6->type) { - case LINE6_TONEPORT_UX1: - case LINE6_TONEPORT_UX2: - case LINE6_PODSTUDIO_UX1: - case LINE6_PODSTUDIO_UX2: - err = - snd_ctl_add(line6->card, - snd_ctl_new1(&toneport_control_source, - line6->line6pcm)); - if (err < 0) - return err; - - default: - break; - } - - /* register audio system: */ - err = line6_register_audio(line6); - if (err < 0) - return err; - - line6_read_serial_number(line6, &toneport->serial_number); - line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1); - - if (toneport_has_led(line6->type)) { - CHECK_RETURN(device_create_file - (&interface->dev, &dev_attr_led_red)); - CHECK_RETURN(device_create_file - (&interface->dev, &dev_attr_led_green)); - } - - toneport_setup(toneport); - - init_timer(&toneport->timer); - toneport->timer.expires = jiffies + TONEPORT_PCM_DELAY * HZ; - toneport->timer.function = toneport_start_pcm; - toneport->timer.data = (unsigned long)toneport; - add_timer(&toneport->timer); - - return 0; -} - -/* - Init Toneport device (and clean up in case of failure). -*/ -int line6_toneport_init(struct usb_interface *interface, - struct usb_line6 *line6) -{ - int err = toneport_try_init(interface, line6); - - if (err < 0) - toneport_destruct(interface); - - return err; -} - -/* - Resume Toneport device after reset. -*/ -void line6_toneport_reset_resume(struct usb_line6_toneport *toneport) -{ - toneport_setup(toneport); -} diff --git a/drivers/staging/line6/toneport.h b/drivers/staging/line6/toneport.h deleted file mode 100644 index 8cb1442..0000000 --- a/drivers/staging/line6/toneport.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef TONEPORT_H -#define TONEPORT_H - -#include -#include - -#include "driver.h" - -struct usb_line6_toneport { - /** - Generic Line6 USB data. - */ - struct usb_line6 line6; - - /** - Source selector. - */ - int source; - - /** - Serial number of device. - */ - int serial_number; - - /** - Firmware version (x 100). - */ - int firmware_version; - - /** - Timer for delayed PCM startup. - */ - struct timer_list timer; -}; - -extern int line6_toneport_init(struct usb_interface *interface, - struct usb_line6 *line6); -extern void line6_toneport_reset_resume(struct usb_line6_toneport *toneport); - -#endif diff --git a/drivers/staging/line6/usbdefs.h b/drivers/staging/line6/usbdefs.h deleted file mode 100644 index f4d080e..0000000 --- a/drivers/staging/line6/usbdefs.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2005-2008 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef USBDEFS_H -#define USBDEFS_H - -#define USB_INTERVALS_PER_SECOND 1000 - -/* device supports settings parameter via USB */ -#define LINE6_CAP_CONTROL (1 << 0) -/* device supports PCM input/output via USB */ -#define LINE6_CAP_PCM (1 << 1) -/* device support hardware monitoring */ -#define LINE6_CAP_HWMON (1 << 2) - -#define LINE6_FALLBACK_INTERVAL 10 -#define LINE6_FALLBACK_MAXPACKETSIZE 16 - -#endif diff --git a/drivers/staging/line6/variax.c b/drivers/staging/line6/variax.c deleted file mode 100644 index b4a41b0..0000000 --- a/drivers/staging/line6/variax.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#include - -#include "audio.h" -#include "driver.h" -#include "variax.h" - -#define VARIAX_OFFSET_ACTIVATE 7 - -/* - This message is sent by the device during initialization and identifies - the connected guitar version. -*/ -static const char variax_init_version[] = { - 0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c, - 0x07, 0x00, 0x00, 0x00 -}; - -/* - This message is the last one sent by the device during initialization. -*/ -static const char variax_init_done[] = { - 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b -}; - -static const char variax_activate[] = { - 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01, - 0xf7 -}; - -/* forward declarations: */ -static void variax_startup2(unsigned long data); -static void variax_startup4(unsigned long data); -static void variax_startup5(unsigned long data); - -static void variax_activate_async(struct usb_line6_variax *variax, int a) -{ - variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a; - line6_send_raw_message_async(&variax->line6, variax->buffer_activate, - sizeof(variax_activate)); -} - -/* - Variax startup procedure. - This is a sequence of functions with special requirements (e.g., must - not run immediately after initialization, must not run in interrupt - context). After the last one has finished, the device is ready to use. -*/ - -static void variax_startup1(struct usb_line6_variax *variax) -{ - CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT); - - /* delay startup procedure: */ - line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, - variax_startup2, (unsigned long)variax); -} - -static void variax_startup2(unsigned long data) -{ - struct usb_line6_variax *variax = (struct usb_line6_variax *)data; - struct usb_line6 *line6 = &variax->line6; - - /* schedule another startup procedure until startup is complete: */ - if (variax->startup_progress >= VARIAX_STARTUP_LAST) - return; - - variax->startup_progress = VARIAX_STARTUP_VERSIONREQ; - line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, - variax_startup2, (unsigned long)variax); - - /* request firmware version: */ - line6_version_request_async(line6); -} - -static void variax_startup3(struct usb_line6_variax *variax) -{ - CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT); - - /* delay startup procedure: */ - line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3, - variax_startup4, (unsigned long)variax); -} - -static void variax_startup4(unsigned long data) -{ - struct usb_line6_variax *variax = (struct usb_line6_variax *)data; - - CHECK_STARTUP_PROGRESS(variax->startup_progress, - VARIAX_STARTUP_ACTIVATE); - - /* activate device: */ - variax_activate_async(variax, 1); - line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4, - variax_startup5, (unsigned long)variax); -} - -static void variax_startup5(unsigned long data) -{ - struct usb_line6_variax *variax = (struct usb_line6_variax *)data; - - CHECK_STARTUP_PROGRESS(variax->startup_progress, - VARIAX_STARTUP_WORKQUEUE); - - /* schedule work for global work queue: */ - schedule_work(&variax->startup_work); -} - -static void variax_startup6(struct work_struct *work) -{ - struct usb_line6_variax *variax = - container_of(work, struct usb_line6_variax, startup_work); - - CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP); - - /* ALSA audio interface: */ - line6_register_audio(&variax->line6); -} - -/* - Process a completely received message. -*/ -static void line6_variax_process_message(struct usb_line6 *line6) -{ - struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; - const unsigned char *buf = variax->line6.buffer_message; - - switch (buf[0]) { - case LINE6_RESET: - dev_info(variax->line6.ifcdev, "VARIAX reset\n"); - break; - - case LINE6_SYSEX_BEGIN: - if (memcmp(buf + 1, variax_init_version + 1, - sizeof(variax_init_version) - 1) == 0) { - variax_startup3(variax); - } else if (memcmp(buf + 1, variax_init_done + 1, - sizeof(variax_init_done) - 1) == 0) { - /* notify of complete initialization: */ - variax_startup4((unsigned long)variax); - } - break; - } -} - -/* - Variax destructor. -*/ -static void variax_destruct(struct usb_interface *interface) -{ - struct usb_line6_variax *variax = usb_get_intfdata(interface); - - if (variax == NULL) - return; - line6_cleanup_audio(&variax->line6); - - del_timer(&variax->startup_timer1); - del_timer(&variax->startup_timer2); - cancel_work_sync(&variax->startup_work); - - kfree(variax->buffer_activate); -} - -/* - Workbench device disconnected. -*/ -static void line6_variax_disconnect(struct usb_interface *interface) -{ - if (interface == NULL) - return; - - variax_destruct(interface); -} - -/* - Try to init workbench device. -*/ -static int variax_try_init(struct usb_interface *interface, - struct usb_line6 *line6) -{ - struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; - int err; - - line6->process_message = line6_variax_process_message; - line6->disconnect = line6_variax_disconnect; - - init_timer(&variax->startup_timer1); - init_timer(&variax->startup_timer2); - INIT_WORK(&variax->startup_work, variax_startup6); - - if ((interface == NULL) || (variax == NULL)) - return -ENODEV; - - /* initialize USB buffers: */ - variax->buffer_activate = kmemdup(variax_activate, - sizeof(variax_activate), GFP_KERNEL); - - if (variax->buffer_activate == NULL) { - dev_err(&interface->dev, "Out of memory\n"); - return -ENOMEM; - } - - /* initialize audio system: */ - err = line6_init_audio(&variax->line6); - if (err < 0) - return err; - - /* initialize MIDI subsystem: */ - err = line6_init_midi(&variax->line6); - if (err < 0) - return err; - - /* initiate startup procedure: */ - variax_startup1(variax); - return 0; -} - -/* - Init workbench device (and clean up in case of failure). -*/ -int line6_variax_init(struct usb_interface *interface, struct usb_line6 *line6) -{ - int err = variax_try_init(interface, line6); - - if (err < 0) - variax_destruct(interface); - - return err; -} diff --git a/drivers/staging/line6/variax.h b/drivers/staging/line6/variax.h deleted file mode 100644 index dfb94e5..0000000 --- a/drivers/staging/line6/variax.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef VARIAX_H -#define VARIAX_H - -#include -#include -#include -#include - -#include "driver.h" - -#define VARIAX_STARTUP_DELAY1 1000 -#define VARIAX_STARTUP_DELAY3 100 -#define VARIAX_STARTUP_DELAY4 100 - -/* - Stages of Variax startup procedure -*/ -enum { - VARIAX_STARTUP_INIT = 1, - VARIAX_STARTUP_VERSIONREQ, - VARIAX_STARTUP_WAIT, - VARIAX_STARTUP_ACTIVATE, - VARIAX_STARTUP_WORKQUEUE, - VARIAX_STARTUP_SETUP, - VARIAX_STARTUP_LAST = VARIAX_STARTUP_SETUP - 1 -}; - -struct usb_line6_variax { - /** - Generic Line6 USB data. - */ - struct usb_line6 line6; - - /** - Buffer for activation code. - */ - unsigned char *buffer_activate; - - /** - Handler for device initializaton. - */ - struct work_struct startup_work; - - /** - Timers for device initializaton. - */ - struct timer_list startup_timer1; - struct timer_list startup_timer2; - - /** - Current progress in startup procedure. - */ - int startup_progress; -}; - -extern int line6_variax_init(struct usb_interface *interface, - struct usb_line6 *line6); - -#endif diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index d393153..a452ad7 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -160,5 +160,7 @@ config SND_BCD2000 To compile this driver as a module, choose M here: the module will be called snd-bcd2000. +source "sound/usb/line6/Kconfig" + endif # SND_USB diff --git a/sound/usb/Makefile b/sound/usb/Makefile index bcee406..54045b7 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -25,3 +25,4 @@ obj-$(CONFIG_SND_USB_USX2Y) += snd-usbmidi-lib.o obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ bcd2000/ +obj-$(CONFIG_LINE6_USB) += line6/ diff --git a/sound/usb/line6/Kconfig b/sound/usb/line6/Kconfig new file mode 100644 index 0000000..4f1219b --- /dev/null +++ b/sound/usb/line6/Kconfig @@ -0,0 +1,38 @@ +menuconfig LINE6_USB + tristate "Line6 USB support" + depends on USB && SND + select SND_RAWMIDI + select SND_PCM + help + This is a driver for the guitar amp, cab, and effects modeller + PODxt Pro by Line6 (and similar devices), supporting the + following features: + * Reading/writing individual parameters + * Reading/writing complete channel, effects setup, and amp + setup data + * Channel switching + * Virtual MIDI interface + * Tuner access + * Playback/capture/mixer device for any ALSA-compatible PCM + audio application + * Signal routing (record clean/processed guitar signal, + re-amping) + + Preliminary support for the Variax Workbench and TonePort + devices is included. + +if LINE6_USB + +config LINE6_USB_IMPULSE_RESPONSE + bool "measure impulse response" + default n + help + Say Y here to add code to measure the impulse response of a Line6 + device. This is more accurate than user-space methods since it + bypasses any PCM data buffering (e.g., by ALSA or jack). This is + useful for assessing the performance of new devices, but is not + required for normal operation. + + If unsure, say N. + +endif # LINE6_USB diff --git a/sound/usb/line6/Makefile b/sound/usb/line6/Makefile new file mode 100644 index 0000000..ae5c374 --- /dev/null +++ b/sound/usb/line6/Makefile @@ -0,0 +1,14 @@ +obj-$(CONFIG_LINE6_USB) += line6usb.o + +line6usb-y := \ + audio.o \ + capture.o \ + driver.o \ + midi.o \ + midibuf.o \ + pcm.o \ + playback.o \ + pod.o \ + toneport.o \ + variax.o \ + podhd.o diff --git a/sound/usb/line6/audio.c b/sound/usb/line6/audio.c new file mode 100644 index 0000000..171d80c --- /dev/null +++ b/sound/usb/line6/audio.c @@ -0,0 +1,71 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include +#include +#include + +#include "driver.h" +#include "audio.h" + +/* + Initialize the Line6 USB audio system. +*/ +int line6_init_audio(struct usb_line6 *line6) +{ + struct snd_card *card; + int err; + + err = snd_card_new(line6->ifcdev, + SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); + if (err < 0) + return err; + + line6->card = card; + + strcpy(card->id, line6->properties->id); + strcpy(card->driver, DRIVER_NAME); + strcpy(card->shortname, line6->properties->name); + /* longname is 80 chars - see asound.h */ + sprintf(card->longname, "Line6 %s at USB %s", line6->properties->name, + dev_name(line6->ifcdev)); + return 0; +} + +/* + Register the Line6 USB audio system. +*/ +int line6_register_audio(struct usb_line6 *line6) +{ + int err; + + err = snd_card_register(line6->card); + if (err < 0) + return err; + + return 0; +} + +/* + Cleanup the Line6 USB audio system. +*/ +void line6_cleanup_audio(struct usb_line6 *line6) +{ + struct snd_card *card = line6->card; + + if (card == NULL) + return; + + snd_card_disconnect(card); + snd_card_free(card); + line6->card = NULL; +} diff --git a/sound/usb/line6/audio.h b/sound/usb/line6/audio.h new file mode 100644 index 0000000..5f8a09a --- /dev/null +++ b/sound/usb/line6/audio.h @@ -0,0 +1,21 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef AUDIO_H +#define AUDIO_H + +#include "driver.h" + +extern void line6_cleanup_audio(struct usb_line6 *); +extern int line6_init_audio(struct usb_line6 *); +extern int line6_register_audio(struct usb_line6 *); + +#endif diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c new file mode 100644 index 0000000..f24c7c5 --- /dev/null +++ b/sound/usb/line6/capture.c @@ -0,0 +1,433 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include +#include +#include +#include + +#include "audio.h" +#include "capture.h" +#include "driver.h" +#include "pcm.h" +#include "pod.h" + +/* + Find a free URB and submit it. +*/ +static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm) +{ + int index; + unsigned long flags; + int i, urb_size; + int ret; + struct urb *urb_in; + + spin_lock_irqsave(&line6pcm->lock_audio_in, flags); + index = + find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS); + + if (index < 0 || index >= LINE6_ISO_BUFFERS) { + spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); + dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); + return -EINVAL; + } + + urb_in = line6pcm->urb_audio_in[index]; + urb_size = 0; + + for (i = 0; i < LINE6_ISO_PACKETS; ++i) { + struct usb_iso_packet_descriptor *fin = + &urb_in->iso_frame_desc[i]; + fin->offset = urb_size; + fin->length = line6pcm->max_packet_size; + urb_size += line6pcm->max_packet_size; + } + + urb_in->transfer_buffer = + line6pcm->buffer_in + + index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; + urb_in->transfer_buffer_length = urb_size; + urb_in->context = line6pcm; + + ret = usb_submit_urb(urb_in, GFP_ATOMIC); + + if (ret == 0) + set_bit(index, &line6pcm->active_urb_in); + else + dev_err(line6pcm->line6->ifcdev, + "URB in #%d submission failed (%d)\n", index, ret); + + spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); + return 0; +} + +/* + Submit all currently available capture URBs. +*/ +int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm) +{ + int ret, i; + + for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { + ret = submit_audio_in_urb(line6pcm); + if (ret < 0) + return ret; + } + + return 0; +} + +/* + Unlink all currently active capture URBs. +*/ +void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm) +{ + unsigned int i; + + for (i = LINE6_ISO_BUFFERS; i--;) { + if (test_bit(i, &line6pcm->active_urb_in)) { + if (!test_and_set_bit(i, &line6pcm->unlink_urb_in)) { + struct urb *u = line6pcm->urb_audio_in[i]; + + usb_unlink_urb(u); + } + } + } +} + +/* + Wait until unlinking of all currently active capture URBs has been + finished. +*/ +void line6_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) +{ + int timeout = HZ; + unsigned int i; + int alive; + + do { + alive = 0; + for (i = LINE6_ISO_BUFFERS; i--;) { + if (test_bit(i, &line6pcm->active_urb_in)) + alive++; + } + if (!alive) + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (--timeout > 0); + if (alive) + snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); +} + +/* + Unlink all currently active capture URBs, and wait for finishing. +*/ +void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) +{ + line6_unlink_audio_in_urbs(line6pcm); + line6_wait_clear_audio_in_urbs(line6pcm); +} + +/* + Copy data into ALSA capture buffer. +*/ +void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize) +{ + struct snd_pcm_substream *substream = + get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE); + struct snd_pcm_runtime *runtime = substream->runtime; + const int bytes_per_frame = line6pcm->properties->bytes_per_frame; + int frames = fsize / bytes_per_frame; + + if (runtime == NULL) + return; + + if (line6pcm->pos_in_done + frames > runtime->buffer_size) { + /* + The transferred area goes over buffer boundary, + copy two separate chunks. + */ + int len; + + len = runtime->buffer_size - line6pcm->pos_in_done; + + if (len > 0) { + memcpy(runtime->dma_area + + line6pcm->pos_in_done * bytes_per_frame, fbuf, + len * bytes_per_frame); + memcpy(runtime->dma_area, fbuf + len * bytes_per_frame, + (frames - len) * bytes_per_frame); + } else { + /* this is somewhat paranoid */ + dev_err(line6pcm->line6->ifcdev, + "driver bug: len = %d\n", len); + } + } else { + /* copy single chunk */ + memcpy(runtime->dma_area + + line6pcm->pos_in_done * bytes_per_frame, fbuf, fsize); + } + + line6pcm->pos_in_done += frames; + if (line6pcm->pos_in_done >= runtime->buffer_size) + line6pcm->pos_in_done -= runtime->buffer_size; +} + +void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length) +{ + struct snd_pcm_substream *substream = + get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE); + + line6pcm->bytes_in += length; + if (line6pcm->bytes_in >= line6pcm->period_in) { + line6pcm->bytes_in %= line6pcm->period_in; + snd_pcm_period_elapsed(substream); + } +} + +void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm) +{ + kfree(line6pcm->buffer_in); + line6pcm->buffer_in = NULL; +} + +/* + * Callback for completed capture URB. + */ +static void audio_in_callback(struct urb *urb) +{ + int i, index, length = 0, shutdown = 0; + unsigned long flags; + + struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context; + + line6pcm->last_frame_in = urb->start_frame; + + /* find index of URB */ + for (index = 0; index < LINE6_ISO_BUFFERS; ++index) + if (urb == line6pcm->urb_audio_in[index]) + break; + + spin_lock_irqsave(&line6pcm->lock_audio_in, flags); + + for (i = 0; i < LINE6_ISO_PACKETS; ++i) { + char *fbuf; + int fsize; + struct usb_iso_packet_descriptor *fin = &urb->iso_frame_desc[i]; + + if (fin->status == -EXDEV) { + shutdown = 1; + break; + } + + fbuf = urb->transfer_buffer + fin->offset; + fsize = fin->actual_length; + + if (fsize > line6pcm->max_packet_size) { + dev_err(line6pcm->line6->ifcdev, + "driver and/or device bug: packet too large (%d > %d)\n", + fsize, line6pcm->max_packet_size); + } + + length += fsize; + + /* the following assumes LINE6_ISO_PACKETS == 1: */ + line6pcm->prev_fbuf = fbuf; + line6pcm->prev_fsize = fsize; + +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE)) +#endif + if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, + &line6pcm->flags) && (fsize > 0)) + line6_capture_copy(line6pcm, fbuf, fsize); + } + + clear_bit(index, &line6pcm->active_urb_in); + + if (test_and_clear_bit(index, &line6pcm->unlink_urb_in)) + shutdown = 1; + + spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); + + if (!shutdown) { + submit_audio_in_urb(line6pcm); + +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE)) +#endif + if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, + &line6pcm->flags)) + line6_capture_check_period(line6pcm, length); + } +} + +/* open capture callback */ +static int snd_line6_capture_open(struct snd_pcm_substream *substream) +{ + int err; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); + + err = snd_pcm_hw_constraint_ratdens(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + (&line6pcm-> + properties->snd_line6_rates)); + if (err < 0) + return err; + + runtime->hw = line6pcm->properties->snd_line6_capture_hw; + return 0; +} + +/* close capture callback */ +static int snd_line6_capture_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +/* hw_params capture callback */ +static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + int ret; + struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); + + /* -- Florian Demski [FD] */ + /* don't ask me why, but this fixes the bug on my machine */ + if (line6pcm == NULL) { + if (substream->pcm == NULL) + return -ENOMEM; + if (substream->pcm->private_data == NULL) + return -ENOMEM; + substream->private_data = substream->pcm->private_data; + line6pcm = snd_pcm_substream_chip(substream); + } + /* -- [FD] end */ + + ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); + + if (ret < 0) + return ret; + + ret = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + if (ret < 0) { + line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); + return ret; + } + + line6pcm->period_in = params_period_bytes(hw_params); + return 0; +} + +/* hw_free capture callback */ +static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); + + line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); + return snd_pcm_lib_free_pages(substream); +} + +/* trigger callback */ +int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd) +{ + int err; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: +#ifdef CONFIG_PM + case SNDRV_PCM_TRIGGER_RESUME: +#endif + err = line6_pcm_acquire(line6pcm, + LINE6_BIT_PCM_ALSA_CAPTURE_STREAM); + + if (err < 0) + return err; + + break; + + case SNDRV_PCM_TRIGGER_STOP: +#ifdef CONFIG_PM + case SNDRV_PCM_TRIGGER_SUSPEND: +#endif + err = line6_pcm_release(line6pcm, + LINE6_BIT_PCM_ALSA_CAPTURE_STREAM); + + if (err < 0) + return err; + + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* capture pointer callback */ +static snd_pcm_uframes_t +snd_line6_capture_pointer(struct snd_pcm_substream *substream) +{ + struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); + + return line6pcm->pos_in_done; +} + +/* capture operators */ +struct snd_pcm_ops snd_line6_capture_ops = { + .open = snd_line6_capture_open, + .close = snd_line6_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_line6_capture_hw_params, + .hw_free = snd_line6_capture_hw_free, + .prepare = snd_line6_prepare, + .trigger = snd_line6_trigger, + .pointer = snd_line6_capture_pointer, +}; + +int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm) +{ + struct usb_line6 *line6 = line6pcm->line6; + int i; + + /* create audio URBs and fill in constant values: */ + for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { + struct urb *urb; + + /* URB for audio in: */ + urb = line6pcm->urb_audio_in[i] = + usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); + + if (urb == NULL) { + dev_err(line6->ifcdev, "Out of memory\n"); + return -ENOMEM; + } + + urb->dev = line6->usbdev; + urb->pipe = + usb_rcvisocpipe(line6->usbdev, + line6->properties->ep_audio_r & + USB_ENDPOINT_NUMBER_MASK); + urb->transfer_flags = URB_ISO_ASAP; + urb->start_frame = -1; + urb->number_of_packets = LINE6_ISO_PACKETS; + urb->interval = LINE6_ISO_INTERVAL; + urb->error_count = 0; + urb->complete = audio_in_callback; + } + + return 0; +} diff --git a/sound/usb/line6/capture.h b/sound/usb/line6/capture.h new file mode 100644 index 0000000..4157bcb --- /dev/null +++ b/sound/usb/line6/capture.h @@ -0,0 +1,35 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef CAPTURE_H +#define CAPTURE_H + +#include + +#include "driver.h" +#include "pcm.h" + +extern struct snd_pcm_ops snd_line6_capture_ops; + +extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, + int fsize); +extern void line6_capture_check_period(struct snd_line6_pcm *line6pcm, + int length); +extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm); +extern void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm); +extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm); +extern void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm); +extern void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm + *line6pcm); +extern void line6_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm); +extern int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd); + +#endif diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c new file mode 100644 index 0000000..fc852f6 --- /dev/null +++ b/sound/usb/line6/driver.c @@ -0,0 +1,1114 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include +#include +#include +#include + +#include "audio.h" +#include "capture.h" +#include "driver.h" +#include "midi.h" +#include "playback.h" +#include "pod.h" +#include "podhd.h" +#include "revision.h" +#include "toneport.h" +#include "usbdefs.h" +#include "variax.h" + +#define DRIVER_AUTHOR "Markus Grabner " +#define DRIVER_DESC "Line6 USB Driver" +#define DRIVER_VERSION "0.9.1beta" DRIVER_REVISION + +#define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod) +#define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n) + +/* table of devices that work with this driver */ +static const struct usb_device_id line6_id_table[] = { + { LINE6_DEVICE(0x4250), .driver_info = LINE6_BASSPODXT }, + { LINE6_DEVICE(0x4642), .driver_info = LINE6_BASSPODXTLIVE }, + { LINE6_DEVICE(0x4252), .driver_info = LINE6_BASSPODXTPRO }, + { LINE6_DEVICE(0x4750), .driver_info = LINE6_GUITARPORT }, + { LINE6_IF_NUM(0x5051, 1), .driver_info = LINE6_POCKETPOD }, + { LINE6_DEVICE(0x5057), .driver_info = LINE6_PODHD300 }, + { LINE6_DEVICE(0x5058), .driver_info = LINE6_PODHD400 }, + { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 }, + { LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 }, + { LINE6_DEVICE(0x4153), .driver_info = LINE6_PODSTUDIO_GX }, + { LINE6_DEVICE(0x4150), .driver_info = LINE6_PODSTUDIO_UX1 }, + { LINE6_IF_NUM(0x4151, 0), .driver_info = LINE6_PODSTUDIO_UX2 }, + { LINE6_DEVICE(0x5044), .driver_info = LINE6_PODXT }, + { LINE6_IF_NUM(0x4650, 0), .driver_info = LINE6_PODXTLIVE_POD }, + { LINE6_IF_NUM(0x4650, 1), .driver_info = LINE6_PODXTLIVE_VARIAX }, + { LINE6_DEVICE(0x5050), .driver_info = LINE6_PODXTPRO }, + { LINE6_DEVICE(0x4147), .driver_info = LINE6_TONEPORT_GX }, + { LINE6_DEVICE(0x4141), .driver_info = LINE6_TONEPORT_UX1 }, + { LINE6_IF_NUM(0x4142, 0), .driver_info = LINE6_TONEPORT_UX2 }, + { LINE6_DEVICE(0x534d), .driver_info = LINE6_VARIAX }, + {} +}; + +MODULE_DEVICE_TABLE(usb, line6_id_table); + +static const struct line6_properties line6_properties_table[] = { + [LINE6_BASSPODXT] = { + .id = "BassPODxt", + .name = "BassPODxt", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, + .altsetting = 5, + .ep_ctrl_r = 0x84, + .ep_ctrl_w = 0x03, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, + }, + [LINE6_BASSPODXTLIVE] = { + .id = "BassPODxtLive", + .name = "BassPODxt Live", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, + .altsetting = 1, + .ep_ctrl_r = 0x84, + .ep_ctrl_w = 0x03, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, + }, + [LINE6_BASSPODXTPRO] = { + .id = "BassPODxtPro", + .name = "BassPODxt Pro", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, + .altsetting = 5, + .ep_ctrl_r = 0x84, + .ep_ctrl_w = 0x03, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, + }, + [LINE6_GUITARPORT] = { + .id = "GuitarPort", + .name = "GuitarPort", + .capabilities = LINE6_CAP_PCM, + .altsetting = 2, /* 1..4 seem to be ok */ + /* no control channel */ + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, + }, + [LINE6_POCKETPOD] = { + .id = "PocketPOD", + .name = "Pocket POD", + .capabilities = LINE6_CAP_CONTROL, + .altsetting = 0, + .ep_ctrl_r = 0x82, + .ep_ctrl_w = 0x02, + /* no audio channel */ + }, + [LINE6_PODHD300] = { + .id = "PODHD300", + .name = "POD HD300", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, + .altsetting = 5, + .ep_ctrl_r = 0x84, + .ep_ctrl_w = 0x03, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, + }, + [LINE6_PODHD400] = { + .id = "PODHD400", + .name = "POD HD400", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, + .altsetting = 5, + .ep_ctrl_r = 0x84, + .ep_ctrl_w = 0x03, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, + }, + [LINE6_PODHD500_0] = { + .id = "PODHD500", + .name = "POD HD500", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, + .altsetting = 1, + .ep_ctrl_r = 0x81, + .ep_ctrl_w = 0x01, + .ep_audio_r = 0x86, + .ep_audio_w = 0x02, + }, + [LINE6_PODHD500_1] = { + .id = "PODHD500", + .name = "POD HD500", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, + .altsetting = 1, + .ep_ctrl_r = 0x81, + .ep_ctrl_w = 0x01, + .ep_audio_r = 0x86, + .ep_audio_w = 0x02, + }, + [LINE6_PODSTUDIO_GX] = { + .id = "PODStudioGX", + .name = "POD Studio GX", + .capabilities = LINE6_CAP_PCM, + .altsetting = 2, /* 1..4 seem to be ok */ + /* no control channel */ + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, + }, + [LINE6_PODSTUDIO_UX1] = { + .id = "PODStudioUX1", + .name = "POD Studio UX1", + .capabilities = LINE6_CAP_PCM, + .altsetting = 2, /* 1..4 seem to be ok */ + /* no control channel */ + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, + }, + [LINE6_PODSTUDIO_UX2] = { + .id = "PODStudioUX2", + .name = "POD Studio UX2", + .capabilities = LINE6_CAP_PCM, + .altsetting = 2, /* defaults to 44.1kHz, 16-bit */ + /* no control channel */ + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, + }, + [LINE6_PODXT] = { + .id = "PODxt", + .name = "PODxt", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, + .altsetting = 5, + .ep_ctrl_r = 0x84, + .ep_ctrl_w = 0x03, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, + }, + [LINE6_PODXTLIVE_POD] = { + .id = "PODxtLive", + .name = "PODxt Live", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, + .altsetting = 1, + .ep_ctrl_r = 0x84, + .ep_ctrl_w = 0x03, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, + }, + [LINE6_PODXTLIVE_VARIAX] = { + .id = "PODxtLive", + .name = "PODxt Live", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, + .altsetting = 1, + .ep_ctrl_r = 0x86, + .ep_ctrl_w = 0x05, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, + }, + [LINE6_PODXTPRO] = { + .id = "PODxtPro", + .name = "PODxt Pro", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM + | LINE6_CAP_HWMON, + .altsetting = 5, + .ep_ctrl_r = 0x84, + .ep_ctrl_w = 0x03, + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, + }, + [LINE6_TONEPORT_GX] = { + .id = "TonePortGX", + .name = "TonePort GX", + .capabilities = LINE6_CAP_PCM, + .altsetting = 2, /* 1..4 seem to be ok */ + /* no control channel */ + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, + }, + [LINE6_TONEPORT_UX1] = { + .id = "TonePortUX1", + .name = "TonePort UX1", + .capabilities = LINE6_CAP_PCM, + .altsetting = 2, /* 1..4 seem to be ok */ + /* no control channel */ + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, + }, + [LINE6_TONEPORT_UX2] = { + .id = "TonePortUX2", + .name = "TonePort UX2", + .capabilities = LINE6_CAP_PCM, + .altsetting = 2, /* defaults to 44.1kHz, 16-bit */ + /* no control channel */ + .ep_audio_r = 0x82, + .ep_audio_w = 0x01, + }, + [LINE6_VARIAX] = { + .id = "Variax", + .name = "Variax Workbench", + .capabilities = LINE6_CAP_CONTROL, + .altsetting = 1, + .ep_ctrl_r = 0x82, + .ep_ctrl_w = 0x01, + /* no audio channel */ + } +}; + +/* + This is Line6's MIDI manufacturer ID. +*/ +const unsigned char line6_midi_id[] = { + 0x00, 0x01, 0x0c +}; + +/* + Code to request version of POD, Variax interface + (and maybe other devices). +*/ +static const char line6_request_version[] = { + 0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7 +}; + +/** + Class for asynchronous messages. +*/ +struct message { + struct usb_line6 *line6; + const char *buffer; + int size; + int done; +}; + +/* + Forward declarations. +*/ +static void line6_data_received(struct urb *urb); +static int line6_send_raw_message_async_part(struct message *msg, + struct urb *urb); + +/* + Start to listen on endpoint. +*/ +static int line6_start_listen(struct usb_line6 *line6) +{ + int err; + + usb_fill_int_urb(line6->urb_listen, line6->usbdev, + usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r), + line6->buffer_listen, LINE6_BUFSIZE_LISTEN, + line6_data_received, line6, line6->interval); + line6->urb_listen->actual_length = 0; + err = usb_submit_urb(line6->urb_listen, GFP_ATOMIC); + return err; +} + +/* + Stop listening on endpoint. +*/ +static void line6_stop_listen(struct usb_line6 *line6) +{ + usb_kill_urb(line6->urb_listen); +} + +/* + Send raw message in pieces of wMaxPacketSize bytes. +*/ +int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, + int size) +{ + int i, done = 0; + + for (i = 0; i < size; i += line6->max_packet_size) { + int partial; + const char *frag_buf = buffer + i; + int frag_size = min(line6->max_packet_size, size - i); + int retval; + + retval = usb_interrupt_msg(line6->usbdev, + usb_sndintpipe(line6->usbdev, + line6->properties->ep_ctrl_w), + (char *)frag_buf, frag_size, + &partial, LINE6_TIMEOUT * HZ); + + if (retval) { + dev_err(line6->ifcdev, + "usb_interrupt_msg failed (%d)\n", retval); + break; + } + + done += frag_size; + } + + return done; +} + +/* + Notification of completion of asynchronous request transmission. +*/ +static void line6_async_request_sent(struct urb *urb) +{ + struct message *msg = (struct message *)urb->context; + + if (msg->done >= msg->size) { + usb_free_urb(urb); + kfree(msg); + } else + line6_send_raw_message_async_part(msg, urb); +} + +/* + Asynchronously send part of a raw message. +*/ +static int line6_send_raw_message_async_part(struct message *msg, + struct urb *urb) +{ + int retval; + struct usb_line6 *line6 = msg->line6; + int done = msg->done; + int bytes = min(msg->size - done, line6->max_packet_size); + + usb_fill_int_urb(urb, line6->usbdev, + usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w), + (char *)msg->buffer + done, bytes, + line6_async_request_sent, msg, line6->interval); + + msg->done += bytes; + retval = usb_submit_urb(urb, GFP_ATOMIC); + + if (retval < 0) { + dev_err(line6->ifcdev, "%s: usb_submit_urb failed (%d)\n", + __func__, retval); + usb_free_urb(urb); + kfree(msg); + return retval; + } + + return 0; +} + +/* + Setup and start timer. +*/ +void line6_start_timer(struct timer_list *timer, unsigned int msecs, + void (*function)(unsigned long), unsigned long data) +{ + setup_timer(timer, function, data); + timer->expires = jiffies + msecs * HZ / 1000; + add_timer(timer); +} + +/* + Asynchronously send raw message. +*/ +int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer, + int size) +{ + struct message *msg; + struct urb *urb; + + /* create message: */ + msg = kmalloc(sizeof(struct message), GFP_ATOMIC); + if (msg == NULL) + return -ENOMEM; + + /* create URB: */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + + if (urb == NULL) { + kfree(msg); + dev_err(line6->ifcdev, "Out of memory\n"); + return -ENOMEM; + } + + /* set message data: */ + msg->line6 = line6; + msg->buffer = buffer; + msg->size = size; + msg->done = 0; + + /* start sending: */ + return line6_send_raw_message_async_part(msg, urb); +} + +/* + Send asynchronous device version request. +*/ +int line6_version_request_async(struct usb_line6 *line6) +{ + char *buffer; + int retval; + + buffer = kmemdup(line6_request_version, + sizeof(line6_request_version), GFP_ATOMIC); + if (buffer == NULL) { + dev_err(line6->ifcdev, "Out of memory"); + return -ENOMEM; + } + + retval = line6_send_raw_message_async(line6, buffer, + sizeof(line6_request_version)); + kfree(buffer); + return retval; +} + +/* + Send sysex message in pieces of wMaxPacketSize bytes. +*/ +int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer, + int size) +{ + return line6_send_raw_message(line6, buffer, + size + SYSEX_EXTRA_SIZE) - + SYSEX_EXTRA_SIZE; +} + +/* + Allocate buffer for sysex message and prepare header. + @param code sysex message code + @param size number of bytes between code and sysex end +*/ +char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2, + int size) +{ + char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_ATOMIC); + + if (!buffer) + return NULL; + + buffer[0] = LINE6_SYSEX_BEGIN; + memcpy(buffer + 1, line6_midi_id, sizeof(line6_midi_id)); + buffer[sizeof(line6_midi_id) + 1] = code1; + buffer[sizeof(line6_midi_id) + 2] = code2; + buffer[sizeof(line6_midi_id) + 3 + size] = LINE6_SYSEX_END; + return buffer; +} + +/* + Notification of data received from the Line6 device. +*/ +static void line6_data_received(struct urb *urb) +{ + struct usb_line6 *line6 = (struct usb_line6 *)urb->context; + struct midi_buffer *mb = &line6->line6midi->midibuf_in; + int done; + + if (urb->status == -ESHUTDOWN) + return; + + done = + line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length); + + if (done < urb->actual_length) { + line6_midibuf_ignore(mb, done); + dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n", + done, urb->actual_length); + } + + for (;;) { + done = + line6_midibuf_read(mb, line6->buffer_message, + LINE6_MESSAGE_MAXLEN); + + if (done == 0) + break; + + line6->message_length = done; + line6_midi_receive(line6, line6->buffer_message, done); + + if (line6->process_message) + line6->process_message(line6); + } + + line6_start_listen(line6); +} + +/* + Send channel number (i.e., switch to a different sound). +*/ +int line6_send_program(struct usb_line6 *line6, u8 value) +{ + int retval; + unsigned char *buffer; + int partial; + + buffer = kmalloc(2, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer[0] = LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST; + buffer[1] = value; + + retval = usb_interrupt_msg(line6->usbdev, + usb_sndintpipe(line6->usbdev, + line6->properties->ep_ctrl_w), + buffer, 2, &partial, LINE6_TIMEOUT * HZ); + + if (retval) + dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", + retval); + + kfree(buffer); + return retval; +} + +/* + Transmit Line6 control parameter. +*/ +int line6_transmit_parameter(struct usb_line6 *line6, int param, u8 value) +{ + int retval; + unsigned char *buffer; + int partial; + + buffer = kmalloc(3, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer[0] = LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST; + buffer[1] = param; + buffer[2] = value; + + retval = usb_interrupt_msg(line6->usbdev, + usb_sndintpipe(line6->usbdev, + line6->properties->ep_ctrl_w), + buffer, 3, &partial, LINE6_TIMEOUT * HZ); + + if (retval) + dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", + retval); + + kfree(buffer); + return retval; +} + +/* + Read data from device. +*/ +int line6_read_data(struct usb_line6 *line6, int address, void *data, + size_t datalen) +{ + struct usb_device *usbdev = line6->usbdev; + int ret; + unsigned char len; + + /* query the serial number: */ + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + (datalen << 8) | 0x21, address, + NULL, 0, LINE6_TIMEOUT * HZ); + + if (ret < 0) { + dev_err(line6->ifcdev, "read request failed (error %d)\n", ret); + return ret; + } + + /* Wait for data length. We'll get 0xff until length arrives. */ + do { + ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | + USB_DIR_IN, + 0x0012, 0x0000, &len, 1, + LINE6_TIMEOUT * HZ); + if (ret < 0) { + dev_err(line6->ifcdev, + "receive length failed (error %d)\n", ret); + return ret; + } + } while (len == 0xff); + + if (len != datalen) { + /* should be equal or something went wrong */ + dev_err(line6->ifcdev, + "length mismatch (expected %d, got %d)\n", + (int)datalen, (int)len); + return -EINVAL; + } + + /* receive the result: */ + ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x0013, 0x0000, data, datalen, + LINE6_TIMEOUT * HZ); + + if (ret < 0) { + dev_err(line6->ifcdev, "read failed (error %d)\n", ret); + return ret; + } + + return 0; +} + +/* + Write data to device. +*/ +int line6_write_data(struct usb_line6 *line6, int address, void *data, + size_t datalen) +{ + struct usb_device *usbdev = line6->usbdev; + int ret; + unsigned char status; + + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + 0x0022, address, data, datalen, + LINE6_TIMEOUT * HZ); + + if (ret < 0) { + dev_err(line6->ifcdev, + "write request failed (error %d)\n", ret); + return ret; + } + + do { + ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), + 0x67, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | + USB_DIR_IN, + 0x0012, 0x0000, + &status, 1, LINE6_TIMEOUT * HZ); + + if (ret < 0) { + dev_err(line6->ifcdev, + "receiving status failed (error %d)\n", ret); + return ret; + } + } while (status == 0xff); + + if (status != 0) { + dev_err(line6->ifcdev, "write failed (error %d)\n", ret); + return -EINVAL; + } + + return 0; +} + +/* + Read Line6 device serial number. + (POD, TonePort, GuitarPort) +*/ +int line6_read_serial_number(struct usb_line6 *line6, int *serial_number) +{ + return line6_read_data(line6, 0x80d0, serial_number, + sizeof(*serial_number)); +} + +/* + No operation (i.e., unsupported). +*/ +ssize_t line6_nop_read(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return 0; +} + +/* + Generic destructor. +*/ +static void line6_destruct(struct usb_interface *interface) +{ + struct usb_line6 *line6; + + if (interface == NULL) + return; + line6 = usb_get_intfdata(interface); + if (line6 == NULL) + return; + + /* free buffer memory first: */ + kfree(line6->buffer_message); + kfree(line6->buffer_listen); + + /* then free URBs: */ + usb_free_urb(line6->urb_listen); + + /* make sure the device isn't destructed twice: */ + usb_set_intfdata(interface, NULL); + + /* free interface data: */ + kfree(line6); +} + +/* + Probe USB device. +*/ +static int line6_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + enum line6_device_type devtype; + struct usb_device *usbdev; + struct usb_line6 *line6; + const struct line6_properties *properties; + int interface_number; + int size = 0; + int ret; + + if (interface == NULL) + return -ENODEV; + usbdev = interface_to_usbdev(interface); + if (usbdev == NULL) + return -ENODEV; + + /* we don't handle multiple configurations */ + if (usbdev->descriptor.bNumConfigurations != 1) { + ret = -ENODEV; + goto err_put; + } + + devtype = id->driver_info; + + /* initialize device info: */ + properties = &line6_properties_table[devtype]; + dev_info(&interface->dev, "Line6 %s found\n", properties->name); + + /* query interface number */ + interface_number = interface->cur_altsetting->desc.bInterfaceNumber; + + ret = usb_set_interface(usbdev, interface_number, + properties->altsetting); + if (ret < 0) { + dev_err(&interface->dev, "set_interface failed\n"); + goto err_put; + } + + /* initialize device data based on device: */ + switch (devtype) { + case LINE6_BASSPODXT: + case LINE6_BASSPODXTLIVE: + case LINE6_BASSPODXTPRO: + case LINE6_PODXT: + case LINE6_PODXTPRO: + size = sizeof(struct usb_line6_pod); + break; + + case LINE6_PODHD300: + case LINE6_PODHD400: + size = sizeof(struct usb_line6_podhd); + break; + + case LINE6_PODHD500_0: + case LINE6_PODHD500_1: + size = sizeof(struct usb_line6_podhd); + break; + + case LINE6_POCKETPOD: + size = sizeof(struct usb_line6_pod); + break; + + case LINE6_PODSTUDIO_GX: + case LINE6_PODSTUDIO_UX1: + case LINE6_PODSTUDIO_UX2: + case LINE6_TONEPORT_GX: + case LINE6_TONEPORT_UX1: + case LINE6_TONEPORT_UX2: + case LINE6_GUITARPORT: + size = sizeof(struct usb_line6_toneport); + break; + + case LINE6_PODXTLIVE_POD: + size = sizeof(struct usb_line6_pod); + break; + + case LINE6_PODXTLIVE_VARIAX: + size = sizeof(struct usb_line6_variax); + break; + + case LINE6_VARIAX: + size = sizeof(struct usb_line6_variax); + break; + + default: + MISSING_CASE; + ret = -ENODEV; + goto err_put; + } + + if (size == 0) { + dev_err(&interface->dev, + "driver bug: interface data size not set\n"); + ret = -ENODEV; + goto err_put; + } + + line6 = kzalloc(size, GFP_KERNEL); + if (line6 == NULL) { + ret = -ENODEV; + goto err_put; + } + + /* store basic data: */ + line6->properties = properties; + line6->usbdev = usbdev; + line6->ifcdev = &interface->dev; + line6->type = devtype; + + /* get data from endpoint descriptor (see usb_maxpacket): */ + { + struct usb_host_endpoint *ep; + unsigned pipe = usb_rcvintpipe(usbdev, properties->ep_ctrl_r); + unsigned epnum = usb_pipeendpoint(pipe); + ep = usbdev->ep_in[epnum]; + + if (ep != NULL) { + line6->interval = ep->desc.bInterval; + line6->max_packet_size = + le16_to_cpu(ep->desc.wMaxPacketSize); + } else { + line6->interval = LINE6_FALLBACK_INTERVAL; + line6->max_packet_size = LINE6_FALLBACK_MAXPACKETSIZE; + dev_err(line6->ifcdev, + "endpoint not available, using fallback values"); + } + } + + usb_set_intfdata(interface, line6); + + if (properties->capabilities & LINE6_CAP_CONTROL) { + /* initialize USB buffers: */ + line6->buffer_listen = + kmalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL); + if (line6->buffer_listen == NULL) { + ret = -ENOMEM; + goto err_destruct; + } + + line6->buffer_message = + kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL); + if (line6->buffer_message == NULL) { + ret = -ENOMEM; + goto err_destruct; + } + + line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL); + + if (line6->urb_listen == NULL) { + dev_err(&interface->dev, "Out of memory\n"); + line6_destruct(interface); + ret = -ENOMEM; + goto err_destruct; + } + + ret = line6_start_listen(line6); + if (ret < 0) { + dev_err(&interface->dev, "%s: usb_submit_urb failed\n", + __func__); + goto err_destruct; + } + } + + /* initialize device data based on device: */ + switch (devtype) { + case LINE6_BASSPODXT: + case LINE6_BASSPODXTLIVE: + case LINE6_BASSPODXTPRO: + case LINE6_POCKETPOD: + case LINE6_PODXT: + case LINE6_PODXTPRO: + ret = line6_pod_init(interface, line6); + break; + + case LINE6_PODHD300: + case LINE6_PODHD400: + case LINE6_PODHD500_0: + case LINE6_PODHD500_1: + ret = line6_podhd_init(interface, line6); + break; + + case LINE6_PODXTLIVE_POD: + ret = line6_pod_init(interface, line6); + break; + + case LINE6_PODXTLIVE_VARIAX: + ret = line6_variax_init(interface, line6); + break; + + case LINE6_VARIAX: + ret = line6_variax_init(interface, line6); + break; + + case LINE6_PODSTUDIO_GX: + case LINE6_PODSTUDIO_UX1: + case LINE6_PODSTUDIO_UX2: + case LINE6_TONEPORT_GX: + case LINE6_TONEPORT_UX1: + case LINE6_TONEPORT_UX2: + case LINE6_GUITARPORT: + ret = line6_toneport_init(interface, line6); + break; + + default: + MISSING_CASE; + ret = -ENODEV; + } + + if (ret < 0) + goto err_destruct; + + ret = sysfs_create_link(&interface->dev.kobj, &usbdev->dev.kobj, + "usb_device"); + if (ret < 0) + goto err_destruct; + + /* creation of additional special files should go here */ + + dev_info(&interface->dev, "Line6 %s now attached\n", + line6->properties->name); + + /* increment reference counters: */ + usb_get_intf(interface); + usb_get_dev(usbdev); + + return 0; + +err_destruct: + line6_destruct(interface); +err_put: + return ret; +} + +/* + Line6 device disconnected. +*/ +static void line6_disconnect(struct usb_interface *interface) +{ + struct usb_line6 *line6; + struct usb_device *usbdev; + int interface_number; + + if (interface == NULL) + return; + usbdev = interface_to_usbdev(interface); + if (usbdev == NULL) + return; + + /* removal of additional special files should go here */ + + sysfs_remove_link(&interface->dev.kobj, "usb_device"); + + interface_number = interface->cur_altsetting->desc.bInterfaceNumber; + line6 = usb_get_intfdata(interface); + + if (line6 != NULL) { + if (line6->urb_listen != NULL) + line6_stop_listen(line6); + + if (usbdev != line6->usbdev) + dev_err(line6->ifcdev, + "driver bug: inconsistent usb device\n"); + + line6->disconnect(interface); + + dev_info(&interface->dev, "Line6 %s now disconnected\n", + line6->properties->name); + } + + line6_destruct(interface); + + /* decrement reference counters: */ + usb_put_intf(interface); + usb_put_dev(usbdev); +} + +#ifdef CONFIG_PM + +/* + Suspend Line6 device. +*/ +static int line6_suspend(struct usb_interface *interface, pm_message_t message) +{ + struct usb_line6 *line6 = usb_get_intfdata(interface); + struct snd_line6_pcm *line6pcm = line6->line6pcm; + + snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot); + + if (line6->properties->capabilities & LINE6_CAP_CONTROL) + line6_stop_listen(line6); + + if (line6pcm != NULL) { + snd_pcm_suspend_all(line6pcm->pcm); + line6_pcm_disconnect(line6pcm); + line6pcm->flags = 0; + } + + return 0; +} + +/* + Resume Line6 device. +*/ +static int line6_resume(struct usb_interface *interface) +{ + struct usb_line6 *line6 = usb_get_intfdata(interface); + + if (line6->properties->capabilities & LINE6_CAP_CONTROL) + line6_start_listen(line6); + + snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0); + return 0; +} + +/* + Resume Line6 device after reset. +*/ +static int line6_reset_resume(struct usb_interface *interface) +{ + struct usb_line6 *line6 = usb_get_intfdata(interface); + + switch (line6->type) { + case LINE6_PODSTUDIO_GX: + case LINE6_PODSTUDIO_UX1: + case LINE6_PODSTUDIO_UX2: + case LINE6_TONEPORT_GX: + case LINE6_TONEPORT_UX1: + case LINE6_TONEPORT_UX2: + case LINE6_GUITARPORT: + line6_toneport_reset_resume((struct usb_line6_toneport *)line6); + + default: + break; + } + + return line6_resume(interface); +} + +#endif /* CONFIG_PM */ + +static struct usb_driver line6_driver = { + .name = DRIVER_NAME, + .probe = line6_probe, + .disconnect = line6_disconnect, +#ifdef CONFIG_PM + .suspend = line6_suspend, + .resume = line6_resume, + .reset_resume = line6_reset_resume, +#endif + .id_table = line6_id_table, +}; + +module_usb_driver(line6_driver); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h new file mode 100644 index 0000000..ad203f1 --- /dev/null +++ b/sound/usb/line6/driver.h @@ -0,0 +1,228 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef DRIVER_H +#define DRIVER_H + +#include +#include +#include + +#include "midi.h" + +#define DRIVER_NAME "line6usb" + +enum line6_device_type { + LINE6_BASSPODXT, + LINE6_BASSPODXTLIVE, + LINE6_BASSPODXTPRO, + LINE6_GUITARPORT, + LINE6_POCKETPOD, + LINE6_PODHD300, + LINE6_PODHD400, + LINE6_PODHD500_0, + LINE6_PODHD500_1, + LINE6_PODSTUDIO_GX, + LINE6_PODSTUDIO_UX1, + LINE6_PODSTUDIO_UX2, + LINE6_PODXT, + LINE6_PODXTLIVE_POD, + LINE6_PODXTLIVE_VARIAX, + LINE6_PODXTPRO, + LINE6_TONEPORT_GX, + LINE6_TONEPORT_UX1, + LINE6_TONEPORT_UX2, + LINE6_VARIAX +}; + +#define LINE6_TIMEOUT 1 +#define LINE6_BUFSIZE_LISTEN 32 +#define LINE6_MESSAGE_MAXLEN 256 + +/* + Line6 MIDI control commands +*/ +#define LINE6_PARAM_CHANGE 0xb0 +#define LINE6_PROGRAM_CHANGE 0xc0 +#define LINE6_SYSEX_BEGIN 0xf0 +#define LINE6_SYSEX_END 0xf7 +#define LINE6_RESET 0xff + +/* + MIDI channel for messages initiated by the host + (and eventually echoed back by the device) +*/ +#define LINE6_CHANNEL_HOST 0x00 + +/* + MIDI channel for messages initiated by the device +*/ +#define LINE6_CHANNEL_DEVICE 0x02 + +#define LINE6_CHANNEL_UNKNOWN 5 /* don't know yet what this is good for */ + +#define LINE6_CHANNEL_MASK 0x0f + +#define MISSING_CASE \ + pr_err("line6usb driver bug: missing case in %s:%d\n", \ + __FILE__, __LINE__) + +#define CHECK_RETURN(x) \ +do { \ + err = x; \ + if (err < 0) \ + return err; \ +} while (0) + +#define CHECK_STARTUP_PROGRESS(x, n) \ +do { \ + if ((x) >= (n)) \ + return; \ + x = (n); \ +} while (0) + +extern const unsigned char line6_midi_id[3]; + +static const int SYSEX_DATA_OFS = sizeof(line6_midi_id) + 3; +static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4; + +/** + Common properties of Line6 devices. +*/ +struct line6_properties { + /** + Card id string (maximum 16 characters). + This can be used to address the device in ALSA programs as + "default:CARD=" + */ + const char *id; + + /** + Card short name (maximum 32 characters). + */ + const char *name; + + /** + Bit vector defining this device's capabilities in the + line6usb driver. + */ + int capabilities; + + int altsetting; + + unsigned ep_ctrl_r; + unsigned ep_ctrl_w; + unsigned ep_audio_r; + unsigned ep_audio_w; +}; + +/** + Common data shared by all Line6 devices. + Corresponds to a pair of USB endpoints. +*/ +struct usb_line6 { + /** + USB device. + */ + struct usb_device *usbdev; + + /** + Device type. + */ + enum line6_device_type type; + + /** + Properties. + */ + const struct line6_properties *properties; + + /** + Interval (ms). + */ + int interval; + + /** + Maximum size of USB packet. + */ + int max_packet_size; + + /** + Device representing the USB interface. + */ + struct device *ifcdev; + + /** + Line6 sound card data structure. + Each device has at least MIDI or PCM. + */ + struct snd_card *card; + + /** + Line6 PCM device data structure. + */ + struct snd_line6_pcm *line6pcm; + + /** + Line6 MIDI device data structure. + */ + struct snd_line6_midi *line6midi; + + /** + URB for listening to PODxt Pro control endpoint. + */ + struct urb *urb_listen; + + /** + Buffer for listening to PODxt Pro control endpoint. + */ + unsigned char *buffer_listen; + + /** + Buffer for message to be processed. + */ + unsigned char *buffer_message; + + /** + Length of message to be processed. + */ + int message_length; + + void (*process_message)(struct usb_line6 *); + void (*disconnect)(struct usb_interface *); +}; + +extern char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, + int code2, int size); +extern ssize_t line6_nop_read(struct device *dev, + struct device_attribute *attr, char *buf); +extern int line6_read_data(struct usb_line6 *line6, int address, void *data, + size_t datalen); +extern int line6_read_serial_number(struct usb_line6 *line6, + int *serial_number); +extern int line6_send_program(struct usb_line6 *line6, u8 value); +extern int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, + int size); +extern int line6_send_raw_message_async(struct usb_line6 *line6, + const char *buffer, int size); +extern int line6_send_sysex_message(struct usb_line6 *line6, + const char *buffer, int size); +extern ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); +extern void line6_start_timer(struct timer_list *timer, unsigned int msecs, + void (*function)(unsigned long), + unsigned long data); +extern int line6_transmit_parameter(struct usb_line6 *line6, int param, + u8 value); +extern int line6_version_request_async(struct usb_line6 *line6); +extern int line6_write_data(struct usb_line6 *line6, int address, void *data, + size_t datalen); + +#endif diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c new file mode 100644 index 0000000..c9d725a --- /dev/null +++ b/sound/usb/line6/midi.c @@ -0,0 +1,321 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include +#include +#include +#include + +#include "audio.h" +#include "driver.h" +#include "midi.h" +#include "pod.h" +#include "usbdefs.h" + +#define line6_rawmidi_substream_midi(substream) \ + ((struct snd_line6_midi *)((substream)->rmidi->private_data)) + +static int send_midi_async(struct usb_line6 *line6, unsigned char *data, + int length); + +/* + Pass data received via USB to MIDI. +*/ +void line6_midi_receive(struct usb_line6 *line6, unsigned char *data, + int length) +{ + if (line6->line6midi->substream_receive) + snd_rawmidi_receive(line6->line6midi->substream_receive, + data, length); +} + +/* + Read data from MIDI buffer and transmit them via USB. +*/ +static void line6_midi_transmit(struct snd_rawmidi_substream *substream) +{ + struct usb_line6 *line6 = + line6_rawmidi_substream_midi(substream)->line6; + struct snd_line6_midi *line6midi = line6->line6midi; + struct midi_buffer *mb = &line6midi->midibuf_out; + unsigned long flags; + unsigned char chunk[LINE6_FALLBACK_MAXPACKETSIZE]; + int req, done; + + spin_lock_irqsave(&line6->line6midi->midi_transmit_lock, flags); + + for (;;) { + req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size); + done = snd_rawmidi_transmit_peek(substream, chunk, req); + + if (done == 0) + break; + + line6_midibuf_write(mb, chunk, done); + snd_rawmidi_transmit_ack(substream, done); + } + + for (;;) { + done = line6_midibuf_read(mb, chunk, + LINE6_FALLBACK_MAXPACKETSIZE); + + if (done == 0) + break; + + send_midi_async(line6, chunk, done); + } + + spin_unlock_irqrestore(&line6->line6midi->midi_transmit_lock, flags); +} + +/* + Notification of completion of MIDI transmission. +*/ +static void midi_sent(struct urb *urb) +{ + unsigned long flags; + int status; + int num; + struct usb_line6 *line6 = (struct usb_line6 *)urb->context; + + status = urb->status; + kfree(urb->transfer_buffer); + usb_free_urb(urb); + + if (status == -ESHUTDOWN) + return; + + spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags); + num = --line6->line6midi->num_active_send_urbs; + + if (num == 0) { + line6_midi_transmit(line6->line6midi->substream_transmit); + num = line6->line6midi->num_active_send_urbs; + } + + if (num == 0) + wake_up(&line6->line6midi->send_wait); + + spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags); +} + +/* + Send an asynchronous MIDI message. + Assumes that line6->line6midi->send_urb_lock is held + (i.e., this function is serialized). +*/ +static int send_midi_async(struct usb_line6 *line6, unsigned char *data, + int length) +{ + struct urb *urb; + int retval; + unsigned char *transfer_buffer; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + + if (urb == NULL) { + dev_err(line6->ifcdev, "Out of memory\n"); + return -ENOMEM; + } + + transfer_buffer = kmemdup(data, length, GFP_ATOMIC); + + if (transfer_buffer == NULL) { + usb_free_urb(urb); + dev_err(line6->ifcdev, "Out of memory\n"); + return -ENOMEM; + } + + usb_fill_int_urb(urb, line6->usbdev, + usb_sndbulkpipe(line6->usbdev, + line6->properties->ep_ctrl_w), + transfer_buffer, length, midi_sent, line6, + line6->interval); + urb->actual_length = 0; + retval = usb_submit_urb(urb, GFP_ATOMIC); + + if (retval < 0) { + dev_err(line6->ifcdev, "usb_submit_urb failed\n"); + usb_free_urb(urb); + return retval; + } + + ++line6->line6midi->num_active_send_urbs; + return 0; +} + +static int line6_midi_output_open(struct snd_rawmidi_substream *substream) +{ + return 0; +} + +static int line6_midi_output_close(struct snd_rawmidi_substream *substream) +{ + return 0; +} + +static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream, + int up) +{ + unsigned long flags; + struct usb_line6 *line6 = + line6_rawmidi_substream_midi(substream)->line6; + + line6->line6midi->substream_transmit = substream; + spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags); + + if (line6->line6midi->num_active_send_urbs == 0) + line6_midi_transmit(substream); + + spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags); +} + +static void line6_midi_output_drain(struct snd_rawmidi_substream *substream) +{ + struct usb_line6 *line6 = + line6_rawmidi_substream_midi(substream)->line6; + struct snd_line6_midi *midi = line6->line6midi; + + wait_event_interruptible(midi->send_wait, + midi->num_active_send_urbs == 0); +} + +static int line6_midi_input_open(struct snd_rawmidi_substream *substream) +{ + return 0; +} + +static int line6_midi_input_close(struct snd_rawmidi_substream *substream) +{ + return 0; +} + +static void line6_midi_input_trigger(struct snd_rawmidi_substream *substream, + int up) +{ + struct usb_line6 *line6 = + line6_rawmidi_substream_midi(substream)->line6; + + if (up) + line6->line6midi->substream_receive = substream; + else + line6->line6midi->substream_receive = NULL; +} + +static struct snd_rawmidi_ops line6_midi_output_ops = { + .open = line6_midi_output_open, + .close = line6_midi_output_close, + .trigger = line6_midi_output_trigger, + .drain = line6_midi_output_drain, +}; + +static struct snd_rawmidi_ops line6_midi_input_ops = { + .open = line6_midi_input_open, + .close = line6_midi_input_close, + .trigger = line6_midi_input_trigger, +}; + +/* + Cleanup the Line6 MIDI device. +*/ +static void line6_cleanup_midi(struct snd_rawmidi *rmidi) +{ +} + +/* Create a MIDI device */ +static int snd_line6_new_midi(struct snd_line6_midi *line6midi) +{ + struct snd_rawmidi *rmidi; + int err; + + err = snd_rawmidi_new(line6midi->line6->card, "Line6 MIDI", 0, 1, 1, + &rmidi); + if (err < 0) + return err; + + rmidi->private_data = line6midi; + rmidi->private_free = line6_cleanup_midi; + strcpy(rmidi->id, line6midi->line6->properties->id); + strcpy(rmidi->name, line6midi->line6->properties->name); + + rmidi->info_flags = + SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + &line6_midi_output_ops); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &line6_midi_input_ops); + return 0; +} + +/* MIDI device destructor */ +static int snd_line6_midi_free(struct snd_device *device) +{ + struct snd_line6_midi *line6midi = device->device_data; + + line6_midibuf_destroy(&line6midi->midibuf_in); + line6_midibuf_destroy(&line6midi->midibuf_out); + return 0; +} + +/* + Initialize the Line6 MIDI subsystem. +*/ +int line6_init_midi(struct usb_line6 *line6) +{ + static struct snd_device_ops midi_ops = { + .dev_free = snd_line6_midi_free, + }; + + int err; + struct snd_line6_midi *line6midi; + + if (!(line6->properties->capabilities & LINE6_CAP_CONTROL)) { + /* skip MIDI initialization and report success */ + return 0; + } + + line6midi = kzalloc(sizeof(struct snd_line6_midi), GFP_KERNEL); + + if (line6midi == NULL) + return -ENOMEM; + + err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0); + if (err < 0) { + kfree(line6midi); + return err; + } + + err = line6_midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1); + if (err < 0) { + kfree(line6midi->midibuf_in.buf); + kfree(line6midi); + return err; + } + + line6midi->line6 = line6; + line6->line6midi = line6midi; + + err = snd_device_new(line6->card, SNDRV_DEV_RAWMIDI, line6midi, + &midi_ops); + if (err < 0) + return err; + + err = snd_line6_new_midi(line6midi); + if (err < 0) + return err; + + init_waitqueue_head(&line6midi->send_wait); + spin_lock_init(&line6midi->send_urb_lock); + spin_lock_init(&line6midi->midi_transmit_lock); + return 0; +} diff --git a/sound/usb/line6/midi.h b/sound/usb/line6/midi.h new file mode 100644 index 0000000..78f903f --- /dev/null +++ b/sound/usb/line6/midi.h @@ -0,0 +1,72 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef MIDI_H +#define MIDI_H + +#include + +#include "midibuf.h" + +#define MIDI_BUFFER_SIZE 1024 + +struct snd_line6_midi { + /** + Pointer back to the Line6 driver data structure. + */ + struct usb_line6 *line6; + + /** + MIDI substream for receiving (or NULL if not active). + */ + struct snd_rawmidi_substream *substream_receive; + + /** + MIDI substream for transmitting (or NULL if not active). + */ + struct snd_rawmidi_substream *substream_transmit; + + /** + Number of currently active MIDI send URBs. + */ + int num_active_send_urbs; + + /** + Spin lock to protect updates of send_urb. + */ + spinlock_t send_urb_lock; + + /** + Spin lock to protect MIDI buffer handling. + */ + spinlock_t midi_transmit_lock; + + /** + Wait queue for MIDI transmission. + */ + wait_queue_head_t send_wait; + + /** + Buffer for incoming MIDI stream. + */ + struct midi_buffer midibuf_in; + + /** + Buffer for outgoing MIDI stream. + */ + struct midi_buffer midibuf_out; +}; + +extern int line6_init_midi(struct usb_line6 *line6); +extern void line6_midi_receive(struct usb_line6 *line6, unsigned char *data, + int length); + +#endif diff --git a/sound/usb/line6/midibuf.c b/sound/usb/line6/midibuf.c new file mode 100644 index 0000000..1ff8569 --- /dev/null +++ b/sound/usb/line6/midibuf.c @@ -0,0 +1,270 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include + +#include "midibuf.h" + +static int midibuf_message_length(unsigned char code) +{ + int message_length; + + if (code < 0x80) + message_length = -1; + else if (code < 0xf0) { + static const int length[] = { 3, 3, 3, 3, 2, 2, 3 }; + + message_length = length[(code >> 4) - 8]; + } else { + /* + Note that according to the MIDI specification 0xf2 is + the "Song Position Pointer", but this is used by Line6 + to send sysex messages to the host. + */ + static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1, + 1, 1, 1, -1, 1, 1 + }; + message_length = length[code & 0x0f]; + } + + return message_length; +} + +static int midibuf_is_empty(struct midi_buffer *this) +{ + return (this->pos_read == this->pos_write) && !this->full; +} + +static int midibuf_is_full(struct midi_buffer *this) +{ + return this->full; +} + +void line6_midibuf_reset(struct midi_buffer *this) +{ + this->pos_read = this->pos_write = this->full = 0; + this->command_prev = -1; +} + +int line6_midibuf_init(struct midi_buffer *this, int size, int split) +{ + this->buf = kmalloc(size, GFP_KERNEL); + + if (this->buf == NULL) + return -ENOMEM; + + this->size = size; + this->split = split; + line6_midibuf_reset(this); + return 0; +} + +void line6_midibuf_status(struct midi_buffer *this) +{ + pr_debug("midibuf size=%d split=%d pos_read=%d pos_write=%d full=%d command_prev=%02x\n", + this->size, this->split, this->pos_read, this->pos_write, + this->full, this->command_prev); +} + +int line6_midibuf_bytes_free(struct midi_buffer *this) +{ + return + midibuf_is_full(this) ? + 0 : + (this->pos_read - this->pos_write + this->size - 1) % this->size + + 1; +} + +int line6_midibuf_bytes_used(struct midi_buffer *this) +{ + return + midibuf_is_empty(this) ? + 0 : + (this->pos_write - this->pos_read + this->size - 1) % this->size + + 1; +} + +int line6_midibuf_write(struct midi_buffer *this, unsigned char *data, + int length) +{ + int bytes_free; + int length1, length2; + int skip_active_sense = 0; + + if (midibuf_is_full(this) || (length <= 0)) + return 0; + + /* skip trailing active sense */ + if (data[length - 1] == 0xfe) { + --length; + skip_active_sense = 1; + } + + bytes_free = line6_midibuf_bytes_free(this); + + if (length > bytes_free) + length = bytes_free; + + if (length > 0) { + length1 = this->size - this->pos_write; + + if (length < length1) { + /* no buffer wraparound */ + memcpy(this->buf + this->pos_write, data, length); + this->pos_write += length; + } else { + /* buffer wraparound */ + length2 = length - length1; + memcpy(this->buf + this->pos_write, data, length1); + memcpy(this->buf, data + length1, length2); + this->pos_write = length2; + } + + if (this->pos_write == this->pos_read) + this->full = 1; + } + + return length + skip_active_sense; +} + +int line6_midibuf_read(struct midi_buffer *this, unsigned char *data, + int length) +{ + int bytes_used; + int length1, length2; + int command; + int midi_length; + int repeat = 0; + int i; + + /* we need to be able to store at least a 3 byte MIDI message */ + if (length < 3) + return -EINVAL; + + if (midibuf_is_empty(this)) + return 0; + + bytes_used = line6_midibuf_bytes_used(this); + + if (length > bytes_used) + length = bytes_used; + + length1 = this->size - this->pos_read; + + /* check MIDI command length */ + command = this->buf[this->pos_read]; + + if (command & 0x80) { + midi_length = midibuf_message_length(command); + this->command_prev = command; + } else { + if (this->command_prev > 0) { + int midi_length_prev = + midibuf_message_length(this->command_prev); + + if (midi_length_prev > 0) { + midi_length = midi_length_prev - 1; + repeat = 1; + } else + midi_length = -1; + } else + midi_length = -1; + } + + if (midi_length < 0) { + /* search for end of message */ + if (length < length1) { + /* no buffer wraparound */ + for (i = 1; i < length; ++i) + if (this->buf[this->pos_read + i] & 0x80) + break; + + midi_length = i; + } else { + /* buffer wraparound */ + length2 = length - length1; + + for (i = 1; i < length1; ++i) + if (this->buf[this->pos_read + i] & 0x80) + break; + + if (i < length1) + midi_length = i; + else { + for (i = 0; i < length2; ++i) + if (this->buf[i] & 0x80) + break; + + midi_length = length1 + i; + } + } + + if (midi_length == length) + midi_length = -1; /* end of message not found */ + } + + if (midi_length < 0) { + if (!this->split) + return 0; /* command is not yet complete */ + } else { + if (length < midi_length) + return 0; /* command is not yet complete */ + + length = midi_length; + } + + if (length < length1) { + /* no buffer wraparound */ + memcpy(data + repeat, this->buf + this->pos_read, length); + this->pos_read += length; + } else { + /* buffer wraparound */ + length2 = length - length1; + memcpy(data + repeat, this->buf + this->pos_read, length1); + memcpy(data + repeat + length1, this->buf, length2); + this->pos_read = length2; + } + + if (repeat) + data[0] = this->command_prev; + + this->full = 0; + return length + repeat; +} + +int line6_midibuf_ignore(struct midi_buffer *this, int length) +{ + int bytes_used = line6_midibuf_bytes_used(this); + + if (length > bytes_used) + length = bytes_used; + + this->pos_read = (this->pos_read + length) % this->size; + this->full = 0; + return length; +} + +int line6_midibuf_skip_message(struct midi_buffer *this, unsigned short mask) +{ + int cmd = this->command_prev; + + if ((cmd >= 0x80) && (cmd < 0xf0)) + if ((mask & (1 << (cmd & 0x0f))) == 0) + return 1; + + return 0; +} + +void line6_midibuf_destroy(struct midi_buffer *this) +{ + kfree(this->buf); + this->buf = NULL; +} diff --git a/sound/usb/line6/midibuf.h b/sound/usb/line6/midibuf.h new file mode 100644 index 0000000..707482b --- /dev/null +++ b/sound/usb/line6/midibuf.h @@ -0,0 +1,38 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef MIDIBUF_H +#define MIDIBUF_H + +struct midi_buffer { + unsigned char *buf; + int size; + int split; + int pos_read, pos_write; + int full; + int command_prev; +}; + +extern int line6_midibuf_bytes_used(struct midi_buffer *mb); +extern int line6_midibuf_bytes_free(struct midi_buffer *mb); +extern void line6_midibuf_destroy(struct midi_buffer *mb); +extern int line6_midibuf_ignore(struct midi_buffer *mb, int length); +extern int line6_midibuf_init(struct midi_buffer *mb, int size, int split); +extern int line6_midibuf_read(struct midi_buffer *mb, unsigned char *data, + int length); +extern void line6_midibuf_reset(struct midi_buffer *mb); +extern int line6_midibuf_skip_message(struct midi_buffer *mb, + unsigned short mask); +extern void line6_midibuf_status(struct midi_buffer *mb); +extern int line6_midibuf_write(struct midi_buffer *mb, unsigned char *data, + int length); + +#endif diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c new file mode 100644 index 0000000..6d4e5cd --- /dev/null +++ b/sound/usb/line6/pcm.c @@ -0,0 +1,527 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include +#include +#include +#include +#include + +#include "audio.h" +#include "capture.h" +#include "driver.h" +#include "playback.h" +#include "pod.h" + +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + +static struct snd_line6_pcm *dev2pcm(struct device *dev) +{ + struct usb_interface *interface = to_usb_interface(dev); + struct usb_line6 *line6 = usb_get_intfdata(interface); + struct snd_line6_pcm *line6pcm = line6->line6pcm; + return line6pcm; +} + +/* + "read" request on "impulse_volume" special file. +*/ +static ssize_t impulse_volume_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_volume); +} + +/* + "write" request on "impulse_volume" special file. +*/ +static ssize_t impulse_volume_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct snd_line6_pcm *line6pcm = dev2pcm(dev); + int value; + int ret; + + ret = kstrtoint(buf, 10, &value); + if (ret < 0) + return ret; + + line6pcm->impulse_volume = value; + + if (value > 0) + line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_IMPULSE); + else + line6_pcm_release(line6pcm, LINE6_BITS_PCM_IMPULSE); + + return count; +} +static DEVICE_ATTR_RW(impulse_volume); + +/* + "read" request on "impulse_period" special file. +*/ +static ssize_t impulse_period_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_period); +} + +/* + "write" request on "impulse_period" special file. +*/ +static ssize_t impulse_period_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int value; + int ret; + + ret = kstrtoint(buf, 10, &value); + if (ret < 0) + return ret; + + dev2pcm(dev)->impulse_period = value; + return count; +} +static DEVICE_ATTR_RW(impulse_period); + +#endif + +static bool test_flags(unsigned long flags0, unsigned long flags1, + unsigned long mask) +{ + return ((flags0 & mask) == 0) && ((flags1 & mask) != 0); +} + +int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels) +{ + unsigned long flags_old, flags_new, flags_final; + int err; + + do { + flags_old = ACCESS_ONCE(line6pcm->flags); + flags_new = flags_old | channels; + } while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); + + flags_final = flags_old; + + line6pcm->prev_fbuf = NULL; + + if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_BUFFER)) { + /* Invoked multiple times in a row so allocate once only */ + if (!line6pcm->buffer_in) { + line6pcm->buffer_in = + kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * + line6pcm->max_packet_size, GFP_KERNEL); + if (!line6pcm->buffer_in) { + err = -ENOMEM; + goto pcm_acquire_error; + } + + flags_final |= channels & LINE6_BITS_CAPTURE_BUFFER; + } + } + + if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_STREAM)) { + /* + Waiting for completion of active URBs in the stop handler is + a bug, we therefore report an error if capturing is restarted + too soon. + */ + if (line6pcm->active_urb_in | line6pcm->unlink_urb_in) { + dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); + return -EBUSY; + } + + line6pcm->count_in = 0; + line6pcm->prev_fsize = 0; + err = line6_submit_audio_in_all_urbs(line6pcm); + + if (err < 0) + goto pcm_acquire_error; + + flags_final |= channels & LINE6_BITS_CAPTURE_STREAM; + } + + if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_BUFFER)) { + /* Invoked multiple times in a row so allocate once only */ + if (!line6pcm->buffer_out) { + line6pcm->buffer_out = + kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * + line6pcm->max_packet_size, GFP_KERNEL); + if (!line6pcm->buffer_out) { + err = -ENOMEM; + goto pcm_acquire_error; + } + + flags_final |= channels & LINE6_BITS_PLAYBACK_BUFFER; + } + } + + if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_STREAM)) { + /* + See comment above regarding PCM restart. + */ + if (line6pcm->active_urb_out | line6pcm->unlink_urb_out) { + dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); + return -EBUSY; + } + + line6pcm->count_out = 0; + err = line6_submit_audio_out_all_urbs(line6pcm); + + if (err < 0) + goto pcm_acquire_error; + + flags_final |= channels & LINE6_BITS_PLAYBACK_STREAM; + } + + return 0; + +pcm_acquire_error: + /* + If not all requested resources/streams could be obtained, release + those which were successfully obtained (if any). + */ + line6_pcm_release(line6pcm, flags_final & channels); + return err; +} + +int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels) +{ + unsigned long flags_old, flags_new; + + do { + flags_old = ACCESS_ONCE(line6pcm->flags); + flags_new = flags_old & ~channels; + } while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); + + if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_STREAM)) + line6_unlink_audio_in_urbs(line6pcm); + + if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_BUFFER)) { + line6_wait_clear_audio_in_urbs(line6pcm); + line6_free_capture_buffer(line6pcm); + } + + if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_STREAM)) + line6_unlink_audio_out_urbs(line6pcm); + + if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_BUFFER)) { + line6_wait_clear_audio_out_urbs(line6pcm); + line6_free_playback_buffer(line6pcm); + } + + return 0; +} + +/* trigger callback */ +int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); + struct snd_pcm_substream *s; + int err; + unsigned long flags; + + spin_lock_irqsave(&line6pcm->lock_trigger, flags); + clear_bit(LINE6_INDEX_PREPARED, &line6pcm->flags); + + snd_pcm_group_for_each_entry(s, substream) { + switch (s->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + err = snd_line6_playback_trigger(line6pcm, cmd); + + if (err < 0) { + spin_unlock_irqrestore(&line6pcm->lock_trigger, + flags); + return err; + } + + break; + + case SNDRV_PCM_STREAM_CAPTURE: + err = snd_line6_capture_trigger(line6pcm, cmd); + + if (err < 0) { + spin_unlock_irqrestore(&line6pcm->lock_trigger, + flags); + return err; + } + + break; + + default: + dev_err(line6pcm->line6->ifcdev, + "Unknown stream direction %d\n", s->stream); + } + } + + spin_unlock_irqrestore(&line6pcm->lock_trigger, flags); + return 0; +} + +/* control info callback */ +static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 256; + return 0; +} + +/* control get callback */ +static int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i; + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + + for (i = 2; i--;) + ucontrol->value.integer.value[i] = line6pcm->volume_playback[i]; + + return 0; +} + +/* control put callback */ +static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i, changed = 0; + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + + for (i = 2; i--;) + if (line6pcm->volume_playback[i] != + ucontrol->value.integer.value[i]) { + line6pcm->volume_playback[i] = + ucontrol->value.integer.value[i]; + changed = 1; + } + + return changed; +} + +/* control definition */ +static struct snd_kcontrol_new line6_control_playback = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_line6_control_playback_info, + .get = snd_line6_control_playback_get, + .put = snd_line6_control_playback_put +}; + +/* + Cleanup the PCM device. +*/ +static void line6_cleanup_pcm(struct snd_pcm *pcm) +{ + int i; + struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm); + +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_volume); + device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_period); +#endif + + for (i = LINE6_ISO_BUFFERS; i--;) { + if (line6pcm->urb_audio_out[i]) { + usb_kill_urb(line6pcm->urb_audio_out[i]); + usb_free_urb(line6pcm->urb_audio_out[i]); + } + if (line6pcm->urb_audio_in[i]) { + usb_kill_urb(line6pcm->urb_audio_in[i]); + usb_free_urb(line6pcm->urb_audio_in[i]); + } + } +} + +/* create a PCM device */ +static int snd_line6_new_pcm(struct snd_line6_pcm *line6pcm) +{ + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(line6pcm->line6->card, + (char *)line6pcm->line6->properties->name, + 0, 1, 1, &pcm); + if (err < 0) + return err; + + pcm->private_data = line6pcm; + pcm->private_free = line6_cleanup_pcm; + line6pcm->pcm = pcm; + strcpy(pcm->name, line6pcm->line6->properties->name); + + /* set operators */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_line6_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_line6_capture_ops); + + /* pre-allocation of buffers */ + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data + (GFP_KERNEL), 64 * 1024, + 128 * 1024); + + return 0; +} + +/* PCM device destructor */ +static int snd_line6_pcm_free(struct snd_device *device) +{ + return 0; +} + +/* + Stop substream if still running. +*/ +static void pcm_disconnect_substream(struct snd_pcm_substream *substream) +{ + if (substream->runtime && snd_pcm_running(substream)) { + snd_pcm_stream_lock_irq(substream); + snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + snd_pcm_stream_unlock_irq(substream); + } +} + +/* + Stop PCM stream. +*/ +void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm) +{ + pcm_disconnect_substream(get_substream + (line6pcm, SNDRV_PCM_STREAM_CAPTURE)); + pcm_disconnect_substream(get_substream + (line6pcm, SNDRV_PCM_STREAM_PLAYBACK)); + line6_unlink_wait_clear_audio_out_urbs(line6pcm); + line6_unlink_wait_clear_audio_in_urbs(line6pcm); +} + +/* + Create and register the PCM device and mixer entries. + Create URBs for playback and capture. +*/ +int line6_init_pcm(struct usb_line6 *line6, + struct line6_pcm_properties *properties) +{ + static struct snd_device_ops pcm_ops = { + .dev_free = snd_line6_pcm_free, + }; + + int err; + unsigned ep_read = line6->properties->ep_audio_r; + unsigned ep_write = line6->properties->ep_audio_w; + struct snd_line6_pcm *line6pcm; + + if (!(line6->properties->capabilities & LINE6_CAP_PCM)) + return 0; /* skip PCM initialization and report success */ + + line6pcm = kzalloc(sizeof(*line6pcm), GFP_KERNEL); + + if (line6pcm == NULL) + return -ENOMEM; + + line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255; + line6pcm->volume_monitor = 255; + line6pcm->line6 = line6; + + /* Read and write buffers are sized identically, so choose minimum */ + line6pcm->max_packet_size = min( + usb_maxpacket(line6->usbdev, + usb_rcvisocpipe(line6->usbdev, ep_read), 0), + usb_maxpacket(line6->usbdev, + usb_sndisocpipe(line6->usbdev, ep_write), 1)); + + line6pcm->properties = properties; + line6->line6pcm = line6pcm; + + /* PCM device: */ + err = snd_device_new(line6->card, SNDRV_DEV_PCM, line6, &pcm_ops); + if (err < 0) + return err; + + err = snd_line6_new_pcm(line6pcm); + if (err < 0) + return err; + + spin_lock_init(&line6pcm->lock_audio_out); + spin_lock_init(&line6pcm->lock_audio_in); + spin_lock_init(&line6pcm->lock_trigger); + + err = line6_create_audio_out_urbs(line6pcm); + if (err < 0) + return err; + + err = line6_create_audio_in_urbs(line6pcm); + if (err < 0) + return err; + + /* mixer: */ + err = + snd_ctl_add(line6->card, + snd_ctl_new1(&line6_control_playback, line6pcm)); + if (err < 0) + return err; + +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + /* impulse response test: */ + err = device_create_file(line6->ifcdev, &dev_attr_impulse_volume); + if (err < 0) + return err; + + err = device_create_file(line6->ifcdev, &dev_attr_impulse_period); + if (err < 0) + return err; + + line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD; +#endif + + return 0; +} + +/* prepare pcm callback */ +int snd_line6_prepare(struct snd_pcm_substream *substream) +{ + struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + if ((line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM) == 0) + line6_unlink_wait_clear_audio_out_urbs(line6pcm); + + break; + + case SNDRV_PCM_STREAM_CAPTURE: + if ((line6pcm->flags & LINE6_BITS_CAPTURE_STREAM) == 0) + line6_unlink_wait_clear_audio_in_urbs(line6pcm); + + break; + + default: + MISSING_CASE; + } + + if (!test_and_set_bit(LINE6_INDEX_PREPARED, &line6pcm->flags)) { + line6pcm->count_out = 0; + line6pcm->pos_out = 0; + line6pcm->pos_out_done = 0; + line6pcm->bytes_out = 0; + line6pcm->count_in = 0; + line6pcm->pos_in_done = 0; + line6pcm->bytes_in = 0; + } + + return 0; +} diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h new file mode 100644 index 0000000..7315e81 --- /dev/null +++ b/sound/usb/line6/pcm.h @@ -0,0 +1,374 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +/* + PCM interface to POD series devices. +*/ + +#ifndef PCM_H +#define PCM_H + +#include + +#include "driver.h" +#include "usbdefs.h" + +/* number of URBs */ +#define LINE6_ISO_BUFFERS 2 + +/* + number of USB frames per URB + The Line6 Windows driver always transmits two frames per packet, but + the Linux driver performs significantly better (i.e., lower latency) + with only one frame per packet. +*/ +#define LINE6_ISO_PACKETS 1 + +/* in a "full speed" device (such as the PODxt Pro) this means 1ms */ +#define LINE6_ISO_INTERVAL 1 + +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE +#define LINE6_IMPULSE_DEFAULT_PERIOD 100 +#endif + +/* + Get substream from Line6 PCM data structure +*/ +#define get_substream(line6pcm, stream) \ + (line6pcm->pcm->streams[stream].substream) + +/* + PCM mode bits. + + There are several features of the Line6 USB driver which require PCM + data to be exchanged with the device: + *) PCM playback and capture via ALSA + *) software monitoring (for devices without hardware monitoring) + *) optional impulse response measurement + However, from the device's point of view, there is just a single + capture and playback stream, which must be shared between these + subsystems. It is therefore necessary to maintain the state of the + subsystems with respect to PCM usage. We define several constants of + the form LINE6_BIT_PCM___ with the + following meanings: + *) is one of + -) ALSA: PCM playback and capture via ALSA + -) MONITOR: software monitoring + -) IMPULSE: optional impulse response measurement + *) is one of + -) PLAYBACK: audio output (from host to device) + -) CAPTURE: audio input (from device to host) + *) is one of + -) BUFFER: buffer required by PCM data stream + -) STREAM: actual PCM data stream + + The subsystems call line6_pcm_acquire() to acquire the (shared) + resources needed for a particular operation (e.g., allocate the buffer + for ALSA playback or start the capture stream for software monitoring). + When a resource is no longer needed, it is released by calling + line6_pcm_release(). Buffer allocation and stream startup are handled + separately to allow the ALSA kernel driver to perform them at + appropriate places (since the callback which starts a PCM stream is not + allowed to sleep). +*/ +enum { + /* individual bit indices: */ + LINE6_INDEX_PCM_ALSA_PLAYBACK_BUFFER, + LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, + LINE6_INDEX_PCM_ALSA_CAPTURE_BUFFER, + LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, + LINE6_INDEX_PCM_MONITOR_PLAYBACK_BUFFER, + LINE6_INDEX_PCM_MONITOR_PLAYBACK_STREAM, + LINE6_INDEX_PCM_MONITOR_CAPTURE_BUFFER, + LINE6_INDEX_PCM_MONITOR_CAPTURE_STREAM, +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + LINE6_INDEX_PCM_IMPULSE_PLAYBACK_BUFFER, + LINE6_INDEX_PCM_IMPULSE_PLAYBACK_STREAM, + LINE6_INDEX_PCM_IMPULSE_CAPTURE_BUFFER, + LINE6_INDEX_PCM_IMPULSE_CAPTURE_STREAM, +#endif + LINE6_INDEX_PAUSE_PLAYBACK, + LINE6_INDEX_PREPARED, + +#define LINE6_BIT(x) LINE6_BIT_ ## x = 1 << LINE6_INDEX_ ## x + + /* individual bit masks: */ + LINE6_BIT(PCM_ALSA_PLAYBACK_BUFFER), + LINE6_BIT(PCM_ALSA_PLAYBACK_STREAM), + LINE6_BIT(PCM_ALSA_CAPTURE_BUFFER), + LINE6_BIT(PCM_ALSA_CAPTURE_STREAM), + LINE6_BIT(PCM_MONITOR_PLAYBACK_BUFFER), + LINE6_BIT(PCM_MONITOR_PLAYBACK_STREAM), + LINE6_BIT(PCM_MONITOR_CAPTURE_BUFFER), + LINE6_BIT(PCM_MONITOR_CAPTURE_STREAM), +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + LINE6_BIT(PCM_IMPULSE_PLAYBACK_BUFFER), + LINE6_BIT(PCM_IMPULSE_PLAYBACK_STREAM), + LINE6_BIT(PCM_IMPULSE_CAPTURE_BUFFER), + LINE6_BIT(PCM_IMPULSE_CAPTURE_STREAM), +#endif + LINE6_BIT(PAUSE_PLAYBACK), + LINE6_BIT(PREPARED), + + /* combined bit masks (by operation): */ + LINE6_BITS_PCM_ALSA_BUFFER = + LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER | + LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER, + + LINE6_BITS_PCM_ALSA_STREAM = + LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM | + LINE6_BIT_PCM_ALSA_CAPTURE_STREAM, + + LINE6_BITS_PCM_MONITOR = + LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER | + LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM | + LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER | + LINE6_BIT_PCM_MONITOR_CAPTURE_STREAM, + +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + LINE6_BITS_PCM_IMPULSE = + LINE6_BIT_PCM_IMPULSE_PLAYBACK_BUFFER | + LINE6_BIT_PCM_IMPULSE_PLAYBACK_STREAM | + LINE6_BIT_PCM_IMPULSE_CAPTURE_BUFFER | + LINE6_BIT_PCM_IMPULSE_CAPTURE_STREAM, +#endif + + /* combined bit masks (by direction): */ + LINE6_BITS_PLAYBACK_BUFFER = +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + LINE6_BIT_PCM_IMPULSE_PLAYBACK_BUFFER | +#endif + LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER | + LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER, + + LINE6_BITS_PLAYBACK_STREAM = +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + LINE6_BIT_PCM_IMPULSE_PLAYBACK_STREAM | +#endif + LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM | + LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM, + + LINE6_BITS_CAPTURE_BUFFER = +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + LINE6_BIT_PCM_IMPULSE_CAPTURE_BUFFER | +#endif + LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER | + LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER, + + LINE6_BITS_CAPTURE_STREAM = +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + LINE6_BIT_PCM_IMPULSE_CAPTURE_STREAM | +#endif + LINE6_BIT_PCM_ALSA_CAPTURE_STREAM | + LINE6_BIT_PCM_MONITOR_CAPTURE_STREAM, + + LINE6_BITS_STREAM = + LINE6_BITS_PLAYBACK_STREAM | + LINE6_BITS_CAPTURE_STREAM +}; + +struct line6_pcm_properties { + struct snd_pcm_hardware snd_line6_playback_hw, snd_line6_capture_hw; + struct snd_pcm_hw_constraint_ratdens snd_line6_rates; + int bytes_per_frame; +}; + +struct snd_line6_pcm { + /** + Pointer back to the Line6 driver data structure. + */ + struct usb_line6 *line6; + + /** + Properties. + */ + struct line6_pcm_properties *properties; + + /** + ALSA pcm stream + */ + struct snd_pcm *pcm; + + /** + URBs for audio playback. + */ + struct urb *urb_audio_out[LINE6_ISO_BUFFERS]; + + /** + URBs for audio capture. + */ + struct urb *urb_audio_in[LINE6_ISO_BUFFERS]; + + /** + Temporary buffer for playback. + Since the packet size is not known in advance, this buffer is + large enough to store maximum size packets. + */ + unsigned char *buffer_out; + + /** + Temporary buffer for capture. + Since the packet size is not known in advance, this buffer is + large enough to store maximum size packets. + */ + unsigned char *buffer_in; + + /** + Previously captured frame (for software monitoring). + */ + unsigned char *prev_fbuf; + + /** + Size of previously captured frame (for software monitoring). + */ + int prev_fsize; + + /** + Free frame position in the playback buffer. + */ + snd_pcm_uframes_t pos_out; + + /** + Count processed bytes for playback. + This is modulo period size (to determine when a period is + finished). + */ + unsigned bytes_out; + + /** + Counter to create desired playback sample rate. + */ + unsigned count_out; + + /** + Playback period size in bytes + */ + unsigned period_out; + + /** + Processed frame position in the playback buffer. + The contents of the output ring buffer have been consumed by + the USB subsystem (i.e., sent to the USB device) up to this + position. + */ + snd_pcm_uframes_t pos_out_done; + + /** + Count processed bytes for capture. + This is modulo period size (to determine when a period is + finished). + */ + unsigned bytes_in; + + /** + Counter to create desired capture sample rate. + */ + unsigned count_in; + + /** + Capture period size in bytes + */ + unsigned period_in; + + /** + Processed frame position in the capture buffer. + The contents of the output ring buffer have been consumed by + the USB subsystem (i.e., sent to the USB device) up to this + position. + */ + snd_pcm_uframes_t pos_in_done; + + /** + Bit mask of active playback URBs. + */ + unsigned long active_urb_out; + + /** + Maximum size of USB packet. + */ + int max_packet_size; + + /** + Bit mask of active capture URBs. + */ + unsigned long active_urb_in; + + /** + Bit mask of playback URBs currently being unlinked. + */ + unsigned long unlink_urb_out; + + /** + Bit mask of capture URBs currently being unlinked. + */ + unsigned long unlink_urb_in; + + /** + Spin lock to protect updates of the playback buffer positions (not + contents!) + */ + spinlock_t lock_audio_out; + + /** + Spin lock to protect updates of the capture buffer positions (not + contents!) + */ + spinlock_t lock_audio_in; + + /** + Spin lock to protect trigger. + */ + spinlock_t lock_trigger; + + /** + PCM playback volume (left and right). + */ + int volume_playback[2]; + + /** + PCM monitor volume. + */ + int volume_monitor; + +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + /** + Volume of impulse response test signal (if zero, test is disabled). + */ + int impulse_volume; + + /** + Period of impulse response test signal. + */ + int impulse_period; + + /** + Counter for impulse response test signal. + */ + int impulse_count; +#endif + + /** + Several status bits (see LINE6_BIT_*). + */ + unsigned long flags; + + int last_frame_in, last_frame_out; +}; + +extern int line6_init_pcm(struct usb_line6 *line6, + struct line6_pcm_properties *properties); +extern int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd); +extern int snd_line6_prepare(struct snd_pcm_substream *substream); +extern void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm); +extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels); +extern int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels); + +#endif diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c new file mode 100644 index 0000000..da2e3b8 --- /dev/null +++ b/sound/usb/line6/playback.c @@ -0,0 +1,593 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include +#include +#include +#include + +#include "audio.h" +#include "capture.h" +#include "driver.h" +#include "pcm.h" +#include "pod.h" +#include "playback.h" + +/* + Software stereo volume control. +*/ +static void change_volume(struct urb *urb_out, int volume[], + int bytes_per_frame) +{ + int chn = 0; + + if (volume[0] == 256 && volume[1] == 256) + return; /* maximum volume - no change */ + + if (bytes_per_frame == 4) { + short *p, *buf_end; + + p = (short *)urb_out->transfer_buffer; + buf_end = p + urb_out->transfer_buffer_length / sizeof(*p); + + for (; p < buf_end; ++p) { + *p = (*p * volume[chn & 1]) >> 8; + ++chn; + } + } else if (bytes_per_frame == 6) { + unsigned char *p, *buf_end; + + p = (unsigned char *)urb_out->transfer_buffer; + buf_end = p + urb_out->transfer_buffer_length; + + for (; p < buf_end; p += 3) { + int val; + + val = p[0] + (p[1] << 8) + ((signed char)p[2] << 16); + val = (val * volume[chn & 1]) >> 8; + p[0] = val; + p[1] = val >> 8; + p[2] = val >> 16; + ++chn; + } + } +} + +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + +/* + Create signal for impulse response test. +*/ +static void create_impulse_test_signal(struct snd_line6_pcm *line6pcm, + struct urb *urb_out, int bytes_per_frame) +{ + int frames = urb_out->transfer_buffer_length / bytes_per_frame; + + if (bytes_per_frame == 4) { + int i; + short *pi = (short *)line6pcm->prev_fbuf; + short *po = (short *)urb_out->transfer_buffer; + + for (i = 0; i < frames; ++i) { + po[0] = pi[0]; + po[1] = 0; + pi += 2; + po += 2; + } + } else if (bytes_per_frame == 6) { + int i, j; + unsigned char *pi = line6pcm->prev_fbuf; + unsigned char *po = urb_out->transfer_buffer; + + for (i = 0; i < frames; ++i) { + for (j = 0; j < bytes_per_frame / 2; ++j) + po[j] = pi[j]; + + for (; j < bytes_per_frame; ++j) + po[j] = 0; + + pi += bytes_per_frame; + po += bytes_per_frame; + } + } + if (--line6pcm->impulse_count <= 0) { + ((unsigned char *)(urb_out->transfer_buffer))[bytes_per_frame - + 1] = + line6pcm->impulse_volume; + line6pcm->impulse_count = line6pcm->impulse_period; + } +} + +#endif + +/* + Add signal to buffer for software monitoring. +*/ +static void add_monitor_signal(struct urb *urb_out, unsigned char *signal, + int volume, int bytes_per_frame) +{ + if (volume == 0) + return; /* zero volume - no change */ + + if (bytes_per_frame == 4) { + short *pi, *po, *buf_end; + + pi = (short *)signal; + po = (short *)urb_out->transfer_buffer; + buf_end = po + urb_out->transfer_buffer_length / sizeof(*po); + + for (; po < buf_end; ++pi, ++po) + *po += (*pi * volume) >> 8; + } + + /* + We don't need to handle devices with 6 bytes per frame here + since they all support hardware monitoring. + */ +} + +/* + Find a free URB, prepare audio data, and submit URB. +*/ +static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) +{ + int index; + unsigned long flags; + int i, urb_size, urb_frames; + int ret; + const int bytes_per_frame = line6pcm->properties->bytes_per_frame; + const int frame_increment = + line6pcm->properties->snd_line6_rates.rats[0].num_min; + const int frame_factor = + line6pcm->properties->snd_line6_rates.rats[0].den * + (USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL); + struct urb *urb_out; + + spin_lock_irqsave(&line6pcm->lock_audio_out, flags); + index = + find_first_zero_bit(&line6pcm->active_urb_out, LINE6_ISO_BUFFERS); + + if (index < 0 || index >= LINE6_ISO_BUFFERS) { + spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); + dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); + return -EINVAL; + } + + urb_out = line6pcm->urb_audio_out[index]; + urb_size = 0; + + for (i = 0; i < LINE6_ISO_PACKETS; ++i) { + /* compute frame size for given sampling rate */ + int fsize = 0; + struct usb_iso_packet_descriptor *fout = + &urb_out->iso_frame_desc[i]; + + if (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM) + fsize = line6pcm->prev_fsize; + + if (fsize == 0) { + int n; + + line6pcm->count_out += frame_increment; + n = line6pcm->count_out / frame_factor; + line6pcm->count_out -= n * frame_factor; + fsize = n * bytes_per_frame; + } + + fout->offset = urb_size; + fout->length = fsize; + urb_size += fsize; + } + + if (urb_size == 0) { + /* can't determine URB size */ + spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); + dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n"); + return -EINVAL; + } + + urb_frames = urb_size / bytes_per_frame; + urb_out->transfer_buffer = + line6pcm->buffer_out + + index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; + urb_out->transfer_buffer_length = urb_size; + urb_out->context = line6pcm; + + if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags) && + !test_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags)) { + struct snd_pcm_runtime *runtime = + get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)->runtime; + + if (line6pcm->pos_out + urb_frames > runtime->buffer_size) { + /* + The transferred area goes over buffer boundary, + copy the data to the temp buffer. + */ + int len; + + len = runtime->buffer_size - line6pcm->pos_out; + + if (len > 0) { + memcpy(urb_out->transfer_buffer, + runtime->dma_area + + line6pcm->pos_out * bytes_per_frame, + len * bytes_per_frame); + memcpy(urb_out->transfer_buffer + + len * bytes_per_frame, runtime->dma_area, + (urb_frames - len) * bytes_per_frame); + } else + dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n", + len); + } else { + memcpy(urb_out->transfer_buffer, + runtime->dma_area + + line6pcm->pos_out * bytes_per_frame, + urb_out->transfer_buffer_length); + } + + line6pcm->pos_out += urb_frames; + if (line6pcm->pos_out >= runtime->buffer_size) + line6pcm->pos_out -= runtime->buffer_size; + } else { + memset(urb_out->transfer_buffer, 0, + urb_out->transfer_buffer_length); + } + + change_volume(urb_out, line6pcm->volume_playback, bytes_per_frame); + + if (line6pcm->prev_fbuf != NULL) { +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + if (line6pcm->flags & LINE6_BITS_PCM_IMPULSE) { + create_impulse_test_signal(line6pcm, urb_out, + bytes_per_frame); + if (line6pcm->flags & + LINE6_BIT_PCM_ALSA_CAPTURE_STREAM) { + line6_capture_copy(line6pcm, + urb_out->transfer_buffer, + urb_out-> + transfer_buffer_length); + line6_capture_check_period(line6pcm, + urb_out->transfer_buffer_length); + } + } else { +#endif + if (! + (line6pcm->line6-> + properties->capabilities & LINE6_CAP_HWMON) + && (line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM) + && (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM)) + add_monitor_signal(urb_out, line6pcm->prev_fbuf, + line6pcm->volume_monitor, + bytes_per_frame); +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + } +#endif + } + + ret = usb_submit_urb(urb_out, GFP_ATOMIC); + + if (ret == 0) + set_bit(index, &line6pcm->active_urb_out); + else + dev_err(line6pcm->line6->ifcdev, + "URB out #%d submission failed (%d)\n", index, ret); + + spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); + return 0; +} + +/* + Submit all currently available playback URBs. +*/ +int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm) +{ + int ret, i; + + for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { + ret = submit_audio_out_urb(line6pcm); + if (ret < 0) + return ret; + } + + return 0; +} + +/* + Unlink all currently active playback URBs. +*/ +void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm) +{ + unsigned int i; + + for (i = LINE6_ISO_BUFFERS; i--;) { + if (test_bit(i, &line6pcm->active_urb_out)) { + if (!test_and_set_bit(i, &line6pcm->unlink_urb_out)) { + struct urb *u = line6pcm->urb_audio_out[i]; + + usb_unlink_urb(u); + } + } + } +} + +/* + Wait until unlinking of all currently active playback URBs has been + finished. +*/ +void line6_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) +{ + int timeout = HZ; + unsigned int i; + int alive; + + do { + alive = 0; + for (i = LINE6_ISO_BUFFERS; i--;) { + if (test_bit(i, &line6pcm->active_urb_out)) + alive++; + } + if (!alive) + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (--timeout > 0); + if (alive) + snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); +} + +/* + Unlink all currently active playback URBs, and wait for finishing. +*/ +void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) +{ + line6_unlink_audio_out_urbs(line6pcm); + line6_wait_clear_audio_out_urbs(line6pcm); +} + +void line6_free_playback_buffer(struct snd_line6_pcm *line6pcm) +{ + kfree(line6pcm->buffer_out); + line6pcm->buffer_out = NULL; +} + +/* + Callback for completed playback URB. +*/ +static void audio_out_callback(struct urb *urb) +{ + int i, index, length = 0, shutdown = 0; + unsigned long flags; + struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context; + struct snd_pcm_substream *substream = + get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK); + +#if USE_CLEAR_BUFFER_WORKAROUND + memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); +#endif + + line6pcm->last_frame_out = urb->start_frame; + + /* find index of URB */ + for (index = LINE6_ISO_BUFFERS; index--;) + if (urb == line6pcm->urb_audio_out[index]) + break; + + if (index < 0) + return; /* URB has been unlinked asynchronously */ + + for (i = LINE6_ISO_PACKETS; i--;) + length += urb->iso_frame_desc[i].length; + + spin_lock_irqsave(&line6pcm->lock_audio_out, flags); + + if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags)) { + struct snd_pcm_runtime *runtime = substream->runtime; + + line6pcm->pos_out_done += + length / line6pcm->properties->bytes_per_frame; + + if (line6pcm->pos_out_done >= runtime->buffer_size) + line6pcm->pos_out_done -= runtime->buffer_size; + } + + clear_bit(index, &line6pcm->active_urb_out); + + for (i = LINE6_ISO_PACKETS; i--;) + if (urb->iso_frame_desc[i].status == -EXDEV) { + shutdown = 1; + break; + } + + if (test_and_clear_bit(index, &line6pcm->unlink_urb_out)) + shutdown = 1; + + spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); + + if (!shutdown) { + submit_audio_out_urb(line6pcm); + + if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, + &line6pcm->flags)) { + line6pcm->bytes_out += length; + if (line6pcm->bytes_out >= line6pcm->period_out) { + line6pcm->bytes_out %= line6pcm->period_out; + snd_pcm_period_elapsed(substream); + } + } + } +} + +/* open playback callback */ +static int snd_line6_playback_open(struct snd_pcm_substream *substream) +{ + int err; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); + + err = snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + (&line6pcm-> + properties->snd_line6_rates)); + if (err < 0) + return err; + + runtime->hw = line6pcm->properties->snd_line6_playback_hw; + return 0; +} + +/* close playback callback */ +static int snd_line6_playback_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +/* hw_params playback callback */ +static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + int ret; + struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); + + /* -- Florian Demski [FD] */ + /* don't ask me why, but this fixes the bug on my machine */ + if (line6pcm == NULL) { + if (substream->pcm == NULL) + return -ENOMEM; + if (substream->pcm->private_data == NULL) + return -ENOMEM; + substream->private_data = substream->pcm->private_data; + line6pcm = snd_pcm_substream_chip(substream); + } + /* -- [FD] end */ + + ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); + + if (ret < 0) + return ret; + + ret = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + if (ret < 0) { + line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); + return ret; + } + + line6pcm->period_out = params_period_bytes(hw_params); + return 0; +} + +/* hw_free playback callback */ +static int snd_line6_playback_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); + + line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); + return snd_pcm_lib_free_pages(substream); +} + +/* trigger playback callback */ +int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd) +{ + int err; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: +#ifdef CONFIG_PM + case SNDRV_PCM_TRIGGER_RESUME: +#endif + err = line6_pcm_acquire(line6pcm, + LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM); + + if (err < 0) + return err; + + break; + + case SNDRV_PCM_TRIGGER_STOP: +#ifdef CONFIG_PM + case SNDRV_PCM_TRIGGER_SUSPEND: +#endif + err = line6_pcm_release(line6pcm, + LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM); + + if (err < 0) + return err; + + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + set_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + clear_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* playback pointer callback */ +static snd_pcm_uframes_t +snd_line6_playback_pointer(struct snd_pcm_substream *substream) +{ + struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); + + return line6pcm->pos_out_done; +} + +/* playback operators */ +struct snd_pcm_ops snd_line6_playback_ops = { + .open = snd_line6_playback_open, + .close = snd_line6_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_line6_playback_hw_params, + .hw_free = snd_line6_playback_hw_free, + .prepare = snd_line6_prepare, + .trigger = snd_line6_trigger, + .pointer = snd_line6_playback_pointer, +}; + +int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm) +{ + struct usb_line6 *line6 = line6pcm->line6; + int i; + + /* create audio URBs and fill in constant values: */ + for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { + struct urb *urb; + + /* URB for audio out: */ + urb = line6pcm->urb_audio_out[i] = + usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); + + if (urb == NULL) { + dev_err(line6->ifcdev, "Out of memory\n"); + return -ENOMEM; + } + + urb->dev = line6->usbdev; + urb->pipe = + usb_sndisocpipe(line6->usbdev, + line6->properties->ep_audio_w & + USB_ENDPOINT_NUMBER_MASK); + urb->transfer_flags = URB_ISO_ASAP; + urb->start_frame = -1; + urb->number_of_packets = LINE6_ISO_PACKETS; + urb->interval = LINE6_ISO_INTERVAL; + urb->error_count = 0; + urb->complete = audio_out_callback; + } + + return 0; +} diff --git a/sound/usb/line6/playback.h b/sound/usb/line6/playback.h new file mode 100644 index 0000000..743bd6f --- /dev/null +++ b/sound/usb/line6/playback.h @@ -0,0 +1,41 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef PLAYBACK_H +#define PLAYBACK_H + +#include + +#include "driver.h" + +/* + * When the TonePort is used with jack in full duplex mode and the outputs are + * not connected, the software monitor produces an ugly noise since everything + * written to the output buffer (i.e., the input signal) will be repeated in + * the next period (sounds like a delay effect). As a workaround, the output + * buffer is cleared after the data have been read, but there must be a better + * solution. Until one is found, this workaround can be used to fix the + * problem. + */ +#define USE_CLEAR_BUFFER_WORKAROUND 1 + +extern struct snd_pcm_ops snd_line6_playback_ops; + +extern int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm); +extern void line6_free_playback_buffer(struct snd_line6_pcm *line6pcm); +extern int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm); +extern void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm); +extern void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm + *line6pcm); +extern void line6_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm); +extern int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd); + +#endif diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c new file mode 100644 index 0000000..85a4363 --- /dev/null +++ b/sound/usb/line6/pod.c @@ -0,0 +1,453 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include +#include +#include + +#include "audio.h" +#include "capture.h" +#include "driver.h" +#include "playback.h" +#include "pod.h" + +#define POD_SYSEX_CODE 3 +#define POD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */ + +/* *INDENT-OFF* */ + +enum { + POD_SYSEX_SAVE = 0x24, + POD_SYSEX_SYSTEM = 0x56, + POD_SYSEX_SYSTEMREQ = 0x57, + /* POD_SYSEX_UPDATE = 0x6c, */ /* software update! */ + POD_SYSEX_STORE = 0x71, + POD_SYSEX_FINISH = 0x72, + POD_SYSEX_DUMPMEM = 0x73, + POD_SYSEX_DUMP = 0x74, + POD_SYSEX_DUMPREQ = 0x75 + + /* dumps entire internal memory of PODxt Pro */ + /* POD_SYSEX_DUMPMEM2 = 0x76 */ +}; + +enum { + POD_MONITOR_LEVEL = 0x04, + POD_SYSTEM_INVALID = 0x10000 +}; + +/* *INDENT-ON* */ + +enum { + POD_DUMP_MEMORY = 2 +}; + +enum { + POD_BUSY_READ, + POD_BUSY_WRITE, + POD_CHANNEL_DIRTY, + POD_SAVE_PRESSED, + POD_BUSY_MIDISEND +}; + +static struct snd_ratden pod_ratden = { + .num_min = 78125, + .num_max = 78125, + .num_step = 1, + .den = 2 +}; + +static struct line6_pcm_properties pod_pcm_properties = { + .snd_line6_playback_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | +#ifdef CONFIG_PM + SNDRV_PCM_INFO_RESUME | +#endif + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .rates = SNDRV_PCM_RATE_KNOT, + .rate_min = 39062, + .rate_max = 39063, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024}, + .snd_line6_capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | +#ifdef CONFIG_PM + SNDRV_PCM_INFO_RESUME | +#endif + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .rates = SNDRV_PCM_RATE_KNOT, + .rate_min = 39062, + .rate_max = 39063, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024}, + .snd_line6_rates = { + .nrats = 1, + .rats = &pod_ratden}, + .bytes_per_frame = POD_BYTES_PER_FRAME +}; + +static const char pod_version_header[] = { + 0xf2, 0x7e, 0x7f, 0x06, 0x02 +}; + +/* forward declarations: */ +static void pod_startup2(unsigned long data); +static void pod_startup3(struct usb_line6_pod *pod); + +static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, + int size) +{ + return line6_alloc_sysex_buffer(&pod->line6, POD_SYSEX_CODE, code, + size); +} + +/* + Process a completely received message. +*/ +static void line6_pod_process_message(struct usb_line6 *line6) +{ + struct usb_line6_pod *pod = (struct usb_line6_pod *) line6; + const unsigned char *buf = pod->line6.buffer_message; + + if (memcmp(buf, pod_version_header, sizeof(pod_version_header)) == 0) { + pod->firmware_version = buf[13] * 100 + buf[14] * 10 + buf[15]; + pod->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) | + (int) buf[10]; + pod_startup3(pod); + return; + } + + /* Only look for sysex messages from this device */ + if (buf[0] != (LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE) && + buf[0] != (LINE6_SYSEX_BEGIN | LINE6_CHANNEL_UNKNOWN)) { + return; + } + if (memcmp(buf + 1, line6_midi_id, sizeof(line6_midi_id)) != 0) + return; + + if (buf[5] == POD_SYSEX_SYSTEM && buf[6] == POD_MONITOR_LEVEL) { + short value = ((int)buf[7] << 12) | ((int)buf[8] << 8) | + ((int)buf[9] << 4) | (int)buf[10]; + pod->monitor_level = value; + } +} + +/* + Send system parameter (from integer). +*/ +static int pod_set_system_param_int(struct usb_line6_pod *pod, int value, + int code) +{ + char *sysex; + static const int size = 5; + + sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEM, size); + if (!sysex) + return -ENOMEM; + sysex[SYSEX_DATA_OFS] = code; + sysex[SYSEX_DATA_OFS + 1] = (value >> 12) & 0x0f; + sysex[SYSEX_DATA_OFS + 2] = (value >> 8) & 0x0f; + sysex[SYSEX_DATA_OFS + 3] = (value >> 4) & 0x0f; + sysex[SYSEX_DATA_OFS + 4] = (value) & 0x0f; + line6_send_sysex_message(&pod->line6, sysex, size); + kfree(sysex); + return 0; +} + +/* + "read" request on "serial_number" special file. +*/ +static ssize_t serial_number_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *interface = to_usb_interface(dev); + struct usb_line6_pod *pod = usb_get_intfdata(interface); + + return sprintf(buf, "%d\n", pod->serial_number); +} + +/* + "read" request on "firmware_version" special file. +*/ +static ssize_t firmware_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *interface = to_usb_interface(dev); + struct usb_line6_pod *pod = usb_get_intfdata(interface); + + return sprintf(buf, "%d.%02d\n", pod->firmware_version / 100, + pod->firmware_version % 100); +} + +/* + "read" request on "device_id" special file. +*/ +static ssize_t device_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *interface = to_usb_interface(dev); + struct usb_line6_pod *pod = usb_get_intfdata(interface); + + return sprintf(buf, "%d\n", pod->device_id); +} + +/* + POD startup procedure. + This is a sequence of functions with special requirements (e.g., must + not run immediately after initialization, must not run in interrupt + context). After the last one has finished, the device is ready to use. +*/ + +static void pod_startup1(struct usb_line6_pod *pod) +{ + CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_INIT); + + /* delay startup procedure: */ + line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2, + (unsigned long)pod); +} + +static void pod_startup2(unsigned long data) +{ + struct usb_line6_pod *pod = (struct usb_line6_pod *)data; + struct usb_line6 *line6 = &pod->line6; + + CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_VERSIONREQ); + + /* request firmware version: */ + line6_version_request_async(line6); +} + +static void pod_startup3(struct usb_line6_pod *pod) +{ + CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_WORKQUEUE); + + /* schedule work for global work queue: */ + schedule_work(&pod->startup_work); +} + +static void pod_startup4(struct work_struct *work) +{ + struct usb_line6_pod *pod = + container_of(work, struct usb_line6_pod, startup_work); + struct usb_line6 *line6 = &pod->line6; + + CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_SETUP); + + /* serial number: */ + line6_read_serial_number(&pod->line6, &pod->serial_number); + + /* ALSA audio interface: */ + line6_register_audio(line6); +} + +/* POD special files: */ +static DEVICE_ATTR_RO(device_id); +static DEVICE_ATTR_RO(firmware_version); +static DEVICE_ATTR_RO(serial_number); + +/* control info callback */ +static int snd_pod_control_monitor_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 65535; + return 0; +} + +/* control get callback */ +static int snd_pod_control_monitor_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; + + ucontrol->value.integer.value[0] = pod->monitor_level; + return 0; +} + +/* control put callback */ +static int snd_pod_control_monitor_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; + + if (ucontrol->value.integer.value[0] == pod->monitor_level) + return 0; + + pod->monitor_level = ucontrol->value.integer.value[0]; + pod_set_system_param_int(pod, ucontrol->value.integer.value[0], + POD_MONITOR_LEVEL); + return 1; +} + +/* control definition */ +static struct snd_kcontrol_new pod_control_monitor = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Monitor Playback Volume", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_pod_control_monitor_info, + .get = snd_pod_control_monitor_get, + .put = snd_pod_control_monitor_put +}; + +/* + POD destructor. +*/ +static void pod_destruct(struct usb_interface *interface) +{ + struct usb_line6_pod *pod = usb_get_intfdata(interface); + + if (pod == NULL) + return; + line6_cleanup_audio(&pod->line6); + + del_timer(&pod->startup_timer); + cancel_work_sync(&pod->startup_work); +} + +/* + POD device disconnected. +*/ +static void line6_pod_disconnect(struct usb_interface *interface) +{ + struct usb_line6_pod *pod; + + if (interface == NULL) + return; + pod = usb_get_intfdata(interface); + + if (pod != NULL) { + struct snd_line6_pcm *line6pcm = pod->line6.line6pcm; + struct device *dev = &interface->dev; + + if (line6pcm != NULL) + line6_pcm_disconnect(line6pcm); + + if (dev != NULL) { + /* remove sysfs entries: */ + device_remove_file(dev, &dev_attr_device_id); + device_remove_file(dev, &dev_attr_firmware_version); + device_remove_file(dev, &dev_attr_serial_number); + } + } + + pod_destruct(interface); +} + +/* + Create sysfs entries. +*/ +static int pod_create_files2(struct device *dev) +{ + int err; + + CHECK_RETURN(device_create_file(dev, &dev_attr_device_id)); + CHECK_RETURN(device_create_file(dev, &dev_attr_firmware_version)); + CHECK_RETURN(device_create_file(dev, &dev_attr_serial_number)); + return 0; +} + +/* + Try to init POD device. +*/ +static int pod_try_init(struct usb_interface *interface, + struct usb_line6 *line6) +{ + int err; + struct usb_line6_pod *pod = (struct usb_line6_pod *) line6; + + line6->process_message = line6_pod_process_message; + line6->disconnect = line6_pod_disconnect; + + init_timer(&pod->startup_timer); + INIT_WORK(&pod->startup_work, pod_startup4); + + if ((interface == NULL) || (pod == NULL)) + return -ENODEV; + + /* create sysfs entries: */ + err = pod_create_files2(&interface->dev); + if (err < 0) + return err; + + /* initialize audio system: */ + err = line6_init_audio(line6); + if (err < 0) + return err; + + /* initialize MIDI subsystem: */ + err = line6_init_midi(line6); + if (err < 0) + return err; + + /* initialize PCM subsystem: */ + err = line6_init_pcm(line6, &pod_pcm_properties); + if (err < 0) + return err; + + /* register monitor control: */ + err = snd_ctl_add(line6->card, + snd_ctl_new1(&pod_control_monitor, line6->line6pcm)); + if (err < 0) + return err; + + /* + When the sound card is registered at this point, the PODxt Live + displays "Invalid Code Error 07", so we do it later in the event + handler. + */ + + if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) { + pod->monitor_level = POD_SYSTEM_INVALID; + + /* initiate startup procedure: */ + pod_startup1(pod); + } + + return 0; +} + +/* + Init POD device (and clean up in case of failure). +*/ +int line6_pod_init(struct usb_interface *interface, struct usb_line6 *line6) +{ + int err = pod_try_init(interface, line6); + + if (err < 0) + pod_destruct(interface); + + return err; +} diff --git a/sound/usb/line6/pod.h b/sound/usb/line6/pod.h new file mode 100644 index 0000000..87a8f0f --- /dev/null +++ b/sound/usb/line6/pod.h @@ -0,0 +1,92 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef POD_H +#define POD_H + +#include +#include +#include + +#include + +#include "driver.h" + +/* + Locate name in binary program dump +*/ +#define POD_NAME_OFFSET 0 +#define POD_NAME_LENGTH 16 + +/* + Other constants +*/ +#define POD_CONTROL_SIZE 0x80 +#define POD_BUFSIZE_DUMPREQ 7 +#define POD_STARTUP_DELAY 1000 + +/* + Stages of POD startup procedure +*/ +enum { + POD_STARTUP_INIT = 1, + POD_STARTUP_VERSIONREQ, + POD_STARTUP_WORKQUEUE, + POD_STARTUP_SETUP, + POD_STARTUP_LAST = POD_STARTUP_SETUP - 1 +}; + +struct usb_line6_pod { + /** + Generic Line6 USB data. + */ + struct usb_line6 line6; + + /** + Instrument monitor level. + */ + int monitor_level; + + /** + Timer for device initializaton. + */ + struct timer_list startup_timer; + + /** + Work handler for device initializaton. + */ + struct work_struct startup_work; + + /** + Current progress in startup procedure. + */ + int startup_progress; + + /** + Serial number of device. + */ + int serial_number; + + /** + Firmware version (x 100). + */ + int firmware_version; + + /** + Device ID. + */ + int device_id; +}; + +extern int line6_pod_init(struct usb_interface *interface, + struct usb_line6 *line6); + +#endif diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c new file mode 100644 index 0000000..27c5402 --- /dev/null +++ b/sound/usb/line6/podhd.c @@ -0,0 +1,156 @@ +/* + * Line6 Pod HD + * + * Copyright (C) 2011 Stefan Hajnoczi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include +#include + +#include "audio.h" +#include "driver.h" +#include "pcm.h" +#include "podhd.h" + +#define PODHD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */ + +static struct snd_ratden podhd_ratden = { + .num_min = 48000, + .num_max = 48000, + .num_step = 1, + .den = 1, +}; + +static struct line6_pcm_properties podhd_pcm_properties = { + .snd_line6_playback_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | +#ifdef CONFIG_PM + SNDRV_PCM_INFO_RESUME | +#endif + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024}, + .snd_line6_capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | +#ifdef CONFIG_PM + SNDRV_PCM_INFO_RESUME | +#endif + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024}, + .snd_line6_rates = { + .nrats = 1, + .rats = &podhd_ratden}, + .bytes_per_frame = PODHD_BYTES_PER_FRAME +}; + +/* + POD HD destructor. +*/ +static void podhd_destruct(struct usb_interface *interface) +{ + struct usb_line6_podhd *podhd = usb_get_intfdata(interface); + + if (podhd == NULL) + return; + line6_cleanup_audio(&podhd->line6); +} + +/* + POD HD device disconnected. +*/ +static void line6_podhd_disconnect(struct usb_interface *interface) +{ + struct usb_line6_podhd *podhd; + + if (interface == NULL) + return; + podhd = usb_get_intfdata(interface); + + if (podhd != NULL) { + struct snd_line6_pcm *line6pcm = podhd->line6.line6pcm; + + if (line6pcm != NULL) + line6_pcm_disconnect(line6pcm); + } + + podhd_destruct(interface); +} + +/* + Try to init POD HD device. +*/ +static int podhd_try_init(struct usb_interface *interface, + struct usb_line6_podhd *podhd) +{ + int err; + struct usb_line6 *line6 = &podhd->line6; + + if ((interface == NULL) || (podhd == NULL)) + return -ENODEV; + + line6->disconnect = line6_podhd_disconnect; + + /* initialize audio system: */ + err = line6_init_audio(line6); + if (err < 0) + return err; + + /* initialize MIDI subsystem: */ + err = line6_init_midi(line6); + if (err < 0) + return err; + + /* initialize PCM subsystem: */ + err = line6_init_pcm(line6, &podhd_pcm_properties); + if (err < 0) + return err; + + /* register USB audio system: */ + err = line6_register_audio(line6); + return err; +} + +/* + Init POD HD device (and clean up in case of failure). +*/ +int line6_podhd_init(struct usb_interface *interface, struct usb_line6 *line6) +{ + struct usb_line6_podhd *podhd = (struct usb_line6_podhd *) line6; + int err = podhd_try_init(interface, podhd); + + if (err < 0) + podhd_destruct(interface); + + return err; +} diff --git a/sound/usb/line6/podhd.h b/sound/usb/line6/podhd.h new file mode 100644 index 0000000..a14f711 --- /dev/null +++ b/sound/usb/line6/podhd.h @@ -0,0 +1,29 @@ +/* + * Line6 Pod HD + * + * Copyright (C) 2011 Stefan Hajnoczi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef PODHD_H +#define PODHD_H + +#include + +#include "driver.h" + +struct usb_line6_podhd { + /** + Generic Line6 USB data. + */ + struct usb_line6 line6; +}; + +extern int line6_podhd_init(struct usb_interface *interface, + struct usb_line6 *line6); + +#endif /* PODHD_H */ diff --git a/sound/usb/line6/revision.h b/sound/usb/line6/revision.h new file mode 100644 index 0000000..b4eee2b --- /dev/null +++ b/sound/usb/line6/revision.h @@ -0,0 +1,4 @@ +#ifndef DRIVER_REVISION +/* current subversion revision */ +#define DRIVER_REVISION " (904)" +#endif diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c new file mode 100644 index 0000000..aae78d8 --- /dev/null +++ b/sound/usb/line6/toneport.c @@ -0,0 +1,465 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * Emil Myhrman (emil.myhrman@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, version 2. + * + */ + +#include +#include + +#include "audio.h" +#include "capture.h" +#include "driver.h" +#include "playback.h" +#include "toneport.h" + +static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2); + +#define TONEPORT_PCM_DELAY 1 + +static struct snd_ratden toneport_ratden = { + .num_min = 44100, + .num_max = 44100, + .num_step = 1, + .den = 1 +}; + +static struct line6_pcm_properties toneport_pcm_properties = { + .snd_line6_playback_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | +#ifdef CONFIG_PM + SNDRV_PCM_INFO_RESUME | +#endif + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_KNOT, + .rate_min = 44100, + .rate_max = 44100, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024}, + .snd_line6_capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | +#ifdef CONFIG_PM + SNDRV_PCM_INFO_RESUME | +#endif + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_KNOT, + .rate_min = 44100, + .rate_max = 44100, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024}, + .snd_line6_rates = { + .nrats = 1, + .rats = &toneport_ratden}, + .bytes_per_frame = 4 +}; + +/* + For the led on Guitarport. + Brightness goes from 0x00 to 0x26. Set a value above this to have led + blink. + (void cmd_0x02(byte red, byte green) +*/ +static int led_red = 0x00; +static int led_green = 0x26; + +static const struct { + const char *name; + int code; +} toneport_source_info[] = { + {"Microphone", 0x0a01}, + {"Line", 0x0801}, + {"Instrument", 0x0b01}, + {"Inst & Mic", 0x0901} +}; + +static bool toneport_has_led(enum line6_device_type type) +{ + return + (type == LINE6_GUITARPORT) || + (type == LINE6_TONEPORT_GX); + /* add your device here if you are missing support for the LEDs */ +} + +static void toneport_update_led(struct device *dev) +{ + struct usb_interface *interface = to_usb_interface(dev); + struct usb_line6_toneport *tp = usb_get_intfdata(interface); + struct usb_line6 *line6; + + if (!tp) + return; + + line6 = &tp->line6; + if (line6) + toneport_send_cmd(line6->usbdev, (led_red << 8) | 0x0002, + led_green); +} + +static ssize_t toneport_set_led_red(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int retval; + + retval = kstrtoint(buf, 10, &led_red); + if (retval) + return retval; + + toneport_update_led(dev); + return count; +} + +static ssize_t toneport_set_led_green(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int retval; + + retval = kstrtoint(buf, 10, &led_green); + if (retval) + return retval; + + toneport_update_led(dev); + return count; +} + +static DEVICE_ATTR(led_red, S_IWUSR | S_IRUGO, line6_nop_read, + toneport_set_led_red); +static DEVICE_ATTR(led_green, S_IWUSR | S_IRUGO, line6_nop_read, + toneport_set_led_green); + +static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2) +{ + int ret; + + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + cmd1, cmd2, NULL, 0, LINE6_TIMEOUT * HZ); + + if (ret < 0) { + dev_err(&usbdev->dev, "send failed (error %d)\n", ret); + return ret; + } + + return 0; +} + +/* monitor info callback */ +static int snd_toneport_monitor_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 256; + return 0; +} + +/* monitor get callback */ +static int snd_toneport_monitor_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = line6pcm->volume_monitor; + return 0; +} + +/* monitor put callback */ +static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + + if (ucontrol->value.integer.value[0] == line6pcm->volume_monitor) + return 0; + + line6pcm->volume_monitor = ucontrol->value.integer.value[0]; + + if (line6pcm->volume_monitor > 0) + line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_MONITOR); + else + line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); + + return 1; +} + +/* source info callback */ +static int snd_toneport_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + const int size = ARRAY_SIZE(toneport_source_info); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = size; + + if (uinfo->value.enumerated.item >= size) + uinfo->value.enumerated.item = size - 1; + + strcpy(uinfo->value.enumerated.name, + toneport_source_info[uinfo->value.enumerated.item].name); + + return 0; +} + +/* source get callback */ +static int snd_toneport_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + struct usb_line6_toneport *toneport = + (struct usb_line6_toneport *)line6pcm->line6; + ucontrol->value.enumerated.item[0] = toneport->source; + return 0; +} + +/* source put callback */ +static int snd_toneport_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + struct usb_line6_toneport *toneport = + (struct usb_line6_toneport *)line6pcm->line6; + unsigned int source; + + source = ucontrol->value.enumerated.item[0]; + if (source >= ARRAY_SIZE(toneport_source_info)) + return -EINVAL; + if (source == toneport->source) + return 0; + + toneport->source = source; + toneport_send_cmd(toneport->line6.usbdev, + toneport_source_info[source].code, 0x0000); + return 1; +} + +static void toneport_start_pcm(unsigned long arg) +{ + struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg; + struct usb_line6 *line6 = &toneport->line6; + + line6_pcm_acquire(line6->line6pcm, LINE6_BITS_PCM_MONITOR); +} + +/* control definition */ +static struct snd_kcontrol_new toneport_control_monitor = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Monitor Playback Volume", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_toneport_monitor_info, + .get = snd_toneport_monitor_get, + .put = snd_toneport_monitor_put +}; + +/* source selector definition */ +static struct snd_kcontrol_new toneport_control_source = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Capture Source", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_toneport_source_info, + .get = snd_toneport_source_get, + .put = snd_toneport_source_put +}; + +/* + Toneport destructor. +*/ +static void toneport_destruct(struct usb_interface *interface) +{ + struct usb_line6_toneport *toneport = usb_get_intfdata(interface); + + if (toneport == NULL) + return; + line6_cleanup_audio(&toneport->line6); +} + +/* + Setup Toneport device. +*/ +static void toneport_setup(struct usb_line6_toneport *toneport) +{ + int ticks; + struct usb_line6 *line6 = &toneport->line6; + struct usb_device *usbdev = line6->usbdev; + + /* sync time on device with host: */ + ticks = (int)get_seconds(); + line6_write_data(line6, 0x80c6, &ticks, 4); + + /* enable device: */ + toneport_send_cmd(usbdev, 0x0301, 0x0000); + + /* initialize source select: */ + switch (line6->type) { + case LINE6_TONEPORT_UX1: + case LINE6_TONEPORT_UX2: + case LINE6_PODSTUDIO_UX1: + case LINE6_PODSTUDIO_UX2: + toneport_send_cmd(usbdev, + toneport_source_info[toneport->source].code, + 0x0000); + default: + break; + } + + if (toneport_has_led(line6->type)) + toneport_update_led(&usbdev->dev); +} + +/* + Toneport device disconnected. +*/ +static void line6_toneport_disconnect(struct usb_interface *interface) +{ + struct usb_line6_toneport *toneport; + u16 idProduct; + + if (interface == NULL) + return; + + toneport = usb_get_intfdata(interface); + del_timer_sync(&toneport->timer); + idProduct = le16_to_cpu(toneport->line6.usbdev->descriptor.idProduct); + + if (toneport_has_led(idProduct)) { + device_remove_file(&interface->dev, &dev_attr_led_red); + device_remove_file(&interface->dev, &dev_attr_led_green); + } + + if (toneport != NULL) { + struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm; + + if (line6pcm != NULL) { + line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); + line6_pcm_disconnect(line6pcm); + } + } + + toneport_destruct(interface); +} + + +/* + Try to init Toneport device. +*/ +static int toneport_try_init(struct usb_interface *interface, + struct usb_line6 *line6) +{ + int err; + struct usb_line6_toneport *toneport = (struct usb_line6_toneport *) line6; + + if ((interface == NULL) || (toneport == NULL)) + return -ENODEV; + + line6->disconnect = line6_toneport_disconnect; + + /* initialize audio system: */ + err = line6_init_audio(line6); + if (err < 0) + return err; + + /* initialize PCM subsystem: */ + err = line6_init_pcm(line6, &toneport_pcm_properties); + if (err < 0) + return err; + + /* register monitor control: */ + err = snd_ctl_add(line6->card, + snd_ctl_new1(&toneport_control_monitor, + line6->line6pcm)); + if (err < 0) + return err; + + /* register source select control: */ + switch (line6->type) { + case LINE6_TONEPORT_UX1: + case LINE6_TONEPORT_UX2: + case LINE6_PODSTUDIO_UX1: + case LINE6_PODSTUDIO_UX2: + err = + snd_ctl_add(line6->card, + snd_ctl_new1(&toneport_control_source, + line6->line6pcm)); + if (err < 0) + return err; + + default: + break; + } + + /* register audio system: */ + err = line6_register_audio(line6); + if (err < 0) + return err; + + line6_read_serial_number(line6, &toneport->serial_number); + line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1); + + if (toneport_has_led(line6->type)) { + CHECK_RETURN(device_create_file + (&interface->dev, &dev_attr_led_red)); + CHECK_RETURN(device_create_file + (&interface->dev, &dev_attr_led_green)); + } + + toneport_setup(toneport); + + init_timer(&toneport->timer); + toneport->timer.expires = jiffies + TONEPORT_PCM_DELAY * HZ; + toneport->timer.function = toneport_start_pcm; + toneport->timer.data = (unsigned long)toneport; + add_timer(&toneport->timer); + + return 0; +} + +/* + Init Toneport device (and clean up in case of failure). +*/ +int line6_toneport_init(struct usb_interface *interface, + struct usb_line6 *line6) +{ + int err = toneport_try_init(interface, line6); + + if (err < 0) + toneport_destruct(interface); + + return err; +} + +/* + Resume Toneport device after reset. +*/ +void line6_toneport_reset_resume(struct usb_line6_toneport *toneport) +{ + toneport_setup(toneport); +} diff --git a/sound/usb/line6/toneport.h b/sound/usb/line6/toneport.h new file mode 100644 index 0000000..8cb1442 --- /dev/null +++ b/sound/usb/line6/toneport.h @@ -0,0 +1,51 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef TONEPORT_H +#define TONEPORT_H + +#include +#include + +#include "driver.h" + +struct usb_line6_toneport { + /** + Generic Line6 USB data. + */ + struct usb_line6 line6; + + /** + Source selector. + */ + int source; + + /** + Serial number of device. + */ + int serial_number; + + /** + Firmware version (x 100). + */ + int firmware_version; + + /** + Timer for delayed PCM startup. + */ + struct timer_list timer; +}; + +extern int line6_toneport_init(struct usb_interface *interface, + struct usb_line6 *line6); +extern void line6_toneport_reset_resume(struct usb_line6_toneport *toneport); + +#endif diff --git a/sound/usb/line6/usbdefs.h b/sound/usb/line6/usbdefs.h new file mode 100644 index 0000000..f4d080e --- /dev/null +++ b/sound/usb/line6/usbdefs.h @@ -0,0 +1,27 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2005-2008 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef USBDEFS_H +#define USBDEFS_H + +#define USB_INTERVALS_PER_SECOND 1000 + +/* device supports settings parameter via USB */ +#define LINE6_CAP_CONTROL (1 << 0) +/* device supports PCM input/output via USB */ +#define LINE6_CAP_PCM (1 << 1) +/* device support hardware monitoring */ +#define LINE6_CAP_HWMON (1 << 2) + +#define LINE6_FALLBACK_INTERVAL 10 +#define LINE6_FALLBACK_MAXPACKETSIZE 16 + +#endif diff --git a/sound/usb/line6/variax.c b/sound/usb/line6/variax.c new file mode 100644 index 0000000..b4a41b0 --- /dev/null +++ b/sound/usb/line6/variax.c @@ -0,0 +1,239 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include + +#include "audio.h" +#include "driver.h" +#include "variax.h" + +#define VARIAX_OFFSET_ACTIVATE 7 + +/* + This message is sent by the device during initialization and identifies + the connected guitar version. +*/ +static const char variax_init_version[] = { + 0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c, + 0x07, 0x00, 0x00, 0x00 +}; + +/* + This message is the last one sent by the device during initialization. +*/ +static const char variax_init_done[] = { + 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b +}; + +static const char variax_activate[] = { + 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01, + 0xf7 +}; + +/* forward declarations: */ +static void variax_startup2(unsigned long data); +static void variax_startup4(unsigned long data); +static void variax_startup5(unsigned long data); + +static void variax_activate_async(struct usb_line6_variax *variax, int a) +{ + variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a; + line6_send_raw_message_async(&variax->line6, variax->buffer_activate, + sizeof(variax_activate)); +} + +/* + Variax startup procedure. + This is a sequence of functions with special requirements (e.g., must + not run immediately after initialization, must not run in interrupt + context). After the last one has finished, the device is ready to use. +*/ + +static void variax_startup1(struct usb_line6_variax *variax) +{ + CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT); + + /* delay startup procedure: */ + line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, + variax_startup2, (unsigned long)variax); +} + +static void variax_startup2(unsigned long data) +{ + struct usb_line6_variax *variax = (struct usb_line6_variax *)data; + struct usb_line6 *line6 = &variax->line6; + + /* schedule another startup procedure until startup is complete: */ + if (variax->startup_progress >= VARIAX_STARTUP_LAST) + return; + + variax->startup_progress = VARIAX_STARTUP_VERSIONREQ; + line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, + variax_startup2, (unsigned long)variax); + + /* request firmware version: */ + line6_version_request_async(line6); +} + +static void variax_startup3(struct usb_line6_variax *variax) +{ + CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT); + + /* delay startup procedure: */ + line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3, + variax_startup4, (unsigned long)variax); +} + +static void variax_startup4(unsigned long data) +{ + struct usb_line6_variax *variax = (struct usb_line6_variax *)data; + + CHECK_STARTUP_PROGRESS(variax->startup_progress, + VARIAX_STARTUP_ACTIVATE); + + /* activate device: */ + variax_activate_async(variax, 1); + line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4, + variax_startup5, (unsigned long)variax); +} + +static void variax_startup5(unsigned long data) +{ + struct usb_line6_variax *variax = (struct usb_line6_variax *)data; + + CHECK_STARTUP_PROGRESS(variax->startup_progress, + VARIAX_STARTUP_WORKQUEUE); + + /* schedule work for global work queue: */ + schedule_work(&variax->startup_work); +} + +static void variax_startup6(struct work_struct *work) +{ + struct usb_line6_variax *variax = + container_of(work, struct usb_line6_variax, startup_work); + + CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP); + + /* ALSA audio interface: */ + line6_register_audio(&variax->line6); +} + +/* + Process a completely received message. +*/ +static void line6_variax_process_message(struct usb_line6 *line6) +{ + struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; + const unsigned char *buf = variax->line6.buffer_message; + + switch (buf[0]) { + case LINE6_RESET: + dev_info(variax->line6.ifcdev, "VARIAX reset\n"); + break; + + case LINE6_SYSEX_BEGIN: + if (memcmp(buf + 1, variax_init_version + 1, + sizeof(variax_init_version) - 1) == 0) { + variax_startup3(variax); + } else if (memcmp(buf + 1, variax_init_done + 1, + sizeof(variax_init_done) - 1) == 0) { + /* notify of complete initialization: */ + variax_startup4((unsigned long)variax); + } + break; + } +} + +/* + Variax destructor. +*/ +static void variax_destruct(struct usb_interface *interface) +{ + struct usb_line6_variax *variax = usb_get_intfdata(interface); + + if (variax == NULL) + return; + line6_cleanup_audio(&variax->line6); + + del_timer(&variax->startup_timer1); + del_timer(&variax->startup_timer2); + cancel_work_sync(&variax->startup_work); + + kfree(variax->buffer_activate); +} + +/* + Workbench device disconnected. +*/ +static void line6_variax_disconnect(struct usb_interface *interface) +{ + if (interface == NULL) + return; + + variax_destruct(interface); +} + +/* + Try to init workbench device. +*/ +static int variax_try_init(struct usb_interface *interface, + struct usb_line6 *line6) +{ + struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; + int err; + + line6->process_message = line6_variax_process_message; + line6->disconnect = line6_variax_disconnect; + + init_timer(&variax->startup_timer1); + init_timer(&variax->startup_timer2); + INIT_WORK(&variax->startup_work, variax_startup6); + + if ((interface == NULL) || (variax == NULL)) + return -ENODEV; + + /* initialize USB buffers: */ + variax->buffer_activate = kmemdup(variax_activate, + sizeof(variax_activate), GFP_KERNEL); + + if (variax->buffer_activate == NULL) { + dev_err(&interface->dev, "Out of memory\n"); + return -ENOMEM; + } + + /* initialize audio system: */ + err = line6_init_audio(&variax->line6); + if (err < 0) + return err; + + /* initialize MIDI subsystem: */ + err = line6_init_midi(&variax->line6); + if (err < 0) + return err; + + /* initiate startup procedure: */ + variax_startup1(variax); + return 0; +} + +/* + Init workbench device (and clean up in case of failure). +*/ +int line6_variax_init(struct usb_interface *interface, struct usb_line6 *line6) +{ + int err = variax_try_init(interface, line6); + + if (err < 0) + variax_destruct(interface); + + return err; +} diff --git a/sound/usb/line6/variax.h b/sound/usb/line6/variax.h new file mode 100644 index 0000000..dfb94e5 --- /dev/null +++ b/sound/usb/line6/variax.h @@ -0,0 +1,70 @@ +/* + * Line6 Linux USB driver - 0.9.1beta + * + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef VARIAX_H +#define VARIAX_H + +#include +#include +#include +#include + +#include "driver.h" + +#define VARIAX_STARTUP_DELAY1 1000 +#define VARIAX_STARTUP_DELAY3 100 +#define VARIAX_STARTUP_DELAY4 100 + +/* + Stages of Variax startup procedure +*/ +enum { + VARIAX_STARTUP_INIT = 1, + VARIAX_STARTUP_VERSIONREQ, + VARIAX_STARTUP_WAIT, + VARIAX_STARTUP_ACTIVATE, + VARIAX_STARTUP_WORKQUEUE, + VARIAX_STARTUP_SETUP, + VARIAX_STARTUP_LAST = VARIAX_STARTUP_SETUP - 1 +}; + +struct usb_line6_variax { + /** + Generic Line6 USB data. + */ + struct usb_line6 line6; + + /** + Buffer for activation code. + */ + unsigned char *buffer_activate; + + /** + Handler for device initializaton. + */ + struct work_struct startup_work; + + /** + Timers for device initializaton. + */ + struct timer_list startup_timer1; + struct timer_list startup_timer2; + + /** + Current progress in startup procedure. + */ + int startup_progress; +}; + +extern int line6_variax_init(struct usb_interface *interface, + struct usb_line6 *line6); + +#endif -- cgit v0.10.2 From 7bfa014500ceffa8b787ffc3a6e00252443631d0 Mon Sep 17 00:00:00 2001 From: Jonathan Toppins Date: Fri, 9 Jan 2015 13:31:08 -0500 Subject: bonding: cleanup bond_opts array Remove the empty array element initializer and size the array with BOND_OPT_LAST so the compiler will complain if more elements are in there than should be. An interesting unwanted side effect of this initializer is that if one inserts new options into the middle of the array then this initializer will zero out the option that equals BOND_OPT_TLB_DYNAMIC_LB+1. Example: Extend the OPTS enum: enum { ... BOND_OPT_TLB_DYNAMIC_LB, BOND_OPT_LACP_NEW1, BOND_OPT_LAST }; Now insert into bond_opts array: static const struct bond_option bond_opts[] = { ... [BOND_OPT_LACP_RATE] = { .... unchanged stuff .... }, [BOND_OPT_LACP_NEW1] = { ... new stuff ... }, ... [BOND_OPT_TLB_DYNAMIC_LB] = { .... unchanged stuff ....}, { } // MARK A }; Since BOND_OPT_LACP_NEW1 = BOND_OPT_TLB_DYNAMIC_LB+1, the last initializer (MARK A) will overwrite the contents of BOND_OPT_LACP_NEW1 and can be easily viewed with the crash utility. Signed-off-by: Jonathan Toppins Cc: Andy Gospodarek Cc: Nikolay Aleksandrov Signed-off-by: Andy Gospodarek Acked-by: Nikolay Aleksandrov Signed-off-by: David S. Miller diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index 1a61cc9..9bd538d4 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -186,7 +186,7 @@ static const struct bond_opt_value bond_tlb_dynamic_lb_tbl[] = { { NULL, -1, 0} }; -static const struct bond_option bond_opts[] = { +static const struct bond_option bond_opts[BOND_OPT_LAST] = { [BOND_OPT_MODE] = { .id = BOND_OPT_MODE, .name = "mode", @@ -379,8 +379,7 @@ static const struct bond_option bond_opts[] = { .values = bond_tlb_dynamic_lb_tbl, .flags = BOND_OPTFLAG_IFDOWN, .set = bond_option_tlb_dynamic_lb_set, - }, - { } + } }; /* Searches for an option by name */ -- cgit v0.10.2 From 5ec82c1e4c86cf2fa115a2ae6d3576a100b47c42 Mon Sep 17 00:00:00 2001 From: Shrikrishna Khare Date: Fri, 9 Jan 2015 15:19:14 -0800 Subject: Driver: Vmxnet3: Reinitialize vmxnet3 backend on wakeup from hibernate Failing to reinitialize on wakeup results in loss of network connectivity for vmxnet3 interface. Signed-off-by: Srividya Murali Signed-off-by: Shrikrishna Khare Reviewed-by: Shreyas N Bhatewara Signed-off-by: David S. Miller diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index 7af1f5c..3143981 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -3290,27 +3290,15 @@ skip_arp: static int vmxnet3_resume(struct device *device) { - int err, i = 0; + int err; unsigned long flags; struct pci_dev *pdev = to_pci_dev(device); struct net_device *netdev = pci_get_drvdata(pdev); struct vmxnet3_adapter *adapter = netdev_priv(netdev); - struct Vmxnet3_PMConf *pmConf; if (!netif_running(netdev)) return 0; - /* Destroy wake-up filters. */ - pmConf = adapter->pm_conf; - memset(pmConf, 0, sizeof(*pmConf)); - - adapter->shared->devRead.pmConfDesc.confVer = cpu_to_le32(1); - adapter->shared->devRead.pmConfDesc.confLen = cpu_to_le32(sizeof( - *pmConf)); - adapter->shared->devRead.pmConfDesc.confPA = - cpu_to_le64(adapter->pm_conf_pa); - - netif_device_attach(netdev); pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); err = pci_enable_device_mem(pdev); @@ -3319,15 +3307,31 @@ vmxnet3_resume(struct device *device) pci_enable_wake(pdev, PCI_D0, 0); + vmxnet3_alloc_intr_resources(adapter); + + /* During hibernate and suspend, device has to be reinitialized as the + * device state need not be preserved. + */ + + /* Need not check adapter state as other reset tasks cannot run during + * device resume. + */ spin_lock_irqsave(&adapter->cmd_lock, flags); VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, - VMXNET3_CMD_UPDATE_PMCFG); + VMXNET3_CMD_QUIESCE_DEV); spin_unlock_irqrestore(&adapter->cmd_lock, flags); - vmxnet3_alloc_intr_resources(adapter); - vmxnet3_request_irqs(adapter); - for (i = 0; i < adapter->num_rx_queues; i++) - napi_enable(&adapter->rx_queue[i].napi); - vmxnet3_enable_all_intrs(adapter); + vmxnet3_tq_cleanup_all(adapter); + vmxnet3_rq_cleanup_all(adapter); + + vmxnet3_reset_dev(adapter); + err = vmxnet3_activate_dev(adapter); + if (err != 0) { + netdev_err(netdev, + "failed to re-activate on resume, error: %d", err); + vmxnet3_force_close(adapter); + return err; + } + netif_device_attach(netdev); return 0; } @@ -3335,6 +3339,8 @@ vmxnet3_resume(struct device *device) static const struct dev_pm_ops vmxnet3_pm_ops = { .suspend = vmxnet3_suspend, .resume = vmxnet3_resume, + .freeze = vmxnet3_suspend, + .restore = vmxnet3_resume, }; #endif diff --git a/drivers/net/vmxnet3/vmxnet3_int.h b/drivers/net/vmxnet3/vmxnet3_int.h index 048f020..6297d9f 100644 --- a/drivers/net/vmxnet3/vmxnet3_int.h +++ b/drivers/net/vmxnet3/vmxnet3_int.h @@ -69,10 +69,10 @@ /* * Version numbers */ -#define VMXNET3_DRIVER_VERSION_STRING "1.3.1.0-k" +#define VMXNET3_DRIVER_VERSION_STRING "1.3.2.0-k" /* a 32-bit int, each byte encode a verion number in VMXNET3_DRIVER_VERSION */ -#define VMXNET3_DRIVER_VERSION_NUM 0x01030100 +#define VMXNET3_DRIVER_VERSION_NUM 0x01030200 #if defined(CONFIG_PCI_MSI) /* RSS only makes sense if MSI-X is supported. */ -- cgit v0.10.2 From dd2e8bf586a69b824e50d50a5bbc16ed38f50986 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sat, 10 Jan 2015 10:20:25 +0100 Subject: drivers: net: xen-netfront: remove residual dead code This patch removes some unused arrays from the netfront private data structures. These arrays were used in "flip" receive mode. Signed-off-by: Vincenzo Maffione Reviewed-by: David Vrabel Signed-off-by: David S. Miller diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 22bcb4e..a4e5048 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -144,10 +144,6 @@ struct netfront_queue { struct sk_buff *rx_skbs[NET_RX_RING_SIZE]; grant_ref_t gref_rx_head; grant_ref_t grant_rx_ref[NET_RX_RING_SIZE]; - - unsigned long rx_pfn_array[NET_RX_RING_SIZE]; - struct multicall_entry rx_mcl[NET_RX_RING_SIZE+1]; - struct mmu_update rx_mmu[NET_RX_RING_SIZE]; }; struct netfront_info { -- cgit v0.10.2 From bdced7ef7838c1c4aebe9f295e44b7f0dcae2109 Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Sat, 10 Jan 2015 07:31:12 -0800 Subject: bridge: support for multiple vlans and vlan ranges in setlink and dellink requests This patch changes bridge IFLA_AF_SPEC netlink attribute parser to look for more than one IFLA_BRIDGE_VLAN_INFO attribute. This allows userspace to pack more than one vlan in the setlink msg. The dumps were already sending more than one vlan info in the getlink msg. This patch also adds bridge_vlan_info flags BRIDGE_VLAN_INFO_RANGE_BEGIN and BRIDGE_VLAN_INFO_RANGE_END to indicate start and end of vlan range This patch also deletes unused ifla_br_policy. Signed-off-by: Roopa Prabhu Signed-off-by: David S. Miller diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h index b03ee8f..eaaea62 100644 --- a/include/uapi/linux/if_bridge.h +++ b/include/uapi/linux/if_bridge.h @@ -125,6 +125,8 @@ enum { #define BRIDGE_VLAN_INFO_MASTER (1<<0) /* Operate on Bridge device as well */ #define BRIDGE_VLAN_INFO_PVID (1<<1) /* VLAN is PVID, ingress untagged */ #define BRIDGE_VLAN_INFO_UNTAGGED (1<<2) /* VLAN egresses untagged */ +#define BRIDGE_VLAN_INFO_RANGE_BEGIN (1<<3) /* VLAN is start of vlan range */ +#define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */ struct bridge_vlan_info { __u16 flags; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 9f5eb55..6f616a2 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -218,57 +218,89 @@ out: return err; } -static const struct nla_policy ifla_br_policy[IFLA_MAX+1] = { - [IFLA_BRIDGE_FLAGS] = { .type = NLA_U16 }, - [IFLA_BRIDGE_MODE] = { .type = NLA_U16 }, - [IFLA_BRIDGE_VLAN_INFO] = { .type = NLA_BINARY, - .len = sizeof(struct bridge_vlan_info), }, -}; +static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p, + int cmd, struct bridge_vlan_info *vinfo) +{ + int err = 0; + + switch (cmd) { + case RTM_SETLINK: + if (p) { + err = nbp_vlan_add(p, vinfo->vid, vinfo->flags); + if (err) + break; + + if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER) + err = br_vlan_add(p->br, vinfo->vid, + vinfo->flags); + } else { + err = br_vlan_add(br, vinfo->vid, vinfo->flags); + } + break; + + case RTM_DELLINK: + if (p) { + nbp_vlan_delete(p, vinfo->vid); + if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER) + br_vlan_delete(p->br, vinfo->vid); + } else { + br_vlan_delete(br, vinfo->vid); + } + break; + } + + return err; +} static int br_afspec(struct net_bridge *br, struct net_bridge_port *p, struct nlattr *af_spec, int cmd) { - struct nlattr *tb[IFLA_BRIDGE_MAX+1]; + struct bridge_vlan_info *vinfo_start = NULL; + struct bridge_vlan_info *vinfo = NULL; + struct nlattr *attr; int err = 0; + int rem; - err = nla_parse_nested(tb, IFLA_BRIDGE_MAX, af_spec, ifla_br_policy); - if (err) - return err; + nla_for_each_nested(attr, af_spec, rem) { + if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO) + continue; + if (nla_len(attr) != sizeof(struct bridge_vlan_info)) + return -EINVAL; + vinfo = nla_data(attr); + if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { + if (vinfo_start) + return -EINVAL; + vinfo_start = vinfo; + continue; + } + + if (vinfo_start) { + struct bridge_vlan_info tmp_vinfo; + int v; - if (tb[IFLA_BRIDGE_VLAN_INFO]) { - struct bridge_vlan_info *vinfo; + if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END)) + return -EINVAL; - vinfo = nla_data(tb[IFLA_BRIDGE_VLAN_INFO]); + if (vinfo->vid <= vinfo_start->vid) + return -EINVAL; - if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK) - return -EINVAL; + memcpy(&tmp_vinfo, vinfo_start, + sizeof(struct bridge_vlan_info)); - switch (cmd) { - case RTM_SETLINK: - if (p) { - err = nbp_vlan_add(p, vinfo->vid, vinfo->flags); + for (v = vinfo_start->vid; v <= vinfo->vid; v++) { + tmp_vinfo.vid = v; + err = br_vlan_info(br, p, cmd, &tmp_vinfo); if (err) break; - - if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER) - err = br_vlan_add(p->br, vinfo->vid, - vinfo->flags); - } else - err = br_vlan_add(br, vinfo->vid, vinfo->flags); - - break; - - case RTM_DELLINK: - if (p) { - nbp_vlan_delete(p, vinfo->vid); - if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER) - br_vlan_delete(p->br, vinfo->vid); - } else - br_vlan_delete(br, vinfo->vid); - break; + } + vinfo_start = NULL; + } else { + err = br_vlan_info(br, p, cmd, vinfo); } + if (err) + break; } return err; -- cgit v0.10.2 From 35a27cee321e7c4e7cba3550b2f48c2ca44d8a72 Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Sat, 10 Jan 2015 07:31:13 -0800 Subject: rtnetlink: new filter RTEXT_FILTER_BRVLAN_COMPRESSED This filter is same as RTEXT_FILTER_BRVLAN except that it tries to compress the consecutive vlans into ranges. This helps on systems with large number of configured vlans. Signed-off-by: Roopa Prabhu Signed-off-by: David S. Miller diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index d81f22d..a1d1859 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -636,6 +636,7 @@ struct tcamsg { /* New extended info filters for IFLA_EXT_MASK */ #define RTEXT_FILTER_VF (1 << 0) #define RTEXT_FILTER_BRVLAN (1 << 1) +#define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2) /* End of information exported to user level */ -- cgit v0.10.2 From 36cd0ffbab8a65f44ae13fb200bfb5a8f9ea68de Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Sat, 10 Jan 2015 07:31:14 -0800 Subject: bridge: new function to pack vlans into ranges during gets This patch adds new function to pack vlans into ranges whereever applicable using the flags BRIDGE_VLAN_INFO_RANGE_BEGIN and BRIDGE VLAN_INFO_RANGE_END Old vlan packing code is moved to a new function and continues to be called when filter_mask is RTEXT_FILTER_BRVLAN. Signed-off-by: Roopa Prabhu Signed-off-by: David S. Miller diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 6f616a2..0b03879 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -67,6 +67,118 @@ static int br_port_fill_attrs(struct sk_buff *skb, return 0; } +static int br_fill_ifvlaninfo_range(struct sk_buff *skb, u16 vid_start, + u16 vid_end, u16 flags) +{ + struct bridge_vlan_info vinfo; + + if ((vid_end - vid_start) > 0) { + /* add range to skb */ + vinfo.vid = vid_start; + vinfo.flags = flags | BRIDGE_VLAN_INFO_RANGE_BEGIN; + if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO, + sizeof(vinfo), &vinfo)) + goto nla_put_failure; + + vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN; + + vinfo.vid = vid_end; + vinfo.flags = flags | BRIDGE_VLAN_INFO_RANGE_END; + if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO, + sizeof(vinfo), &vinfo)) + goto nla_put_failure; + } else { + vinfo.vid = vid_start; + vinfo.flags = flags; + if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO, + sizeof(vinfo), &vinfo)) + goto nla_put_failure; + } + + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static int br_fill_ifvlaninfo_compressed(struct sk_buff *skb, + const struct net_port_vlans *pv) +{ + u16 vid_range_start = 0, vid_range_end = 0; + u16 vid_range_flags; + u16 pvid, vid, flags; + int err = 0; + + /* Pack IFLA_BRIDGE_VLAN_INFO's for every vlan + * and mark vlan info with begin and end flags + * if vlaninfo represents a range + */ + pvid = br_get_pvid(pv); + for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) { + flags = 0; + if (vid == pvid) + flags |= BRIDGE_VLAN_INFO_PVID; + + if (test_bit(vid, pv->untagged_bitmap)) + flags |= BRIDGE_VLAN_INFO_UNTAGGED; + + if (vid_range_start == 0) { + goto initvars; + } else if ((vid - vid_range_end) == 1 && + flags == vid_range_flags) { + vid_range_end = vid; + continue; + } else { + err = br_fill_ifvlaninfo_range(skb, vid_range_start, + vid_range_end, + vid_range_flags); + if (err) + return err; + } + +initvars: + vid_range_start = vid; + vid_range_end = vid; + vid_range_flags = flags; + } + + /* Call it once more to send any left over vlans */ + err = br_fill_ifvlaninfo_range(skb, vid_range_start, + vid_range_end, + vid_range_flags); + if (err) + return err; + + return 0; +} + +static int br_fill_ifvlaninfo(struct sk_buff *skb, + const struct net_port_vlans *pv) +{ + struct bridge_vlan_info vinfo; + u16 pvid, vid; + + pvid = br_get_pvid(pv); + for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) { + vinfo.vid = vid; + vinfo.flags = 0; + if (vid == pvid) + vinfo.flags |= BRIDGE_VLAN_INFO_PVID; + + if (test_bit(vid, pv->untagged_bitmap)) + vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED; + + if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO, + sizeof(vinfo), &vinfo)) + goto nla_put_failure; + } + + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + /* * Create one netlink message for one interface * Contains port and master info as well as carrier and bridge state. @@ -121,12 +233,11 @@ static int br_fill_ifinfo(struct sk_buff *skb, } /* Check if the VID information is requested */ - if (filter_mask & RTEXT_FILTER_BRVLAN) { - struct nlattr *af; + if ((filter_mask & RTEXT_FILTER_BRVLAN) || + (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)) { const struct net_port_vlans *pv; - struct bridge_vlan_info vinfo; - u16 vid; - u16 pvid; + struct nlattr *af; + int err; if (port) pv = nbp_get_vlan_info(port); @@ -140,21 +251,12 @@ static int br_fill_ifinfo(struct sk_buff *skb, if (!af) goto nla_put_failure; - pvid = br_get_pvid(pv); - for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) { - vinfo.vid = vid; - vinfo.flags = 0; - if (vid == pvid) - vinfo.flags |= BRIDGE_VLAN_INFO_PVID; - - if (test_bit(vid, pv->untagged_bitmap)) - vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED; - - if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO, - sizeof(vinfo), &vinfo)) - goto nla_put_failure; - } - + if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED) + err = br_fill_ifvlaninfo_compressed(skb, pv); + else + err = br_fill_ifvlaninfo(skb, pv); + if (err) + goto nla_put_failure; nla_nest_end(skb, af); } @@ -209,7 +311,8 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, int err = 0; struct net_bridge_port *port = br_port_get_rtnl(dev); - if (!port && !(filter_mask & RTEXT_FILTER_BRVLAN)) + if (!port && !(filter_mask & RTEXT_FILTER_BRVLAN) && + !(filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)) goto out; err = br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, NLM_F_MULTI, -- cgit v0.10.2 From 83400b990cd78a774a932223e83a19b1f816f24e Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 11 Jan 2015 14:03:35 +0100 Subject: net: xfrm: xfrm_algo: Remove unused function Remove the function aead_entries() that is not used anywhere. This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: David S. Miller diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c index debe733..12e82a5 100644 --- a/net/xfrm/xfrm_algo.c +++ b/net/xfrm/xfrm_algo.c @@ -561,11 +561,6 @@ static struct xfrm_algo_desc calg_list[] = { }, }; -static inline int aead_entries(void) -{ - return ARRAY_SIZE(aead_list); -} - static inline int aalg_entries(void) { return ARRAY_SIZE(aalg_list); -- cgit v0.10.2 From ddcde70cbf2f837a083012965eaec6d213b87c48 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 11 Jan 2015 15:08:46 +0100 Subject: net: sched: sch_teql: Remove unused function Remove the function teql_neigh_release() that is not used anywhere. This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: David S. Miller diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 6ada423..4899d4a 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -122,13 +122,6 @@ teql_peek(struct Qdisc *sch) return NULL; } -static inline void -teql_neigh_release(struct neighbour *n) -{ - if (n) - neigh_release(n); -} - static void teql_reset(struct Qdisc *sch) { -- cgit v0.10.2 From b1e8bc617d30fc9360a58889f21a0d0ce982cf04 Mon Sep 17 00:00:00 2001 From: David Decotigny Date: Sun, 11 Jan 2015 11:42:37 -0800 Subject: net: bnx2x: avoid macro redefinition Signed-off-by: David Decotigny Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 792ba72..756053c 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1138,12 +1138,8 @@ struct bnx2x_port { u32 link_config[LINK_CONFIG_SIZE]; u32 supported[LINK_CONFIG_SIZE]; -/* link settings - missing defines */ -#define SUPPORTED_2500baseX_Full (1 << 15) u32 advertising[LINK_CONFIG_SIZE]; -/* link settings - missing defines */ -#define ADVERTISED_2500baseX_Full (1 << 15) u32 phy_addr; -- cgit v0.10.2 From e350a96ec9e6c74f06235ded93ef3155f71c6c6b Mon Sep 17 00:00:00 2001 From: Kenneth Williams Date: Sun, 11 Jan 2015 16:43:54 -0800 Subject: team: Remove dead code The deleted lines are called from a function which is called: 1) Only through __team_options_register via team_options_register and 2) Only during initialization / mode initialization when there are no ports attached. Therefore the ports list is guarenteed to be empty and this code will never be executed. Signed-off-by: Kenneth Williams Acked-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 93e2242..43bcfff 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -176,7 +176,6 @@ static int __team_option_inst_add(struct team *team, struct team_option *option, static int __team_option_inst_add_option(struct team *team, struct team_option *option) { - struct team_port *port; int err; if (!option->per_port) { @@ -184,12 +183,6 @@ static int __team_option_inst_add_option(struct team *team, if (err) goto inst_del_option; } - - list_for_each_entry(port, &team->port_list, list) { - err = __team_option_inst_add(team, option, port); - if (err) - goto inst_del_option; - } return 0; inst_del_option: -- cgit v0.10.2 From 1059590254fa9dce9cafc4f07d1103dbec415e76 Mon Sep 17 00:00:00 2001 From: Pankaj Gupta Date: Mon, 12 Jan 2015 11:41:28 +0530 Subject: net: allow large number of rx queues netif_alloc_rx_queues() uses kcalloc() to allocate memory for "struct netdev_queue *_rx" array. If we are doing large rx queue allocation kcalloc() might fail, so this patch does a fallback to vzalloc(). Similar implementation is done for tx queue allocation in netif_alloc_netdev_queues(). We avoid failure of high order memory allocation with the help of vzalloc(), this allows us to do large rx and tx queue allocation which in turn helps us to increase the number of queues in tun. As vmalloc() adds overhead on a critical network path, __GFP_REPEAT flag is used with kzalloc() to do this fallback only when really needed. Signed-off-by: Pankaj Gupta Reviewed-by: Michael S. Tsirkin Reviewed-by: David Gibson Signed-off-by: David S. Miller diff --git a/net/core/dev.c b/net/core/dev.c index 683d493..8054561 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6172,13 +6172,16 @@ static int netif_alloc_rx_queues(struct net_device *dev) { unsigned int i, count = dev->num_rx_queues; struct netdev_rx_queue *rx; + size_t sz = count * sizeof(*rx); BUG_ON(count < 1); - rx = kcalloc(count, sizeof(struct netdev_rx_queue), GFP_KERNEL); - if (!rx) - return -ENOMEM; - + rx = kzalloc(sz, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT); + if (!rx) { + rx = vzalloc(sz); + if (!rx) + return -ENOMEM; + } dev->_rx = rx; for (i = 0; i < count; i++) @@ -6808,7 +6811,7 @@ void free_netdev(struct net_device *dev) netif_free_tx_queues(dev); #ifdef CONFIG_SYSFS - kfree(dev->_rx); + kvfree(dev->_rx); #endif kfree(rcu_dereference_protected(dev->ingress_queue, 1)); -- cgit v0.10.2 From baf71c5c1f80d82e92924050a60b5baaf97e3094 Mon Sep 17 00:00:00 2001 From: Pankaj Gupta Date: Mon, 12 Jan 2015 11:41:29 +0530 Subject: tuntap: Increase the number of queues in tun. Networking under kvm works best if we allocate a per-vCPU RX and TX queue in a virtual NIC. This requires a per-vCPU queue on the host side. It is now safe to increase the maximum number of queues. Preceding patch: 'net: allow large number of rx queues' made sure this won't cause failures due to high order memory allocations. Increase it to 256: this is the max number of vCPUs KVM supports. Size of tun_struct changes from 8512 to 10496 after this patch. This keeps pages allocated for tun_struct before and after the patch to 3. Signed-off-by: Pankaj Gupta Reviewed-by: David Gibson Signed-off-by: David S. Miller diff --git a/drivers/net/tun.c b/drivers/net/tun.c index c0df872..74fdf11 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -124,10 +124,9 @@ struct tap_filter { unsigned char addr[FLT_EXACT_COUNT][ETH_ALEN]; }; -/* DEFAULT_MAX_NUM_RSS_QUEUES were chosen to let the rx/tx queues allocated for - * the netdevice to be fit in one page. So we can make sure the success of - * memory allocation. TODO: increase the limit. */ -#define MAX_TAP_QUEUES DEFAULT_MAX_NUM_RSS_QUEUES +/* MAX_TAP_QUEUES 256 is chosen to allow rx/tx queues to be equal + * to max number of VCPUs in guest. */ +#define MAX_TAP_QUEUES 256 #define MAX_TAP_FLOWS 4096 #define TUN_FLOW_EXPIRE (3 * HZ) -- cgit v0.10.2 From ad1a62227f2e3d5eb4eb0b61a2d9005369bbef45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Fri, 9 Jan 2015 11:07:49 +0100 Subject: drm/radeon: don't print error on -ERESTARTSYS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian König Signed-off-by: Alex Deucher diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index a46f737..d0b4f7d 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -576,7 +576,7 @@ error_unreserve: error_free: drm_free_large(vm_bos); - if (r) + if (r && r != -ERESTARTSYS) DRM_ERROR("Couldn't update BO_VA (%d)\n", r); } -- cgit v0.10.2 From 5615f890bc6babdc2998dec62f3552326d06eb7b Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 12 Jan 2015 17:15:12 -0500 Subject: drm/radeon: add si dpm quirk list This adds a quirks list to fix stability problems with certain SI boards. bug: https://bugs.freedesktop.org/show_bug.cgi?id=76490 Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index 32e354b..eff8a64 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -2908,6 +2908,22 @@ static int si_init_smc_spll_table(struct radeon_device *rdev) return ret; } +struct si_dpm_quirk { + u32 chip_vendor; + u32 chip_device; + u32 subsys_vendor; + u32 subsys_device; + u32 max_sclk; + u32 max_mclk; +}; + +/* cards with dpm stability problems */ +static struct si_dpm_quirk si_dpm_quirk_list[] = { + /* PITCAIRN - https://bugs.freedesktop.org/show_bug.cgi?id=76490 */ + { PCI_VENDOR_ID_ATI, 0x6810, 0x1462, 0x3036, 0, 120000 }, + { 0, 0, 0, 0 }, +}; + static void si_apply_state_adjust_rules(struct radeon_device *rdev, struct radeon_ps *rps) { @@ -2918,7 +2934,22 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, u32 mclk, sclk; u16 vddc, vddci; u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc; + u32 max_sclk = 0, max_mclk = 0; int i; + struct si_dpm_quirk *p = si_dpm_quirk_list; + + /* Apply dpm quirks */ + while (p && p->chip_device != 0) { + if (rdev->pdev->vendor == p->chip_vendor && + rdev->pdev->device == p->chip_device && + rdev->pdev->subsystem_vendor == p->subsys_vendor && + rdev->pdev->subsystem_device == p->subsys_device) { + max_sclk = p->max_sclk; + max_mclk = p->max_mclk; + break; + } + ++p; + } if ((rdev->pm.dpm.new_active_crtc_count > 1) || ni_dpm_vblank_too_short(rdev)) @@ -2972,6 +3003,14 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, if (ps->performance_levels[i].mclk > max_mclk_vddc) ps->performance_levels[i].mclk = max_mclk_vddc; } + if (max_mclk) { + if (ps->performance_levels[i].mclk > max_mclk) + ps->performance_levels[i].mclk = max_mclk; + } + if (max_sclk) { + if (ps->performance_levels[i].sclk > max_sclk) + ps->performance_levels[i].sclk = max_sclk; + } } /* XXX validate the min clocks required for display */ -- cgit v0.10.2 From 9b3b43d8d1300c36ba5945c12fd505409eaf4807 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Wed, 31 Dec 2014 18:34:56 +0200 Subject: iwlwifi: mvm: set the tx cmd tid for BAR frame correctly BAR tx cmd tid was set to non qos (8). This is wrong as BAR should be sent with the tid of the BA session. This led to a corruption in the firmware. The visible effect of this from the driver side is the BA notification that comes back after the BAR. It was botched and led to the WARNING below. ------------[ cut here ]------------ WARNING: CPU: 2 PID: 17707 at /home/tester/workspace_hostap/iwlwifi/drivers/net/wireless/iwlwifi/mvm/tx.c:976 iwl_mvm_rx_ba_notif+0x4ba/0x4d0 [iwlmvm]() Q 4500, tid 8, flow 65535 Modules linked in: iwlmvm(O) mac80211(O) iwlwifi(O) cfg80211(O) compat(O) netconsole configfs ctr ccm arc4 autofs4 microcode bnep rfcomm snd_hda_codec_hdmi snd_hda_codec_idt snd_hda_codec_generic snd_hda_intel joydev snd_hda_codec uvcvideo videobuf2_core snd_hwdep videodev snd_pcm videobuf2_vmalloc videobuf2_memops i915 snd_seq_midi snd_rawmidi snd_seq_midi_event snd_seq snd_timer snd_seq_device drm_kms_helper dell_wmi dell_laptop drm btusb bluetooth snd psmouse i2c_algo_bit sparse_keymap wmi soundcore 6lowpan_iphc dcdbas serio_raw video lpc_ich ppdev mac_hid parport_pc nfsd nfs_acl auth_rpcgss nfs fscache binfmt_misc lockd sunrpc lp parport msdos sdhci_pci sdhci mmc_core ahci libahci e1000e ptp pps_core [last unloaded: compat] CPU: 2 PID: 17707 Comm: irq/46-iwlwifi Tainted: G W O 3.14.17-patched #4 Hardware name: Dell Inc. Latitude E6430/0CPWYR, BIOS A09 12/13/2012 00000000 00000000 ebd49d6c c1616221 f985dbdc ebd49d9c c1044e44 f9861df4 ebd49dc8 0000452b f985dbdc 000003d0 f98395da f98395da ebd49f10 eaf3d8a4 0000ffff ebd49db4 c1044f03 00000009 ebd49dac f9861df4 ebd49dc8 ebd49e64 Call Trace: [] dump_stack+0x41/0x52 [] warn_slowpath_common+0x84/0xa0 [] ? iwl_mvm_rx_ba_notif+0x4ba/0x4d0 [iwlmvm] [] ? iwl_mvm_rx_ba_notif+0x4ba/0x4d0 [iwlmvm] [] warn_slowpath_fmt+0x33/0x40 [] iwl_mvm_rx_ba_notif+0x4ba/0x4d0 [iwlmvm] [] ? ring_buffer_unlock_commit+0xa2/0xd0 [] ? trace_buffer_unlock_commit+0x37/0x50 [] ? iwl_tm_mvm_send_rx+0x53/0x90 [iwlmvm] [] iwl_mvm_rx_dispatch+0x108/0x130 [iwlmvm] [] iwl_pcie_irq_handler+0xf17/0x15b0 [iwlwifi] [] irq_thread_fn+0x21/0x50 [] irq_thread+0xec/0x110 [] ? irq_thread_dtor+0xb0/0xb0 [] ? irq_finalize_oneshot.part.34+0xc0/0xc0 [] ? wake_threads_waitq+0x40/0x40 [] kthread+0x9b/0xb0 [] ret_from_kernel_thread+0x1b/0x28 [] ? flush_kthread_worker+0x90/0x90 ---[ end trace 5e0f67374816db17 ]--- Signed-off-by: Eyal Shapira Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 4333306..c59d075 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -90,8 +90,6 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, if (ieee80211_is_probe_resp(fc)) tx_flags |= TX_CMD_FLG_TSF; - else if (ieee80211_is_back_req(fc)) - tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR; if (ieee80211_has_morefrags(fc)) tx_flags |= TX_CMD_FLG_MORE_FRAG; @@ -100,6 +98,15 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, u8 *qc = ieee80211_get_qos_ctl(hdr); tx_cmd->tid_tspec = qc[0] & 0xf; tx_flags &= ~TX_CMD_FLG_SEQ_CTL; + } else if (ieee80211_is_back_req(fc)) { + struct ieee80211_bar *bar = (void *)skb->data; + u16 control = le16_to_cpu(bar->control); + + tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR; + tx_cmd->tid_tspec = (control & + IEEE80211_BAR_CTRL_TID_INFO_MASK) >> + IEEE80211_BAR_CTRL_TID_INFO_SHIFT; + WARN_ON_ONCE(tx_cmd->tid_tspec >= IWL_MAX_TID_COUNT); } else { tx_cmd->tid_tspec = IWL_TID_NON_QOS; if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) -- cgit v0.10.2 From e69229f2eb9d5f44981840eaf014fcb5ed60faac Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 15 Aug 2014 10:56:09 +0200 Subject: m68k/atari: Remove obsolete keyboard_tasklet scheduling If CONFIG_VT=n: arch/m68k/atari/built-in.o: In function `atari_keyboard_interrupt': atakeyb.c:(.text+0x1846): undefined reference to `keyboard_tasklet' atakeyb.c:(.text+0x1852): undefined reference to `keyboard_tasklet' I think the keyboard_tasklet scheduling is no longer needed, as I believe it's handled by drivers/tty/vt/keyboard.c based on events received from the input subsystem. So just remove it. Signed-off-by: Geert Uytterhoeven Tested-by: Michael Schmitz diff --git a/arch/m68k/atari/atakeyb.c b/arch/m68k/atari/atakeyb.c index ad91969..7bea01c 100644 --- a/arch/m68k/atari/atakeyb.c +++ b/arch/m68k/atari/atakeyb.c @@ -170,7 +170,6 @@ repeat: if (acia_stat & ACIA_RDRF) { /* received a character */ scancode = acia.key_data; /* get it or reset the ACIA, I'll get it! */ - tasklet_schedule(&keyboard_tasklet); interpret_scancode: switch (kb_state.state) { case KEYBOARD: -- cgit v0.10.2 From 3566c964767811f3965979fa9598027b6550026d Mon Sep 17 00:00:00 2001 From: alex chen Date: Mon, 12 Jan 2015 19:01:03 +0800 Subject: GFS2: fix sprintf format specifier Sprintf format specifier "%d" and "%u" are mixed up in gfs2_recovery_done() and freeze_show(). So correct them. Signed-off-by: Alex Chen Reviewed-by: Joseph Qi Signed-off-by: Steven Whitehouse diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c index 573bd3b..1b64577 100644 --- a/fs/gfs2/recovery.c +++ b/fs/gfs2/recovery.c @@ -439,7 +439,7 @@ static void gfs2_recovery_done(struct gfs2_sbd *sdp, unsigned int jid, ls->ls_recover_jid_done = jid; ls->ls_recover_jid_status = message; - sprintf(env_jid, "JID=%d", jid); + sprintf(env_jid, "JID=%u", jid); sprintf(env_status, "RECOVERY=%s", message == LM_RD_SUCCESS ? "Done" : "Failed"); kobject_uevent_env(&sdp->sd_kobj, KOBJ_CHANGE, envp); diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index 3ab566b..ae8e881 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -96,7 +96,7 @@ static ssize_t freeze_show(struct gfs2_sbd *sdp, char *buf) struct super_block *sb = sdp->sd_vfs; int frozen = (sb->s_writers.frozen == SB_UNFROZEN) ? 0 : 1; - return snprintf(buf, PAGE_SIZE, "%u\n", frozen); + return snprintf(buf, PAGE_SIZE, "%d\n", frozen); } static ssize_t freeze_store(struct gfs2_sbd *sdp, const char *buf, size_t len) -- cgit v0.10.2 From a8fc91afab2f6bc50ce5b5be86dd7fc4af159c70 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sun, 4 Jan 2015 15:04:34 +0100 Subject: atyfb: mark DMI system id table as __initconst We can mark the DMI system id table as __initconst by using a helper variable that'll tell us if we need to unregister the reboot notifier in atyfb_exit() instead of matching the DMI system id again. This frees up ~680 bytes of runtime memory, the DMI table occupies. Signed-off-by: Mathias Krause Signed-off-by: Tomi Valkeinen diff --git a/drivers/video/fbdev/aty/atyfb_base.c b/drivers/video/fbdev/aty/atyfb_base.c index 37ec09b..8789e48 100644 --- a/drivers/video/fbdev/aty/atyfb_base.c +++ b/drivers/video/fbdev/aty/atyfb_base.c @@ -3948,7 +3948,7 @@ static struct notifier_block atyfb_reboot_notifier = { .notifier_call = atyfb_reboot_notify, }; -static const struct dmi_system_id atyfb_reboot_ids[] = { +static const struct dmi_system_id atyfb_reboot_ids[] __initconst = { { .ident = "HP OmniBook 500", .matches = { @@ -3960,6 +3960,7 @@ static const struct dmi_system_id atyfb_reboot_ids[] = { { } }; +static bool registered_notifier = false; static int __init atyfb_init(void) { @@ -3982,15 +3983,17 @@ static int __init atyfb_init(void) if (err1 && err2) return -ENODEV; - if (dmi_check_system(atyfb_reboot_ids)) + if (dmi_check_system(atyfb_reboot_ids)) { register_reboot_notifier(&atyfb_reboot_notifier); + registered_notifier = true; + } return 0; } static void __exit atyfb_exit(void) { - if (dmi_check_system(atyfb_reboot_ids)) + if (registered_notifier) unregister_reboot_notifier(&atyfb_reboot_notifier); #ifdef CONFIG_PCI -- cgit v0.10.2 From 77a7f1837a9c6f9cfb6ce0d5b166e1f1f7b117b3 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Wed, 31 Dec 2014 12:25:38 +0530 Subject: fbdev: geocode: remove unneeded NULL check the check for info is not required as we are checking it immediately after gx1fb_init_fbinfo() and returnig -ENOMEM if it is NULL. Signed-off-by: Sudip Mukherjee Signed-off-by: Tomi Valkeinen diff --git a/drivers/video/fbdev/geode/gx1fb_core.c b/drivers/video/fbdev/geode/gx1fb_core.c index 2794ba1..9bee874 100644 --- a/drivers/video/fbdev/geode/gx1fb_core.c +++ b/drivers/video/fbdev/geode/gx1fb_core.c @@ -374,10 +374,8 @@ static int gx1fb_probe(struct pci_dev *pdev, const struct pci_device_id *id) release_mem_region(gx1_gx_base() + 0x8300, 0x100); } - if (info) { - fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); - } + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); return ret; } -- cgit v0.10.2 From 0e8787313a00d4202aad249d0734a30bbde1485b Mon Sep 17 00:00:00 2001 From: Marek Belisko Date: Wed, 3 Dec 2014 22:33:20 +0100 Subject: video: omapdss: Add opa362 driver opa362 is amplifier for video and can be connected to the tvout pads of the OMAP3. It has one gpio control for enable/disable of the output (high impedance). Signed-off-by: H. Nikolaus Schaller Signed-off-by: Marek Belisko Signed-off-by: Tomi Valkeinen diff --git a/drivers/video/fbdev/omap2/displays-new/Kconfig b/drivers/video/fbdev/omap2/displays-new/Kconfig index e6cfc38..5747101 100644 --- a/drivers/video/fbdev/omap2/displays-new/Kconfig +++ b/drivers/video/fbdev/omap2/displays-new/Kconfig @@ -1,6 +1,12 @@ menu "OMAP Display Device Drivers (new device model)" depends on OMAP2_DSS +config DISPLAY_ENCODER_OPA362 + tristate "OPA362 external analog amplifier" + help + Driver for OPA362 external analog TV amplifier controlled + through a GPIO. + config DISPLAY_ENCODER_TFP410 tristate "TFP410 DPI to DVI Encoder" help diff --git a/drivers/video/fbdev/omap2/displays-new/Makefile b/drivers/video/fbdev/omap2/displays-new/Makefile index 0323a8a..9aa176b 100644 --- a/drivers/video/fbdev/omap2/displays-new/Makefile +++ b/drivers/video/fbdev/omap2/displays-new/Makefile @@ -1,3 +1,4 @@ +obj-$(CONFIG_DISPLAY_ENCODER_OPA362) += encoder-opa362.o obj-$(CONFIG_DISPLAY_ENCODER_TFP410) += encoder-tfp410.o obj-$(CONFIG_DISPLAY_ENCODER_TPD12S015) += encoder-tpd12s015.o obj-$(CONFIG_DISPLAY_CONNECTOR_DVI) += connector-dvi.o diff --git a/drivers/video/fbdev/omap2/displays-new/encoder-opa362.c b/drivers/video/fbdev/omap2/displays-new/encoder-opa362.c new file mode 100644 index 0000000..84a6b33 --- /dev/null +++ b/drivers/video/fbdev/omap2/displays-new/encoder-opa362.c @@ -0,0 +1,285 @@ +/* + * OPA362 analog video amplifier with output/power control + * + * Copyright (C) 2014 Golden Delicious Computers + * Author: H. Nikolaus Schaller + * + * based on encoder-tfp410 + * + * Copyright (C) 2013 Texas Instruments + * Author: Tomi Valkeinen + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include